Implement XML bag entry decoder

This commit is contained in:
REAndroid 2022-12-30 11:45:25 -05:00
parent f2c89bad1e
commit eeeb061925
7 changed files with 388 additions and 10 deletions

View File

@ -15,6 +15,7 @@
*/
package com.reandroid.lib.apk;
import com.reandroid.lib.apk.xmldecoder.XMLBagDecoder;
import com.reandroid.lib.arsc.chunk.PackageBlock;
import com.reandroid.lib.arsc.chunk.TableBlock;
import com.reandroid.lib.arsc.chunk.TypeBlock;
@ -22,10 +23,7 @@ import com.reandroid.lib.arsc.chunk.xml.AndroidManifestBlock;
import com.reandroid.lib.arsc.chunk.xml.ResXmlBlock;
import com.reandroid.lib.arsc.container.SpecTypePair;
import com.reandroid.lib.arsc.decoder.ValueDecoder;
import com.reandroid.lib.arsc.value.EntryBlock;
import com.reandroid.lib.arsc.value.ResConfig;
import com.reandroid.lib.arsc.value.ResValueInt;
import com.reandroid.lib.arsc.value.ValueType;
import com.reandroid.lib.arsc.value.*;
import com.reandroid.lib.common.EntryStore;
import com.reandroid.lib.common.Frameworks;
import com.reandroid.lib.common.TableEntryStore;
@ -42,6 +40,7 @@ import java.util.*;
public class ApkModuleXmlDecoder {
private final ApkModule apkModule;
private final Map<Integer, Set<ResConfig>> decodedEntries;
private XMLBagDecoder xmlBagDecoder;
public ApkModuleXmlDecoder(ApkModule apkModule){
this.apkModule=apkModule;
this.decodedEntries = new HashMap<>();
@ -53,6 +52,7 @@ import java.util.*;
entryStore.add(Frameworks.getAndroid());
TableBlock tableBlock=apkModule.getTableBlock();
entryStore.add(tableBlock);
xmlBagDecoder=new XMLBagDecoder(entryStore);
decodeAndroidManifest(entryStore, outDir);
logMessage("Decoding resource files ...");
List<ResFile> resFileList=apkModule.listResFiles();
@ -183,9 +183,12 @@ import java.util.*;
}
private XMLElement decodeValue(EntryStore entryStore, EntryBlock entryBlock){
XMLElement element=new XMLElement(entryBlock.getTypeName());
element.setResourceId(entryBlock.getResourceId());
int resourceId=entryBlock.getResourceId();
XMLAttribute attribute=new XMLAttribute("name", entryBlock.getName());
element.addAttribute(attribute);
attribute.setNameId(resourceId);
element.setResourceId(resourceId);
if(!entryBlock.isEntryTypeBag()){
String name=entryBlock.getName();
String value;
ResValueInt resValueInt=(ResValueInt) entryBlock.getResValue();
if(resValueInt.getValueType()== ValueType.STRING){
@ -196,12 +199,11 @@ import java.util.*;
resValueInt.getValueType(),
resValueInt.getData());
}
XMLAttribute attribute=new XMLAttribute("name", name);
element.addAttribute(attribute);
element.setTextContent(value);
}else {
// TODO: implement bags entry decoder
return null;
ResValueBag resValueBag=(ResValueBag) entryBlock.getResValue();
xmlBagDecoder.decode(resValueBag, element);
return element;
}
return element;
}

View File

