michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: package org.mozilla.gecko.background.healthreport; michael@0: michael@0: import java.util.Iterator; michael@0: michael@0: import org.json.JSONObject; michael@0: import org.mozilla.gecko.AppConstants; michael@0: import org.mozilla.gecko.SysInfo; michael@0: import org.mozilla.gecko.background.common.GlobalConstants; michael@0: import org.mozilla.gecko.background.common.log.Logger; michael@0: michael@0: import android.content.ContentProvider; michael@0: import android.content.ContentProviderClient; michael@0: import android.content.ContentResolver; michael@0: import android.content.Context; michael@0: michael@0: /** michael@0: * Construct a HealthReport environment from the current running system. michael@0: */ michael@0: public class EnvironmentBuilder { michael@0: private static final String LOG_TAG = "GeckoEnvBuilder"; michael@0: michael@0: public static ContentProviderClient getContentProviderClient(Context context) { michael@0: ContentResolver cr = context.getContentResolver(); michael@0: return cr.acquireContentProviderClient(HealthReportConstants.HEALTH_AUTHORITY); michael@0: } michael@0: michael@0: /** michael@0: * Fetch the storage object associated with the provided michael@0: * {@link ContentProviderClient}. If no storage instance can be found -- michael@0: * perhaps because the {@link ContentProvider} is running in a different michael@0: * process -- returns null. On success, the returned michael@0: * {@link HealthReportDatabaseStorage} instance is owned by the underlying michael@0: * {@link HealthReportProvider} and thus does not need to be closed by the michael@0: * caller. michael@0: * michael@0: * If the provider is not a {@link HealthReportProvider}, throws a michael@0: * {@link ClassCastException}, because that would be disastrous. michael@0: */ michael@0: public static HealthReportDatabaseStorage getStorage(ContentProviderClient cpc, michael@0: String profilePath) { michael@0: ContentProvider pr = cpc.getLocalContentProvider(); michael@0: if (pr == null) { michael@0: Logger.error(LOG_TAG, "Unable to retrieve local content provider. Running in a different process?"); michael@0: return null; michael@0: } michael@0: try { michael@0: return ((HealthReportProvider) pr).getProfileStorage(profilePath); michael@0: } catch (ClassCastException ex) { michael@0: Logger.error(LOG_TAG, "ContentProvider not a HealthReportProvider!", ex); michael@0: throw ex; michael@0: } michael@0: } michael@0: michael@0: public static interface ProfileInformationProvider { michael@0: public boolean isBlocklistEnabled(); michael@0: public boolean isTelemetryEnabled(); michael@0: public boolean isAcceptLangUserSet(); michael@0: public long getProfileCreationTime(); michael@0: michael@0: public String getDistributionString(); michael@0: public String getOSLocale(); michael@0: public String getAppLocale(); michael@0: michael@0: public JSONObject getAddonsJSON(); michael@0: } michael@0: michael@0: protected static void populateEnvironment(Environment e, michael@0: ProfileInformationProvider info) { michael@0: e.cpuCount = SysInfo.getCPUCount(); michael@0: e.memoryMB = SysInfo.getMemSize(); michael@0: michael@0: e.appName = AppConstants.MOZ_APP_NAME; michael@0: e.appID = AppConstants.MOZ_APP_ID; michael@0: e.appVersion = AppConstants.MOZ_APP_VERSION; michael@0: e.appBuildID = AppConstants.MOZ_APP_BUILDID; michael@0: e.updateChannel = AppConstants.MOZ_UPDATE_CHANNEL; michael@0: e.vendor = AppConstants.MOZ_APP_VENDOR; michael@0: e.platformVersion = AppConstants.MOZILLA_VERSION; michael@0: e.platformBuildID = AppConstants.MOZ_APP_BUILDID; michael@0: e.xpcomabi = AppConstants.TARGET_XPCOM_ABI; michael@0: e.os = "Android"; michael@0: e.architecture = SysInfo.getArchABI(); // Not just "arm". michael@0: e.sysName = SysInfo.getName(); michael@0: e.sysVersion = SysInfo.getReleaseVersion(); michael@0: michael@0: e.profileCreation = (int) (info.getProfileCreationTime() / GlobalConstants.MILLISECONDS_PER_DAY); michael@0: michael@0: // Corresponds to Gecko pref "extensions.blocklist.enabled". michael@0: e.isBlocklistEnabled = (info.isBlocklistEnabled() ? 1 : 0); michael@0: michael@0: // Corresponds to Gecko pref "toolkit.telemetry.enabled". michael@0: e.isTelemetryEnabled = (info.isTelemetryEnabled() ? 1 : 0); michael@0: michael@0: e.extensionCount = 0; michael@0: e.pluginCount = 0; michael@0: e.themeCount = 0; michael@0: michael@0: JSONObject addons = info.getAddonsJSON(); michael@0: if (addons == null) { michael@0: return; michael@0: } michael@0: michael@0: @SuppressWarnings("unchecked") michael@0: Iterator it = addons.keys(); michael@0: while (it.hasNext()) { michael@0: String key = it.next(); michael@0: try { michael@0: JSONObject addon = addons.getJSONObject(key); michael@0: String type = addon.optString("type"); michael@0: Logger.pii(LOG_TAG, "Add-on " + key + " is a " + type); michael@0: if ("extension".equals(type)) { michael@0: ++e.extensionCount; michael@0: } else if ("plugin".equals(type)) { michael@0: ++e.pluginCount; michael@0: } else if ("theme".equals(type)) { michael@0: ++e.themeCount; michael@0: } else if ("service".equals(type)) { michael@0: // Later. michael@0: } else { michael@0: Logger.debug(LOG_TAG, "Unknown add-on type: " + type); michael@0: } michael@0: } catch (Exception ex) { michael@0: Logger.warn(LOG_TAG, "Failed to process add-on " + key, ex); michael@0: } michael@0: } michael@0: michael@0: e.addons = addons; michael@0: michael@0: // v2 environment fields. michael@0: e.distribution = info.getDistributionString(); michael@0: e.osLocale = info.getOSLocale(); michael@0: e.appLocale = info.getAppLocale(); michael@0: e.acceptLangSet = info.isAcceptLangUserSet() ? 1 : 0; michael@0: } michael@0: michael@0: /** michael@0: * Returns an {@link Environment} not linked to a storage instance, but michael@0: * populated with current field values. michael@0: * michael@0: * @param info a source of profile data michael@0: * @return the new {@link Environment} michael@0: */ michael@0: public static Environment getCurrentEnvironment(ProfileInformationProvider info) { michael@0: Environment e = new Environment() { michael@0: @Override michael@0: public int register() { michael@0: return 0; michael@0: } michael@0: }; michael@0: populateEnvironment(e, info); michael@0: return e; michael@0: } michael@0: michael@0: /** michael@0: * @return the current environment's ID in the provided storage layer michael@0: */ michael@0: public static int registerCurrentEnvironment(final HealthReportStorage storage, michael@0: final ProfileInformationProvider info) { michael@0: Environment e = storage.getEnvironment(); michael@0: populateEnvironment(e, info); michael@0: e.register(); michael@0: Logger.debug(LOG_TAG, "Registering current environment: " + e.getHash() + " = " + e.id); michael@0: return e.id; michael@0: } michael@0: }