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

View File

@ -124,14 +124,14 @@ import java.util.*;
resFile.getInputSource().write(outputStream);
outputStream.close();
addDecodedEntry(resFile.pickOne());
addDecodedEntry(entry);
}
private void decodeResXml(EntryStore entryStore, File outDir, ResFile resFile)
throws IOException, XMLException{
Entry entry =resFile.pickOne();
PackageBlock packageBlock= entry.getPackageBlock();
ResXmlDocument resXmlDocument =new ResXmlDocument();
resXmlDocument.readBytes(resFile.getInputSource().openStream());
ResXmlDocument resXmlDocument = apkModule.loadResXmlDocument(
resFile.getInputSource().getName());
File pkgDir=new File(outDir, getPackageDirName(packageBlock));
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(manifestSource);
}
public FrameworkTable getTableBlock() throws IOException {
@Override
public FrameworkTable getTableBlock() {
return (FrameworkTable) super.getTableBlock();
}
@Override
@ -67,6 +68,7 @@ public class FrameworkApk extends ApkModule{
}
InputStream inputStream = inputSource.openStream();
FrameworkTable frameworkTable=FrameworkTable.load(inputStream);
frameworkTable.setApkFile(this);
if(hasAndroidManifestBlock()){
optimizeTable(frameworkTable);
}

View File

@ -129,19 +129,6 @@ public class ResFile {
}
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){
String path=getFilePath();
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;
import com.reandroid.arsc.ApkFile;
import com.reandroid.arsc.pool.StringPool;
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;
import com.reandroid.arsc.ApkFile;
import com.reandroid.arsc.BuildInfo;
import com.reandroid.arsc.array.PackageArray;
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.TableHeader;
import com.reandroid.arsc.io.BlockReader;
import com.reandroid.arsc.pool.StringPool;
import com.reandroid.arsc.pool.TableStringPool;
import com.reandroid.arsc.value.StagedAliasEntry;
import com.reandroid.common.EntryStore;
import com.reandroid.common.Frameworks;
import com.reandroid.json.JSONConvert;
import com.reandroid.json.JSONArray;
import com.reandroid.json.JSONObject;
import java.io.*;
import java.util.Collection;
import java.util.HashSet;
import java.util.Set;
import java.util.*;
public class TableBlock extends Chunk<TableHeader>
implements MainChunk, JSONConvert<JSONObject> {
public class TableBlock extends Chunk<TableHeader>
implements MainChunk, JSONConvert<JSONObject>, EntryStore {
private final TableStringPool mTableStringPool;
private final PackageArray mPackageArray;
private final Set<TableBlock> mFrameWorks=new HashSet<>();
private ApkFile mApkFile;
public TableBlock() {
super(new TableHeader(), 2);
TableHeader header = getHeaderBlock();
@ -61,6 +61,14 @@ public class TableBlock extends Chunk<TableHeader>
public TableStringPool getStringPool() {
return mTableStringPool;
}
@Override
public ApkFile getApkFile(){
return mApkFile;
}
@Override
public void setApkFile(ApkFile apkFile){
this.mApkFile = apkFile;
}
public TableStringPool getTableStringPool(){
return mTableStringPool;
}
@ -132,16 +140,23 @@ public class TableBlock extends Chunk<TableHeader>
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){
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){
return null;
}
@ -156,14 +171,36 @@ public class TableBlock extends Chunk<TableHeader>
return entryGroup;
}
}
for(TableBlock tableBlock:getFrameWorks()){
EntryGroup entryGroup = tableBlock.search(resourceId);
if(entryGroup!=null){
return entryGroup;
}
}
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){
for(PackageBlock packageBlock:listPackages()){
StagedAliasEntry stagedAliasEntry =
@ -216,6 +253,16 @@ public class TableBlock extends Chunk<TableHeader>
getPackageArray().merge(tableBlock.getPackageArray());
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{
TableBlock tableBlock=load(inputStream);
tableBlock.addFramework(Frameworks.getAndroid());

View File

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

View File

@ -15,10 +15,8 @@
*/
package com.reandroid.arsc.chunk.xml;
import com.reandroid.arsc.chunk.Chunk;
import com.reandroid.arsc.chunk.ChunkType;
import com.reandroid.arsc.chunk.MainChunk;
import com.reandroid.arsc.chunk.ParentChunk;
import com.reandroid.arsc.ApkFile;
import com.reandroid.arsc.chunk.*;
import com.reandroid.arsc.container.SingleBlockContainer;
import com.reandroid.arsc.header.HeaderBlock;
import com.reandroid.arsc.io.BlockReader;
@ -45,6 +43,7 @@
private final ResXmlIDMap mResXmlIDMap;
private ResXmlElement mResXmlElement;
private final SingleBlockContainer<ResXmlElement> mResXmlElementContainer;
private ApkFile mApkFile;
public ResXmlDocument() {
super(new HeaderBlock(ChunkType.XML),3);
this.mResXmlStringPool=new ResXmlStringPool(true);
@ -169,6 +168,14 @@
return mResXmlStringPool;
}
@Override
public ApkFile getApkFile(){
return mApkFile;
}
@Override
public void setApkFile(ApkFile apkFile){
this.mApkFile = apkFile;
}
@Override
public StringPool<?> getSpecStringPool() {
return null;
}
@ -224,6 +231,24 @@
xmlElement.fromJson(json.optJSONObject(ResXmlDocument.NAME_element));
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 {
XMLDocument xmlDocument = new XMLDocument();
XMLElement xmlElement = getResXmlElement()