ARSCLib/src/main/java/com/reandroid/apk/ApkModule.java
2023-05-01 20:27:16 +02:00

784 lines
30 KiB
Java

/*
* 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.apk;
import com.reandroid.archive.*;
import com.reandroid.archive2.Archive;
import com.reandroid.archive2.block.ApkSignatureBlock;
import com.reandroid.archive2.writer.ApkWriter;
import com.reandroid.arsc.ApkFile;
import com.reandroid.arsc.array.PackageArray;
import com.reandroid.arsc.chunk.Chunk;
import com.reandroid.arsc.chunk.PackageBlock;
import com.reandroid.arsc.chunk.TableBlock;
import com.reandroid.arsc.chunk.xml.AndroidManifestBlock;
import com.reandroid.arsc.chunk.xml.ResXmlDocument;
import com.reandroid.arsc.container.SpecTypePair;
import com.reandroid.arsc.decoder.Decoder;
import com.reandroid.arsc.group.StringGroup;
import com.reandroid.arsc.item.TableString;
import com.reandroid.arsc.pool.TableStringPool;
import com.reandroid.arsc.util.FrameworkTable;
import com.reandroid.arsc.value.Entry;
import com.reandroid.arsc.value.ResConfig;
import com.reandroid.xml.XMLDocument;
import com.reandroid.xml.XMLElement;
import com.reandroid.xml.XMLException;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.util.*;
import java.util.zip.ZipEntry;
public class ApkModule implements ApkFile {
private final String moduleName;
private final APKArchive apkArchive;
private boolean loadDefaultFramework = true;
private boolean mDisableLoadFramework = false;
private TableBlock mTableBlock;
private AndroidManifestBlock mManifestBlock;
private final UncompressedFiles mUncompressedFiles;
private APKLogger apkLogger;
private Decoder mDecoder;
private ApkType mApkType;
private ApkSignatureBlock apkSignatureBlock;
public ApkModule(String moduleName, APKArchive apkArchive){
this.moduleName=moduleName;
this.apkArchive=apkArchive;
this.mUncompressedFiles=new UncompressedFiles();
this.mUncompressedFiles.addPath(apkArchive);
}
public ApkSignatureBlock getApkSignatureBlock() {
return apkSignatureBlock;
}
public void setApkSignatureBlock(ApkSignatureBlock apkSignatureBlock) {
this.apkSignatureBlock = apkSignatureBlock;
}
public boolean hasSignatureBlock(){
return getApkSignatureBlock() != null;
}
public void dumpSignatureInfoFiles(File directory) throws IOException{
ApkSignatureBlock apkSignatureBlock = getApkSignatureBlock();
if(apkSignatureBlock == null){
throw new IOException("Don't have signature block");
}
apkSignatureBlock.writeSplitRawToDirectory(directory);
}
public void dumpSignatureBlock(File file) throws IOException{
ApkSignatureBlock apkSignatureBlock = getApkSignatureBlock();
if(apkSignatureBlock == null){
throw new IOException("Don't have signature block");
}
apkSignatureBlock.writeRaw(file);
}
public void scanSignatureInfoFiles(File directory) throws IOException{
if(!directory.isDirectory()){
throw new IOException("No such directory: " + directory);
}
ApkSignatureBlock apkSignatureBlock = this.apkSignatureBlock;
if(apkSignatureBlock == null){
apkSignatureBlock = new ApkSignatureBlock();
}
apkSignatureBlock.scanSplitFiles(directory);
setApkSignatureBlock(apkSignatureBlock);
}
public void loadSignatureBlock(File file) throws IOException{
if(!file.isFile()){
throw new IOException("No such file: " + file);
}
ApkSignatureBlock apkSignatureBlock = this.apkSignatureBlock;
if(apkSignatureBlock == null){
apkSignatureBlock = new ApkSignatureBlock();
}
apkSignatureBlock.read(file);
setApkSignatureBlock(apkSignatureBlock);
}
public String getSplit(){
if(!hasAndroidManifestBlock()){
return null;
}
return getAndroidManifestBlock().getSplit();
}
public FrameworkApk initializeAndroidFramework(TableBlock tableBlock, Integer version) throws IOException {
if(tableBlock == null || isAndroid(tableBlock)){
return null;
}
List<TableBlock> frameWorkList = tableBlock.getFrameWorks();
for(TableBlock frameWork:frameWorkList){
if(isAndroid(frameWork)){
ApkFile apkFile = frameWork.getApkFile();
if(apkFile instanceof FrameworkApk){
return (FrameworkApk) apkFile;
}
return null;
}
}
logMessage("Initializing android framework ...");
FrameworkApk frameworkApk;
if(version==null){
logMessage("Can not read framework version, loading latest");
frameworkApk = AndroidFrameworks.getLatest();
}else {
logMessage("Loading android framework for version: " + version);
frameworkApk = AndroidFrameworks.getBestMatch(version);
}
FrameworkTable frameworkTable = frameworkApk.getTableBlock();
tableBlock.addFramework(frameworkTable);
logMessage("Initialized framework: "+frameworkApk.getName());
return frameworkApk;
}
private boolean isAndroid(TableBlock tableBlock){
if(tableBlock instanceof FrameworkTable){
FrameworkTable frameworkTable = (FrameworkTable) tableBlock;
return frameworkTable.isAndroid();
}
return false;
}
public FrameworkApk initializeAndroidFramework(XMLDocument xmlDocument) throws IOException {
TableBlock tableBlock = getTableBlock(false);
if(isAndroidCoreApp(xmlDocument)){
logMessage("Looks framework itself, skip loading frameworks");
return null;
}
Integer version = readCompileVersionCode(xmlDocument);
return initializeAndroidFramework(tableBlock, version);
}
private boolean isAndroidCoreApp(XMLDocument manifestDocument){
XMLElement root = manifestDocument.getDocumentElement();
if(root == null){
return false;
}
if(!"android".equals(root.getAttributeValue("package"))){
return false;
}
String coreApp = root.getAttributeValue("coreApp");
return "true".equals(coreApp);
}
private Integer readCompileVersionCode(XMLDocument manifestDocument) {
XMLElement root = manifestDocument.getDocumentElement();
String versionString = readVersionCodeString(root);
if(versionString==null){
return null;
}
try{
return Integer.parseInt(versionString);
}catch (NumberFormatException exception){
logMessage("NumberFormatException on manifest version reading: '"
+versionString+"': "+exception.getMessage());
return null;
}
}
private String readVersionCodeString(XMLElement manifestRoot){
String versionString = manifestRoot.getAttributeValue("android:compileSdkVersion");
if(versionString!=null){
return versionString;
}
versionString = manifestRoot.getAttributeValue("platformBuildVersionCode");
if(versionString!=null){
return versionString;
}
for(XMLElement element:manifestRoot.listChildElements()){
if(AndroidManifestBlock.TAG_uses_sdk.equals(element.getTagName())){
versionString = element.getAttributeValue("android:targetSdkVersion");
if(versionString!=null){
return versionString;
}
}
}
return null;
}
public Integer getAndroidFrameworkVersion(){
if(!hasAndroidManifestBlock()){
return null;
}
AndroidManifestBlock manifestBlock = getAndroidManifestBlock();
Integer version = manifestBlock.getTargetSdkVersion();
if(version == null){
version = manifestBlock.getPlatformBuildVersionCode();
}
if(version == null){
version = manifestBlock.getCompileSdkVersion();
}
return version;
}
public void removeResFilesWithEntry(int resourceId) {
removeResFilesWithEntry(resourceId, null, true);
}
public void removeResFilesWithEntry(int resourceId, ResConfig resConfig, boolean trimEntryArray) {
List<Entry> removedList = removeResFiles(resourceId, resConfig);
SpecTypePair specTypePair = null;
for(Entry entry:removedList){
if(entry == null || entry.isNull()){
continue;
}
if(trimEntryArray && specTypePair==null){
specTypePair = entry.getTypeBlock().getParentSpecTypePair();
}
entry.setNull(true);
}
if(specTypePair!=null){
specTypePair.removeNullEntries(resourceId);
}
}
public List<Entry> removeResFiles(int resourceId) {
return removeResFiles(resourceId, null);
}
public List<Entry> removeResFiles(int resourceId, ResConfig resConfig) {
List<Entry> results = new ArrayList<>();
if(resourceId == 0 && resConfig==null){
return results;
}
List<ResFile> resFileList = listResFiles(resourceId, resConfig);
APKArchive archive = getApkArchive();
for(ResFile resFile:resFileList){
results.addAll(resFile.getEntryList());
String path = resFile.getFilePath();
archive.remove(path);
}
return results;
}
public XMLDocument decodeXMLFile(String path) throws IOException, XMLException {
ResXmlDocument resXmlDocument = loadResXmlDocument(path);
AndroidManifestBlock manifestBlock = getAndroidManifestBlock();
int pkgId = manifestBlock.guessCurrentPackageId();
return resXmlDocument.decodeToXml(getTableBlock(), pkgId);
}
public List<DexFileInputSource> listDexFiles(){
List<DexFileInputSource> results=new ArrayList<>();
for(InputSource source:getApkArchive().listInputSources()){
if(DexFileInputSource.isDexName(source.getAlias())){
results.add(new DexFileInputSource(source.getAlias(), source));
}
}
DexFileInputSource.sort(results);
return results;
}
public boolean isBaseModule(){
if(!hasAndroidManifestBlock()){
return false;
}
AndroidManifestBlock manifestBlock;
try {
manifestBlock=getAndroidManifestBlock();
return manifestBlock.getMainActivity()!=null;
} catch (Exception ignored) {
return false;
}
}
public String getModuleName(){
return moduleName;
}
public void writeApk(File file) throws IOException {
writeApk(file, null);
}
public void writeApk(File file, WriteProgress progress) throws IOException {
writeApk(file, progress, null);
}
public void writeApk(File file, WriteProgress progress, WriteInterceptor interceptor) throws IOException {
uncompressNonXmlResFiles();
APKArchive archive=getApkArchive();
InputSource table=archive.getInputSource(TableBlock.FILE_NAME);
if(table!=null){
table.setMethod(ZipEntry.STORED);
}
UncompressedFiles uf=getUncompressedFiles();
uf.apply(archive);
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);
}
ApkWriter apkWriter = new ApkWriter(file, archive.listInputSources());
apkWriter.setAPKLogger(getApkLogger());
apkWriter.setWriteProgress(progress);
apkWriter.setApkSignatureBlock(getApkSignatureBlock());
apkWriter.write();
apkWriter.close();
}
private void uncompressNonXmlResFiles() {
for(ResFile resFile:listResFiles()){
if(resFile.isBinaryXml()){
continue;
}
resFile.getInputSource().setMethod(ZipEntry.STORED);
}
}
public UncompressedFiles getUncompressedFiles(){
return mUncompressedFiles;
}
public void removeDir(String dirName){
getApkArchive().removeDir(dirName);
}
public void validateResourcesDir() {
List<ResFile> resFileList = listResFiles();
Set<String> existPaths=new HashSet<>();
List<InputSource> sourceList = getApkArchive().listInputSources();
for(InputSource inputSource:sourceList){
existPaths.add(inputSource.getAlias());
}
for(ResFile resFile:resFileList){
String path=resFile.getFilePath();
String pathNew=resFile.validateTypeDirectoryName();
if(pathNew==null || pathNew.equals(path)){
continue;
}
if(existPaths.contains(pathNew)){
continue;
}
existPaths.remove(path);
existPaths.add(pathNew);
resFile.setFilePath(pathNew);
if(resFile.getInputSource().getMethod() == ZipEntry.STORED){
getUncompressedFiles().replacePath(path, pathNew);
}
logVerbose("Dir validated: '"+path+"' -> '"+pathNew+"'");
}
TableStringPool stringPool= getTableBlock().getStringPool();
stringPool.refreshUniqueIdMap();
getTableBlock().refresh();
}
public void setResourcesRootDir(String dirName) {
List<ResFile> resFileList = listResFiles();
Set<String> existPaths=new HashSet<>();
List<InputSource> sourceList = getApkArchive().listInputSources();
for(InputSource inputSource:sourceList){
existPaths.add(inputSource.getAlias());
}
for(ResFile resFile:resFileList){
String path=resFile.getFilePath();
String pathNew=ApkUtil.replaceRootDir(path, dirName);
if(existPaths.contains(pathNew)){
continue;
}
existPaths.remove(path);
existPaths.add(pathNew);
resFile.setFilePath(pathNew);
if(resFile.getInputSource().getMethod() == ZipEntry.STORED){
getUncompressedFiles().replacePath(path, pathNew);
}
logVerbose("Root changed: '"+path+"' -> '"+pathNew+"'");
}
TableStringPool stringPool= getTableBlock().getStringPool();
stringPool.refreshUniqueIdMap();
getTableBlock().refresh();
}
public List<ResFile> listResFiles() {
return listResFiles(0, null);
}
public List<ResFile> listResFiles(int resourceId, ResConfig resConfig) {
List<ResFile> results=new ArrayList<>();
TableBlock tableBlock=getTableBlock();
if (tableBlock==null){
return results;
}
TableStringPool stringPool= tableBlock.getStringPool();
for(InputSource inputSource:getApkArchive().listInputSources()){
String name=inputSource.getAlias();
StringGroup<TableString> groupTableString = stringPool.get(name);
if(groupTableString==null){
continue;
}
for(TableString tableString:groupTableString.listItems()){
List<Entry> entryList = filterResFileEntries(
tableString.listReferencedResValueEntries(), resourceId, resConfig);
if(entryList.size()==0){
continue;
}
ResFile resFile = new ResFile(inputSource, entryList);
results.add(resFile);
}
}
return results;
}
private List<Entry> filterResFileEntries(List<Entry> entryList, int resourceId, ResConfig resConfig){
if(resourceId == 0 && resConfig == null || entryList.size()==0){
return entryList;
}
List<Entry> results = new ArrayList<>();
for(Entry entry:entryList){
if(entry==null || entry.isNull()){
continue;
}
if(resourceId!=0 && resourceId!=entry.getResourceId()){
continue;
}
if(resConfig!=null && !resConfig.equals(entry.getResConfig())){
continue;
}
results.add(entry);
}
return results;
}
public String getPackageName(){
if(hasAndroidManifestBlock()){
return getAndroidManifestBlock().getPackageName();
}
if(!hasTableBlock()){
return null;
}
TableBlock tableBlock=getTableBlock();
PackageArray pkgArray = tableBlock.getPackageArray();
PackageBlock pkg = pkgArray.get(0);
if(pkg==null){
return null;
}
return pkg.getName();
}
public void setPackageName(String name) {
String old=getPackageName();
if(hasAndroidManifestBlock()){
getAndroidManifestBlock().setPackageName(name);
}
if(!hasTableBlock()){
return;
}
TableBlock tableBlock=getTableBlock();
PackageArray pkgArray = tableBlock.getPackageArray();
for(PackageBlock pkg:pkgArray.listItems()){
if(pkgArray.childesCount()==1){
pkg.setName(name);
continue;
}
String pkgName=pkg.getName();
if(pkgName.startsWith(old)){
pkgName=pkgName.replace(old, name);
pkg.setName(pkgName);
}
}
}
public boolean hasAndroidManifestBlock(){
return mManifestBlock!=null
|| getApkArchive().getInputSource(AndroidManifestBlock.FILE_NAME)!=null;
}
public boolean hasTableBlock(){
return mTableBlock!=null
|| getApkArchive().getInputSource(TableBlock.FILE_NAME)!=null;
}
public void destroy(){
getApkArchive().clear();
AndroidManifestBlock manifestBlock = this.mManifestBlock;
if(manifestBlock!=null){
manifestBlock.destroy();
this.mManifestBlock = null;
}
TableBlock tableBlock = this.mTableBlock;
if(tableBlock!=null){
tableBlock.destroy();
this.mTableBlock = null;
}
}
public void setManifest(AndroidManifestBlock manifestBlock){
APKArchive archive = getApkArchive();
if(manifestBlock==null){
mManifestBlock = null;
archive.remove(AndroidManifestBlock.FILE_NAME);
return;
}
manifestBlock.setApkFile(this);
BlockInputSource<AndroidManifestBlock> source =
new BlockInputSource<>(AndroidManifestBlock.FILE_NAME, manifestBlock);
archive.add(source);
mManifestBlock = manifestBlock;
}
public void setTableBlock(TableBlock tableBlock){
APKArchive archive = getApkArchive();
if(tableBlock==null){
mTableBlock = null;
archive.remove(TableBlock.FILE_NAME);
return;
}
tableBlock.setApkFile(this);
BlockInputSource<TableBlock> source =
new BlockInputSource<>(TableBlock.FILE_NAME, tableBlock);
archive.add(source);
mTableBlock = tableBlock;
}
@Override
public AndroidManifestBlock getAndroidManifestBlock() {
if(mManifestBlock!=null){
return mManifestBlock;
}
APKArchive archive=getApkArchive();
InputSource inputSource = archive.getInputSource(AndroidManifestBlock.FILE_NAME);
if(inputSource==null){
return null;
}
InputStream inputStream = null;
try {
inputStream = inputSource.openStream();
AndroidManifestBlock manifestBlock=AndroidManifestBlock.load(inputStream);
inputStream.close();
BlockInputSource<AndroidManifestBlock> blockInputSource=new BlockInputSource<>(inputSource.getName(),manifestBlock);
blockInputSource.setSort(inputSource.getSort());
blockInputSource.setMethod(inputSource.getMethod());
archive.add(blockInputSource);
manifestBlock.setApkFile(this);
mManifestBlock = manifestBlock;
onManifestBlockLoaded(manifestBlock);
} catch (IOException exception) {
throw new IllegalArgumentException(exception);
}
return mManifestBlock;
}
private void onManifestBlockLoaded(AndroidManifestBlock manifestBlock){
initializeApkType(manifestBlock);
}
public TableBlock getTableBlock(boolean initFramework) {
if(mTableBlock==null){
if(!hasTableBlock()){
return null;
}
try {
mTableBlock = loadTableBlock();
if(initFramework && loadDefaultFramework){
Integer version = getAndroidFrameworkVersion();
initializeAndroidFramework(mTableBlock, version);
}
} catch (IOException exception) {
throw new IllegalArgumentException(exception);
}
}
return mTableBlock;
}
@Override
public TableBlock getTableBlock() {
return getTableBlock(!mDisableLoadFramework);
}
@Override
public ResXmlDocument loadResXmlDocument(String path) throws IOException{
InputSource inputSource = getApkArchive().getInputSource(path);
if(inputSource==null){
throw new FileNotFoundException("No such file in apk: " + path);
}
return loadResXmlDocument(inputSource);
}
public ResXmlDocument loadResXmlDocument(InputSource inputSource) throws IOException{
ResXmlDocument resXmlDocument = new ResXmlDocument();
resXmlDocument.setApkFile(this);
resXmlDocument.readBytes(inputSource.openStream());
return resXmlDocument;
}
@Override
public Decoder getDecoder(){
return mDecoder;
}
@Override
public void setDecoder(Decoder decoder){
this.mDecoder = decoder;
}
public ApkType getApkType(){
if(mApkType!=null){
return mApkType;
}
return initializeApkType(mManifestBlock);
}
public void setApkType(ApkType apkType){
this.mApkType = apkType;
}
private ApkType initializeApkType(AndroidManifestBlock manifestBlock){
if(mApkType!=null){
return mApkType;
}
ApkType apkType = null;
if(manifestBlock!=null){
apkType = manifestBlock.guessApkType();
}
if(apkType != null){
mApkType = apkType;
}else {
apkType = ApkType.UNKNOWN;
}
return apkType;
}
// If we need TableStringPool only, this loads pool without
// loading packages and other chunk blocks for faster and less memory usage
public TableStringPool getVolatileTableStringPool() throws IOException{
if(mTableBlock!=null){
return mTableBlock.getStringPool();
}
InputSource inputSource = getApkArchive()
.getInputSource(TableBlock.FILE_NAME);
if(inputSource==null){
throw new IOException("Module don't have: "+TableBlock.FILE_NAME);
}
if((inputSource instanceof ZipEntrySource)
||(inputSource instanceof FileInputSource)){
InputStream inputStream = inputSource.openStream();
TableStringPool stringPool = TableStringPool.readFromTable(inputStream);
inputStream.close();
return stringPool;
}
return getTableBlock().getStringPool();
}
TableBlock loadTableBlock() throws IOException {
APKArchive archive=getApkArchive();
InputSource inputSource = archive.getInputSource(TableBlock.FILE_NAME);
if(inputSource==null){
throw new IOException("Entry not found: "+TableBlock.FILE_NAME);
}
TableBlock tableBlock;
if(inputSource instanceof SplitJsonTableInputSource){
tableBlock=((SplitJsonTableInputSource)inputSource).getTableBlock();
}else if(inputSource instanceof SingleJsonTableInputSource){
tableBlock=((SingleJsonTableInputSource)inputSource).getTableBlock();
}else if(inputSource instanceof BlockInputSource){
Chunk<?> block = ((BlockInputSource<?>) inputSource).getBlock();
tableBlock = (TableBlock) block;
}else {
InputStream inputStream = inputSource.openStream();
tableBlock = TableBlock.load(inputStream);
inputStream.close();
}
BlockInputSource<TableBlock> blockInputSource=new BlockInputSource<>(inputSource.getName(), tableBlock);
blockInputSource.setMethod(inputSource.getMethod());
blockInputSource.setSort(inputSource.getSort());
archive.add(blockInputSource);
tableBlock.setApkFile(this);
return tableBlock;
}
public APKArchive getApkArchive() {
return apkArchive;
}
public void setLoadDefaultFramework(boolean loadDefaultFramework) {
this.loadDefaultFramework = loadDefaultFramework;
this.mDisableLoadFramework = !loadDefaultFramework;
}
public void merge(ApkModule module) throws IOException {
if(module==null||module==this){
return;
}
logMessage("Merging: "+module.getModuleName());
mergeDexFiles(module);
mergeTable(module);
mergeFiles(module);
getUncompressedFiles().merge(module.getUncompressedFiles());
}
private void mergeTable(ApkModule module) {
if(!module.hasTableBlock()){
return;
}
logMessage("Merging resource table: "+module.getModuleName());
TableBlock exist;
if(!hasTableBlock()){
exist=new TableBlock();
BlockInputSource<TableBlock> inputSource=new BlockInputSource<>(TableBlock.FILE_NAME, exist);
getApkArchive().add(inputSource);
}else{
exist=getTableBlock();
}
TableBlock coming=module.getTableBlock();
exist.merge(coming);
}
private void mergeFiles(ApkModule module) {
APKArchive archiveExist = getApkArchive();
APKArchive archiveComing = module.getApkArchive();
Map<String, InputSource> comingAlias=ApkUtil.toAliasMap(archiveComing.listInputSources());
Map<String, InputSource> existAlias=ApkUtil.toAliasMap(archiveExist.listInputSources());
UncompressedFiles uncompressedFiles = module.getUncompressedFiles();
for(InputSource inputSource:comingAlias.values()){
if(existAlias.containsKey(inputSource.getAlias())||existAlias.containsKey(inputSource.getName())){
continue;
}
if(DexFileInputSource.isDexName(inputSource.getName())){
continue;
}
if (inputSource.getAlias().startsWith("lib/")){
uncompressedFiles.removePath(inputSource.getAlias());
}
logVerbose("Added: "+inputSource.getAlias());
archiveExist.add(inputSource);
}
}
private void mergeDexFiles(ApkModule module){
UncompressedFiles uncompressedFiles=module.getUncompressedFiles();
List<DexFileInputSource> existList=listDexFiles();
List<DexFileInputSource> comingList=module.listDexFiles();
APKArchive archive=getApkArchive();
int index=0;
if(existList.size()>0){
index=existList.get(existList.size()-1).getDexNumber();
if(index==0){
index=2;
}else {
index++;
}
}
for(DexFileInputSource source:comingList){
uncompressedFiles.removePath(source.getAlias());
String name= DexFileInputSource.getDexName(index);
DexFileInputSource add=new DexFileInputSource(name, source.getInputSource());
archive.add(add);
logMessage("Added ["+module.getModuleName()+"] "
+source.getAlias()+" -> "+name);
index++;
if(index==1){
index=2;
}
}
}
APKLogger getApkLogger(){
return apkLogger;
}
public void setAPKLogger(APKLogger logger) {
this.apkLogger = logger;
}
void logMessage(String msg) {
if(apkLogger!=null){
apkLogger.logMessage(msg);
}
}
private void logError(String msg, Throwable tr) {
if(apkLogger!=null){
apkLogger.logError(msg, tr);
}
}
private void logVerbose(String msg) {
if(apkLogger!=null){
apkLogger.logVerbose(msg);
}
}
@Override
public String toString(){
return getModuleName();
}
public static ApkModule loadApkFile(File apkFile) throws IOException {
return loadApkFile(apkFile, ApkUtil.DEF_MODULE_NAME);
}
public static ApkModule loadApkFile(File apkFile, String moduleName) throws IOException {
Archive archive = new Archive(apkFile);
ApkModule apkModule = new ApkModule(moduleName, archive.createAPKArchive());
apkModule.setApkSignatureBlock(archive.getApkSignatureBlock());
return apkModule;
}
}