diff --git a/mobile/android/geckoview/build.gradle b/mobile/android/geckoview/build.gradle index ae4390794107..8501591028cf 100644 --- a/mobile/android/geckoview/build.gradle +++ b/mobile/android/geckoview/build.gradle @@ -69,8 +69,6 @@ android { testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" buildConfigField 'String', "GRE_MILESTONE", "\"${mozconfig.substs.GRE_MILESTONE}\"" - // This should really come from the included binaries, but that's not easy. - buildConfigField 'String', "MOZ_APP_ABI", mozconfig.substs['COMPILE_ENVIRONMENT'] ? "\"${ mozconfig.substs.TARGET_XPCOM_ABI}\"" : '"arm-eabi-gcc3"'; buildConfigField 'String', "MOZ_APP_BASENAME", "\"${mozconfig.substs.MOZ_APP_BASENAME}\""; // For the benefit of future archaeologists: @@ -92,8 +90,6 @@ android { buildConfigField 'String', "USER_AGENT_GECKOVIEW_MOBILE", "\"Mozilla/5.0 (Android \" + android.os.Build.VERSION.RELEASE + \"; Mobile; rv: ${mozconfig.substs.MOZ_APP_VERSION}) Gecko/${mozconfig.substs.MOZ_APP_VERSION} GeckoView/${mozconfig.substs.MOZ_APP_VERSION}\""; buildConfigField 'String', "USER_AGENT_GECKOVIEW_TABLET", "\"Mozilla/5.0 (Android \" + android.os.Build.VERSION.RELEASE + \"; Tablet; rv: ${mozconfig.substs.MOZ_APP_VERSION}) Gecko/${mozconfig.substs.MOZ_APP_VERSION} GeckoView/${mozconfig.substs.MOZ_APP_VERSION}\""; - buildConfigField 'String', "ANDROID_CPU_ARCH", "\"${mozconfig.substs.ANDROID_CPU_ARCH}\""; - buildConfigField 'int', 'MIN_SDK_VERSION', mozconfig.substs.MOZ_ANDROID_MIN_SDK_VERSION; // Is the underlying compiled C/C++ code compiled with --enable-debug? diff --git a/mobile/android/geckoview/src/androidTest/java/org/mozilla/geckoview/test/ContentDelegateTest.kt b/mobile/android/geckoview/src/androidTest/java/org/mozilla/geckoview/test/ContentDelegateTest.kt index f3629515fb6f..27410be56b2c 100644 --- a/mobile/android/geckoview/src/androidTest/java/org/mozilla/geckoview/test/ContentDelegateTest.kt +++ b/mobile/android/geckoview/src/androidTest/java/org/mozilla/geckoview/test/ContentDelegateTest.kt @@ -84,7 +84,7 @@ class ContentDelegateTest : BaseSessionTest() { assumeThat(sessionRule.env.isMultiprocess, equalTo(true)) // Cannot test x86 debug builds due to Gecko's "ah_crap_handler" // that waits for debugger to attach during a SIGSEGV. - assumeThat(sessionRule.env.isDebugBuild && sessionRule.env.cpuArch == "x86", + assumeThat(sessionRule.env.isDebugBuild && sessionRule.env.isX86, equalTo(false)) mainSession.loadUri(CONTENT_CRASH_URL) @@ -115,7 +115,7 @@ class ContentDelegateTest : BaseSessionTest() { assumeThat(sessionRule.env.isMultiprocess, equalTo(true)) // Cannot test x86 debug builds due to Gecko's "ah_crap_handler" // that waits for debugger to attach during a SIGSEGV. - assumeThat(sessionRule.env.isDebugBuild && sessionRule.env.cpuArch == "x86", + assumeThat(sessionRule.env.isDebugBuild && sessionRule.env.isX86, equalTo(false)) mainSession.delegateUntilTestEnd(object : Callbacks.ContentDelegate { @@ -141,7 +141,7 @@ class ContentDelegateTest : BaseSessionTest() { assumeThat(sessionRule.env.isMultiprocess, equalTo(true)) // Cannot test x86 debug builds due to Gecko's "ah_crap_handler" // that waits for debugger to attach during a SIGSEGV. - assumeThat(sessionRule.env.isDebugBuild && sessionRule.env.cpuArch == "x86", + assumeThat(sessionRule.env.isDebugBuild && sessionRule.env.isX86, equalTo(false)) // XXX we need to make sure all sessions in a given content process receive onCrash(). diff --git a/mobile/android/geckoview/src/androidTest/java/org/mozilla/geckoview/test/GeckoSessionTestRuleTest.kt b/mobile/android/geckoview/src/androidTest/java/org/mozilla/geckoview/test/GeckoSessionTestRuleTest.kt index 79b5c923700b..3d931c73eb77 100644 --- a/mobile/android/geckoview/src/androidTest/java/org/mozilla/geckoview/test/GeckoSessionTestRuleTest.kt +++ b/mobile/android/geckoview/src/androidTest/java/org/mozilla/geckoview/test/GeckoSessionTestRuleTest.kt @@ -1661,7 +1661,7 @@ class GeckoSessionTestRuleTest : BaseSessionTest(noErrorCollector = true) { assumeThat(sessionRule.env.isMultiprocess, equalTo(true)) // Cannot test x86 debug builds due to Gecko's "ah_crap_handler" // that waits for debugger to attach during a SIGSEGV. - assumeThat(sessionRule.env.isDebugBuild && sessionRule.env.cpuArch == "x86", + assumeThat(sessionRule.env.isDebugBuild && sessionRule.env.isX86, equalTo(false)) mainSession.loadUri(CONTENT_CRASH_URL) @@ -1678,7 +1678,7 @@ class GeckoSessionTestRuleTest : BaseSessionTest(noErrorCollector = true) { assumeThat(sessionRule.env.shouldShutdownOnCrash(), equalTo(false)) // Cannot test x86 debug builds due to Gecko's "ah_crap_handler" // that waits for debugger to attach during a SIGSEGV. - assumeThat(sessionRule.env.isDebugBuild && sessionRule.env.cpuArch == "x86", + assumeThat(sessionRule.env.isDebugBuild && sessionRule.env.isX86, equalTo(false)) sessionRule.session.loadUri(CONTENT_CRASH_URL) diff --git a/mobile/android/geckoview/src/androidTest/java/org/mozilla/geckoview/test/crash/CrashTest.kt b/mobile/android/geckoview/src/androidTest/java/org/mozilla/geckoview/test/crash/CrashTest.kt index e240f16bbd1f..3f4461154f8e 100644 --- a/mobile/android/geckoview/src/androidTest/java/org/mozilla/geckoview/test/crash/CrashTest.kt +++ b/mobile/android/geckoview/src/androidTest/java/org/mozilla/geckoview/test/crash/CrashTest.kt @@ -37,7 +37,7 @@ class CrashTest { fun getTimeoutMillis(): Long { val env = Environment() - if ("x86" == env.cpuArch) { + if (env.isX86) { return if (env.isEmulator) CrashTest.DEFAULT_X86_EMULATOR_TIMEOUT_MILLIS else diff --git a/mobile/android/geckoview/src/androidTest/java/org/mozilla/geckoview/test/util/Environment.java b/mobile/android/geckoview/src/androidTest/java/org/mozilla/geckoview/test/util/Environment.java index 9a00c7d4794d..bb7729fef039 100644 --- a/mobile/android/geckoview/src/androidTest/java/org/mozilla/geckoview/test/util/Environment.java +++ b/mobile/android/geckoview/src/androidTest/java/org/mozilla/geckoview/test/util/Environment.java @@ -56,12 +56,19 @@ public class Environment { return BuildConfig.DEBUG_BUILD; } - public String getCPUArch() { - return BuildConfig.ANDROID_CPU_ARCH; + public boolean isX86() { + final String abi; + if (Build.VERSION.SDK_INT >= 21) { + abi = Build.SUPPORTED_ABIS[0]; + } else { + abi = Build.CPU_ABI; + } + + return abi.startsWith("x86"); } public long getScaledTimeoutMillis() { - if ("x86".equals(getCPUArch())) { + if (isX86()) { return isEmulator() ? DEFAULT_X86_EMULATOR_TIMEOUT_MILLIS : DEFAULT_X86_DEVICE_TIMEOUT_MILLIS; } diff --git a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/mozglue/GeckoLoader.java b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/mozglue/GeckoLoader.java index 2178dea8ef72..3fbb083fc4f4 100644 --- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/mozglue/GeckoLoader.java +++ b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/mozglue/GeckoLoader.java @@ -8,6 +8,7 @@ package org.mozilla.gecko.mozglue; import org.mozilla.gecko.GeckoAppShell; import org.mozilla.gecko.annotation.JNITarget; import org.mozilla.gecko.annotation.RobocopTarget; +import org.mozilla.gecko.util.HardwareUtils; import org.mozilla.geckoview.BuildConfig; import android.content.Context; @@ -314,8 +315,10 @@ public final class GeckoLoader { message.append(lib); // These might differ. If so, we know why the library won't load! - message.append(": ABI: " + BuildConfig.MOZ_APP_ABI + ", " + getCPUABI()); + HardwareUtils.init(context); + message.append(": ABI: " + HardwareUtils.getLibrariesABI() + ", " + getCPUABI()); message.append(": Data: " + context.getApplicationInfo().dataDir); + try { final boolean appLibExists = new File("/data/app-lib/" + androidPackageName + "/lib" + lib + ".so").exists(); final boolean dataDataExists = new File("/data/data/" + androidPackageName + "/lib/lib" + lib + ".so").exists(); diff --git a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/util/HardwareUtils.java b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/util/HardwareUtils.java index 9848f680e0a7..c04e6c47dff2 100644 --- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/util/HardwareUtils.java +++ b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/util/HardwareUtils.java @@ -15,6 +15,11 @@ import android.util.Log; import org.mozilla.gecko.SysInfo; import org.mozilla.geckoview.BuildConfig; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; + public final class HardwareUtils { private static final String LOGTAG = "GeckoHardwareUtils"; @@ -30,13 +35,14 @@ public final class HardwareUtils { private static volatile boolean sIsSmallTablet; private static volatile boolean sIsTelevision; + private static volatile File sLibDir; + private static volatile int sMachineType = -1; + private HardwareUtils() { } public static void init(Context context) { if (sInited) { - // This is unavoidable, given that HardwareUtils is called from background services. - Log.d(LOGTAG, "HardwareUtils already inited."); return; } @@ -53,6 +59,7 @@ public final class HardwareUtils { } } + sLibDir = new File(context.getApplicationInfo().dataDir, "lib"); sInited = true; } @@ -120,6 +127,63 @@ public final class HardwareUtils { return getPreferredAbi(); } + private static final int ELF_MACHINE_UNKNOWN = 0; + private static final int ELF_MACHINE_X86 = 0x03; + private static final int ELF_MACHINE_X86_64 = 0x3e; + private static final int ELF_MACHINE_ARM = 0x28; + private static final int ELF_MACHINE_AARCH64 = 0xb7; + + private static int readElfMachineType(final File file) { + try (final FileInputStream is = new FileInputStream(file)) { + final byte[] buf = new byte[17]; + int count = 0; + while (count != buf.length) { + count += is.read(buf, count, buf.length - count); + } + + return buf[16]; + } catch (FileNotFoundException e) { + Log.w(LOGTAG, String.format("Failed to open %s", file.getAbsolutePath())); + return ELF_MACHINE_UNKNOWN; + } catch (IOException e) { + Log.w(LOGTAG, "Failed to read library", e); + return ELF_MACHINE_UNKNOWN; + } + } + + private static String machineTypeToString(int machineType) { + switch (machineType) { + case ELF_MACHINE_X86: + return "x86"; + case ELF_MACHINE_X86_64: + return "x86_64"; + case ELF_MACHINE_ARM: + return "arm"; + case ELF_MACHINE_AARCH64: + return "aarch64"; + case ELF_MACHINE_UNKNOWN: + default: + return "unknown"; + } + } + + private static void initMachineType() { + if (sMachineType >= 0) { + return; + } + + sMachineType = readElfMachineType(new File(sLibDir, System.mapLibraryName("mozglue"))); + } + + /** + * @return The ABI of the libraries installed for this app. + */ + public static String getLibrariesABI() { + initMachineType(); + + return machineTypeToString(sMachineType); + } + /** * @return false if the current system is not supported (e.g. APK/system ABI mismatch). */ @@ -130,14 +194,16 @@ public final class HardwareUtils { return false; } + initMachineType(); + // See http://developer.android.com/ndk/guides/abis.html final boolean isSystemX86 = isX86System(); final boolean isSystemARM = !isSystemX86 && isARMSystem(); final boolean isSystemARM64 = isARM64System(); - boolean isAppARM = BuildConfig.ANDROID_CPU_ARCH.startsWith("armeabi-v7a"); - boolean isAppARM64 = BuildConfig.ANDROID_CPU_ARCH.startsWith("arm64-v8a"); - boolean isAppX86 = BuildConfig.ANDROID_CPU_ARCH.startsWith("x86"); + final boolean isAppARM = sMachineType == ELF_MACHINE_ARM; + final boolean isAppARM64 = sMachineType == ELF_MACHINE_AARCH64; + final boolean isAppX86 = sMachineType == ELF_MACHINE_X86; // Only reject known incompatible ABIs. Better safe than sorry. if ((isSystemX86 && isAppARM) || (isSystemARM && isAppX86)) { @@ -150,7 +216,7 @@ public final class HardwareUtils { } Log.w(LOGTAG, "Unknown app/system ABI combination: " + - BuildConfig.MOZ_APP_ABI + " / " + getRealAbi()); + machineTypeToString(sMachineType) + " / " + getRealAbi()); return true; } }