blob: 111bca8d0128756d8c7cf1fa95a6832a9137e558 [file] [log] [blame]
// Copyright 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package org.chromium.base;
import android.content.Context;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.os.Build;
import android.os.Build.VERSION;
import android.text.TextUtils;
import org.chromium.base.annotations.CalledByNative;
/**
* BuildInfo is a utility class providing easy access to {@link PackageInfo} information. This is
* primarily of use for accessing package information from native code.
*/
public class BuildInfo {
private static final String TAG = "BuildInfo";
private static final int MAX_FINGERPRINT_LENGTH = 128;
private static PackageInfo sBrowserPackageInfo;
private static boolean sInitialized;
/** The application name (e.g. "Chrome"). For WebView, this is name of the embedding app. */
public final String hostPackageLabel;
/** By default: same as versionCode. For WebView: versionCode of the embedding app. */
public final int hostVersionCode;
/** The packageName of Chrome/WebView. Use application context for host app packageName. */
public final String packageName;
/** The versionCode of the apk. */
public final int versionCode;
/** The versionName of Chrome/WebView. Use application context for host app versionName. */
public final String versionName;
/** Result of PackageManager.getInstallerPackageName(). Never null, but may be "". */
public final String installerPackageName;
/** The versionCode of Play Services (for crash reporting). */
public final String gmsVersionCode;
/** Formatted ABI string (for crash reporting). */
public final String abiString;
/** Truncated version of Build.FINGERPRINT (for crash reporting). */
public final String androidBuildFingerprint;
/** A string that is different each time the apk changes. */
public final String extractedFileSuffix;
/** Whether or not the device has apps installed for using custom themes. */
public final String customThemes;
/** Product version as stored in Android resources. */
public final String resourcesVersion;
private static class Holder { private static BuildInfo sInstance = new BuildInfo(); }
@CalledByNative
private static String[] getAll() {
BuildInfo buildInfo = getInstance();
String hostPackageName = ContextUtils.getApplicationContext().getPackageName();
return new String[] {
Build.BRAND, Build.DEVICE, Build.ID, Build.MANUFACTURER, Build.MODEL,
String.valueOf(Build.VERSION.SDK_INT), Build.TYPE, Build.BOARD, hostPackageName,
String.valueOf(buildInfo.hostVersionCode), buildInfo.hostPackageLabel,
buildInfo.packageName, String.valueOf(buildInfo.versionCode), buildInfo.versionName,
buildInfo.androidBuildFingerprint, buildInfo.gmsVersionCode,
buildInfo.installerPackageName, buildInfo.abiString, BuildConfig.FIREBASE_APP_ID,
buildInfo.customThemes, buildInfo.resourcesVersion, buildInfo.extractedFileSuffix,
isAtLeastP() ? "1" : "0",
};
}
private static String nullToEmpty(CharSequence seq) {
return seq == null ? "" : seq.toString();
}
/**
* @param packageInfo Package for Chrome/WebView (as opposed to host app).
*/
public static void setBrowserPackageInfo(PackageInfo packageInfo) {
assert !sInitialized;
sBrowserPackageInfo = packageInfo;
}
public static BuildInfo getInstance() {
return Holder.sInstance;
}
private BuildInfo() {
sInitialized = true;
try {
Context appContext = ContextUtils.getApplicationContext();
String hostPackageName = appContext.getPackageName();
PackageManager pm = appContext.getPackageManager();
PackageInfo pi = pm.getPackageInfo(hostPackageName, 0);
hostVersionCode = pi.versionCode;
if (sBrowserPackageInfo != null) {
packageName = sBrowserPackageInfo.packageName;
versionCode = sBrowserPackageInfo.versionCode;
versionName = nullToEmpty(sBrowserPackageInfo.versionName);
sBrowserPackageInfo = null;
} else {
packageName = hostPackageName;
versionCode = hostVersionCode;
versionName = nullToEmpty(pi.versionName);
}
hostPackageLabel = nullToEmpty(pm.getApplicationLabel(pi.applicationInfo));
installerPackageName = nullToEmpty(pm.getInstallerPackageName(packageName));
PackageInfo gmsPackageInfo = null;
try {
gmsPackageInfo = pm.getPackageInfo("com.google.android.gms", 0);
} catch (NameNotFoundException e) {
Log.d(TAG, "GMS package is not found.", e);
}
gmsVersionCode = gmsPackageInfo != null ? String.valueOf(gmsPackageInfo.versionCode)
: "gms versionCode not available.";
String hasCustomThemes = "true";
try {
// Substratum is a theme engine that enables users to use custom themes provided
// by theme apps. Sometimes these can cause crashs if not installed correctly.
// These crashes can be difficult to debug, so knowing if the theme manager is
// present on the device is useful (http://crbug.com/820591).
pm.getPackageInfo("projekt.substratum", 0);
} catch (NameNotFoundException e) {
hasCustomThemes = "false";
}
customThemes = hasCustomThemes;
String currentResourcesVersion = "Not Enabled";
// Controlled by target specific build flags.
if (BuildConfig.R_STRING_PRODUCT_VERSION != 0) {
try {
// This value can be compared with the actual product version to determine if
// corrupted resources were the cause of a crash. This can happen if the app
// loads resources from the outdated package during an update
// (http://crbug.com/820591).
currentResourcesVersion = ContextUtils.getApplicationContext().getString(
BuildConfig.R_STRING_PRODUCT_VERSION);
} catch (Exception e) {
currentResourcesVersion = "Not found";
}
}
resourcesVersion = currentResourcesVersion;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
abiString = TextUtils.join(", ", Build.SUPPORTED_ABIS);
} else {
abiString = String.format("ABI1: %s, ABI2: %s", Build.CPU_ABI, Build.CPU_ABI2);
}
// Use lastUpdateTime when developing locally, since versionCode does not normally
// change in this case.
long version = versionCode > 10 ? versionCode : pi.lastUpdateTime;
extractedFileSuffix = String.format("@%x", version);
// The value is truncated, as this is used for crash and UMA reporting.
androidBuildFingerprint = Build.FINGERPRINT.substring(
0, Math.min(Build.FINGERPRINT.length(), MAX_FINGERPRINT_LENGTH));
} catch (NameNotFoundException e) {
throw new RuntimeException(e);
}
}
/**
* Check if this is a debuggable build of Android. Use this to enable developer-only features.
*/
public static boolean isDebugAndroid() {
return "eng".equals(Build.TYPE) || "userdebug".equals(Build.TYPE);
}
// The markers Begin:BuildCompat and End:BuildCompat delimit code
// that is autogenerated from Android sources.
// Begin:BuildCompat P
/**
* Checks if the device is running on a pre-release version of Android P or newer.
* <p>
* @return {@code true} if P APIs are available for use, {@code false} otherwise
*/
public static boolean isAtLeastP() {
return VERSION.SDK_INT >= 28;
}
/**
* Checks if the application targets at least released SDK P
*/
public static boolean targetsAtLeastP() {
return ContextUtils.getApplicationContext().getApplicationInfo().targetSdkVersion >= 28;
}
// End:BuildCompat
}