mobile/android/base/background/healthreport/EnvironmentV1.java

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

     1 /* This Source Code Form is subject to the terms of the Mozilla Public
     2  * License, v. 2.0. If a copy of the MPL was not distributed with this
     3  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     5 package org.mozilla.gecko.background.healthreport;
     7 import java.io.UnsupportedEncodingException;
     8 import java.security.NoSuchAlgorithmException;
     9 import java.util.Iterator;
    10 import java.util.SortedSet;
    12 import org.json.JSONException;
    13 import org.json.JSONObject;
    14 import org.mozilla.apache.commons.codec.binary.Base64;
    15 import org.mozilla.gecko.background.common.log.Logger;
    16 import org.mozilla.gecko.background.nativecode.NativeCrypto;
    18 public abstract class EnvironmentV1 {
    19   private static final String LOG_TAG = "GeckoEnvironment";
    20   private static final int VERSION = 1;
    22   protected final Class<? extends EnvironmentAppender> appenderClass;
    24   protected volatile String hash = null;
    25   protected volatile int id = -1;
    27   public int version = VERSION;
    29   // org.mozilla.profile.age.
    30   public int profileCreation;
    32   // org.mozilla.sysinfo.sysinfo.
    33   public int cpuCount;
    34   public int memoryMB;
    35   public String architecture;
    36   public String sysName;
    37   public String sysVersion;       // Kernel.
    39   // geckoAppInfo.
    40   public String vendor;
    41   public String appName;
    42   public String appID;
    43   public String appVersion;
    44   public String appBuildID;
    45   public String platformVersion;
    46   public String platformBuildID;
    47   public String os;
    48   public String xpcomabi;
    49   public String updateChannel;
    51   // appinfo.
    52   public int isBlocklistEnabled;
    53   public int isTelemetryEnabled;
    55   // org.mozilla.addons.active.
    56   public JSONObject addons = null;
    58   // org.mozilla.addons.counts.
    59   public int extensionCount;
    60   public int pluginCount;
    61   public int themeCount;
    63   /**
    64    * We break out this interface in order to allow for testing -- pass in your
    65    * own appender that just records strings, for example.
    66    */
    67   public static abstract class EnvironmentAppender {
    68     public abstract void append(String s);
    69     public abstract void append(int v);
    70   }
    72   public static class HashAppender extends EnvironmentAppender {
    73     private final StringBuilder builder;
    75     public HashAppender() throws NoSuchAlgorithmException {
    76       builder = new StringBuilder();
    77     }
    79     @Override
    80     public void append(String s) {
    81       builder.append((s == null) ? "null" : s);
    82     }
    84     @Override
    85     public void append(int profileCreation) {
    86       append(Integer.toString(profileCreation, 10));
    87     }
    89     @Override
    90     public String toString() {
    91       // We *could* use ASCII85… but the savings would be negated by the
    92       // inclusion of JSON-unsafe characters like double-quote.
    93       final byte[] inputBytes;
    94       try {
    95         inputBytes = builder.toString().getBytes("UTF-8");
    96       } catch (UnsupportedEncodingException e) {
    97         Logger.warn(LOG_TAG, "Invalid charset String passed to getBytes", e);
    98         return null;
    99       }
   101       // Note to the security-minded reader: we deliberately use SHA-1 here, not
   102       // a stronger hash. These identifiers don't strictly need a cryptographic
   103       // hash function, because there is negligible value in attacking the hash.
   104       // We use SHA-1 because it's *shorter* -- the exact same reason that Git
   105       // chose SHA-1.
   106       final byte[] hash = NativeCrypto.sha1(inputBytes);
   107       return new Base64(-1, null, false).encodeAsString(hash);
   108     }
   109   }
   111   /**
   112    * Ensure that the {@link Environment} has been registered with its
   113    * storage layer, and can be used to annotate events.
   114    *
   115    * It's safe to call this method more than once, and each time you'll
   116    * get the same ID.
   117    *
   118    * @return the integer ID to use in subsequent DB insertions.
   119    */
   120   public abstract int register();
   122   protected EnvironmentAppender getAppender() {
   123     EnvironmentAppender appender = null;
   124     try {
   125       appender = appenderClass.newInstance();
   126     } catch (InstantiationException ex) {
   127       // Should never happen, but...
   128       Logger.warn(LOG_TAG,  "Could not compute hash.", ex);
   129     } catch (IllegalAccessException ex) {
   130       // Should never happen, but...
   131       Logger.warn(LOG_TAG,  "Could not compute hash.", ex);
   132     }
   133     return appender;
   134   }
   136   protected void appendHash(EnvironmentAppender appender) {
   137     appender.append(profileCreation);
   138     appender.append(cpuCount);
   139     appender.append(memoryMB);
   140     appender.append(architecture);
   141     appender.append(sysName);
   142     appender.append(sysVersion);
   143     appender.append(vendor);
   144     appender.append(appName);
   145     appender.append(appID);
   146     appender.append(appVersion);
   147     appender.append(appBuildID);
   148     appender.append(platformVersion);
   149     appender.append(platformBuildID);
   150     appender.append(os);
   151     appender.append(xpcomabi);
   152     appender.append(updateChannel);
   153     appender.append(isBlocklistEnabled);
   154     appender.append(isTelemetryEnabled);
   155     appender.append(extensionCount);
   156     appender.append(pluginCount);
   157     appender.append(themeCount);
   159     // We need sorted values.
   160     if (addons != null) {
   161       appendSortedAddons(getNonIgnoredAddons(), appender);
   162     }
   163   }
   165   /**
   166    * Compute the stable hash of the configured environment.
   167    *
   168    * @return the hash in base34, or null if there was a problem.
   169    */
   170   public String getHash() {
   171     // It's never unset, so we only care about partial reads. volatile is enough.
   172     if (hash != null) {
   173       return hash;
   174     }
   176     EnvironmentAppender appender = getAppender();
   177     if (appender == null) {
   178       return null;
   179     }
   181     appendHash(appender);
   182     return hash = appender.toString();
   183   }
   185   public EnvironmentV1(Class<? extends EnvironmentAppender> appenderClass) {
   186     super();
   187     this.appenderClass = appenderClass;
   188   }
   190   public JSONObject getNonIgnoredAddons() {
   191     if (addons == null) {
   192       return null;
   193     }
   194     JSONObject out = new JSONObject();
   195     @SuppressWarnings("unchecked")
   196     Iterator<String> keys = addons.keys();
   197     while (keys.hasNext()) {
   198       try {
   199         final String key = keys.next();
   200         final Object obj = addons.get(key);
   201         if (obj != null &&
   202             obj instanceof JSONObject &&
   203             ((JSONObject) obj).optBoolean("ignore", false)) {
   204           continue;
   205         }
   206         out.put(key, obj);
   207       } catch (JSONException ex) {
   208         // Do nothing.
   209       }
   210     }
   211     return out;
   212   }
   214   /**
   215    * Take a collection of add-on descriptors, appending a consistent string
   216    * to the provided builder.
   217    */
   218   public static void appendSortedAddons(JSONObject addons, final EnvironmentAppender builder) {
   219     final SortedSet<String> keys = HealthReportUtils.sortedKeySet(addons);
   221     // For each add-on, produce a consistent, sorted mapping of its descriptor.
   222     for (String key : keys) {
   223       try {
   224         JSONObject addon = addons.getJSONObject(key);
   226         // Now produce the output for this add-on.
   227         builder.append(key);
   228         builder.append("={");
   230         for (String addonKey : HealthReportUtils.sortedKeySet(addon)) {
   231           builder.append(addonKey);
   232           builder.append("==");
   233           try {
   234             builder.append(addon.get(addonKey).toString());
   235           } catch (JSONException e) {
   236             builder.append("_e_");
   237           }
   238         }
   240         builder.append("}");
   241       } catch (Exception e) {
   242         // Muffle.
   243         Logger.warn(LOG_TAG, "Invalid add-on for ID " + key);
   244       }
   245     }
   246   }
   248   public void setJSONForAddons(byte[] json) throws Exception {
   249     setJSONForAddons(new String(json, "UTF-8"));
   250   }
   252   public void setJSONForAddons(String json) throws Exception {
   253     if (json == null || "null".equals(json)) {
   254       addons = null;
   255       return;
   256     }
   257     addons = new JSONObject(json);
   258   }
   260   public void setJSONForAddons(JSONObject json) {
   261     addons = json;
   262   }
   264   /**
   265    * Includes ignored add-ons.
   266    */
   267   public String getNormalizedAddonsJSON() {
   268     // We trust that our input will already be normalized. If that assumption
   269     // is invalidated, then we'll be sorry.
   270     return (addons == null) ? "null" : addons.toString();
   271   }
   272 }

mercurial