diff --git a/Bcore/src/main/java/top/niunaijun/blackbox/BlackBoxCore.java b/Bcore/src/main/java/top/niunaijun/blackbox/BlackBoxCore.java index 8a5ec11..108618a 100644 --- a/Bcore/src/main/java/top/niunaijun/blackbox/BlackBoxCore.java +++ b/Bcore/src/main/java/top/niunaijun/blackbox/BlackBoxCore.java @@ -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() { diff --git a/Bcore/src/main/java/top/niunaijun/blackbox/core/system/pm/BPackageManagerService.java b/Bcore/src/main/java/top/niunaijun/blackbox/core/system/pm/BPackageManagerService.java index d5d0a05..a8d9ffd 100644 --- a/Bcore/src/main/java/top/niunaijun/blackbox/core/system/pm/BPackageManagerService.java +++ b/Bcore/src/main/java/top/niunaijun/blackbox/core/system/pm/BPackageManagerService.java @@ -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); } } -} \ No newline at end of file +} diff --git a/Bcore/src/main/java/top/niunaijun/blackbox/core/system/pm/PackageManagerCompat.java b/Bcore/src/main/java/top/niunaijun/blackbox/core/system/pm/PackageManagerCompat.java index ab9983b..24fd19d 100644 --- a/Bcore/src/main/java/top/niunaijun/blackbox/core/system/pm/PackageManagerCompat.java +++ b/Bcore/src/main/java/top/niunaijun/blackbox/core/system/pm/PackageManagerCompat.java @@ -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()); } diff --git a/Bcore/src/main/java/top/niunaijun/blackbox/instrumentation/DownloadedGadgetRuntime.java b/Bcore/src/main/java/top/niunaijun/blackbox/instrumentation/DownloadedGadgetRuntime.java index a5010b7..f1fac34 100644 --- a/Bcore/src/main/java/top/niunaijun/blackbox/instrumentation/DownloadedGadgetRuntime.java +++ b/Bcore/src/main/java/top/niunaijun/blackbox/instrumentation/DownloadedGadgetRuntime.java @@ -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) { diff --git a/Bcore/src/main/java/top/niunaijun/blackbox/utils/AbiUtils.java b/Bcore/src/main/java/top/niunaijun/blackbox/utils/AbiUtils.java index 7a85863..9b79e64 100644 --- a/Bcore/src/main/java/top/niunaijun/blackbox/utils/AbiUtils.java +++ b/Bcore/src/main/java/top/niunaijun/blackbox/utils/AbiUtils.java @@ -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); + } } diff --git a/Bcore/src/main/java/top/niunaijun/blackbox/utils/NativeUtils.java b/Bcore/src/main/java/top/niunaijun/blackbox/utils/NativeUtils.java index 170b150..85cb2ad 100644 --- a/Bcore/src/main/java/top/niunaijun/blackbox/utils/NativeUtils.java +++ b/Bcore/src/main/java/top/niunaijun/blackbox/utils/NativeUtils.java @@ -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"); } diff --git a/Bcore/src/main/java/top/niunaijun/blackbox/utils/ProcessAbi.java b/Bcore/src/main/java/top/niunaijun/blackbox/utils/ProcessAbi.java new file mode 100644 index 0000000..0bb74dd --- /dev/null +++ b/Bcore/src/main/java/top/niunaijun/blackbox/utils/ProcessAbi.java @@ -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); + } +} diff --git a/Bcore/src/test/java/top/niunaijun/blackbox/utils/AbiUtilsTest.java b/Bcore/src/test/java/top/niunaijun/blackbox/utils/AbiUtilsTest.java new file mode 100644 index 0000000..465aad2 --- /dev/null +++ b/Bcore/src/test/java/top/niunaijun/blackbox/utils/AbiUtilsTest.java @@ -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; + } +} diff --git a/Bcore/src/test/java/top/niunaijun/blackbox/utils/ProcessAbiTest.java b/Bcore/src/test/java/top/niunaijun/blackbox/utils/ProcessAbiTest.java new file mode 100644 index 0000000..4f4b73e --- /dev/null +++ b/Bcore/src/test/java/top/niunaijun/blackbox/utils/ProcessAbiTest.java @@ -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; + } +} diff --git a/CHANGELOG.md b/CHANGELOG.md index abff1d2..d75ef93 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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 diff --git a/app/src/main/java/com/qm4rs/fridabox/ApkInspector.java b/app/src/main/java/com/qm4rs/fridabox/ApkInspector.java index 31a5e9c..5c5c128 100644 --- a/app/src/main/java/com/qm4rs/fridabox/ApkInspector.java +++ b/app/src/main/java/com/qm4rs/fridabox/ApkInspector.java @@ -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 apks) throws IOException { + return inspect(apks, null); + } + + public static Result inspect(Iterable apks, String processAbi) throws IOException { Set 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 abis; + public final String processAbi; - Result(boolean hasNativeLibraries, boolean supported, Set abis) { + Result(boolean hasNativeLibraries, boolean supported, Set 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 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 SUPPORTED_ABIS = Collections.unmodifiableSet(new LinkedHashSet<>(Arrays.asList( "armeabi-v7a", "arm64-v8a", "x86", "x86_64" ))); diff --git a/app/src/main/java/com/qm4rs/fridabox/FridaBoxActivity.kt b/app/src/main/java/com/qm4rs/fridabox/FridaBoxActivity.kt index 89f621c..28029ff 100644 --- a/app/src/main/java/com/qm4rs/fridabox/FridaBoxActivity.kt +++ b/app/src/main/java/com/qm4rs/fridabox/FridaBoxActivity.kt @@ -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") diff --git a/app/src/main/java/com/qm4rs/fridabox/GadgetManager.kt b/app/src/main/java/com/qm4rs/fridabox/GadgetManager.kt index 38f3d7e..0c39ba6 100644 --- a/app/src/main/java/com/qm4rs/fridabox/GadgetManager.kt +++ b/app/src/main/java/com/qm4rs/fridabox/GadgetManager.kt @@ -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") diff --git a/app/src/test/java/com/qm4rs/fridabox/ApkInspectorTest.java b/app/src/test/java/com/qm4rs/fridabox/ApkInspectorTest.java index 72f8c76..507442d 100644 --- a/app/src/test/java/com/qm4rs/fridabox/ApkInspectorTest.java +++ b/app/src/test/java/com/qm4rs/fridabox/ApkInspectorTest.java @@ -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))) { diff --git a/build.gradle b/build.gradle index fd3bd07..1ab8f6d 100644 --- a/build.gradle +++ b/build.gradle @@ -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' }