mirror of
https://github.com/QM4RS/FridaBox.git
synced 2026-10-07 08:22:03 +02:00
Fix runtime ABI selection across release variants
This commit is contained in:
@@ -68,6 +68,7 @@ import top.niunaijun.blackbox.utils.StackTraceFilter;
|
||||
import top.niunaijun.blackbox.utils.SocialMediaAppCrashPrevention;
|
||||
import top.niunaijun.blackbox.utils.DexCrashPrevention;
|
||||
import top.niunaijun.blackbox.utils.NativeCrashPrevention;
|
||||
import top.niunaijun.blackbox.utils.ProcessAbi;
|
||||
import top.niunaijun.blackbox.utils.CrashMonitor;
|
||||
import top.niunaijun.blackbox.utils.StoragePermissionHelper;
|
||||
import top.niunaijun.blackbox.utils.LogSender;
|
||||
@@ -1463,11 +1464,9 @@ public class BlackBoxCore extends ClientConfiguration {
|
||||
}
|
||||
|
||||
public static boolean is64Bit() {
|
||||
if (BuildCompat.isM()) {
|
||||
return Process.is64Bit();
|
||||
} else {
|
||||
return Build.CPU_ABI.equals("arm64-v8a");
|
||||
}
|
||||
String processAbi = ProcessAbi.detect(getContext());
|
||||
return processAbi != null ? ProcessAbi.is64Bit(processAbi)
|
||||
: BuildCompat.isM() && Process.is64Bit();
|
||||
}
|
||||
|
||||
private void initNotificationManager() {
|
||||
|
||||
+2
-2
@@ -686,7 +686,7 @@ public class BPackageManagerService extends IBPackageManagerService.Stub impleme
|
||||
if (!support) {
|
||||
String msg = packageArchiveInfo.applicationInfo.loadLabel(BlackBoxCore.getPackageManager()) + "[" + packageArchiveInfo.packageName + "]";
|
||||
return result.installError(packageArchiveInfo.packageName,
|
||||
msg + "\n" + (BlackBoxCore.is64Bit() ? "The box does not support 32-bit Application" : "The box does not support 64-bit Application"));
|
||||
msg + "\nThe application has no native libraries for the FridaBox process ABI");
|
||||
}
|
||||
PackageParser.Package aPackage = parserApk(apkFile.getAbsolutePath());
|
||||
if (aPackage == null) {
|
||||
@@ -808,4 +808,4 @@ public class BPackageManagerService extends IBPackageManagerService.Stub impleme
|
||||
mComponentResolver.addAllComponents(value.pkg);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,7 +15,6 @@ import android.content.pm.ProviderInfo;
|
||||
import android.content.pm.ServiceInfo;
|
||||
import android.content.res.AssetManager;
|
||||
import android.content.res.Resources;
|
||||
import android.os.Build;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
@@ -31,6 +30,7 @@ import top.niunaijun.blackbox.core.env.BEnvironment;
|
||||
import top.niunaijun.blackbox.entity.pm.InstallOption;
|
||||
import top.niunaijun.blackbox.utils.ArrayUtils;
|
||||
import top.niunaijun.blackbox.utils.FileUtils;
|
||||
import top.niunaijun.blackbox.utils.ProcessAbi;
|
||||
import top.niunaijun.blackbox.utils.compat.BuildCompat;
|
||||
|
||||
|
||||
@@ -308,7 +308,7 @@ public class PackageManagerCompat {
|
||||
|
||||
|
||||
if (BuildCompat.isL()) {
|
||||
BRApplicationInfoL.get(ai)._set_primaryCpuAbi(Build.CPU_ABI);
|
||||
BRApplicationInfoL.get(ai)._set_primaryCpuAbi(ProcessAbi.detect(BlackBoxCore.getContext()));
|
||||
BRApplicationInfoL.get(ai)._set_scanPublicSourceDir(BRApplicationInfoL.get(baseApplication).scanPublicSourceDir());
|
||||
BRApplicationInfoL.get(ai)._set_scanSourceDir(BRApplicationInfoL.get(baseApplication).scanSourceDir());
|
||||
}
|
||||
|
||||
+5
-9
@@ -1,7 +1,6 @@
|
||||
package top.niunaijun.blackbox.instrumentation;
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.Build;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.File;
|
||||
@@ -11,6 +10,7 @@ import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
import top.niunaijun.blackbox.BlackBoxCore;
|
||||
import top.niunaijun.blackbox.utils.ProcessAbi;
|
||||
|
||||
/** Resolves a selected, downloaded Gadget and prepares per-guest runtime copies. */
|
||||
final class DownloadedGadgetRuntime {
|
||||
@@ -48,8 +48,8 @@ final class DownloadedGadgetRuntime {
|
||||
if (!isInside(root, selected) || !selected.isFile()) {
|
||||
throw new IOException("The selected Frida Gadget is missing or outside private storage");
|
||||
}
|
||||
if (!supportsAbi(selectedAbi)) {
|
||||
throw new IOException("The selected Frida Gadget ABI is not supported by this device");
|
||||
if (!supportsAbi(context, selectedAbi)) {
|
||||
throw new IOException("The selected Frida Gadget ABI does not match this process");
|
||||
}
|
||||
validateElf(selected, selectedAbi);
|
||||
return selected;
|
||||
@@ -108,12 +108,8 @@ final class DownloadedGadgetRuntime {
|
||||
return -1;
|
||||
}
|
||||
|
||||
private static boolean supportsAbi(String abi) {
|
||||
if (abi == null) return false;
|
||||
for (String supported : Build.SUPPORTED_ABIS) {
|
||||
if (abi.equals(supported)) return true;
|
||||
}
|
||||
return false;
|
||||
private static boolean supportsAbi(Context context, String abi) {
|
||||
return abi != null && abi.equals(ProcessAbi.detect(context));
|
||||
}
|
||||
|
||||
private static boolean isInside(File root, File child) {
|
||||
|
||||
@@ -26,11 +26,7 @@ public class AbiUtils {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (BlackBoxCore.is64Bit()) {
|
||||
return abiUtils.is64Bit();
|
||||
} else {
|
||||
return abiUtils.is32Bit();
|
||||
}
|
||||
return abiUtils.supports(ProcessAbi.detect(BlackBoxCore.getContext()));
|
||||
}
|
||||
|
||||
public AbiUtils(File apkFile) {
|
||||
@@ -71,4 +67,11 @@ public class AbiUtils {
|
||||
public boolean isEmptyAib() {
|
||||
return mLibs.isEmpty();
|
||||
}
|
||||
|
||||
public boolean supports(String processAbi) {
|
||||
if ("armeabi-v7a".equals(processAbi)) {
|
||||
return mLibs.contains("armeabi-v7a") || mLibs.contains("armeabi");
|
||||
}
|
||||
return processAbi != null && mLibs.contains(processAbi);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
package top.niunaijun.blackbox.utils;
|
||||
|
||||
import android.os.Build;
|
||||
import android.util.Log;
|
||||
|
||||
import java.io.BufferedInputStream;
|
||||
@@ -14,6 +13,7 @@ import java.util.Enumeration;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipFile;
|
||||
|
||||
import top.niunaijun.blackbox.BlackBoxCore;
|
||||
|
||||
|
||||
public class NativeUtils {
|
||||
@@ -25,13 +25,13 @@ public class NativeUtils {
|
||||
nativeLibDir.mkdirs();
|
||||
}
|
||||
try (ZipFile zipfile = new ZipFile(apk.getAbsolutePath())) {
|
||||
for (String abi : Build.SUPPORTED_ABIS) {
|
||||
if (findAndCopyNativeLib(zipfile, abi, nativeLibDir)) {
|
||||
return;
|
||||
}
|
||||
String processAbi = ProcessAbi.detect(BlackBoxCore.getContext());
|
||||
if (processAbi != null && findAndCopyNativeLib(zipfile, processAbi, nativeLibDir)) {
|
||||
return;
|
||||
}
|
||||
if ("armeabi-v7a".equals(processAbi)) {
|
||||
findAndCopyNativeLib(zipfile, "armeabi", nativeLibDir);
|
||||
}
|
||||
|
||||
findAndCopyNativeLib(zipfile, "armeabi", nativeLibDir);
|
||||
} finally {
|
||||
Log.d(TAG, "Done! +" + (System.currentTimeMillis() - startTime) + "ms");
|
||||
}
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
package top.niunaijun.blackbox.utils;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.Locale;
|
||||
|
||||
/** Detects the ABI of the current host process instead of the device's preferred ABI. */
|
||||
public final class ProcessAbi {
|
||||
private static final String HOST_LIBRARY = "libblackbox.so";
|
||||
|
||||
private ProcessAbi() {
|
||||
}
|
||||
|
||||
public static String detect(Context context) {
|
||||
if (context != null && context.getApplicationInfo() != null) {
|
||||
String nativeLibraryDir = context.getApplicationInfo().nativeLibraryDir;
|
||||
if (nativeLibraryDir != null && !nativeLibraryDir.trim().isEmpty()) {
|
||||
String abi = fromElf(new File(nativeLibraryDir, HOST_LIBRARY));
|
||||
if (abi != null) return abi;
|
||||
}
|
||||
}
|
||||
return fromOsArch(System.getProperty("os.arch"));
|
||||
}
|
||||
|
||||
public static String fromElf(File file) {
|
||||
if (file == null || !file.isFile()) return null;
|
||||
byte[] header = new byte[20];
|
||||
try (FileInputStream input = new FileInputStream(file)) {
|
||||
if (input.read(header) != header.length) return null;
|
||||
return fromElfHeader(header);
|
||||
} catch (IOException ignored) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
static String fromElfHeader(byte[] header) {
|
||||
if (header == null || header.length < 20
|
||||
|| (header[0] & 0xff) != 0x7f || header[1] != 'E'
|
||||
|| header[2] != 'L' || header[3] != 'F' || header[5] != 1) {
|
||||
return null;
|
||||
}
|
||||
int elfClass = header[4] & 0xff;
|
||||
int machine = (header[18] & 0xff) | ((header[19] & 0xff) << 8);
|
||||
if (elfClass == 2 && machine == 183) return "arm64-v8a";
|
||||
if (elfClass == 1 && machine == 40) return "armeabi-v7a";
|
||||
if (elfClass == 2 && machine == 62) return "x86_64";
|
||||
if (elfClass == 1 && machine == 3) return "x86";
|
||||
return null;
|
||||
}
|
||||
|
||||
public static String fromOsArch(String value) {
|
||||
if (value == null) return null;
|
||||
String arch = value.trim().toLowerCase(Locale.ROOT);
|
||||
if ("aarch64".equals(arch) || "arm64".equals(arch) || "arm64-v8a".equals(arch)) {
|
||||
return "arm64-v8a";
|
||||
}
|
||||
if ("arm".equals(arch) || "armeabi-v7a".equals(arch) || arch.startsWith("armv7")) {
|
||||
return "armeabi-v7a";
|
||||
}
|
||||
if ("x86_64".equals(arch) || "amd64".equals(arch)) return "x86_64";
|
||||
if ("x86".equals(arch) || arch.matches("i[3-6]86")) return "x86";
|
||||
return null;
|
||||
}
|
||||
|
||||
public static boolean is64Bit(String abi) {
|
||||
return "arm64-v8a".equals(abi) || "x86_64".equals(abi);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
package top.niunaijun.blackbox.utils;
|
||||
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.TemporaryFolder;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipOutputStream;
|
||||
|
||||
public class AbiUtilsTest {
|
||||
@Rule
|
||||
public final TemporaryFolder temporary = new TemporaryFolder();
|
||||
|
||||
@Test
|
||||
public void x86ProcessRejectsArmOnlyGuest() throws Exception {
|
||||
AbiUtils abi = new AbiUtils(apkWith("lib/armeabi-v7a/libsample.so"));
|
||||
assertFalse(abi.supports("x86"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void armProcessAcceptsLegacyArmeabiFallback() throws Exception {
|
||||
AbiUtils abi = new AbiUtils(apkWith("lib/armeabi/libsample.so"));
|
||||
assertTrue(abi.supports("armeabi-v7a"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void sixtyFourBitProcessRejectsThirtyTwoBitGuest() throws Exception {
|
||||
AbiUtils abi = new AbiUtils(apkWith("lib/x86/libsample.so"));
|
||||
assertFalse(abi.supports("x86_64"));
|
||||
}
|
||||
|
||||
private File apkWith(String entry) throws Exception {
|
||||
File file = temporary.newFile("guest-" + System.nanoTime() + ".apk");
|
||||
try (ZipOutputStream output = new ZipOutputStream(new FileOutputStream(file))) {
|
||||
output.putNextEntry(new ZipEntry(entry));
|
||||
output.write(1);
|
||||
output.closeEntry();
|
||||
}
|
||||
return file;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
package top.niunaijun.blackbox.utils;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNull;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
public class ProcessAbiTest {
|
||||
@Test
|
||||
public void processArchitectureKeepsThirtyTwoBitArmOnArm64Device() {
|
||||
assertEquals("armeabi-v7a", ProcessAbi.fromOsArch("armv7l"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void processArchitectureKeepsThirtyTwoBitX86OnX8664Device() {
|
||||
assertEquals("x86", ProcessAbi.fromOsArch("i686"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void elfHeaderDistinguishesAllReleaseArchitectures() {
|
||||
assertEquals("arm64-v8a", ProcessAbi.fromElfHeader(elfHeader(2, 183)));
|
||||
assertEquals("armeabi-v7a", ProcessAbi.fromElfHeader(elfHeader(1, 40)));
|
||||
assertEquals("x86_64", ProcessAbi.fromElfHeader(elfHeader(2, 62)));
|
||||
assertEquals("x86", ProcessAbi.fromElfHeader(elfHeader(1, 3)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void elfHeaderRejectsMachineAndClassMismatch() {
|
||||
assertNull(ProcessAbi.fromElfHeader(elfHeader(2, 40)));
|
||||
assertNull(ProcessAbi.fromElfHeader(elfHeader(1, 62)));
|
||||
}
|
||||
|
||||
private byte[] elfHeader(int elfClass, int machine) {
|
||||
byte[] header = new byte[20];
|
||||
header[0] = 0x7f;
|
||||
header[1] = 'E';
|
||||
header[2] = 'L';
|
||||
header[3] = 'F';
|
||||
header[4] = (byte) elfClass;
|
||||
header[5] = 1;
|
||||
header[18] = (byte) (machine & 0xff);
|
||||
header[19] = (byte) ((machine >> 8) & 0xff);
|
||||
return header;
|
||||
}
|
||||
}
|
||||
@@ -5,6 +5,17 @@ versioning for its public releases.
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
## [4.1.1] - 2026-07-28
|
||||
|
||||
### Fixed
|
||||
|
||||
- Select downloaded Frida Gadgets by the actual host process ABI instead of the
|
||||
device's preferred ABI, preventing 64-bit Gadgets from being selected by
|
||||
32-bit ARM or x86 release APKs.
|
||||
- Require guest native libraries to match the exact FridaBox process ABI and
|
||||
extract only that ABI, preventing cross-architecture guest import failures.
|
||||
- Report the correct x86_64 process bitness on Android 5.0 and 5.1.
|
||||
|
||||
## [4.1.0] - 2026-07-28
|
||||
|
||||
### Added
|
||||
|
||||
@@ -14,14 +14,18 @@ public final class ApkInspector {
|
||||
}
|
||||
|
||||
public static Result inspect(File apk) throws IOException {
|
||||
return inspect(Collections.singletonList(apk));
|
||||
return inspect(Collections.singletonList(apk), null);
|
||||
}
|
||||
|
||||
public static Result inspect(File... apks) throws IOException {
|
||||
return inspect(Arrays.asList(apks));
|
||||
return inspect(Arrays.asList(apks), null);
|
||||
}
|
||||
|
||||
public static Result inspect(Iterable<File> apks) throws IOException {
|
||||
return inspect(apks, null);
|
||||
}
|
||||
|
||||
public static Result inspect(Iterable<File> apks, String processAbi) throws IOException {
|
||||
Set<String> abis = new LinkedHashSet<>();
|
||||
boolean hasNativeLibraries = false;
|
||||
boolean inspectedAny = false;
|
||||
@@ -44,27 +48,41 @@ public final class ApkInspector {
|
||||
}
|
||||
}
|
||||
if (!inspectedAny) throw new IOException("No APK files were provided");
|
||||
boolean supported = !hasNativeLibraries || !Collections.disjoint(abis, SUPPORTED_ABIS);
|
||||
return new Result(hasNativeLibraries, supported, abis);
|
||||
boolean supported = !hasNativeLibraries || supportsProcessAbi(abis, processAbi);
|
||||
return new Result(hasNativeLibraries, supported, abis, processAbi);
|
||||
}
|
||||
|
||||
public static final class Result {
|
||||
public final boolean hasNativeLibraries;
|
||||
public final boolean supported;
|
||||
public final Set<String> abis;
|
||||
public final String processAbi;
|
||||
|
||||
Result(boolean hasNativeLibraries, boolean supported, Set<String> abis) {
|
||||
Result(boolean hasNativeLibraries, boolean supported, Set<String> abis, String processAbi) {
|
||||
this.hasNativeLibraries = hasNativeLibraries;
|
||||
this.supported = supported;
|
||||
this.abis = Collections.unmodifiableSet(new LinkedHashSet<>(abis));
|
||||
this.processAbi = processAbi;
|
||||
}
|
||||
|
||||
public String description() {
|
||||
if (!hasNativeLibraries) return "Pure Java/Kotlin (accepted)";
|
||||
return supported ? "Supported ABI: " + abis : "Rejected; unsupported ABI: " + abis;
|
||||
if (processAbi == null) {
|
||||
return !Collections.disjoint(abis, SUPPORTED_ABIS)
|
||||
? "Recognized ABI: " + abis : "Rejected; unsupported ABI: " + abis;
|
||||
}
|
||||
return supported ? "Compatible with " + processAbi + ": " + abis
|
||||
: "Rejected; requires " + processAbi + ", found: " + abis;
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean supportsProcessAbi(Set<String> abis, String processAbi) {
|
||||
if ("armeabi-v7a".equals(processAbi)) {
|
||||
return abis.contains("armeabi-v7a") || abis.contains("armeabi");
|
||||
}
|
||||
return processAbi != null && abis.contains(processAbi);
|
||||
}
|
||||
|
||||
private static final Set<String> SUPPORTED_ABIS = Collections.unmodifiableSet(new LinkedHashSet<>(Arrays.asList(
|
||||
"armeabi-v7a", "arm64-v8a", "x86", "x86_64"
|
||||
)));
|
||||
|
||||
@@ -54,6 +54,7 @@ import com.google.android.material.textfield.TextInputEditText
|
||||
import com.google.android.material.textfield.TextInputLayout
|
||||
import top.niunaijun.blackbox.BlackBoxCore
|
||||
import top.niunaijun.blackbox.instrumentation.InstrumentationSettings
|
||||
import top.niunaijun.blackbox.utils.ProcessAbi
|
||||
import com.qm4rs.fridabox.databinding.ActivityFridaboxBinding
|
||||
import java.io.File
|
||||
import java.io.FileOutputStream
|
||||
@@ -1350,7 +1351,10 @@ class FridaBoxActivity : AppCompatActivity() {
|
||||
require(apkFiles.isNotEmpty() && apkFiles.all { it.isFile }) {
|
||||
"Installed APK files are unavailable"
|
||||
}
|
||||
val abi = ApkInspector.inspect(apkFiles)
|
||||
val processAbi = requireNotNull(ProcessAbi.detect(this)) {
|
||||
"Unable to determine the FridaBox process ABI"
|
||||
}
|
||||
val abi = ApkInspector.inspect(apkFiles, processAbi)
|
||||
if (!abi.supported) error("Unsupported native ABI: ${abi.description()}")
|
||||
val originalHash = installedPackageSha256(apkFiles)
|
||||
val install = BlackBoxCore.get().installPackageAsUser(packageName, 0)
|
||||
@@ -1411,7 +1415,10 @@ class FridaBoxActivity : AppCompatActivity() {
|
||||
val archiveInfo = packageManager.getPackageArchiveInfo(temporary.absolutePath, PackageManager.GET_META_DATA)
|
||||
?: error("Android could not parse this APK")
|
||||
if (!archiveInfo.splitNames.isNullOrEmpty()) error("Split-only APKs are not supported")
|
||||
val abi = ApkInspector.inspect(temporary)
|
||||
val processAbi = requireNotNull(ProcessAbi.detect(this)) {
|
||||
"Unable to determine the FridaBox process ABI"
|
||||
}
|
||||
val abi = ApkInspector.inspect(listOf(temporary), processAbi)
|
||||
if (!abi.supported) error("Unsupported native ABI: ${abi.description()}")
|
||||
val safePackage = safePackageName(archiveInfo.packageName)
|
||||
val stored = File(directory, "$safePackage-${originalHash.take(12)}.apk")
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
package com.qm4rs.fridabox
|
||||
|
||||
import android.content.Context
|
||||
import android.os.Build
|
||||
import org.json.JSONArray
|
||||
import org.json.JSONObject
|
||||
import org.tukaani.xz.XZInputStream
|
||||
import top.niunaijun.blackbox.instrumentation.InstrumentationSettings
|
||||
import top.niunaijun.blackbox.utils.ProcessAbi
|
||||
import java.io.ByteArrayOutputStream
|
||||
import java.io.File
|
||||
import java.io.FileInputStream
|
||||
@@ -42,8 +42,10 @@ data class GadgetAbi(val androidName: String, val releaseName: String) {
|
||||
GadgetAbi("x86", "x86")
|
||||
)
|
||||
|
||||
fun detect(): GadgetAbi? = Build.SUPPORTED_ABIS
|
||||
.firstNotNullOfOrNull { deviceAbi -> supported.firstOrNull { it.androidName == deviceAbi } }
|
||||
fun detect(context: Context): GadgetAbi? {
|
||||
val processAbi = ProcessAbi.detect(context) ?: return null
|
||||
return supported.firstOrNull { it.androidName == processAbi }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -85,7 +87,7 @@ class GadgetManager(private val context: Context) {
|
||||
}
|
||||
}
|
||||
|
||||
fun detectedAbi(): GadgetAbi? = GadgetAbi.detect()
|
||||
fun detectedAbi(): GadgetAbi? = GadgetAbi.detect(context)
|
||||
|
||||
fun loadCatalog(source: GadgetSource, abi: GadgetAbi, page: Int): GadgetCatalog {
|
||||
val cache = File(cacheRoot, "${source.id}-${abi.androidName}-$page.json")
|
||||
|
||||
@@ -18,28 +18,31 @@ public class ApkInspectorTest {
|
||||
@Test
|
||||
public void pureJavaApkIsAccepted() throws Exception {
|
||||
File apk = apkWith("classes.dex");
|
||||
ApkInspector.Result result = ApkInspector.inspect(apk);
|
||||
ApkInspector.Result result = ApkInspector.inspect(java.util.Collections.singletonList(apk), "x86");
|
||||
assertFalse(result.hasNativeLibraries);
|
||||
assertTrue(result.supported);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void arm64ApkIsAccepted() throws Exception {
|
||||
ApkInspector.Result result = ApkInspector.inspect(apkWith("lib/arm64-v8a/libsample.so"));
|
||||
ApkInspector.Result result = ApkInspector.inspect(java.util.Collections.singletonList(
|
||||
apkWith("lib/arm64-v8a/libsample.so")), "arm64-v8a");
|
||||
assertTrue(result.hasNativeLibraries);
|
||||
assertTrue(result.supported);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void thirtyTwoBitOnlyApkIsAccepted() throws Exception {
|
||||
ApkInspector.Result result = ApkInspector.inspect(apkWith("lib/armeabi-v7a/libsample.so"));
|
||||
ApkInspector.Result result = ApkInspector.inspect(java.util.Collections.singletonList(
|
||||
apkWith("lib/armeabi-v7a/libsample.so")), "armeabi-v7a");
|
||||
assertTrue(result.hasNativeLibraries);
|
||||
assertTrue(result.supported);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void x86ApkIsAccepted() throws Exception {
|
||||
ApkInspector.Result result = ApkInspector.inspect(apkWith("lib/x86/libsample.so"));
|
||||
ApkInspector.Result result = ApkInspector.inspect(java.util.Collections.singletonList(
|
||||
apkWith("lib/x86/libsample.so")), "x86");
|
||||
assertTrue(result.hasNativeLibraries);
|
||||
assertTrue(result.supported);
|
||||
}
|
||||
@@ -48,11 +51,27 @@ public class ApkInspectorTest {
|
||||
public void splitNativeLibrariesAreInspectedTogether() throws Exception {
|
||||
File base = apkWith("classes.dex");
|
||||
File arm64Split = apkWith("lib/arm64-v8a/libsample.so");
|
||||
ApkInspector.Result result = ApkInspector.inspect(base, arm64Split);
|
||||
ApkInspector.Result result = ApkInspector.inspect(java.util.Arrays.asList(base, arm64Split), "arm64-v8a");
|
||||
assertTrue(result.hasNativeLibraries);
|
||||
assertTrue(result.supported);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void armGuestIsRejectedByX86Process() throws Exception {
|
||||
ApkInspector.Result result = ApkInspector.inspect(java.util.Collections.singletonList(
|
||||
apkWith("lib/armeabi-v7a/libsample.so")), "x86");
|
||||
assertTrue(result.hasNativeLibraries);
|
||||
assertFalse(result.supported);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void thirtyTwoBitGuestIsRejectedBySixtyFourBitProcessOfSameFamily() throws Exception {
|
||||
ApkInspector.Result result = ApkInspector.inspect(java.util.Collections.singletonList(
|
||||
apkWith("lib/armeabi-v7a/libsample.so")), "arm64-v8a");
|
||||
assertTrue(result.hasNativeLibraries);
|
||||
assertFalse(result.supported);
|
||||
}
|
||||
|
||||
private File apkWith(String entry) throws Exception {
|
||||
File file = temporary.newFile("test-" + System.nanoTime() + ".apk");
|
||||
try (ZipOutputStream output = new ZipOutputStream(new FileOutputStream(file))) {
|
||||
|
||||
+2
-2
@@ -10,8 +10,8 @@ ext {
|
||||
targetSdkVersion = 28
|
||||
|
||||
minSdk = 21
|
||||
versionCode = 401
|
||||
versionName = "4.1.0"
|
||||
versionCode = 402
|
||||
versionName = "4.1.1"
|
||||
xVersion = '1.1.0'
|
||||
hiddenApiBypass = '4.3'
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user