Determine minSdkVersion with baksmali for JARs + don't compress webp

We take advantage of the fact that baksmali can determine the minimum API version needed for a given collection of opcodes and extract it, then save to mMinSdkVersion, which is only used by smali via buildSourcesSmali.
This change, in addition to my previous change, allows to decompile and recompile JARs without worrying about specifying an API version via -api/--api-level.

As an extra, added webp file extension to NO_COMPRESS_PATTERN, according to AAPT2 standards, and since it's factually never compresses as it's an already compressed format.
https://cs.android.com/android/platform/superproject/+/master:frameworks/base/tools/aapt2/cmd/Link.cpp;l=2328
This commit is contained in:
IgorEisberg 2021-07-04 02:14:56 +03:00
parent 1ed1076a84
commit 6cfa79e456
3 changed files with 29 additions and 10 deletions

View File

@ -50,16 +50,15 @@ public class Androlib {
public ApkOptions apkOptions; public ApkOptions apkOptions;
private int mMinSdkVersion = 0; private int mMinSdkVersion = 0;
public Androlib() {
this(new ApkOptions());
}
public Androlib(ApkOptions apkOptions) { public Androlib(ApkOptions apkOptions) {
this.apkOptions = apkOptions; this.apkOptions = apkOptions;
mAndRes.apkOptions = apkOptions; mAndRes.apkOptions = apkOptions;
} }
public Androlib() {
this.apkOptions = new ApkOptions();
mAndRes.apkOptions = this.apkOptions;
}
public ResTable getResTable(ExtFile apkFile) public ResTable getResTable(ExtFile apkFile)
throws AndrolibException { throws AndrolibException {
return mAndRes.getResTable(apkFile, true); return mAndRes.getResTable(apkFile, true);
@ -70,6 +69,10 @@ public class Androlib {
return mAndRes.getResTable(apkFile, loadMainPkg); return mAndRes.getResTable(apkFile, loadMainPkg);
} }
public int getMinSdkVersion() {
return mMinSdkVersion;
}
public void decodeSourcesRaw(ExtFile apkFile, File outDir, String filename) public void decodeSourcesRaw(ExtFile apkFile, File outDir, String filename)
throws AndrolibException { throws AndrolibException {
try { try {
@ -92,7 +95,10 @@ public class Androlib {
OS.rmdir(smaliDir); OS.rmdir(smaliDir);
smaliDir.mkdirs(); smaliDir.mkdirs();
LOGGER.info("Baksmaling " + filename + "..."); LOGGER.info("Baksmaling " + filename + "...");
SmaliDecoder.decode(apkFile, smaliDir, filename, bakDeb, apiLevel); apiLevel = SmaliDecoder.decode(apkFile, smaliDir, filename, bakDeb, apiLevel);
if (mMinSdkVersion == 0 || mMinSdkVersion > apiLevel) {
mMinSdkVersion = apiLevel;
}
} catch (BrutException ex) { } catch (BrutException ex) {
throw new AndrolibException(ex); throw new AndrolibException(ex);
} }
@ -809,5 +815,5 @@ public class Androlib {
"lib", "libs", "assets", "META-INF", "kotlin" }; "lib", "libs", "assets", "META-INF", "kotlin" };
private final static Pattern NO_COMPRESS_PATTERN = Pattern.compile("(" + private final static Pattern NO_COMPRESS_PATTERN = Pattern.compile("(" +
"jpg|jpeg|png|gif|wav|mp2|mp3|ogg|aac|mpg|mpeg|mid|midi|smf|jet|rtttl|imy|xmf|mp4|" + "jpg|jpeg|png|gif|wav|mp2|mp3|ogg|aac|mpg|mpeg|mid|midi|smf|jet|rtttl|imy|xmf|mp4|" +
"m4a|m4v|3gp|3gpp|3g2|3gpp2|amr|awb|wma|wmv|webm|mkv)$"); "m4a|m4v|3gp|3gpp|3g2|3gpp2|amr|awb|wma|wmv|webm|webp|mkv)$");
} }

View File

@ -332,6 +332,8 @@ public class ApkDecoder {
putVersionInfo(meta); putVersionInfo(meta);
putSharedLibraryInfo(meta); putSharedLibraryInfo(meta);
putSparseResourcesInfo(meta); putSparseResourcesInfo(meta);
} else {
putMinSdkInfo(meta);
} }
putUnknownInfo(meta); putUnknownInfo(meta);
putFileCompressionInfo(meta); putFileCompressionInfo(meta);
@ -386,6 +388,15 @@ public class ApkDecoder {
} }
} }
private void putMinSdkInfo(MetaInfo meta) {
int minSdkVersion = mAndrolib.getMinSdkVersion();
if (minSdkVersion > 0) {
Map<String, String> sdkInfo = new LinkedHashMap<>();
sdkInfo.put("minSdkVersion", Integer.toString(minSdkVersion));
meta.sdkInfo = sdkInfo;
}
}
private void putPackageInfo(MetaInfo meta) throws AndrolibException { private void putPackageInfo(MetaInfo meta) throws AndrolibException {
String renamed = mResTable.getPackageRenamed(); String renamed = mResTable.getPackageRenamed();
String original = mResTable.getPackageOriginal(); String original = mResTable.getPackageOriginal();

View File

@ -31,9 +31,9 @@ import java.io.IOException;
public class SmaliDecoder { public class SmaliDecoder {
public static void decode(File apkFile, File outDir, String dexName, boolean bakDeb, int apiLevel) public static int decode(File apkFile, File outDir, String dexName, boolean bakDeb, int apiLevel)
throws AndrolibException { throws AndrolibException {
new SmaliDecoder(apkFile, outDir, dexName, bakDeb, apiLevel).decode(); return new SmaliDecoder(apkFile, outDir, dexName, bakDeb, apiLevel).decode();
} }
private SmaliDecoder(File apkFile, File outDir, String dexName, boolean bakDeb, int apiLevel) { private SmaliDecoder(File apkFile, File outDir, String dexName, boolean bakDeb, int apiLevel) {
@ -44,7 +44,7 @@ public class SmaliDecoder {
mApiLevel = apiLevel; mApiLevel = apiLevel;
} }
private void decode() throws AndrolibException { private int decode() throws AndrolibException {
try { try {
final BaksmaliOptions options = new BaksmaliOptions(); final BaksmaliOptions options = new BaksmaliOptions();
@ -96,6 +96,8 @@ public class SmaliDecoder {
} }
Baksmali.disassembleDexFile(dexFile, mOutDir, jobs, options); Baksmali.disassembleDexFile(dexFile, mOutDir, jobs, options);
return dexFile.getOpcodes().api;
} catch (IOException ex) { } catch (IOException ex) {
throw new AndrolibException(ex); throw new AndrolibException(ex);
} }