make ApkFile as parent

This commit is contained in:
REAndroid 2023-03-06 11:51:50 -05:00
parent 76aa8ec7d7
commit 71437db825
9 changed files with 190 additions and 67 deletions

View File

@ -16,6 +16,7 @@
package com.reandroid.apk; package com.reandroid.apk;
import com.reandroid.archive.*; import com.reandroid.archive.*;
import com.reandroid.arsc.ApkFile;
import com.reandroid.arsc.array.PackageArray; import com.reandroid.arsc.array.PackageArray;
import com.reandroid.arsc.chunk.Chunk; import com.reandroid.arsc.chunk.Chunk;
import com.reandroid.arsc.chunk.PackageBlock; import com.reandroid.arsc.chunk.PackageBlock;
@ -34,12 +35,13 @@ import com.reandroid.xml.XMLDocument;
import com.reandroid.xml.XMLException; import com.reandroid.xml.XMLException;
import java.io.File; import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.util.*; import java.util.*;
import java.util.zip.ZipEntry; import java.util.zip.ZipEntry;
public class ApkModule { public class ApkModule implements ApkFile {
private final String moduleName; private final String moduleName;
private final APKArchive apkArchive; private final APKArchive apkArchive;
private boolean loadDefaultFramework = true; private boolean loadDefaultFramework = true;
@ -91,9 +93,7 @@ public class ApkModule {
return results; return results;
} }
public XMLDocument decodeXMLFile(String path) throws IOException, XMLException { public XMLDocument decodeXMLFile(String path) throws IOException, XMLException {
InputSource inputSource = apkArchive.getInputSource(path); ResXmlDocument resXmlDocument = loadResXmlDocument(path);
ResXmlDocument resXmlDocument = new ResXmlDocument();
resXmlDocument.readBytes(inputSource.openStream());
AndroidManifestBlock manifestBlock = getAndroidManifestBlock(); AndroidManifestBlock manifestBlock = getAndroidManifestBlock();
int pkgId = manifestBlock.guessCurrentPackageId(); int pkgId = manifestBlock.guessCurrentPackageId();
return resXmlDocument.decodeToXml(getTableEntryStore(), pkgId); return resXmlDocument.decodeToXml(getTableEntryStore(), pkgId);
@ -124,10 +124,10 @@ public class ApkModule {
AndroidManifestBlock manifestBlock; AndroidManifestBlock manifestBlock;
try { try {
manifestBlock=getAndroidManifestBlock(); manifestBlock=getAndroidManifestBlock();
} catch (IOException ignored) { return manifestBlock.getMainActivity()!=null;
} catch (Exception ignored) {
return false; return false;
} }
return manifestBlock.getMainActivity()!=null;
} }
public String getModuleName(){ public String getModuleName(){
return moduleName; return moduleName;
@ -315,35 +315,61 @@ public class ApkModule {
return mManifestBlock!=null return mManifestBlock!=null
|| getApkArchive().getInputSource(AndroidManifestBlock.FILE_NAME)!=null; || getApkArchive().getInputSource(AndroidManifestBlock.FILE_NAME)!=null;
} }
public AndroidManifestBlock getAndroidManifestBlock() throws IOException { public boolean hasTableBlock(){
return mTableBlock!=null
|| getApkArchive().getInputSource(TableBlock.FILE_NAME)!=null;
}
public AndroidManifestBlock getAndroidManifestBlock() {
if(mManifestBlock!=null){ if(mManifestBlock!=null){
return mManifestBlock; return mManifestBlock;
} }
APKArchive archive=getApkArchive(); APKArchive archive=getApkArchive();
InputSource inputSource = archive.getInputSource(AndroidManifestBlock.FILE_NAME); InputSource inputSource = archive.getInputSource(AndroidManifestBlock.FILE_NAME);
if(inputSource==null){ if(inputSource==null){
throw new IOException("Entry not found: "+AndroidManifestBlock.FILE_NAME); 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;
} catch (IOException exception) {
throw new IllegalArgumentException(exception);
} }
InputStream 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);
mManifestBlock=manifestBlock;
return mManifestBlock; return mManifestBlock;
} }
public boolean hasTableBlock(){ @Override
return mTableBlock!=null public TableBlock getTableBlock() {
|| getApkArchive().getInputSource(TableBlock.FILE_NAME)!=null;
}
public TableBlock getTableBlock() throws IOException {
if(mTableBlock==null){ if(mTableBlock==null){
mTableBlock=loadTableBlock(); if(!hasTableBlock()){
return null;
}
try {
mTableBlock = loadTableBlock();
} catch (IOException exception) {
throw new IllegalArgumentException(exception);
}
} }
return mTableBlock; return mTableBlock;
} }
@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);
}
ResXmlDocument resXmlDocument = new ResXmlDocument();
resXmlDocument.setApkFile(this);
resXmlDocument.readBytes(inputSource.openStream());
return resXmlDocument;
}
// If we need TableStringPool only, this loads pool without // If we need TableStringPool only, this loads pool without
// loading packages and other chunk blocks for faster and less memory usage // loading packages and other chunk blocks for faster and less memory usage
public TableStringPool getVolatileTableStringPool() throws IOException{ public TableStringPool getVolatileTableStringPool() throws IOException{
@ -391,6 +417,7 @@ public class ApkModule {
blockInputSource.setMethod(inputSource.getMethod()); blockInputSource.setMethod(inputSource.getMethod());
blockInputSource.setSort(inputSource.getSort()); blockInputSource.setSort(inputSource.getSort());
archive.add(blockInputSource); archive.add(blockInputSource);
tableBlock.setApkFile(this);
return tableBlock; return tableBlock;
} }
public APKArchive getApkArchive() { public APKArchive getApkArchive() {

View File

@ -124,14 +124,14 @@ import java.util.*;
resFile.getInputSource().write(outputStream); resFile.getInputSource().write(outputStream);
outputStream.close(); outputStream.close();
addDecodedEntry(resFile.pickOne()); addDecodedEntry(entry);
} }
private void decodeResXml(EntryStore entryStore, File outDir, ResFile resFile) private void decodeResXml(EntryStore entryStore, File outDir, ResFile resFile)
throws IOException, XMLException{ throws IOException, XMLException{
Entry entry =resFile.pickOne(); Entry entry =resFile.pickOne();
PackageBlock packageBlock= entry.getPackageBlock(); PackageBlock packageBlock= entry.getPackageBlock();
ResXmlDocument resXmlDocument =new ResXmlDocument(); ResXmlDocument resXmlDocument = apkModule.loadResXmlDocument(
resXmlDocument.readBytes(resFile.getInputSource().openStream()); resFile.getInputSource().getName());
File pkgDir=new File(outDir, getPackageDirName(packageBlock)); File pkgDir=new File(outDir, getPackageDirName(packageBlock));
File resDir=new File(pkgDir, ApkUtil.RES_DIR_NAME); File resDir=new File(pkgDir, ApkUtil.RES_DIR_NAME);

View File

@ -55,7 +55,8 @@ public class FrameworkApk extends ApkModule{
archive.add(tableSource); archive.add(tableSource);
archive.add(manifestSource); archive.add(manifestSource);
} }
public FrameworkTable getTableBlock() throws IOException { @Override
public FrameworkTable getTableBlock() {
return (FrameworkTable) super.getTableBlock(); return (FrameworkTable) super.getTableBlock();
} }
@Override @Override
@ -67,6 +68,7 @@ public class FrameworkApk extends ApkModule{
} }
InputStream inputStream = inputSource.openStream(); InputStream inputStream = inputSource.openStream();
FrameworkTable frameworkTable=FrameworkTable.load(inputStream); FrameworkTable frameworkTable=FrameworkTable.load(inputStream);
frameworkTable.setApkFile(this);
if(hasAndroidManifestBlock()){ if(hasAndroidManifestBlock()){
optimizeTable(frameworkTable); optimizeTable(frameworkTable);
} }

View File

@ -129,19 +129,6 @@ public class ResFile {
} }
return mBinXml; return mBinXml;
} }
public boolean dumpToJson(File rootDir) throws IOException {
if(!isBinaryXml()){
return false;
}
String fileName=getFilePath()+ApkUtil.JSON_FILE_EXTENSION;
fileName=fileName.replace('/', File.separatorChar);
File file=new File(rootDir, fileName);
ResXmlDocument resXmlDocument =new ResXmlDocument();
resXmlDocument.readBytes(getInputSource().openStream());
JSONObject jsonObject= resXmlDocument.toJson();
jsonObject.write(file);
return true;
}
public File buildOutFile(File dir){ public File buildOutFile(File dir){
String path=getFilePath(); String path=getFilePath();
path=path.replace('/', File.separatorChar); path=path.replace('/', File.separatorChar);

View File

@ -0,0 +1,28 @@
/*
* 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;
import com.reandroid.arsc.chunk.TableBlock;
import com.reandroid.arsc.chunk.xml.AndroidManifestBlock;
import com.reandroid.arsc.chunk.xml.ResXmlDocument;
import java.io.IOException;
public interface ApkFile {
AndroidManifestBlock getAndroidManifestBlock();
TableBlock getTableBlock();
ResXmlDocument loadResXmlDocument(String path) throws IOException;
}

View File

@ -1,7 +1,10 @@
package com.reandroid.arsc.chunk; package com.reandroid.arsc.chunk;
import com.reandroid.arsc.ApkFile;
import com.reandroid.arsc.pool.StringPool; import com.reandroid.arsc.pool.StringPool;
public interface MainChunk { public interface MainChunk {
public StringPool<?> getStringPool(); StringPool<?> getStringPool();
ApkFile getApkFile();
void setApkFile(ApkFile apkFile);
} }

View File

@ -15,6 +15,7 @@
*/ */
package com.reandroid.arsc.chunk; package com.reandroid.arsc.chunk;
import com.reandroid.arsc.ApkFile;
import com.reandroid.arsc.BuildInfo; import com.reandroid.arsc.BuildInfo;
import com.reandroid.arsc.array.PackageArray; import com.reandroid.arsc.array.PackageArray;
import com.reandroid.arsc.group.EntryGroup; import com.reandroid.arsc.group.EntryGroup;
@ -22,24 +23,23 @@ import com.reandroid.arsc.header.HeaderBlock;
import com.reandroid.arsc.header.InfoHeader; import com.reandroid.arsc.header.InfoHeader;
import com.reandroid.arsc.header.TableHeader; import com.reandroid.arsc.header.TableHeader;
import com.reandroid.arsc.io.BlockReader; import com.reandroid.arsc.io.BlockReader;
import com.reandroid.arsc.pool.StringPool;
import com.reandroid.arsc.pool.TableStringPool; import com.reandroid.arsc.pool.TableStringPool;
import com.reandroid.arsc.value.StagedAliasEntry; import com.reandroid.arsc.value.StagedAliasEntry;
import com.reandroid.common.EntryStore;
import com.reandroid.common.Frameworks; import com.reandroid.common.Frameworks;
import com.reandroid.json.JSONConvert; import com.reandroid.json.JSONConvert;
import com.reandroid.json.JSONArray; import com.reandroid.json.JSONArray;
import com.reandroid.json.JSONObject; import com.reandroid.json.JSONObject;
import java.io.*; import java.io.*;
import java.util.Collection; import java.util.*;
import java.util.HashSet;
import java.util.Set;
public class TableBlock extends Chunk<TableHeader> public class TableBlock extends Chunk<TableHeader>
implements MainChunk, JSONConvert<JSONObject> { implements MainChunk, JSONConvert<JSONObject>, EntryStore {
private final TableStringPool mTableStringPool; private final TableStringPool mTableStringPool;
private final PackageArray mPackageArray; private final PackageArray mPackageArray;
private final Set<TableBlock> mFrameWorks=new HashSet<>(); private final Set<TableBlock> mFrameWorks=new HashSet<>();
private ApkFile mApkFile;
public TableBlock() { public TableBlock() {
super(new TableHeader(), 2); super(new TableHeader(), 2);
TableHeader header = getHeaderBlock(); TableHeader header = getHeaderBlock();
@ -61,6 +61,14 @@ public class TableBlock extends Chunk<TableHeader>
public TableStringPool getStringPool() { public TableStringPool getStringPool() {
return mTableStringPool; return mTableStringPool;
} }
@Override
public ApkFile getApkFile(){
return mApkFile;
}
@Override
public void setApkFile(ApkFile apkFile){
this.mApkFile = apkFile;
}
public TableStringPool getTableStringPool(){ public TableStringPool getTableStringPool(){
return mTableStringPool; return mTableStringPool;
} }
@ -132,16 +140,23 @@ public class TableBlock extends Chunk<TableHeader>
return length; return length;
} }
@Override
public String toString(){
StringBuilder builder=new StringBuilder();
builder.append(super.toString());
builder.append(", packages=");
int pkgCount=mPackageArray.childesCount();
builder.append(pkgCount);
return builder.toString();
}
public EntryGroup search(int resourceId){ public EntryGroup search(int resourceId){
if(resourceId==0){
return null;
}
EntryGroup entryGroup = searchLocal(resourceId);
if(entryGroup!=null){
return entryGroup;
}
for(TableBlock tableBlock:getFrameWorks()){
entryGroup = tableBlock.search(resourceId);
if(entryGroup!=null){
return entryGroup;
}
}
return null;
}
private EntryGroup searchLocal(int resourceId){
if(resourceId==0){ if(resourceId==0){
return null; return null;
} }
@ -156,14 +171,36 @@ public class TableBlock extends Chunk<TableHeader>
return entryGroup; return entryGroup;
} }
} }
for(TableBlock tableBlock:getFrameWorks()){
EntryGroup entryGroup = tableBlock.search(resourceId);
if(entryGroup!=null){
return entryGroup;
}
}
return null; return null;
} }
@Override
public Collection<EntryGroup> getEntryGroups(int resourceId) {
List<EntryGroup> results = new ArrayList<>();
EntryGroup entryGroup = searchLocal(resourceId);
if(entryGroup!=null){
results.add(entryGroup);
}
for(TableBlock framework:getFrameWorks()){
results.addAll(framework.getEntryGroups(resourceId));
}
return results;
}
@Override
public EntryGroup getEntryGroup(int resourceId) {
return search(resourceId);
}
@Override
public Collection<PackageBlock> getPackageBlocks(int packageId) {
List<PackageBlock> results=new ArrayList<>();
PackageBlock packageBlock = getPackageBlockById(packageId);
if(packageBlock!=null){
results.add(packageBlock);
}
for(TableBlock tableBlock:getFrameWorks()){
results.addAll(tableBlock.getPackageBlocks(packageId));
}
return results;
}
public int searchResourceIdAlias(int resourceId){ public int searchResourceIdAlias(int resourceId){
for(PackageBlock packageBlock:listPackages()){ for(PackageBlock packageBlock:listPackages()){
StagedAliasEntry stagedAliasEntry = StagedAliasEntry stagedAliasEntry =
@ -216,6 +253,16 @@ public class TableBlock extends Chunk<TableHeader>
getPackageArray().merge(tableBlock.getPackageArray()); getPackageArray().merge(tableBlock.getPackageArray());
refresh(); refresh();
} }
@Override
public String toString(){
StringBuilder builder=new StringBuilder();
builder.append(super.toString());
builder.append(", packages=");
int pkgCount=mPackageArray.childesCount();
builder.append(pkgCount);
return builder.toString();
}
public static TableBlock loadWithAndroidFramework(InputStream inputStream) throws IOException{ public static TableBlock loadWithAndroidFramework(InputStream inputStream) throws IOException{
TableBlock tableBlock=load(inputStream); TableBlock tableBlock=load(inputStream);
tableBlock.addFramework(Frameworks.getAndroid()); tableBlock.addFramework(Frameworks.getAndroid());

View File

@ -25,13 +25,17 @@ import java.util.ArrayList;
import java.util.List; import java.util.List;
public class AndroidManifestBlock extends ResXmlDocument { public class AndroidManifestBlock extends ResXmlDocument {
private int mGuessedPackageId;
public AndroidManifestBlock(){ public AndroidManifestBlock(){
super(); super();
super.getStringPool().setUtf8(false); super.getStringPool().setUtf8(false);
} }
// TODO: find a better way // TODO: find a better way
public byte guessCurrentPackageId(){ public int guessCurrentPackageId(){
return (byte) ((getIconResourceId()>>24) & 0xff); if(mGuessedPackageId == 0){
mGuessedPackageId = ((getIconResourceId()>>24) & 0xff);
}
return mGuessedPackageId;
} }
public int getIconResourceId(){ public int getIconResourceId(){
ResXmlElement applicationElement = getApplicationElement(); ResXmlElement applicationElement = getApplicationElement();

View File

@ -15,10 +15,8 @@
*/ */
package com.reandroid.arsc.chunk.xml; package com.reandroid.arsc.chunk.xml;
import com.reandroid.arsc.chunk.Chunk; import com.reandroid.arsc.ApkFile;
import com.reandroid.arsc.chunk.ChunkType; import com.reandroid.arsc.chunk.*;
import com.reandroid.arsc.chunk.MainChunk;
import com.reandroid.arsc.chunk.ParentChunk;
import com.reandroid.arsc.container.SingleBlockContainer; import com.reandroid.arsc.container.SingleBlockContainer;
import com.reandroid.arsc.header.HeaderBlock; import com.reandroid.arsc.header.HeaderBlock;
import com.reandroid.arsc.io.BlockReader; import com.reandroid.arsc.io.BlockReader;
@ -45,6 +43,7 @@
private final ResXmlIDMap mResXmlIDMap; private final ResXmlIDMap mResXmlIDMap;
private ResXmlElement mResXmlElement; private ResXmlElement mResXmlElement;
private final SingleBlockContainer<ResXmlElement> mResXmlElementContainer; private final SingleBlockContainer<ResXmlElement> mResXmlElementContainer;
private ApkFile mApkFile;
public ResXmlDocument() { public ResXmlDocument() {
super(new HeaderBlock(ChunkType.XML),3); super(new HeaderBlock(ChunkType.XML),3);
this.mResXmlStringPool=new ResXmlStringPool(true); this.mResXmlStringPool=new ResXmlStringPool(true);
@ -169,6 +168,14 @@
return mResXmlStringPool; return mResXmlStringPool;
} }
@Override @Override
public ApkFile getApkFile(){
return mApkFile;
}
@Override
public void setApkFile(ApkFile apkFile){
this.mApkFile = apkFile;
}
@Override
public StringPool<?> getSpecStringPool() { public StringPool<?> getSpecStringPool() {
return null; return null;
} }
@ -224,6 +231,24 @@
xmlElement.fromJson(json.optJSONObject(ResXmlDocument.NAME_element)); xmlElement.fromJson(json.optJSONObject(ResXmlDocument.NAME_element));
refresh(); refresh();
} }
public XMLDocument decodeToXml() throws XMLException {
ApkFile apkFile = getApkFile();
if(apkFile == null){
throw new XMLException("Null parent apk file");
}
int currentPackageId = 0;
AndroidManifestBlock manifestBlock;
if(this instanceof AndroidManifestBlock){
manifestBlock = ((AndroidManifestBlock)this);
}else {
manifestBlock = apkFile.getAndroidManifestBlock();
}
if(manifestBlock!=null){
currentPackageId = manifestBlock.guessCurrentPackageId();
}
TableBlock tableBlock = apkFile.getTableBlock();
return decodeToXml(tableBlock, currentPackageId);
}
public XMLDocument decodeToXml(EntryStore entryStore, int currentPackageId) throws XMLException { public XMLDocument decodeToXml(EntryStore entryStore, int currentPackageId) throws XMLException {
XMLDocument xmlDocument = new XMLDocument(); XMLDocument xmlDocument = new XMLDocument();
XMLElement xmlElement = getResXmlElement() XMLElement xmlElement = getResXmlElement()