fix multi-package xml compiling #40

This commit is contained in:
REAndroid 2023-05-09 18:03:02 +02:00
parent e136a8d570
commit ada5aaf17d
6 changed files with 497 additions and 341 deletions

View File

@ -18,8 +18,11 @@ package com.reandroid.apk;
import com.reandroid.archive.APKArchive; import com.reandroid.archive.APKArchive;
import com.reandroid.archive.FileInputSource; import com.reandroid.archive.FileInputSource;
import com.reandroid.apk.xmlencoder.RESEncoder; import com.reandroid.apk.xmlencoder.RESEncoder;
import com.reandroid.archive.InputSource;
import com.reandroid.archive.InputSourceUtil;
import com.reandroid.archive2.block.ApkSignatureBlock; import com.reandroid.archive2.block.ApkSignatureBlock;
import com.reandroid.arsc.chunk.TableBlock; import com.reandroid.arsc.chunk.TableBlock;
import com.reandroid.arsc.chunk.xml.AndroidManifestBlock;
import com.reandroid.json.JSONArray; import com.reandroid.json.JSONArray;
import com.reandroid.xml.XMLException; import com.reandroid.xml.XMLException;
@ -43,6 +46,7 @@ public class ApkModuleXmlEncoder {
scanRootDir(rootDir); scanRootDir(rootDir);
restorePathMap(mainDirectory); restorePathMap(mainDirectory);
restoreSignatures(mainDirectory); restoreSignatures(mainDirectory);
sortFiles();
} }
private void restoreSignatures(File dir) throws IOException { private void restoreSignatures(File dir) throws IOException {
File sigDir = new File(dir, ApkUtil.SIGNATURE_DIR_NAME); File sigDir = new File(dir, ApkUtil.SIGNATURE_DIR_NAME);
@ -79,6 +83,24 @@ public class ApkModuleXmlEncoder {
archive.add(inputSource); archive.add(inputSource);
} }
} }
private void sortFiles(){
APKArchive archive=getApkModule().getApkArchive();
int i = 1;
for(InputSource inputSource:archive.listInputSources()){
if(inputSource.getSort() == 0){
inputSource.setSort(i);
i++;
}
}
InputSource manifest = archive.getInputSource(AndroidManifestBlock.FILE_NAME);
if(manifest != null){
manifest.setSort(0);
}
List<InputSource> sourceList = archive.listInputSources();
InputSourceUtil.sort(sourceList);
archive.clear();
archive.addAll(sourceList);
}
private void loadUncompressedFiles(File mainDirectory) throws IOException, XMLException { private void loadUncompressedFiles(File mainDirectory) throws IOException, XMLException {
File file=new File(mainDirectory, UncompressedFiles.JSON_FILE); File file=new File(mainDirectory, UncompressedFiles.JSON_FILE);
UncompressedFiles uncompressedFiles = getApkModule().getUncompressedFiles(); UncompressedFiles uncompressedFiles = getApkModule().getUncompressedFiles();

View File

@ -29,7 +29,6 @@ import com.reandroid.arsc.util.FrameworkTable;
import com.reandroid.arsc.util.HexUtil; import com.reandroid.arsc.util.HexUtil;
import com.reandroid.arsc.util.ResNameMap; import com.reandroid.arsc.util.ResNameMap;
import com.reandroid.arsc.value.Entry; import com.reandroid.arsc.value.Entry;
import com.reandroid.common.Frameworks;
import java.util.HashSet; import java.util.HashSet;
import java.util.Set; import java.util.Set;
@ -38,6 +37,7 @@ import java.util.regex.Matcher;
public class EncodeMaterials { public class EncodeMaterials {
private final Set<ResourceIds.Table.Package> packageIdSet = new HashSet<>(); private final Set<ResourceIds.Table.Package> packageIdSet = new HashSet<>();
private PackageBlock currentPackage; private PackageBlock currentPackage;
private ResourceIds.Table.Package currentLocalPackage;
private final Set<FrameworkTable> frameworkTables = new HashSet<>(); private final Set<FrameworkTable> frameworkTables = new HashSet<>();
private APKLogger apkLogger; private APKLogger apkLogger;
private boolean mForceCreateNamespaces = true; private boolean mForceCreateNamespaces = true;
@ -99,6 +99,10 @@ public class EncodeMaterials {
} }
String type = matcher.group(4); String type = matcher.group(4);
String name = matcher.group(5); String name = matcher.group(5);
if(isLocalPackageName(packageName)){
return resolveLocalResourceId(packageName, type, name);
}
if(EncodeUtil.isEmpty(packageName) if(EncodeUtil.isEmpty(packageName)
|| packageName.equals(getCurrentPackageName()) || packageName.equals(getCurrentPackageName())
|| !isFrameworkPackageName(packageName)){ || !isFrameworkPackageName(packageName)){
@ -106,13 +110,36 @@ public class EncodeMaterials {
} }
return resolveFrameworkResourceId(packageName, type, name); return resolveFrameworkResourceId(packageName, type, name);
} }
private int resolveLocalResourceId(String packageName, String type, String name){
ResourceIds.Table.Package pkg = getLocalPackage(packageName);
Integer resourceId = pkg.getResourceId(type, name);
if(resourceId != null){
return resourceId;
}
EntryGroup entryGroup=getLocalEntryGroup(type, name);
if(entryGroup!=null){
return entryGroup.getResourceId();
}
throw new EncodeException("Local entry not found: " +
"package=" + packageName +
", type=" + type +
", name=" + name);
}
public int resolveLocalResourceId(String type, String name){ public int resolveLocalResourceId(String type, String name){
ResourceIds.Table.Package current = this.currentLocalPackage;
if(current != null){
Integer resId = current.getResourceId(type, name);
if(resId != null){
return resId;
}
}else {
for(ResourceIds.Table.Package pkg:packageIdSet){ for(ResourceIds.Table.Package pkg:packageIdSet){
Integer resId = pkg.getResourceId(type, name); Integer resId = pkg.getResourceId(type, name);
if(resId!=null){ if(resId!=null){
return resId; return resId;
} }
} }
}
EntryGroup entryGroup=getLocalEntryGroup(type, name); EntryGroup entryGroup=getLocalEntryGroup(type, name);
if(entryGroup!=null){ if(entryGroup!=null){
return entryGroup.getResourceId(); return entryGroup.getResourceId();
@ -286,8 +313,73 @@ public class EncodeMaterials {
} }
public EncodeMaterials setCurrentPackage(PackageBlock currentPackage) { public EncodeMaterials setCurrentPackage(PackageBlock currentPackage) {
this.currentPackage = currentPackage; this.currentPackage = currentPackage;
onCurrentPackageChanged(currentPackage);
return this; return this;
} }
public EncodeMaterials setCurrentLocalPackage(ResourceIds.Table.Package currentLocalPackage) {
this.currentLocalPackage = currentLocalPackage;
return this;
}
private void onCurrentPackageChanged(PackageBlock currentPackage){
if(currentPackage == null){
return;
}
ResourceIds.Table.Package current = null;
if(isUniquePackageIds()){
current = getLocalPackage(currentPackage.getId());
}
if(current == null && isUniquePackageNames()){
current = getLocalPackage(currentPackage.getName());
}
if(current != null){
this.currentLocalPackage = current;
}
}
private ResourceIds.Table.Package getLocalPackage(int packageId){
byte id = (byte) packageId;
for(ResourceIds.Table.Package pkg : packageIdSet){
if(id == pkg.id){
return pkg;
}
}
return null;
}
private ResourceIds.Table.Package getLocalPackage(String name){
if(name == null){
return null;
}
for(ResourceIds.Table.Package pkg : packageIdSet){
if(name.equals(pkg.name)){
return pkg;
}
}
return null;
}
private boolean isLocalPackageName(String packageName){
if(packageName == null){
return false;
}
for(ResourceIds.Table.Package pkg : packageIdSet){
if(packageName.equals(pkg.name)){
return true;
}
}
return false;
}
private boolean isUniquePackageNames(){
Set<String> names = new HashSet<>();
for(ResourceIds.Table.Package pkg : packageIdSet){
names.add(pkg.name);
}
return names.size() == packageIdSet.size();
}
private boolean isUniquePackageIds(){
Set<Byte> ids = new HashSet<>();
for(ResourceIds.Table.Package pkg : packageIdSet){
ids.add(pkg.id);
}
return ids.size() == packageIdSet.size();
}
public EncodeMaterials addFramework(FrameworkApk frameworkApk) { public EncodeMaterials addFramework(FrameworkApk frameworkApk) {
if(frameworkApk!=null){ if(frameworkApk!=null){
addFramework(frameworkApk.getTableBlock()); addFramework(frameworkApk.getTableBlock());

View File

@ -1,4 +1,4 @@
/* /*
* Copyright (C) 2022 github.com/REAndroid * Copyright (C) 2022 github.com/REAndroid
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
@ -15,21 +15,21 @@
*/ */
package com.reandroid.apk.xmlencoder; package com.reandroid.apk.xmlencoder;
import com.reandroid.archive.APKArchive; import com.reandroid.archive.APKArchive;
import com.reandroid.archive.FileInputSource; import com.reandroid.archive.FileInputSource;
import com.reandroid.archive.InputSource; import com.reandroid.archive.InputSource;
import com.reandroid.apk.ApkUtil; import com.reandroid.apk.ApkUtil;
import com.reandroid.apk.UncompressedFiles; import com.reandroid.apk.UncompressedFiles;
import com.reandroid.arsc.chunk.PackageBlock; import com.reandroid.arsc.chunk.PackageBlock;
import com.reandroid.arsc.chunk.TypeBlock; import com.reandroid.arsc.chunk.TypeBlock;
import com.reandroid.arsc.value.Entry; import com.reandroid.arsc.value.Entry;
import com.reandroid.xml.source.XMLFileSource; import com.reandroid.xml.source.XMLFileSource;
import com.reandroid.xml.source.XMLSource; import com.reandroid.xml.source.XMLSource;
import java.io.File; import java.io.File;
import java.util.List; import java.util.List;
public class FilePathEncoder { public class FilePathEncoder {
private final EncodeMaterials materials; private final EncodeMaterials materials;
private APKArchive apkArchive; private APKArchive apkArchive;
private UncompressedFiles uncompressedFiles; private UncompressedFiles uncompressedFiles;
@ -78,6 +78,9 @@ package com.reandroid.apk.xmlencoder;
entry.setValueAsString(path); entry.setValueAsString(path);
entry.setSpecReference(materials.getSpecString(name)); entry.setSpecReference(materials.getSpecString(name));
InputSource inputSource=createInputSource(path, resFile); InputSource inputSource=createInputSource(path, resFile);
if(inputSource instanceof XMLEncodeSource){
((XMLEncodeSource)inputSource).setEntry(entry);
}
addInputSource(inputSource); addInputSource(inputSource);
return inputSource; return inputSource;
} }

View File

@ -1,4 +1,4 @@
/* /*
* Copyright (C) 2022 github.com/REAndroid * Copyright (C) 2022 github.com/REAndroid
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
@ -45,14 +45,13 @@ class PackageCreator {
} }
public PackageBlock createNew(TableBlock tableBlock, ResourceIds.Table.Package pkgResourceIds){ public PackageBlock createNew(TableBlock tableBlock, ResourceIds.Table.Package pkgResourceIds){
loadNames(pkgResourceIds); loadNames(pkgResourceIds);
PackageBlock packageBlock=new PackageBlock(); PackageBlock packageBlock = tableBlock.getPackageArray().createNext();
packageBlock.setName(mPackageName); packageBlock.setName(mPackageName);
packageBlock.setId(mPackageId); packageBlock.setId(mPackageId);
loadPackageInfoJson(packageBlock); loadPackageInfoJson(packageBlock);
this.mPackageName = packageBlock.getName();
tableBlock.getPackageArray()
.add(packageBlock);
packageBlock.getSpecStringPool() packageBlock.getSpecStringPool()
.addStrings(mSpecNames); .addStrings(mSpecNames);

View File

@ -1,4 +1,4 @@
/* /*
* Copyright (C) 2022 github.com/REAndroid * Copyright (C) 2022 github.com/REAndroid
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
@ -13,24 +13,24 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package com.reandroid.apk.xmlencoder; package com.reandroid.apk.xmlencoder;
import com.reandroid.apk.*; import com.reandroid.apk.*;
import com.reandroid.archive.APKArchive; import com.reandroid.archive.APKArchive;
import com.reandroid.arsc.chunk.PackageBlock; import com.reandroid.arsc.chunk.PackageBlock;
import com.reandroid.arsc.chunk.TableBlock; import com.reandroid.arsc.chunk.TableBlock;
import com.reandroid.arsc.chunk.xml.AndroidManifestBlock; import com.reandroid.arsc.chunk.xml.AndroidManifestBlock;
import com.reandroid.xml.XMLDocument; import com.reandroid.xml.XMLDocument;
import com.reandroid.xml.XMLException; import com.reandroid.xml.XMLException;
import com.reandroid.xml.source.XMLFileSource; import com.reandroid.xml.source.XMLFileSource;
import com.reandroid.xml.source.XMLSource; import com.reandroid.xml.source.XMLSource;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.util.*; import java.util.*;
import java.util.zip.ZipEntry; import java.util.zip.ZipEntry;
public class RESEncoder { public class RESEncoder {
private APKLogger apkLogger; private APKLogger apkLogger;
private final TableBlock tableBlock; private final TableBlock tableBlock;
private final Set<File> parsedFiles = new HashSet<>(); private final Set<File> parsedFiles = new HashSet<>();
@ -75,7 +75,7 @@
Map<File, PackageBlock> packageBlockMap=new HashMap<>(); Map<File, PackageBlock> packageBlockMap=new HashMap<>();
for(File pubXmlFile:pubXmlFileList){ for(File pubXmlFile:pubXmlFileList){
ResourceIds.Table.Package pkgResourceIds=map.get(pubXmlFile); ResourceIds.Table.Package pkgResourceIds = map.get(pubXmlFile);
if(pkgResourceIds==null){ if(pkgResourceIds==null){
continue; continue;
} }
@ -83,6 +83,7 @@
PackageBlock packageBlock = createPackage(pkgResourceIds, pubXmlFile); PackageBlock packageBlock = createPackage(pkgResourceIds, pubXmlFile);
encodeMaterials.setCurrentPackage(packageBlock); encodeMaterials.setCurrentPackage(packageBlock);
encodeMaterials.setCurrentLocalPackage(pkgResourceIds);
packageBlockMap.put(pubXmlFile, packageBlock); packageBlockMap.put(pubXmlFile, packageBlock);
ValuesEncoder valuesEncoder = new ValuesEncoder(encodeMaterials); ValuesEncoder valuesEncoder = new ValuesEncoder(encodeMaterials);
@ -101,12 +102,16 @@
addParsedFiles(fileAttrs); addParsedFiles(fileAttrs);
} }
} }
File manifestFile = null;
for(File pubXmlFile:pubXmlFileList){ for(File pubXmlFile:pubXmlFileList){
ResourceIds.Table.Package pkgResourceIds=map.get(pubXmlFile); ResourceIds.Table.Package pkgResourceIds = map.get(pubXmlFile);
if(pkgResourceIds==null){ if(pkgResourceIds==null){
continue; continue;
} }
addParsedFiles(pubXmlFile); addParsedFiles(pubXmlFile);
if(manifestFile == null){
manifestFile = toAndroidManifest(pubXmlFile);
}
PackageBlock packageBlock=packageBlockMap.get(pubXmlFile); PackageBlock packageBlock=packageBlockMap.get(pubXmlFile);
@ -114,6 +119,7 @@
packageBlock = createPackage(pkgResourceIds, pubXmlFile); packageBlock = createPackage(pkgResourceIds, pubXmlFile);
} }
encodeMaterials.setCurrentPackage(packageBlock); encodeMaterials.setCurrentPackage(packageBlock);
encodeMaterials.setCurrentLocalPackage(pkgResourceIds);
File resDir=toResDirectory(pubXmlFile); File resDir=toResDirectory(pubXmlFile);
encodeResDir(encodeMaterials, resDir); encodeResDir(encodeMaterials, resDir);
@ -124,16 +130,19 @@
packageBlock.sortTypes(); packageBlock.sortTypes();
packageBlock.refresh(); packageBlock.refresh();
}
File manifestFile=toAndroidManifest(pubXmlFile); tableBlock.refresh();
PackageBlock packageBlock = tableBlock.pickOne();
if(manifestFile != null){
if(packageBlock != null){
encodeMaterials.setCurrentPackage(packageBlock);
}
XMLSource xmlSource = XMLSource xmlSource =
new XMLFileSource(AndroidManifestBlock.FILE_NAME, manifestFile); new XMLFileSource(AndroidManifestBlock.FILE_NAME, manifestFile);
XMLEncodeSource xmlEncodeSource = XMLEncodeSource xmlEncodeSource =
new XMLEncodeSource(encodeMaterials, xmlSource); new XMLEncodeSource(encodeMaterials, xmlSource);
getApkModule().getApkArchive().add(xmlEncodeSource); getApkModule().getApkArchive().add(xmlEncodeSource);
} }
tableBlock.refresh();
} }
private PackageBlock createPackage(ResourceIds.Table.Package pkgResourceIds private PackageBlock createPackage(ResourceIds.Table.Package pkgResourceIds
, File pubXmlFile){ , File pubXmlFile){
@ -303,4 +312,4 @@
apkLogger.logVerbose(msg); apkLogger.logVerbose(msg);
} }
} }
} }

View File

@ -1,4 +1,4 @@
/* /*
* Copyright (C) 2022 github.com/REAndroid * Copyright (C) 2022 github.com/REAndroid
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
@ -17,7 +17,9 @@ package com.reandroid.apk.xmlencoder;
import com.reandroid.archive.ByteInputSource; import com.reandroid.archive.ByteInputSource;
import com.reandroid.apk.CrcOutputStream; import com.reandroid.apk.CrcOutputStream;
import com.reandroid.arsc.chunk.PackageBlock;
import com.reandroid.arsc.chunk.xml.ResXmlDocument; import com.reandroid.arsc.chunk.xml.ResXmlDocument;
import com.reandroid.arsc.value.Entry;
import com.reandroid.xml.XMLException; import com.reandroid.xml.XMLException;
import com.reandroid.xml.source.XMLSource; import com.reandroid.xml.source.XMLSource;
@ -28,11 +30,27 @@ public class XMLEncodeSource extends ByteInputSource {
private final EncodeMaterials encodeMaterials; private final EncodeMaterials encodeMaterials;
private final XMLSource xmlSource; private final XMLSource xmlSource;
private ResXmlDocument resXmlDocument; private ResXmlDocument resXmlDocument;
public XMLEncodeSource(EncodeMaterials encodeMaterials, XMLSource xmlSource){ private Entry mEntry;
public XMLEncodeSource(EncodeMaterials encodeMaterials, XMLSource xmlSource, Entry entry){
super(new byte[0], xmlSource.getPath()); super(new byte[0], xmlSource.getPath());
this.encodeMaterials=encodeMaterials; this.encodeMaterials = encodeMaterials;
this.xmlSource=xmlSource; this.xmlSource = xmlSource;
this.mEntry = entry;
} }
public XMLEncodeSource(EncodeMaterials encodeMaterials, XMLSource xmlSource){
this(encodeMaterials, xmlSource, null);
}
public XMLSource getXmlSource() {
return xmlSource;
}
public Entry getEntry(){
return mEntry;
}
public void setEntry(Entry entry) {
this.mEntry = entry;
}
@Override @Override
public long getLength() throws IOException{ public long getLength() throws IOException{
return getResXmlBlock().countBytes(); return getResXmlBlock().countBytes();
@ -64,7 +82,13 @@ public class XMLEncodeSource extends ByteInputSource {
try { try {
XMLFileEncoder xmlFileEncoder=new XMLFileEncoder(encodeMaterials); XMLFileEncoder xmlFileEncoder=new XMLFileEncoder(encodeMaterials);
xmlFileEncoder.setCurrentPath(xmlSource.getPath()); xmlFileEncoder.setCurrentPath(xmlSource.getPath());
encodeMaterials.logVerbose("Encoding xml: "+xmlSource.getPath()); EncodeMaterials encodeMaterials = this.encodeMaterials;
encodeMaterials.logVerbose("Encoding xml: " + xmlSource.getPath());
PackageBlock currentPackage = encodeMaterials.getCurrentPackage();
PackageBlock packageBlock = getEntryPackageBlock();
if(packageBlock != null && packageBlock != currentPackage){
encodeMaterials.setCurrentPackage(packageBlock);
}
resXmlDocument = xmlFileEncoder.encode(xmlSource.getXMLDocument()); resXmlDocument = xmlFileEncoder.encode(xmlSource.getXMLDocument());
} catch (XMLException ex) { } catch (XMLException ex) {
throw new EncodeException("XMLException on: '"+xmlSource.getPath() throw new EncodeException("XMLException on: '"+xmlSource.getPath()
@ -72,6 +96,13 @@ public class XMLEncodeSource extends ByteInputSource {
} }
return resXmlDocument; return resXmlDocument;
} }
private PackageBlock getEntryPackageBlock(){
Entry entry = getEntry();
if(entry != null){
return entry.getPackageBlock();
}
return null;
}
@Override @Override
public void disposeInputSource(){ public void disposeInputSource(){
this.xmlSource.disposeXml(); this.xmlSource.disposeXml();