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: }