package com.bugsnag.android; import android.content.Context; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.support.annotation.NonNull; import android.support.annotation.Nullable; import java.io.IOException; /** * Information about the running Android app which doesn't change over time, * including app name, version and release stage. * <p/> * App information in this class is cached during construction for faster * subsequent lookups and to reduce GC overhead. */ class AppData implements JsonStream.Streamable { private final Configuration config; protected final String packageName; protected final String appName; protected final Integer versionCode; protected final String versionName; protected final String guessedReleaseStage; AppData(@NonNull Context appContext, @NonNull Configuration config) { this.config = config; packageName = getPackageName(appContext); appName = getAppName(appContext); versionCode = getVersionCode(appContext); versionName = getVersionName(appContext); guessedReleaseStage = guessReleaseStage(appContext); } public void toStream(@NonNull JsonStream writer) throws IOException { writer.beginObject(); writer.name("id").value(packageName); writer.name("name").value(appName); writer.name("packageName").value(packageName); writer.name("versionName").value(versionName); writer.name("versionCode").value(versionCode); writer.name("buildUUID").value(config.getBuildUUID()); // Prefer user-configured appVersion + releaseStage writer.name("version").value(getAppVersion()); writer.name("releaseStage").value(getReleaseStage()); writer.endObject(); } @NonNull public String getReleaseStage() { if (config.getReleaseStage() != null) { return config.getReleaseStage(); } else { return guessedReleaseStage; } } @Nullable public String getAppVersion() { if (config.getAppVersion() != null) { return config.getAppVersion(); } else { return versionName; } } /** * The package name of the running Android app, eg: com.example.myapp */ @NonNull private static String getPackageName(Context appContext) { return appContext.getPackageName(); } /** * The name of the running Android app, from android:label in * AndroidManifest.xml */ @Nullable private static String getAppName(Context appContext) { try { PackageManager packageManager = appContext.getPackageManager(); ApplicationInfo appInfo = packageManager.getApplicationInfo(appContext.getPackageName(), 0); return (String) packageManager.getApplicationLabel(appInfo); } catch (PackageManager.NameNotFoundException e) { Logger.warn("Could not get app name"); } return null; } /** * The version code of the running Android app, from android:versionCode * in AndroidManifest.xml */ @Nullable private static Integer getVersionCode(Context appContext) { try { return appContext.getPackageManager().getPackageInfo(appContext.getPackageName(), 0).versionCode; } catch (PackageManager.NameNotFoundException e) { Logger.warn("Could not get versionCode"); } return null; } /** * The version code of the running Android app, from android:versionName * in AndroidManifest.xml */ @Nullable private static String getVersionName(Context appContext) { try { return appContext.getPackageManager().getPackageInfo(appContext.getPackageName(), 0).versionName; } catch (PackageManager.NameNotFoundException e) { Logger.warn("Could not get versionName"); } return null; } /** * Guess the release stage of the running Android app by checking the * android:debuggable flag from AndroidManifest.xml */ @NonNull private static String guessReleaseStage(Context appContext) { try { int appFlags = appContext.getPackageManager().getApplicationInfo(appContext.getPackageName(), 0).flags; if ((appFlags & ApplicationInfo.FLAG_DEBUGGABLE) != 0) { return "development"; } } catch (PackageManager.NameNotFoundException e) { Logger.warn("Could not get releaseStage"); } return "production"; } }