implement StagedAlias and Overlayable

This commit is contained in:
REAndroid 2023-02-11 13:31:44 -05:00
parent 611b78a755
commit cf7befd92a
10 changed files with 350 additions and 40 deletions

View File

@ -34,6 +34,19 @@
this.count=count; this.count=count;
this.count.setBlockLoad(this); this.count.setBlockLoad(this);
} }
public boolean contains(StagedAliasEntry aliasEntry){
StagedAliasEntry[] childes=getChildes();
if(childes==null){
return false;
}
for(int i=0;i<childes.length;i++){
StagedAliasEntry entry=childes[i];
if(entry.isEqual(aliasEntry)){
return true;
}
}
return false;
}
public StagedAliasEntry searchByStagedResId(int stagedResId){ public StagedAliasEntry searchByStagedResId(int stagedResId){
StagedAliasEntry[] childes=getChildes(); StagedAliasEntry[] childes=getChildes();
if(childes==null){ if(childes==null){

View File

@ -15,36 +15,53 @@
*/ */
package com.reandroid.arsc.chunk; package com.reandroid.arsc.chunk;
import com.reandroid.arsc.base.Block; import com.reandroid.arsc.container.BlockList;
import com.reandroid.arsc.header.HeaderBlock; import com.reandroid.arsc.header.HeaderBlock;
import com.reandroid.arsc.header.OverlayableHeader; import com.reandroid.arsc.header.OverlayableHeader;
import com.reandroid.arsc.io.BlockLoad;
import com.reandroid.arsc.io.BlockReader; import com.reandroid.arsc.io.BlockReader;
import com.reandroid.arsc.item.ByteArray; import com.reandroid.arsc.item.ByteArray;
import com.reandroid.json.JSONArray;
import com.reandroid.json.JSONConvert;
import com.reandroid.json.JSONObject;
import java.io.IOException; import java.io.IOException;
import java.util.List;
/** /**
* Replica of struct "ResTable_overlayable_header" as on AOSP androidfw/ResourceTypes.h * Replica of struct "ResTable_overlayable_header" as on AOSP androidfw/ResourceTypes.h
* We didn't test this class with resource table, if someone found a resource/apk please * We didn't test this class with resource table, if someone found a resource/apk please
* create issue on https://github.com/REAndroid/ARSCLib * create issue on https://github.com/REAndroid/ARSCLib
* */ * */
public class Overlayable extends Chunk<OverlayableHeader> implements BlockLoad { public class Overlayable extends Chunk<OverlayableHeader> implements JSONConvert<JSONObject> {
/** private final BlockList<OverlayablePolicy> policyList;
* @link body private final ByteArray extraBytes;
* As on AOSP there is only a description of header struct but no mention about
* chunk-content/body, thus we will use empty body byte array to avoid parse error
* */
private final ByteArray body;
public Overlayable() { public Overlayable() {
super(new OverlayableHeader(), 1); super(new OverlayableHeader(), 2);
this.body = new ByteArray(); this.policyList = new BlockList<>();
addChild(this.body); this.extraBytes = new ByteArray();
getHeaderBlock().getActor().setBlockLoad(this); addChild(this.policyList);
addChild(this.extraBytes);
} }
public ByteArray getBody() {
return body; public OverlayablePolicy get(int flags){
for(OverlayablePolicy policy:listOverlayablePolicies()){
if(flags==policy.getFlags()){
return policy;
}
}
return null;
} }
public void addOverlayablePolicy(OverlayablePolicy overlayablePolicy){
this.policyList.add(overlayablePolicy);
}
public List<OverlayablePolicy> listOverlayablePolicies() {
return policyList.getChildes();
}
public ByteArray getExtraBytes() {
return extraBytes;
}
public String getName(){ public String getName(){
return getHeaderBlock().getName().get(); return getHeaderBlock().getName().get();
} }
@ -57,22 +74,95 @@
public void setActor(String str){ public void setActor(String str){
getHeaderBlock().getActor().set(str); getHeaderBlock().getActor().set(str);
} }
@Override
public void onBlockLoaded(BlockReader reader, Block sender) throws IOException {
if(sender==getHeaderBlock().getActor()){
HeaderBlock header = getHeaderBlock();
int bodySize=header.getChunkSize()-header.getHeaderSize();
this.body.setSize(bodySize);
}
}
@Override @Override
protected void onChunkRefreshed() { protected void onChunkRefreshed() {
} }
@Override
public void onReadBytes(BlockReader reader) throws IOException {
HeaderBlock headerBlock = reader.readHeaderBlock();
checkInvalidChunk(headerBlock);
int size = headerBlock.getChunkSize();
BlockReader chunkReader = reader.create(size);
headerBlock = getHeaderBlock();
headerBlock.readBytes(chunkReader);
readOverlayablePlolicies(chunkReader);
readExtraBytes(chunkReader);
reader.offset(size);
chunkReader.close();
onChunkLoaded();
}
private void readOverlayablePlolicies(BlockReader reader) throws IOException {
HeaderBlock headerBlock = reader.readHeaderBlock();
BlockList<OverlayablePolicy> policyList = this.policyList;
while (headerBlock!=null && headerBlock.getChunkType()==ChunkType.OVERLAYABLE_POLICY){
OverlayablePolicy policy = new OverlayablePolicy();
policyList.add(policy);
policy.readBytes(reader);
headerBlock = reader.readHeaderBlock();
}
}
private void readExtraBytes(BlockReader reader) throws IOException {
int remaining = reader.available();
this.extraBytes.setSize(remaining);
this.extraBytes.readBytes(reader);
}
@Override
public JSONObject toJson() {
JSONObject jsonObject = new JSONObject();
jsonObject.put(NAME_name, getName());
jsonObject.put(NAME_actor, getActor());
JSONArray jsonArray = new JSONArray();
for(OverlayablePolicy policy:listOverlayablePolicies()){
jsonArray.put(policy);
}
return jsonObject;
}
@Override
public void fromJson(JSONObject json) {
setName(json.getString(NAME_name));
setActor(json.getString(NAME_actor));
JSONArray jsonArray = json.getJSONArray(NAME_policies);
int length = jsonArray.length();
BlockList<OverlayablePolicy> policyList = this.policyList;
for(int i=0;i<length;i++){
OverlayablePolicy policy = new OverlayablePolicy();
policyList.add(policy);
policy.fromJson(jsonArray.getJSONObject(i));
}
}
public void merge(Overlayable overlayable){
if(overlayable==null||overlayable==this){
return;
}
setName(overlayable.getName());
setActor(overlayable.getActor());
for(OverlayablePolicy policy:overlayable.listOverlayablePolicies()){
OverlayablePolicy exist = get(policy.getFlags());
if(exist==null){
exist = new OverlayablePolicy();
addOverlayablePolicy(exist);
}
exist.merge(policy);
}
}
@Override @Override
public String toString(){ public String toString(){
return getClass().getSimpleName()+ return getClass().getSimpleName()+
": name='"+getName() ": name='" + getName()
+"', actor='"+getActor() +"', actor='" + getActor()
+"', body-size="+getBody().size(); +"', policies=" + policyList.size()
+"', extra=" + getExtraBytes().size();
} }
public static final String NAME_name = "name";
public static final String NAME_actor = "actor";
public static final String NAME_policies = "policies";
} }

View File

@ -21,6 +21,9 @@
import com.reandroid.arsc.io.BlockReader; import com.reandroid.arsc.io.BlockReader;
import com.reandroid.arsc.item.IntegerArray; import com.reandroid.arsc.item.IntegerArray;
import com.reandroid.arsc.item.IntegerItem; import com.reandroid.arsc.item.IntegerItem;
import com.reandroid.json.JSONArray;
import com.reandroid.json.JSONConvert;
import com.reandroid.json.JSONObject;
import java.io.IOException; import java.io.IOException;
import java.util.Collection; import java.util.Collection;
@ -30,7 +33,8 @@
* We didn't test this class with resource table, if someone found a resource/apk please * We didn't test this class with resource table, if someone found a resource/apk please
* create issue on https://github.com/REAndroid/ARSCLib * create issue on https://github.com/REAndroid/ARSCLib
* */ * */
public class OverlayablePolicy extends Chunk<OverlayablePolicyHeader> implements BlockLoad { public class OverlayablePolicy extends Chunk<OverlayablePolicyHeader> implements BlockLoad,
JSONConvert<JSONObject> {
private final IntegerArray tableRefArray; private final IntegerArray tableRefArray;
public OverlayablePolicy(){ public OverlayablePolicy(){
super(new OverlayablePolicyHeader(), 1); super(new OverlayablePolicyHeader(), 1);
@ -81,6 +85,43 @@
this.tableRefArray.setSize(entryCount.get()); this.tableRefArray.setSize(entryCount.get());
} }
} }
@Override
public JSONObject toJson() {
JSONObject jsonObject = new JSONObject();
jsonObject.put(NAME_flags, getFlags());
JSONArray jsonArray = new JSONArray();
for(Integer reference:listTableReferences()){
jsonArray.put(reference);
}
jsonObject.put(NAME_references, jsonArray);
return jsonObject;
}
@Override
public void fromJson(JSONObject json) {
setFlags(json.getInt(NAME_flags));
JSONArray jsonArray = json.getJSONArray(NAME_references);
IntegerArray integerArray = getTableRefArray();
int length = jsonArray.length();
integerArray.setSize(length);
for(int i=0;i<length;i++){
integerArray.put(i, jsonArray.getInt(i));
}
}
public void merge(OverlayablePolicy policy){
if(policy==null||policy==this){
return;
}
setFlags(policy.getFlags());
IntegerArray exist = getTableRefArray();
IntegerArray coming = policy.getTableRefArray();
for(int reference: coming.toArray()){
if(!exist.contains(reference)){
exist.add(reference);
}
}
}
@Override @Override
public String toString(){ public String toString(){
return getClass().getSimpleName()+ return getClass().getSimpleName()+
@ -234,4 +275,7 @@
return null; return null;
} }
} }
public static final String NAME_flags = "flags";
public static final String NAME_references = "references";
} }

