1 // Copyright 2012 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 package org.chromium.base; 6 7 import android.content.Context; 8 import android.content.pm.PackageInfo; 9 import android.content.pm.PackageManager; 10 import android.content.pm.PackageManager.NameNotFoundException; 11 import android.os.Build; 12 import android.os.Build.VERSION; 13 import android.text.TextUtils; 14 15 import org.chromium.base.annotations.CalledByNative; 16 17 /** 18 * BuildInfo is a utility class providing easy access to {@link PackageInfo} information. This is 19 * primarily of use for accessing package information from native code. 20 */ 21 public class BuildInfo { 22 private static final String TAG = "BuildInfo"; 23 private static final int MAX_FINGERPRINT_LENGTH = 128; 24 25 private static PackageInfo sBrowserPackageInfo; 26 private static boolean sInitialized; 27 28 /** The application name (e.g. "Chrome"). For WebView, this is name of the embedding app. */ 29 public final String hostPackageLabel; 30 /** By default: same as versionCode. For WebView: versionCode of the embedding app. */ 31 public final int hostVersionCode; 32 /** The packageName of Chrome/WebView. Use application context for host app packageName. */ 33 public final String packageName; 34 /** The versionCode of the apk. */ 35 public final int versionCode; 36 /** The versionName of Chrome/WebView. Use application context for host app versionName. */ 37 public final String versionName; 38 /** Result of PackageManager.getInstallerPackageName(). Never null, but may be "". */ 39 public final String installerPackageName; 40 /** The versionCode of Play Services (for crash reporting). */ 41 public final String gmsVersionCode; 42 /** Formatted ABI string (for crash reporting). */ 43 public final String abiString; 44 /** Truncated version of Build.FINGERPRINT (for crash reporting). */ 45 public final String androidBuildFingerprint; 46 /** A string that is different each time the apk changes. */ 47 public final String extractedFileSuffix; 48 /** Whether or not the device has apps installed for using custom themes. */ 49 public final String customThemes; 50 /** Product version as stored in Android resources. */ 51 public final String resourcesVersion; 52 53 private static class Holder { private static BuildInfo sInstance = new BuildInfo(); } 54 55 @CalledByNative getAll()56 private static String[] getAll() { 57 BuildInfo buildInfo = getInstance(); 58 String hostPackageName = ContextUtils.getApplicationContext().getPackageName(); 59 return new String[] { 60 Build.BRAND, Build.DEVICE, Build.ID, Build.MANUFACTURER, Build.MODEL, 61 String.valueOf(Build.VERSION.SDK_INT), Build.TYPE, Build.BOARD, hostPackageName, 62 String.valueOf(buildInfo.hostVersionCode), buildInfo.hostPackageLabel, 63 buildInfo.packageName, String.valueOf(buildInfo.versionCode), buildInfo.versionName, 64 buildInfo.androidBuildFingerprint, buildInfo.gmsVersionCode, 65 buildInfo.installerPackageName, buildInfo.abiString, BuildConfig.FIREBASE_APP_ID, 66 buildInfo.customThemes, buildInfo.resourcesVersion, buildInfo.extractedFileSuffix, 67 isAtLeastP() ? "1" : "0", 68 }; 69 } 70 nullToEmpty(CharSequence seq)71 private static String nullToEmpty(CharSequence seq) { 72 return seq == null ? "" : seq.toString(); 73 } 74 75 /** 76 * @param packageInfo Package for Chrome/WebView (as opposed to host app). 77 */ setBrowserPackageInfo(PackageInfo packageInfo)78 public static void setBrowserPackageInfo(PackageInfo packageInfo) { 79 assert !sInitialized; 80 sBrowserPackageInfo = packageInfo; 81 } 82 getInstance()83 public static BuildInfo getInstance() { 84 return Holder.sInstance; 85 } 86 BuildInfo()87 private BuildInfo() { 88 sInitialized = true; 89 try { 90 Context appContext = ContextUtils.getApplicationContext(); 91 String hostPackageName = appContext.getPackageName(); 92 PackageManager pm = appContext.getPackageManager(); 93 PackageInfo pi = pm.getPackageInfo(hostPackageName, 0); 94 hostVersionCode = pi.versionCode; 95 if (sBrowserPackageInfo != null) { 96 packageName = sBrowserPackageInfo.packageName; 97 versionCode = sBrowserPackageInfo.versionCode; 98 versionName = nullToEmpty(sBrowserPackageInfo.versionName); 99 sBrowserPackageInfo = null; 100 } else { 101 packageName = hostPackageName; 102 versionCode = hostVersionCode; 103 versionName = nullToEmpty(pi.versionName); 104 } 105 106 hostPackageLabel = nullToEmpty(pm.getApplicationLabel(pi.applicationInfo)); 107 installerPackageName = nullToEmpty(pm.getInstallerPackageName(packageName)); 108 109 PackageInfo gmsPackageInfo = null; 110 try { 111 gmsPackageInfo = pm.getPackageInfo("com.google.android.gms", 0); 112 } catch (NameNotFoundException e) { 113 Log.d(TAG, "GMS package is not found.", e); 114 } 115 gmsVersionCode = gmsPackageInfo != null ? String.valueOf(gmsPackageInfo.versionCode) 116 : "gms versionCode not available."; 117 118 String hasCustomThemes = "true"; 119 try { 120 // Substratum is a theme engine that enables users to use custom themes provided 121 // by theme apps. Sometimes these can cause crashs if not installed correctly. 122 // These crashes can be difficult to debug, so knowing if the theme manager is 123 // present on the device is useful (http://crbug.com/820591). 124 pm.getPackageInfo("projekt.substratum", 0); 125 } catch (NameNotFoundException e) { 126 hasCustomThemes = "false"; 127 } 128 customThemes = hasCustomThemes; 129 130 String currentResourcesVersion = "Not Enabled"; 131 // Controlled by target specific build flags. 132 if (BuildConfig.R_STRING_PRODUCT_VERSION != 0) { 133 try { 134 // This value can be compared with the actual product version to determine if 135 // corrupted resources were the cause of a crash. This can happen if the app 136 // loads resources from the outdated package during an update 137 // (http://crbug.com/820591). 138 currentResourcesVersion = ContextUtils.getApplicationContext().getString( 139 BuildConfig.R_STRING_PRODUCT_VERSION); 140 } catch (Exception e) { 141 currentResourcesVersion = "Not found"; 142 } 143 } 144 resourcesVersion = currentResourcesVersion; 145 146 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { 147 abiString = TextUtils.join(", ", Build.SUPPORTED_ABIS); 148 } else { 149 abiString = String.format("ABI1: %s, ABI2: %s", Build.CPU_ABI, Build.CPU_ABI2); 150 } 151 152 // Use lastUpdateTime when developing locally, since versionCode does not normally 153 // change in this case. 154 long version = versionCode > 10 ? versionCode : pi.lastUpdateTime; 155 extractedFileSuffix = String.format("@%x", version); 156 157 // The value is truncated, as this is used for crash and UMA reporting. 158 androidBuildFingerprint = Build.FINGERPRINT.substring( 159 0, Math.min(Build.FINGERPRINT.length(), MAX_FINGERPRINT_LENGTH)); 160 } catch (NameNotFoundException e) { 161 throw new RuntimeException(e); 162 } 163 } 164 165 /** 166 * Check if this is a debuggable build of Android. Use this to enable developer-only features. 167 */ isDebugAndroid()168 public static boolean isDebugAndroid() { 169 return "eng".equals(Build.TYPE) || "userdebug".equals(Build.TYPE); 170 } 171 172 // The markers Begin:BuildCompat and End:BuildCompat delimit code 173 // that is autogenerated from Android sources. 174 // Begin:BuildCompat P 175 176 /** 177 * Checks if the device is running on a pre-release version of Android P or newer. 178 * <p> 179 * @return {@code true} if P APIs are available for use, {@code false} otherwise 180 */ isAtLeastP()181 public static boolean isAtLeastP() { 182 return VERSION.SDK_INT >= 28; 183 } 184 185 /** 186 * Checks if the application targets at least released SDK P 187 */ targetsAtLeastP()188 public static boolean targetsAtLeastP() { 189 return ContextUtils.getApplicationContext().getApplicationInfo().targetSdkVersion >= 28; 190 } 191 192 // End:BuildCompat 193 } 194