continent method for framework

This commit is contained in:
REAndroid 2023-04-29 19:18:08 +02:00
parent 17a9e18fd2
commit f1f6300e64
2 changed files with 273 additions and 264 deletions

View File

@ -1,270 +1,279 @@
/* /*
* 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");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at * You may obtain a copy of the License at
* *
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* *
* Unless required by applicable law or agreed to in writing, software * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* 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; package com.reandroid.apk;
import com.reandroid.archive.APKArchive; import com.reandroid.archive.APKArchive;
import com.reandroid.archive.ByteInputSource; import com.reandroid.archive.ByteInputSource;
import com.reandroid.archive.InputSource; import com.reandroid.archive.InputSource;
import com.reandroid.archive.InputSourceUtil; import com.reandroid.archive.InputSourceUtil;
import com.reandroid.arsc.chunk.PackageBlock; import com.reandroid.archive2.Archive;
import com.reandroid.arsc.chunk.TableBlock; import com.reandroid.arsc.chunk.PackageBlock;
import com.reandroid.arsc.chunk.xml.AndroidManifestBlock; import com.reandroid.arsc.chunk.TableBlock;
import com.reandroid.arsc.chunk.xml.ResXmlAttribute; import com.reandroid.arsc.chunk.xml.AndroidManifestBlock;
import com.reandroid.arsc.chunk.xml.ResXmlElement; import com.reandroid.arsc.chunk.xml.ResXmlAttribute;
import com.reandroid.arsc.util.FrameworkTable; import com.reandroid.arsc.chunk.xml.ResXmlElement;
import com.reandroid.arsc.value.ValueType; import com.reandroid.arsc.util.FrameworkTable;
import com.reandroid.arsc.value.ValueType;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.util.Map; import java.util.Map;
import java.util.Objects; import java.util.Objects;
/* /*
* Produces compressed framework apk by removing irrelevant files and entries, * Produces compressed framework apk by removing irrelevant files and entries,
* basically it keeps only resources.arsc and AndroidManifest.xml * basically it keeps only resources.arsc and AndroidManifest.xml
*/ */
public class FrameworkApk extends ApkModule{ public class FrameworkApk extends ApkModule{
private final Object mLock = new Object(); private final Object mLock = new Object();
private int versionCode; private int versionCode;
private String versionName; private String versionName;
private String packageName; private String packageName;
private boolean mOptimizing; private boolean mOptimizing;
private boolean mDestroyed; private boolean mDestroyed;
public FrameworkApk(String moduleName, APKArchive apkArchive) { public FrameworkApk(String moduleName, APKArchive apkArchive) {
super(moduleName, apkArchive); super(moduleName, apkArchive);
super.setLoadDefaultFramework(false); super.setLoadDefaultFramework(false);
} }
public FrameworkApk(APKArchive apkArchive) { public FrameworkApk(APKArchive apkArchive) {
this("framework", apkArchive); this("framework", apkArchive);
} }
public void destroy(){ public void destroy(){
synchronized (mLock){ synchronized (mLock){
this.versionCode = -1; this.versionCode = -1;
this.versionName = "-1"; this.versionName = "-1";
this.packageName = "destroyed"; this.packageName = "destroyed";
super.destroy(); super.destroy();
this.mDestroyed = true; this.mDestroyed = true;
} }
} }
public boolean isDestroyed() { public boolean isDestroyed() {
synchronized (mLock){ synchronized (mLock){
if(!mDestroyed){ if(!mDestroyed){
return false; return false;
} }
if(hasTableBlock()){ if(hasTableBlock()){
this.versionCode = 0; this.versionCode = 0;
this.versionName = null; this.versionName = null;
this.packageName = null; this.packageName = null;
mDestroyed = false; mDestroyed = false;
return false; return false;
} }
return true; return true;
} }
} }
public int getVersionCode() { public int getVersionCode() {
if(this.versionCode == 0){ if(this.versionCode == 0){
initValues(); initValues();
} }
return this.versionCode; return this.versionCode;
} }
public String getVersionName() { public String getVersionName() {
if(this.versionName == null){ if(this.versionName == null){
initValues(); initValues();
} }
return this.versionName; return this.versionName;
} }
@Override @Override
public String getPackageName() { public String getPackageName() {
if(this.packageName == null){ if(this.packageName == null){
initValues(); initValues();
} }
return this.packageName; return this.packageName;
} }
@Override @Override
public void setPackageName(String packageName) { public void setPackageName(String packageName) {
super.setPackageName(packageName); super.setPackageName(packageName);
this.packageName = null; this.packageName = null;
} }
private void initValues() { private void initValues() {
if(hasAndroidManifestBlock()){ if(hasAndroidManifestBlock()){
AndroidManifestBlock manifest = getAndroidManifestBlock(); AndroidManifestBlock manifest = getAndroidManifestBlock();
Integer code = manifest.getVersionCode(); Integer code = manifest.getVersionCode();
if(code!=null){ if(code!=null){
this.versionCode = code; this.versionCode = code;
} }
if(this.versionName == null){ if(this.versionName == null){
this.versionName = manifest.getVersionName(); this.versionName = manifest.getVersionName();
} }
if(this.packageName == null){ if(this.packageName == null){
this.packageName = manifest.getPackageName(); this.packageName = manifest.getPackageName();
} }
} }
if(hasTableBlock()){ if(hasTableBlock()){
FrameworkTable table = getTableBlock(); FrameworkTable table = getTableBlock();
if(this.versionCode == 0 && table.isOptimized()){ if(this.versionCode == 0 && table.isOptimized()){
int version = table.getVersionCode(); int version = table.getVersionCode();
if(version!=0){ if(version!=0){
versionCode = version; versionCode = version;
if(this.versionName == null){ if(this.versionName == null){
this.versionName = String.valueOf(version); this.versionName = String.valueOf(version);
} }
} }
} }
if(this.packageName == null){ if(this.packageName == null){
PackageBlock packageBlock = table.pickOne(); PackageBlock packageBlock = table.pickOne();
if(packageBlock!=null){ if(packageBlock!=null){
this.packageName = packageBlock.getName(); this.packageName = packageBlock.getName();
} }
} }
} }
} }
@Override @Override
public void setManifest(AndroidManifestBlock manifestBlock){ public void setManifest(AndroidManifestBlock manifestBlock){
synchronized (mLock){ synchronized (mLock){
super.setManifest(manifestBlock); super.setManifest(manifestBlock);
this.versionCode = 0; this.versionCode = 0;
this.versionName = null; this.versionName = null;
this.packageName = null; this.packageName = null;
} }
} }
@Override @Override
public void setTableBlock(TableBlock tableBlock){ public void setTableBlock(TableBlock tableBlock){
synchronized (mLock){ synchronized (mLock){
super.setTableBlock(tableBlock); super.setTableBlock(tableBlock);
this.versionCode = 0; this.versionCode = 0;
this.versionName = null; this.versionName = null;
this.packageName = null; this.packageName = null;
} }
} }
@Override @Override
public FrameworkTable getTableBlock() { public FrameworkTable getTableBlock() {
return (FrameworkTable) super.getTableBlock(); return (FrameworkTable) super.getTableBlock();
} }
@Override @Override
FrameworkTable loadTableBlock() throws IOException { FrameworkTable loadTableBlock() throws IOException {
APKArchive archive=getApkArchive(); APKArchive archive=getApkArchive();
InputSource inputSource = archive.getInputSource(TableBlock.FILE_NAME); InputSource inputSource = archive.getInputSource(TableBlock.FILE_NAME);
if(inputSource==null){ if(inputSource==null){
throw new IOException("Entry not found: "+TableBlock.FILE_NAME); throw new IOException("Entry not found: "+TableBlock.FILE_NAME);
} }
InputStream inputStream = inputSource.openStream(); InputStream inputStream = inputSource.openStream();
FrameworkTable frameworkTable=FrameworkTable.load(inputStream); FrameworkTable frameworkTable=FrameworkTable.load(inputStream);
frameworkTable.setApkFile(this); frameworkTable.setApkFile(this);
BlockInputSource<FrameworkTable> blockInputSource=new BlockInputSource<>(inputSource.getName(), frameworkTable); BlockInputSource<FrameworkTable> blockInputSource=new BlockInputSource<>(inputSource.getName(), frameworkTable);
blockInputSource.setMethod(inputSource.getMethod()); blockInputSource.setMethod(inputSource.getMethod());
blockInputSource.setSort(inputSource.getSort()); blockInputSource.setSort(inputSource.getSort());
archive.add(blockInputSource); archive.add(blockInputSource);
return frameworkTable; return frameworkTable;
} }
public void optimize(){ public void optimize(){
synchronized (mLock){ synchronized (mLock){
if(mOptimizing){ if(mOptimizing){
return; return;
} }
if(!hasTableBlock()){ if(!hasTableBlock()){
mOptimizing = false; mOptimizing = false;
return; return;
} }
FrameworkTable frameworkTable = getTableBlock(); FrameworkTable frameworkTable = getTableBlock();
if(frameworkTable.isOptimized()){ if(frameworkTable.isOptimized()){
mOptimizing = false; mOptimizing = false;
return; return;
} }
FrameworkOptimizer optimizer = new FrameworkOptimizer(this); FrameworkOptimizer optimizer = new FrameworkOptimizer(this);
optimizer.optimize(); optimizer.optimize();
mOptimizing = false; mOptimizing = false;
} }
} }
public String getName(){ public String getName(){
if(isDestroyed()){ if(isDestroyed()){
return "destroyed"; return "destroyed";
} }
String pkg = getPackageName(); String pkg = getPackageName();
if(pkg==null){ if(pkg==null){
return ""; return "";
} }
return pkg + "-" + getVersionCode(); return pkg + "-" + getVersionCode();
} }
@Override @Override
public int hashCode(){ public int hashCode(){
return Objects.hash(getClass(), getName()); return Objects.hash(getClass(), getName());
} }
@Override @Override
public boolean equals(Object obj){ public boolean equals(Object obj){
if(obj==this){ if(obj==this){
return true; return true;
} }
if(getClass()!=obj.getClass()){ if(getClass()!=obj.getClass()){
return false; return false;
} }
FrameworkApk other = (FrameworkApk) obj; FrameworkApk other = (FrameworkApk) obj;
return getName().equals(other.getName()); return getName().equals(other.getName());
} }
@Override @Override
public String toString(){ public String toString(){
return getName(); return getName();
} }
public static FrameworkApk loadApkFile(File apkFile) throws IOException { public static FrameworkApk loadApkFile(File apkFile) throws IOException {
APKArchive archive=APKArchive.loadZippedApk(apkFile); Archive archive = new Archive(apkFile);
return new FrameworkApk(archive); APKArchive apkArchive = new APKArchive(archive.mapEntrySource());
} return new FrameworkApk(apkArchive);
public static FrameworkApk loadApkFile(File apkFile, String moduleName) throws IOException { }
APKArchive archive=APKArchive.loadZippedApk(apkFile); public static FrameworkApk loadApkFile(File apkFile, String moduleName) throws IOException {
return new FrameworkApk(moduleName, archive); Archive archive = new Archive(apkFile);
} APKArchive apkArchive = new APKArchive(archive.mapEntrySource());
public static boolean isFramework(ApkModule apkModule) { return new FrameworkApk(moduleName, apkArchive);
if(!apkModule.hasAndroidManifestBlock()){ }
return false; public static boolean isFramework(ApkModule apkModule) {
} if(!apkModule.hasAndroidManifestBlock()){
return isFramework(apkModule.getAndroidManifestBlock()); return false;
} }
public static boolean isFramework(AndroidManifestBlock manifestBlock){ return isFramework(apkModule.getAndroidManifestBlock());
ResXmlElement root = manifestBlock.getManifestElement(); }
ResXmlAttribute attribute = root.getStartElement() public static boolean isFramework(AndroidManifestBlock manifestBlock){
.searchAttributeByName(AndroidManifestBlock.NAME_coreApp); ResXmlElement root = manifestBlock.getManifestElement();
if(attribute==null || attribute.getValueType()!= ValueType.INT_BOOLEAN){ ResXmlAttribute attribute = root.getStartElement()
return false; .searchAttributeByName(AndroidManifestBlock.NAME_coreApp);
} if(attribute==null || attribute.getValueType()!= ValueType.INT_BOOLEAN){
return attribute.getValueAsBoolean(); return false;
} }
public static FrameworkApk loadApkBuffer(InputStream inputStream) throws IOException{ return attribute.getValueAsBoolean();
return loadApkBuffer("framework", inputStream); }
} public static FrameworkApk loadApkBuffer(InputStream inputStream) throws IOException{
public static FrameworkApk loadApkBuffer(String moduleName, InputStream inputStream) throws IOException { return loadApkBuffer("framework", inputStream);
APKArchive archive = new APKArchive(); }
FrameworkApk frameworkApk = new FrameworkApk(moduleName, archive); public static FrameworkApk loadApkBuffer(String moduleName, InputStream inputStream) throws IOException {
Map<String, ByteInputSource> inputSourceMap = InputSourceUtil.mapInputStreamAsBuffer(inputStream); APKArchive archive = new APKArchive();
ByteInputSource source = inputSourceMap.get(TableBlock.FILE_NAME); FrameworkApk frameworkApk = new FrameworkApk(moduleName, archive);
FrameworkTable tableBlock = new FrameworkTable(); Map<String, ByteInputSource> inputSourceMap = InputSourceUtil.mapInputStreamAsBuffer(inputStream);
if(source!=null){ ByteInputSource source = inputSourceMap.get(TableBlock.FILE_NAME);
tableBlock.readBytes(source.openStream()); FrameworkTable tableBlock = new FrameworkTable();
} if(source!=null){
frameworkApk.setTableBlock(tableBlock); tableBlock.readBytes(source.openStream());
}
frameworkApk.setTableBlock(tableBlock);
AndroidManifestBlock manifestBlock = new AndroidManifestBlock(); AndroidManifestBlock manifestBlock = new AndroidManifestBlock();
source = inputSourceMap.get(AndroidManifestBlock.FILE_NAME); source = inputSourceMap.get(AndroidManifestBlock.FILE_NAME);
if(source!=null){ if(source!=null){
manifestBlock.readBytes(source.openStream()); manifestBlock.readBytes(source.openStream());
} }
frameworkApk.setManifest(manifestBlock); frameworkApk.setManifest(manifestBlock);
archive.addAll(inputSourceMap.values()); archive.addAll(inputSourceMap.values());
return frameworkApk; return frameworkApk;
} }
} public static void optimize(File in, File out, APKLogger apkLogger) throws IOException{
FrameworkApk frameworkApk = FrameworkApk.loadApkFile(in);
frameworkApk.setAPKLogger(apkLogger);
frameworkApk.optimize();
frameworkApk.writeApk(out);
}
}

View File

@ -413,7 +413,7 @@ public class FrameworkTable extends TableBlock {
if(headerBlock.getChunkType()!= ChunkType.TABLE){ if(headerBlock.getChunkType()!= ChunkType.TABLE){
return super.toString(); return super.toString();
} }
if(!isOptimized()){ if(!mOptimized){
return "Unoptimized: "+super.toString(); return "Unoptimized: "+super.toString();
} }
return getFrameworkName()+'-'+getVersionCode(); return getFrameworkName()+'-'+getVersionCode();