View File

@ -25,6 +25,8 @@ package com.reandroid.arsc.chunk;
import com.reandroid.arsc.group.EntryGroup; import com.reandroid.arsc.group.EntryGroup;
import com.reandroid.arsc.header.PackageHeader; import com.reandroid.arsc.header.PackageHeader;
import com.reandroid.arsc.item.ReferenceItem; import com.reandroid.arsc.item.ReferenceItem;
import com.reandroid.arsc.list.OverlayableList;
import com.reandroid.arsc.list.StagedAliasList;
import com.reandroid.arsc.pool.SpecStringPool; import com.reandroid.arsc.pool.SpecStringPool;
import com.reandroid.arsc.pool.TableStringPool; import com.reandroid.arsc.pool.TableStringPool;
import com.reandroid.arsc.pool.TypeStringPool; import com.reandroid.arsc.pool.TypeStringPool;
@ -32,6 +34,7 @@ package com.reandroid.arsc.chunk;
import com.reandroid.arsc.value.LibraryInfo; import com.reandroid.arsc.value.LibraryInfo;
import com.reandroid.arsc.value.ResConfig; import com.reandroid.arsc.value.ResConfig;
import com.reandroid.arsc.value.StagedAliasEntry; import com.reandroid.arsc.value.StagedAliasEntry;
import com.reandroid.json.JSONArray;
import com.reandroid.json.JSONConvert; import com.reandroid.json.JSONConvert;
import com.reandroid.json.JSONObject; import com.reandroid.json.JSONObject;
@ -108,10 +111,10 @@ package com.reandroid.arsc.chunk;
public List<StagedAlias> listStagedAlias(){ public List<StagedAlias> listStagedAlias(){
return getStagedAliasList().getChildes(); return getStagedAliasList().getChildes();
} }
public BlockList<StagedAlias> getStagedAliasList(){ public StagedAliasList getStagedAliasList(){
return mBody.getStagedAliasList(); return mBody.getStagedAliasList();
} }
public BlockList<Overlayable> getOverlayableList(){ public OverlayableList getOverlayableList(){
return mBody.getOverlayableList(); return mBody.getOverlayableList();
} }
public BlockList<OverlayablePolicy> getOverlayablePolicyList(){ public BlockList<OverlayablePolicy> getOverlayablePolicyList(){
@ -341,6 +344,10 @@ package com.reandroid.arsc.chunk;
jsonObject.put(NAME_staged_aliases, jsonObject.put(NAME_staged_aliases,
stagedAlias.getStagedAliasEntryArray().toJson()); stagedAlias.getStagedAliasEntryArray().toJson());
} }
JSONArray jsonArray = getOverlayableList().toJson();
if(jsonArray!=null){
jsonObject.put(NAME_overlaybles, jsonArray);
}
return jsonObject; return jsonObject;
} }
@Override @Override
@ -356,6 +363,9 @@ package com.reandroid.arsc.chunk;
.fromJson(json.getJSONArray(NAME_staged_aliases)); .fromJson(json.getJSONArray(NAME_staged_aliases));
getStagedAliasList().add(stagedAlias); getStagedAliasList().add(stagedAlias);
} }
if(json.has(NAME_overlaybles)){
getOverlayableList().fromJson(json.getJSONArray(NAME_overlaybles));
}
} }
public void merge(PackageBlock packageBlock){ public void merge(PackageBlock packageBlock){
if(packageBlock==null||packageBlock==this){ if(packageBlock==null||packageBlock==this){
@ -368,6 +378,8 @@ package com.reandroid.arsc.chunk;
setName(packageBlock.getName()); setName(packageBlock.getName());
getLibraryBlock().merge(packageBlock.getLibraryBlock()); getLibraryBlock().merge(packageBlock.getLibraryBlock());
getSpecTypePairArray().merge(packageBlock.getSpecTypePairArray()); getSpecTypePairArray().merge(packageBlock.getSpecTypePairArray());
getOverlayableList().merge(packageBlock.getOverlayableList());
getStagedAliasList().merge(packageBlock.getStagedAliasList());
} }
/** /**
* It is allowed to have duplicate type name therefore it is not recommend to use this. * It is allowed to have duplicate type name therefore it is not recommend to use this.
@ -400,7 +412,8 @@ package com.reandroid.arsc.chunk;
public static final String NAME_package_id = "package_id"; public static final String NAME_package_id = "package_id";
public static final String NAME_package_name = "package_name"; public static final String NAME_package_name = "package_name";
public static final String JSON_FILE_NAME = "package.json"; public static final String JSON_FILE_NAME = "package.json";
private static final String NAME_specs="specs"; private static final String NAME_specs = "specs";
public static final String NAME_libraries="libraries"; public static final String NAME_libraries = "libraries";
public static final String NAME_staged_aliases="staged_aliases"; public static final String NAME_staged_aliases = "staged_aliases";
public static final String NAME_overlaybles = "overlaybles";
} }

View File

@ -34,8 +34,12 @@
if(stagedAlias==null||stagedAlias==this){ if(stagedAlias==null||stagedAlias==this){
return; return;
} }
stagedAliasEntryArray.addAll(stagedAlias StagedAliasEntryArray exist = getStagedAliasEntryArray();
.getStagedAliasEntryArray().getChildes()); for(StagedAliasEntry entry:stagedAlias.listStagedAliasEntry()){
if(!exist.contains(entry)){
exist.add(entry);
}
}
} }
public StagedAliasEntryArray getStagedAliasEntryArray() { public StagedAliasEntryArray getStagedAliasEntryArray() {
return stagedAliasEntryArray; return stagedAliasEntryArray;
@ -52,6 +56,7 @@
} }
@Override @Override
protected void onChunkRefreshed() { protected void onChunkRefreshed() {
getHeaderBlock().getCount().set(getStagedAliasEntryCount());
} }
@Override @Override
public String toString(){ public String toString(){

View File

@ -19,6 +19,8 @@ import com.reandroid.arsc.chunk.*;
import com.reandroid.arsc.array.SpecTypePairArray; import com.reandroid.arsc.array.SpecTypePairArray;
import com.reandroid.arsc.header.HeaderBlock; import com.reandroid.arsc.header.HeaderBlock;
import com.reandroid.arsc.io.BlockReader; import com.reandroid.arsc.io.BlockReader;
import com.reandroid.arsc.list.OverlayableList;
import com.reandroid.arsc.list.StagedAliasList;
import java.io.IOException; import java.io.IOException;
@ -26,16 +28,16 @@ public class PackageBody extends FixedBlockContainer {
private final SpecTypePairArray mSpecTypePairArray; private final SpecTypePairArray mSpecTypePairArray;
private final LibraryBlock mLibraryBlock; private final LibraryBlock mLibraryBlock;
private final BlockList<StagedAlias> mStagedAliasList; private final StagedAliasList mStagedAliasList;
private final BlockList<Overlayable> mOverlayableList; private final OverlayableList mOverlayableList;
private final BlockList<OverlayablePolicy> mOverlayablePolicyList; private final BlockList<OverlayablePolicy> mOverlayablePolicyList;
private final BlockList<UnknownChunk> mUnknownChunkList; private final BlockList<UnknownChunk> mUnknownChunkList;
public PackageBody(){ public PackageBody(){
super(6); super(6);
this.mSpecTypePairArray = new SpecTypePairArray(); this.mSpecTypePairArray = new SpecTypePairArray();
this.mLibraryBlock = new LibraryBlock(); this.mLibraryBlock = new LibraryBlock();
this.mStagedAliasList = new BlockList<>(); this.mStagedAliasList = new StagedAliasList();
this.mOverlayableList = new BlockList<>(); this.mOverlayableList = new OverlayableList();
this.mOverlayablePolicyList = new BlockList<>(); this.mOverlayablePolicyList = new BlockList<>();
this.mUnknownChunkList = new BlockList<>(); this.mUnknownChunkList = new BlockList<>();
@ -46,13 +48,13 @@ public class PackageBody extends FixedBlockContainer {
addChild(4, mOverlayablePolicyList); addChild(4, mOverlayablePolicyList);
addChild(5, mUnknownChunkList); addChild(5, mUnknownChunkList);
} }
public BlockList<Overlayable> getOverlayableList() { public OverlayableList getOverlayableList() {
return mOverlayableList; return mOverlayableList;
} }
public BlockList<OverlayablePolicy> getOverlayablePolicyList() { public BlockList<OverlayablePolicy> getOverlayablePolicyList() {
return mOverlayablePolicyList; return mOverlayablePolicyList;
} }
public BlockList<StagedAlias> getStagedAliasList() { public StagedAliasList getStagedAliasList() {
return mStagedAliasList; return mStagedAliasList;
} }
public LibraryBlock getLibraryBlock(){ public LibraryBlock getLibraryBlock(){

View File

@ -23,9 +23,23 @@ public class IntegerArray extends BlockItem {
public IntegerArray() { public IntegerArray() {
super(0); super(0);
} }
public final boolean contains(int value){
int s=size();
for(int i=0;i<s;i++){
if(value==get(i)){
return true;
}
}
return false;
}
public final void clear(){ public final void clear(){
setSize(0); setSize(0);
} }
public final void add(int value){
int old=size();
setSize(old+1);
put(old, value);
}
public final void add(int[] values){ public final void add(int[] values){
if(values==null || values.length==0){ if(values==null || values.length==0){
return; return;

View File

@ -0,0 +1,73 @@
/*
* Copyright (C) 2022 github.com/REAndroid
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.reandroid.arsc.list;
import com.reandroid.arsc.chunk.Overlayable;
import com.reandroid.arsc.container.BlockList;
import com.reandroid.json.JSONArray;
import com.reandroid.json.JSONConvert;
import com.reandroid.json.JSONObject;
public class OverlayableList extends BlockList<Overlayable> implements JSONConvert<JSONArray> {
public OverlayableList(){
super();
}
public Overlayable get(String name){
for(Overlayable overlayable:getChildes()){
if(name.equals(overlayable.getName())){
return overlayable;
}
}
return null;
}
@Override
public JSONArray toJson() {
if(size()==0){
return null;
}
JSONArray jsonArray = new JSONArray();
for(Overlayable overlayable:getChildes()){
JSONObject jsonOverlayble = overlayable.toJson();
jsonArray.put(jsonOverlayble);
}
return jsonArray;
}
@Override
public void fromJson(JSONArray json) {
if(json==null){
return;
}
int length = json.length();
for(int i=0;i<length;i++){
Overlayable overlayable = new Overlayable();
add(overlayable);
overlayable.fromJson(json.getJSONObject(i));
}
}
public void merge(OverlayableList overlayableList){
if(overlayableList==null || overlayableList==this){
return;
}
for(Overlayable overlayable: overlayableList.getChildes()){
Overlayable exist=get(overlayable.getName());
if(exist==null){
exist = new Overlayable();
add(exist);
}
exist.merge(overlayable);
}
}
}

View File

@ -0,0 +1,46 @@
/*
* Copyright (C) 2022 github.com/REAndroid
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.reandroid.arsc.list;
import com.reandroid.arsc.chunk.StagedAlias;
import com.reandroid.arsc.container.BlockList;
public class StagedAliasList extends BlockList<StagedAlias> {
public StagedAliasList(){
super();
}
private StagedAlias pickOne(){
for(StagedAlias stagedAlias:getChildes()){
if(stagedAlias!=null){
return stagedAlias;
}
}
return null;
}
public void merge(StagedAliasList stagedAliasList){
if(stagedAliasList==null || stagedAliasList==this || stagedAliasList.size()==0){
return;
}
StagedAlias exist = pickOne();
if(exist==null){
exist=new StagedAlias();
add(exist);
}
for(StagedAlias stagedAlias:stagedAliasList.getChildes()){
exist.merge(stagedAlias);
}
}
}

View File

@ -8,6 +8,16 @@ public class StagedAliasEntry extends ByteArray implements JSONConvert<JSONObjec
public StagedAliasEntry(){ public StagedAliasEntry(){
super(8); super(8);
} }
public boolean isEqual(StagedAliasEntry other){
if(other==null){
return false;
}
if(other==this){
return true;
}
return getStagedResId()==other.getStagedResId()
&& getFinalizedResId()==other.getFinalizedResId();
}
public int getStagedResId(){ public int getStagedResId(){
return getInteger(0); return getInteger(0);
} }