From 512122a016488f71ca3f2868c5950229f8634648 Mon Sep 17 00:00:00 2001 From: Danny Baumann Date: Wed, 23 Nov 2016 10:12:35 +0100 Subject: Make Build.TYPE and Build.FINGERPRINT consistent for apps. Some apps (namely Android Wear) like to do comparisons between TYPE and FINGERPRINT and throw errors on inconsistencies. As our fingerprints are almost always taken from stock ROMs, they don't really match our builds, causing said comparisons to fail. Avoid those failures by taking build type out of fingerprint for apps. Change-Id: I8e8db64de7ea224572ecb3695c85abea91e0e29f --- core/java/android/os/Build.java | 41 ++++++++++++++++++++++ core/java/com/android/internal/os/RuntimeInit.java | 2 ++ 2 files changed, 43 insertions(+) (limited to 'core/java') diff --git a/core/java/android/os/Build.java b/core/java/android/os/Build.java index 862f4c4..883977f 100644 --- a/core/java/android/os/Build.java +++ b/core/java/android/os/Build.java @@ -23,7 +23,11 @@ import com.android.internal.telephony.TelephonyProperties; import dalvik.system.VMRuntime; +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; import java.util.Objects; +import java.util.regex.Matcher; +import java.util.regex.Pattern; /** * Information about the current build, extracted from system properties. @@ -664,6 +668,7 @@ public class Build { /** The type of build, like "user" or "eng". */ public static final String TYPE = getString("ro.build.type"); + private static String TYPE_FOR_APPS = parseBuildTypeFromFingerprint(); /** Comma-separated tags describing the build, like "unsigned,debug". */ public static final String TAGS = getString("ro.build.tags"); @@ -690,6 +695,42 @@ public class Build { return finger; } + // Some apps like to compare the build type embedded in fingerprint + // to the actual build type. As the fingerprint in our case is almost + // always hardcoded to the stock ROM fingerprint, provide that instead + // of the actual one if possible. + private static String parseBuildTypeFromFingerprint() { + final String fingerprint = SystemProperties.get("ro.build.fingerprint"); + if (TextUtils.isEmpty(fingerprint)) { + return null; + } + Pattern fingerprintPattern = + Pattern.compile("(.*)\\/(.*)\\/(.*):(.*)\\/(.*)\\/(.*):(.*)\\/(.*)"); + Matcher matcher = fingerprintPattern.matcher(fingerprint); + return matcher.matches() ? matcher.group(7) : null; + } + + /** @hide */ + public static void adjustBuildTypeIfNeeded() { + if (Process.isApplicationUid(Process.myUid()) && !TextUtils.isEmpty(TYPE_FOR_APPS)) { + try { + // This is sick. TYPE is final (which can't be changed because it's an API + // guarantee), but we have to reassign it. Resort to reflection to unset the + // final modifier, change the value and restore the final modifier afterwards. + Field typeField = Build.class.getField("TYPE"); + Field accessFlagsField = Field.class.getDeclaredField("accessFlags"); + accessFlagsField.setAccessible(true); + int currentFlags = accessFlagsField.getInt(typeField); + accessFlagsField.setInt(typeField, currentFlags & ~Modifier.FINAL); + typeField.set(null, TYPE_FOR_APPS); + accessFlagsField.setInt(typeField, currentFlags); + accessFlagsField.setAccessible(false); + } catch (Exception e) { + // shouldn't happen, but we don't want to crash the app even if it does happen + } + } + } + /** * Ensure that raw fingerprint system property is defined. If it was derived * dynamically by {@link #deriveFingerprint()} this is where we push the diff --git a/core/java/com/android/internal/os/RuntimeInit.java b/core/java/com/android/internal/os/RuntimeInit.java index 3377189..45dac2f 100644 --- a/core/java/com/android/internal/os/RuntimeInit.java +++ b/core/java/com/android/internal/os/RuntimeInit.java @@ -114,6 +114,8 @@ public class RuntimeInit { /* set default handler; this applies to all threads in the VM */ Thread.setDefaultUncaughtExceptionHandler(new UncaughtHandler()); + Build.adjustBuildTypeIfNeeded(); + /* * Install a TimezoneGetter subclass for ZoneInfo.db */ -- cgit v1.1