@ -0,0 +1,32 @@
/*
* 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.lib.apk.xmldecoder;
import com.reandroid.lib.arsc.value.ResValueBag;
import com.reandroid.lib.common.EntryStore;
import com.reandroid.xml.XMLElement;
abstract class BagDecoder {
private final EntryStore entryStore;
public BagDecoder(EntryStore entryStore){
this.entryStore=entryStore;
}
EntryStore getEntryStore(){
return entryStore;
}
public abstract void decode(ResValueBag resValueBag, XMLElement parentElement);
public abstract boolean canDecode(ResValueBag resValueBag);
}

View File

@ -0,0 +1,66 @@
/*
* 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.lib.apk.xmldecoder;
import com.reandroid.lib.arsc.decoder.ValueDecoder;
import com.reandroid.lib.arsc.value.ResValueBag;
import com.reandroid.lib.arsc.value.ResValueBagItem;
import com.reandroid.lib.common.EntryStore;
import com.reandroid.xml.XMLElement;
class XMLArrayDecoder extends BagDecoder{
public XMLArrayDecoder(EntryStore entryStore) {
super(entryStore);
}
@Override
public void decode(ResValueBag resValueBag, XMLElement parentElement) {
ResValueBagItem[] bagItems = resValueBag.getBagItems();
EntryStore entryStore=getEntryStore();
for(int i=0;i<bagItems.length;i++){
String value = ValueDecoder.decodeIntEntry(entryStore, bagItems[i]);
XMLElement child=new XMLElement("item");
child.setTextContent(value);
parentElement.addChild(child);
}
}
@Override
public boolean canDecode(ResValueBag resValueBag) {
return isResBagArrayValue(resValueBag);
}
public static boolean isResBagArrayValue(ResValueBag resValueBag){
int parentId=resValueBag.getParentId();
if(parentId!=0){
return false;
}
ResValueBagItem[] bagItems = resValueBag.getBagItems();
if(bagItems==null||bagItems.length==0){
return false;
}
int len=bagItems.length;
for(int i=0;i<len;i++){
ResValueBagItem item=bagItems[i];
if(item.getIdHigh()!=0x0100 && item.getIdHigh()!=0x0200){
return false;
}
int id=item.getIdLow()-1;
if(id!=i){
return false;
}
}
return true;
}
}

View File

@ -0,0 +1,80 @@
/*
* 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.lib.apk.xmldecoder;
import com.reandroid.lib.arsc.value.ResValueBag;
import com.reandroid.lib.arsc.value.attribute.AttributeBag;
import com.reandroid.lib.arsc.value.attribute.AttributeBagItem;
import com.reandroid.lib.common.EntryStore;
import com.reandroid.xml.XMLElement;
class XMLAttrDecoder extends BagDecoder{
public XMLAttrDecoder(EntryStore entryStore){
super(entryStore);
}
@Override
public void decode(ResValueBag resValueBag, XMLElement parentElement){
AttributeBag attributeBag=AttributeBag.create(resValueBag);
decodeParentAttributes(parentElement, attributeBag);
boolean is_flag=attributeBag.isFlag();
String tagName=is_flag?"flag":"enum";
AttributeBagItem[] bagItems = attributeBag.getBagItems();
EntryStore entryStore=getEntryStore();
for(int i=0;i< bagItems.length;i++){
AttributeBagItem item=bagItems[i];
if(item.isType()){
continue;
}
XMLElement child=new XMLElement(tagName);
String name = item.getNameOrHex(entryStore);
child.setAttribute("name", name);
int rawVal=item.getData();
String value;
if(is_flag){
value=String.format("0x%08x", rawVal);
}else {
value=String.valueOf(rawVal);
}
child.setTextContent(value);
parentElement.addChild(child);
}
}
@Override
public boolean canDecode(ResValueBag resValueBag) {
return AttributeBag.isAttribute(resValueBag);
}
private void decodeParentAttributes(XMLElement element, AttributeBag attributeBag){
String formats= attributeBag.decodeValueType();
if(formats!=null){
element.setAttribute("formats", formats);
}
AttributeBagItem boundItem=attributeBag.getMin();
if(boundItem!=null){
element.setAttribute("min", boundItem.getBound().toString());
}
boundItem=attributeBag.getMax();
if(boundItem!=null){
element.setAttribute("max", boundItem.getBound().toString());
}
boundItem=attributeBag.getL10N();
if(boundItem!=null){
element.setAttribute("l10n", boundItem.getBound().toString());
}
}
}

View File

@ -0,0 +1,49 @@
/*
* 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.lib.apk.xmldecoder;
import com.reandroid.lib.arsc.value.ResValueBag;
import com.reandroid.lib.common.EntryStore;
import com.reandroid.xml.XMLElement;
import java.util.ArrayList;
import java.util.List;
public class XMLBagDecoder {
private final EntryStore entryStore;
private final List<BagDecoder> decoderList;
private final XMLCommonBagDecoder commonBagDecoder;
public XMLBagDecoder(EntryStore entryStore){
this.entryStore=entryStore;
this.decoderList=new ArrayList<>();
this.decoderList.add(new XMLAttrDecoder(entryStore));
this.decoderList.add(new XMLPluralsDecoder(entryStore));
this.decoderList.add(new XMLArrayDecoder(entryStore));
this.commonBagDecoder = new XMLCommonBagDecoder(entryStore);
}
public void decode(ResValueBag resValueBag, XMLElement parentElement){
BagDecoder bagDecoder=getFor(resValueBag);
bagDecoder.decode(resValueBag, parentElement);
}
private BagDecoder getFor(ResValueBag resValueBag){
for(BagDecoder bagDecoder:decoderList){
if(bagDecoder.canDecode(resValueBag)){
return bagDecoder;
}
}
return commonBagDecoder;
}
}

View File

@ -0,0 +1,72 @@
/*
* 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.lib.apk.xmldecoder;
import com.reandroid.lib.arsc.chunk.PackageBlock;
import com.reandroid.lib.arsc.decoder.ValueDecoder;
import com.reandroid.lib.arsc.value.ResValueBag;
import com.reandroid.lib.arsc.value.ResValueBagItem;
import com.reandroid.lib.arsc.value.ValueType;
import com.reandroid.lib.common.EntryStore;
import com.reandroid.xml.XMLElement;
class XMLCommonBagDecoder extends BagDecoder{
public XMLCommonBagDecoder(EntryStore entryStore) {
super(entryStore);
}
@Override
public void decode(ResValueBag resValueBag, XMLElement parentElement) {
PackageBlock currentPackage=resValueBag
.getEntryBlock().getPackageBlock();
int parentId = resValueBag.getParentId();
String parent;
if(parentId!=0){
parent = ValueDecoder.decodeEntryValue(getEntryStore(),
currentPackage, ValueType.REFERENCE, parentId);
}else {
parent=null;
}
if(parent!=null){
parentElement.setAttribute("parent", parent);
}
int currentPackageId=currentPackage.getId();
ResValueBagItem[] bagItems = resValueBag.getBagItems();
EntryStore entryStore = getEntryStore();
for(int i=0;i< bagItems.length;i++){
ResValueBagItem item=bagItems[i];
int resourceId=item.getId();
XMLElement child=new XMLElement("item");
String name = ValueDecoder.decodeAttributeName(
entryStore, currentPackage, item.getId());
child.setAttribute("name", name);
String value = ValueDecoder.decode(entryStore, currentPackageId,
resourceId, item.getValueType(), item.getData());
child.setTextContent(value);
parentElement.addChild(child);
}
}
@Override
public boolean canDecode(ResValueBag resValueBag) {
return resValueBag!=null;
}
}

View File

@ -0,0 +1,77 @@
/*
* 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.lib.apk.xmldecoder;
import com.reandroid.lib.arsc.decoder.ValueDecoder;
import com.reandroid.lib.arsc.value.BaseResValue;
import com.reandroid.lib.arsc.value.ResValueBag;
import com.reandroid.lib.arsc.value.ResValueBagItem;
import com.reandroid.lib.arsc.value.plurals.PluralsQuantity;
import com.reandroid.lib.common.EntryStore;
import com.reandroid.xml.XMLElement;
class XMLPluralsDecoder extends BagDecoder{
public XMLPluralsDecoder(EntryStore entryStore) {
super(entryStore);
}
@Override
public void decode(ResValueBag resValueBag, XMLElement parentElement) {
ResValueBagItem[] bagItems = resValueBag.getBagItems();
int len=bagItems.length;
EntryStore entryStore=getEntryStore();
for(int i=0;i<len;i++){
ResValueBagItem item = bagItems[i];
PluralsQuantity quantity = PluralsQuantity.valueOf(item.getIdLow());
String value = ValueDecoder.decodeIntEntry(entryStore, item);
XMLElement child=new XMLElement("item");
child.setAttribute("quantity", quantity.toString());
child.setTextContent(value);
parentElement.addChild(child);
}
}
@Override
public boolean canDecode(ResValueBag resValueBag) {
return isResBagPluralsValue(resValueBag);
}
public static boolean isResBagPluralsValue(BaseResValue baseResValue){
ResValueBag resValueBag=(ResValueBag)baseResValue;
int parentId=resValueBag.getParentId();
if(parentId!=0){
return false;
}
ResValueBagItem[] bagItems = resValueBag.getBagItems();
if(bagItems==null||bagItems.length==0){
return false;
}
int len=bagItems.length;
for(int i=0;i<len;i++){
ResValueBagItem item=bagItems[i];
if(item.getIdHigh()!=0x0100){
return false;
}
PluralsQuantity pq=PluralsQuantity.valueOf(item.getIdLow());
if(pq==null){
return false;
}
}
return true;
}
}