Bug 1183320 - Remove FHR from android/services r=rnewman a=ritu
authorMark Finkle <mfinkle@mozilla.com>
Mon, 11 Jan 2016 23:08:12 -0500
changeset 292558 e146313ddb54
parent 292557 dbf26af1a8a6
child 292559 8c3efcef8b61
push id262
push usercbook@mozilla.com
push date2016-01-15 08:58 +0000
reviewersrnewman, ritu
bugs1183320
milestone44.0
Bug 1183320 - Remove FHR from android/services r=rnewman a=ritu
mobile/android/base/AndroidManifest.xml.in
mobile/android/base/android-services.mozbuild
mobile/android/base/background/bagheera/BagheeraClient.java
mobile/android/base/background/bagheera/BagheeraRequestDelegate.java
mobile/android/base/background/bagheera/BoundedByteArrayEntity.java
mobile/android/base/background/bagheera/DeflateHelper.java
mobile/android/base/background/common/GlobalConstants.java
mobile/android/base/background/healthreport/AndroidConfigurationProvider.java
mobile/android/base/background/healthreport/Environment.java
mobile/android/base/background/healthreport/EnvironmentBuilder.java
mobile/android/base/background/healthreport/EnvironmentV1.java
mobile/android/base/background/healthreport/EnvironmentV2.java
mobile/android/base/background/healthreport/HealthReportBroadcastReceiver.java
mobile/android/base/background/healthreport/HealthReportBroadcastService.java
mobile/android/base/background/healthreport/HealthReportConstants.java
mobile/android/base/background/healthreport/HealthReportDatabaseStorage.java
mobile/android/base/background/healthreport/HealthReportDatabases.java
mobile/android/base/background/healthreport/HealthReportGenerator.java
mobile/android/base/background/healthreport/HealthReportProvider.java
mobile/android/base/background/healthreport/HealthReportStorage.java
mobile/android/base/background/healthreport/HealthReportUtils.java
mobile/android/base/background/healthreport/ProfileInformationCache.java
mobile/android/base/background/healthreport/prune/HealthReportPruneService.java
mobile/android/base/background/healthreport/prune/PrunePolicy.java
mobile/android/base/background/healthreport/prune/PrunePolicyDatabaseStorage.java
mobile/android/base/background/healthreport/prune/PrunePolicyStorage.java
mobile/android/base/background/healthreport/upload/AndroidSubmissionClient.java
mobile/android/base/background/healthreport/upload/HealthReportUploadService.java
mobile/android/base/background/healthreport/upload/ObsoleteDocumentTracker.java
mobile/android/base/background/healthreport/upload/SubmissionClient.java
mobile/android/base/background/healthreport/upload/SubmissionPolicy.java
mobile/android/services/manifests/HealthReportAndroidManifest_activities.xml.in
mobile/android/services/manifests/HealthReportAndroidManifest_permissions.xml.in
mobile/android/services/manifests/HealthReportAndroidManifest_services.xml.in
mobile/android/services/manifests/SyncAndroidManifest_permissions.xml.in
mobile/android/tests/background/junit3/background_junit3_sources.mozbuild
mobile/android/tests/background/junit3/instrumentation.ini
mobile/android/tests/background/junit3/src/org/mozilla/gecko/background/healthreport/MockDatabaseEnvironment.java
mobile/android/tests/background/junit3/src/org/mozilla/gecko/background/healthreport/MockHealthReportDatabaseStorage.java
mobile/android/tests/background/junit3/src/org/mozilla/gecko/background/healthreport/MockHealthReportSQLiteOpenHelper.java
mobile/android/tests/background/junit3/src/org/mozilla/gecko/background/healthreport/MockProfileInformationCache.java
mobile/android/tests/background/junit3/src/org/mozilla/gecko/background/healthreport/TestEnvironmentBuilder.java
mobile/android/tests/background/junit3/src/org/mozilla/gecko/background/healthreport/TestEnvironmentV1HashAppender.java
mobile/android/tests/background/junit3/src/org/mozilla/gecko/background/healthreport/TestHealthReportBroadcastService.java
mobile/android/tests/background/junit3/src/org/mozilla/gecko/background/healthreport/TestHealthReportDatabaseStorage.java
mobile/android/tests/background/junit3/src/org/mozilla/gecko/background/healthreport/TestHealthReportGenerator.java
mobile/android/tests/background/junit3/src/org/mozilla/gecko/background/healthreport/TestHealthReportProvider.java
mobile/android/tests/background/junit3/src/org/mozilla/gecko/background/healthreport/TestHealthReportSQLiteOpenHelper.java
mobile/android/tests/background/junit3/src/org/mozilla/gecko/background/healthreport/TestProfileInformationCache.java
mobile/android/tests/background/junit3/src/org/mozilla/gecko/background/healthreport/prune/TestHealthReportPruneService.java
mobile/android/tests/background/junit3/src/org/mozilla/gecko/background/healthreport/prune/TestPrunePolicyDatabaseStorage.java
mobile/android/tests/background/junit3/src/org/mozilla/gecko/background/healthreport/upload/TestAndroidSubmissionClient.java
mobile/android/tests/background/junit3/src/org/mozilla/gecko/background/healthreport/upload/TestHealthReportUploadService.java
mobile/android/tests/background/junit3/src/org/mozilla/gecko/background/testhelpers/StubDelegate.java
mobile/android/tests/background/junit4/src/org/mozilla/gecko/background/healthreport/prune/test/TestPrunePolicy.java
mobile/android/tests/background/junit4/src/org/mozilla/gecko/background/healthreport/test/HealthReportStorageStub.java
mobile/android/tests/background/junit4/src/org/mozilla/gecko/background/healthreport/upload/test/MockAndroidSubmissionClient.java
mobile/android/tests/background/junit4/src/org/mozilla/gecko/background/healthreport/upload/test/TestObsoleteDocumentTracker.java
mobile/android/tests/background/junit4/src/org/mozilla/gecko/background/healthreport/upload/test/TestSubmissionPolicy.java
mobile/android/tests/background/junit4/src/org/mozilla/gecko/background/healthreport/upload/test/TestSubmissionsTracker.java
mobile/android/tests/background/junit4/src/org/mozilla/gecko/background/healthreport/upload/test/TestTrackingRequestDelegate.java
mobile/android/tests/background/junit4/src/org/mozilla/gecko/background/test/TestBoundedByteArrayEntity.java
mobile/android/tests/background/junit4/src/org/mozilla/gecko/background/test/TestDeflation.java
mobile/android/tests/background/junit4/src/org/mozilla/gecko/background/testhelpers/StubDelegate.java
--- a/mobile/android/base/AndroidManifest.xml.in
+++ b/mobile/android/base/AndroidManifest.xml.in
@@ -11,17 +11,16 @@
       >
     <uses-sdk android:minSdkVersion="@MOZ_ANDROID_MIN_SDK_VERSION@"
 #ifdef MOZ_ANDROID_MAX_SDK_VERSION
               android:maxSdkVersion="@MOZ_ANDROID_MAX_SDK_VERSION@"
 #endif
               android:targetSdkVersion="22"/>
 
 #include ../services/manifests/FxAccountAndroidManifest_permissions.xml.in
-#include ../services/manifests/HealthReportAndroidManifest_permissions.xml.in
 #include ../services/manifests/SyncAndroidManifest_permissions.xml.in
 
 #ifdef MOZ_ANDROID_SEARCH_ACTIVITY
 #include ../search/manifests/SearchAndroidManifest_permissions.xml.in
 #endif
 
     <uses-permission android:name="android.permission.CHANGE_WIFI_STATE"/>
     <uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
@@ -387,17 +386,16 @@
                  android:exported="false"
                  android:process="@MANGLED_ANDROID_PACKAGE_NAME@.Restarter">
         </service>
 
         <receiver android:name="org.mozilla.gecko.AlarmReceiver" >
         </receiver>
 
 #include ../services/manifests/FxAccountAndroidManifest_activities.xml.in
-#include ../services/manifests/HealthReportAndroidManifest_activities.xml.in
 #include ../services/manifests/SyncAndroidManifest_activities.xml.in
 #ifdef MOZ_ANDROID_SEARCH_ACTIVITY
 #include ../search/manifests/SearchAndroidManifest_activities.xml.in
 #endif
 
 #if MOZ_CRASHREPORTER
   <activity android:name="org.mozilla.gecko.CrashReporter"
             android:process="@ANDROID_PACKAGE_NAME@.CrashReporter"
@@ -492,17 +490,16 @@
 
         <service
             android:exported="false"
             android:name="org.mozilla.gecko.NotificationService">
         </service>
 
 
 #include ../services/manifests/FxAccountAndroidManifest_services.xml.in
-#include ../services/manifests/HealthReportAndroidManifest_services.xml.in
 #include ../services/manifests/SyncAndroidManifest_services.xml.in
 
         <service android:name="org.mozilla.gecko.sync.TabReceivedService"
                   android:exported="false"/>
 
 #ifdef MOZ_ANDROID_SEARCH_ACTIVITY
 #include ../search/manifests/SearchAndroidManifest_services.xml.in
 #endif
--- a/mobile/android/base/android-services.mozbuild
+++ b/mobile/android/base/android-services.mozbuild
@@ -760,20 +760,16 @@ sync_thirdparty_java_files = [
     'org/mozilla/apache/commons/codec/net/Utils.java',
     'org/mozilla/apache/commons/codec/StringDecoder.java',
     'org/mozilla/apache/commons/codec/StringEncoder.java',
     'org/mozilla/apache/commons/codec/StringEncoderComparator.java',
 ]
 
 sync_java_files = [
     'background/BackgroundService.java',
-    'background/bagheera/BagheeraClient.java',
-    'background/bagheera/BagheeraRequestDelegate.java',
-    'background/bagheera/BoundedByteArrayEntity.java',
-    'background/bagheera/DeflateHelper.java',
     'background/common/DateUtils.java',
     'background/common/EditorBranch.java',
     'background/common/GlobalConstants.java',
     'background/common/log/Logger.java',
     'background/common/log/writers/AndroidLevelCachingLogWriter.java',
     'background/common/log/writers/AndroidLogWriter.java',
     'background/common/log/writers/LevelFilteringLogWriter.java',
     'background/common/log/writers/LogWriter.java',
@@ -801,40 +797,16 @@ sync_java_files = [
     'background/fxa/oauth/FxAccountAbstractClient.java',
     'background/fxa/oauth/FxAccountAbstractClientException.java',
     'background/fxa/oauth/FxAccountOAuthClient10.java',
     'background/fxa/oauth/FxAccountOAuthRemoteError.java',
     'background/fxa/PasswordStretcher.java',
     'background/fxa/profile/FxAccountProfileClient10.java',
     'background/fxa/QuickPasswordStretcher.java',
     'background/fxa/SkewHandler.java',
-    'background/healthreport/AndroidConfigurationProvider.java',
-    'background/healthreport/Environment.java',
-    'background/healthreport/EnvironmentBuilder.java',
-    'background/healthreport/EnvironmentV1.java',
-    'background/healthreport/EnvironmentV2.java',
-    'background/healthreport/HealthReportBroadcastReceiver.java',
-    'background/healthreport/HealthReportBroadcastService.java',
-    'background/healthreport/HealthReportConstants.java',
-    'background/healthreport/HealthReportDatabases.java',
-    'background/healthreport/HealthReportDatabaseStorage.java',
-    'background/healthreport/HealthReportGenerator.java',
-    'background/healthreport/HealthReportProvider.java',
-    'background/healthreport/HealthReportStorage.java',
-    'background/healthreport/HealthReportUtils.java',
-    'background/healthreport/ProfileInformationCache.java',
-    'background/healthreport/prune/HealthReportPruneService.java',
-    'background/healthreport/prune/PrunePolicy.java',
-    'background/healthreport/prune/PrunePolicyDatabaseStorage.java',
-    'background/healthreport/prune/PrunePolicyStorage.java',
-    'background/healthreport/upload/AndroidSubmissionClient.java',
-    'background/healthreport/upload/HealthReportUploadService.java',
-    'background/healthreport/upload/ObsoleteDocumentTracker.java',
-    'background/healthreport/upload/SubmissionClient.java',
-    'background/healthreport/upload/SubmissionPolicy.java',
     'background/nativecode/NativeCrypto.java',
     'background/preferences/PreferenceFragment.java',
     'background/preferences/PreferenceManagerCompat.java',
     'background/ReadingListConstants.java',
     'browserid/ASNUtils.java',
     'browserid/BrowserIDKeyPair.java',
     'browserid/DSACryptoImplementation.java',
     'browserid/JSONWebTokenUtils.java',
deleted file mode 100644
--- a/mobile/android/base/background/bagheera/BagheeraClient.java
+++ /dev/null
@@ -1,258 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-package org.mozilla.gecko.background.bagheera;
-
-import java.io.IOException;
-import java.net.URISyntaxException;
-import java.security.GeneralSecurityException;
-import java.util.Collection;
-import java.util.concurrent.Executor;
-import java.util.concurrent.Executors;
-import java.util.regex.Pattern;
-
-import org.mozilla.gecko.sync.Utils;
-import org.mozilla.gecko.sync.net.BaseResource;
-import org.mozilla.gecko.sync.net.BaseResourceDelegate;
-import org.mozilla.gecko.sync.net.Resource;
-
-import ch.boye.httpclientandroidlib.HttpEntity;
-import ch.boye.httpclientandroidlib.HttpResponse;
-import ch.boye.httpclientandroidlib.client.ClientProtocolException;
-import ch.boye.httpclientandroidlib.client.methods.HttpRequestBase;
-import ch.boye.httpclientandroidlib.impl.client.DefaultHttpClient;
-import ch.boye.httpclientandroidlib.protocol.HTTP;
-
-/**
- * Provides encapsulated access to a Bagheera document server.
- * The two permitted operations are:
- * * Delete a document.
- * * Upload a document, optionally deleting an expired document.
- */
-public class BagheeraClient {
-
-  protected final String serverURI;
-  protected final Executor executor;
-  protected static final Pattern URI_PATTERN = Pattern.compile("^[a-zA-Z0-9_-]+$");
-
-  protected static String PROTOCOL_VERSION = "1.0";
-  protected static String SUBMIT_PATH = "/submit/";
-
-  /**
-   * Instantiate a new client pointing at the provided server.
-   * {@link #deleteDocument(String, String, BagheeraRequestDelegate)} and
-   * {@link #uploadJSONDocument(String, String, String, String, BagheeraRequestDelegate)}
-   * both accept delegate arguments; the {@link Executor} provided to this
-   * constructor will be used to invoke callbacks on those delegates.
-   *
-   * @param serverURI
-   *          the destination server URI.
-   * @param executor
-   *          the executor which will be used to invoke delegate callbacks.
-   */
-  public BagheeraClient(final String serverURI, final Executor executor) {
-    if (serverURI == null) {
-      throw new IllegalArgumentException("Must provide a server URI.");
-    }
-    if (executor == null) {
-      throw new IllegalArgumentException("Must provide a non-null executor.");
-    }
-    this.serverURI = serverURI.endsWith("/") ? serverURI : serverURI + "/";
-    this.executor = executor;
-  }
-
-  /**
-   * Instantiate a new client pointing at the provided server.
-   * Delegate callbacks will be invoked on a new background thread.
-   *
-   * See {@link #BagheeraClient(String, Executor)} for more details.
-   *
-   * @param serverURI
-   *          the destination server URI.
-   */
-  public BagheeraClient(final String serverURI) {
-    this(serverURI, Executors.newSingleThreadExecutor());
-  }
-
-  /**
-   * Delete the specified document from the server.
-   * The delegate's callbacks will be invoked by the BagheeraClient's executor.
-   */
-  public void deleteDocument(final String namespace,
-                             final String id,
-                             final BagheeraRequestDelegate delegate) throws URISyntaxException {
-    if (namespace == null) {
-      throw new IllegalArgumentException("Must provide namespace.");
-    }
-    if (id == null) {
-      throw new IllegalArgumentException("Must provide id.");
-    }
-
-    final BaseResource resource = makeResource(namespace, id);
-    resource.delegate = new BagheeraResourceDelegate(resource, namespace, id, delegate);
-    resource.delete();
-  }
-
-  /**
-   * Upload a JSON document to a Bagheera server. The delegate's callbacks will
-   * be invoked in tasks run by the client's executor.
-   *
-   * @param namespace
-   *          the namespace, such as "test"
-   * @param id
-   *          the document ID, which is typically a UUID.
-   * @param payload
-   *          a document, typically JSON-encoded.
-   * @param oldIDs
-   *          an optional collection of IDs which denote documents to supersede. Can be null or empty.
-   * @param delegate
-   *          the delegate whose methods should be invoked on success or
-   *          failure.
-   */
-  public void uploadJSONDocument(final String namespace,
-                                 final String id,
-                                 final String payload,
-                                 Collection<String> oldIDs,
-                                 final BagheeraRequestDelegate delegate) throws URISyntaxException {
-    if (namespace == null) {
-      throw new IllegalArgumentException("Must provide namespace.");
-    }
-    if (id == null) {
-      throw new IllegalArgumentException("Must provide id.");
-    }
-    if (payload == null) {
-      throw new IllegalArgumentException("Must provide payload.");
-    }
-
-    final BaseResource resource = makeResource(namespace, id);
-    final HttpEntity deflatedBody = DeflateHelper.deflateBody(payload);
-
-    resource.delegate = new BagheeraUploadResourceDelegate(resource, namespace, id, oldIDs, delegate);
-    resource.post(deflatedBody);
-  }
-
-  public static boolean isValidURIComponent(final String in) {
-    return URI_PATTERN.matcher(in).matches();
-  }
-
-  protected BaseResource makeResource(final String namespace, final String id) throws URISyntaxException {
-    if (!isValidURIComponent(namespace)) {
-      throw new URISyntaxException(namespace, "Illegal namespace name. Must be alphanumeric + [_-].");
-    }
-
-    if (!isValidURIComponent(id)) {
-      throw new URISyntaxException(id, "Illegal id value. Must be alphanumeric + [_-].");
-    }
-
-    final String uri = this.serverURI + PROTOCOL_VERSION + SUBMIT_PATH +
-                       namespace + "/" + id;
-    return new BaseResource(uri);
-  }
-
-  public class BagheeraResourceDelegate extends BaseResourceDelegate {
-    private static final int DEFAULT_SOCKET_TIMEOUT_MSEC = 5 * 60 * 1000;       // Five minutes.
-    protected final BagheeraRequestDelegate delegate;
-    protected final String namespace;
-    protected final String id;
-
-    public BagheeraResourceDelegate(final Resource resource,
-                                    final String namespace,
-                                    final String id,
-                                    final BagheeraRequestDelegate delegate) {
-      super(resource);
-      this.namespace = namespace;
-      this.id = id;
-      this.delegate = delegate;
-    }
-
-    @Override
-    public String getUserAgent() {
-      return delegate.getUserAgent();
-    }
-
-    @Override
-    public int socketTimeout() {
-      return DEFAULT_SOCKET_TIMEOUT_MSEC;
-    }
-
-    @Override
-    public void handleHttpResponse(HttpResponse response) {
-      final int status = response.getStatusLine().getStatusCode();
-      switch (status) {
-      case 200:
-      case 201:
-        invokeHandleSuccess(status, response);
-        return;
-      default:
-        invokeHandleFailure(status, response);
-      }
-    }
-
-    protected void invokeHandleError(final Exception e) {
-      executor.execute(new Runnable() {
-        @Override
-        public void run() {
-          delegate.handleError(e);
-        }
-      });
-    }
-
-    protected void invokeHandleFailure(final int status, final HttpResponse response) {
-      executor.execute(new Runnable() {
-        @Override
-        public void run() {
-          delegate.handleFailure(status, namespace, response);
-        }
-      });
-    }
-
-    protected void invokeHandleSuccess(final int status, final HttpResponse response) {
-      executor.execute(new Runnable() {
-        @Override
-        public void run() {
-          delegate.handleSuccess(status, namespace, id, response);
-        }
-      });
-    }
-
-    @Override
-    public void handleHttpProtocolException(final ClientProtocolException e) {
-      invokeHandleError(e);
-    }
-
-    @Override
-    public void handleHttpIOException(IOException e) {
-      invokeHandleError(e);
-    }
-
-    @Override
-    public void handleTransportException(GeneralSecurityException e) {
-      invokeHandleError(e);
-    }
-  }
-
-  public final class BagheeraUploadResourceDelegate extends BagheeraResourceDelegate {
-    private static final String HEADER_OBSOLETE_DOCUMENT = "X-Obsolete-Document";
-    private static final String COMPRESSED_CONTENT_TYPE = "application/json+zlib; charset=utf-8";
-    protected final Collection<String> obsoleteDocumentIDs;
-
-    public BagheeraUploadResourceDelegate(Resource resource,
-        String namespace,
-        String id,
-        Collection<String> obsoleteDocumentIDs,
-        BagheeraRequestDelegate delegate) {
-      super(resource, namespace, id, delegate);
-      this.obsoleteDocumentIDs = obsoleteDocumentIDs;
-    }
-
-    @Override
-    public void addHeaders(HttpRequestBase request, DefaultHttpClient client) {
-      super.addHeaders(request, client);
-      request.setHeader(HTTP.CONTENT_TYPE, COMPRESSED_CONTENT_TYPE);
-      if (this.obsoleteDocumentIDs != null && this.obsoleteDocumentIDs.size() > 0) {
-        request.addHeader(HEADER_OBSOLETE_DOCUMENT, Utils.toCommaSeparatedString(this.obsoleteDocumentIDs));
-      }
-    }
-  }
-}
deleted file mode 100644
--- a/mobile/android/base/background/bagheera/BagheeraRequestDelegate.java
+++ /dev/null
@@ -1,15 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-package org.mozilla.gecko.background.bagheera;
-
-import ch.boye.httpclientandroidlib.HttpResponse;
-
-public interface BagheeraRequestDelegate {
-  void handleSuccess(int status, String namespace, String id, HttpResponse response);
-  void handleError(Exception e);
-  void handleFailure(int status, String namespace, HttpResponse response);
-
-  public String getUserAgent();
-}
deleted file mode 100644
--- a/mobile/android/base/background/bagheera/BoundedByteArrayEntity.java
+++ /dev/null
@@ -1,88 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-package org.mozilla.gecko.background.bagheera;
-
-import java.io.ByteArrayInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-
-import ch.boye.httpclientandroidlib.entity.AbstractHttpEntity;
-import ch.boye.httpclientandroidlib.entity.ByteArrayEntity;
-
-/**
- * An entity that acts like {@link ByteArrayEntity}, but exposes a window onto
- * the byte array that is a subsection of the array. The purpose of this is to
- * allow a smaller entity to be created without having to resize the source
- * array.
- */
-public class BoundedByteArrayEntity extends AbstractHttpEntity implements
-    Cloneable {
-  protected final byte[] content;
-  protected final int    start;
-  protected final int    end;
-  protected final int    length;
-
-  /**
-   * Create a new entity that behaves exactly like a {@link ByteArrayEntity}
-   * created with a copy of <code>b</code> truncated to (
-   * <code>end - start</code>) bytes, starting at <code>start</code>.
-   *
-   * @param b the byte array to use.
-   * @param start the start index.
-   * @param end the end index.
-   */
-  public BoundedByteArrayEntity(final byte[] b, final int start, final int end) {
-    if (b == null) {
-      throw new IllegalArgumentException("Source byte array may not be null.");
-    }
-
-    if (end < start ||
-        start < 0   ||
-        end   < 0   ||
-        start > b.length ||
-        end   > b.length) {
-      throw new IllegalArgumentException("Bounds out of range.");
-    }
-    this.content = b;
-    this.start = start;
-    this.end = end;
-    this.length = end - start;
-  }
-
-  @Override
-  public boolean isRepeatable() {
-    return true;
-  }
-
-  @Override
-  public long getContentLength() {
-    return this.length;
-  }
-
-  @Override
-  public InputStream getContent() {
-    return new ByteArrayInputStream(this.content, this.start, this.length);
-  }
-
-  @Override
-  public void writeTo(final OutputStream outstream) throws IOException {
-    if (outstream == null) {
-      throw new IllegalArgumentException("Output stream may not be null.");
-    }
-    outstream.write(this.content);
-    outstream.flush();
-  }
-
-  @Override
-  public boolean isStreaming() {
-    return false;
-  }
-
-  @Override
-  public Object clone() throws CloneNotSupportedException {
-    return super.clone();
-  }
-}
\ No newline at end of file
deleted file mode 100644
--- a/mobile/android/base/background/bagheera/DeflateHelper.java
+++ /dev/null
@@ -1,77 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-package org.mozilla.gecko.background.bagheera;
-
-import java.io.UnsupportedEncodingException;
-import java.util.zip.Deflater;
-
-import ch.boye.httpclientandroidlib.HttpEntity;
-
-public class DeflateHelper {
-  /**
-   * Conservative upper bound for zlib size, equivalent to the first few lines
-   * in zlib's deflateBound function.
-   *
-   * Includes zlib header.
-   *
-   * @param sourceLen
-   *          the number of bytes to compress.
-   * @return the number of bytes to allocate for the compressed output.
-   */
-  public static int deflateBound(final int sourceLen) {
-    return sourceLen + ((sourceLen + 7) >> 3) + ((sourceLen + 63) >> 6) + 5 + 6;
-  }
-
-  /**
-   * Deflate the input into the output array, returning the number of bytes
-   * written to output.
-   */
-  public static int deflate(byte[] input, byte[] output) {
-    final Deflater deflater = new Deflater();
-    deflater.setInput(input);
-    deflater.finish();
-
-    final int length = deflater.deflate(output);
-    deflater.end();
-    return length;
-  }
-
-  /**
-   * Deflate the input, returning an HttpEntity that offers an accurate window
-   * on the output.
-   *
-   * Note that this method does not trim the output array. (Test code can use
-   * TestDeflation#deflateTrimmed(byte[]).)
-   *
-   * Trimming would be more efficient for long-term space use, but we expect this
-   * entity to be transient.
-   *
-   * Note also that deflate can require <b>more</b> space than the input.
-   * {@link #deflateBound(int)} tells us the most it will use.
-   *
-   * @param bytes the input to deflate.
-   * @return the deflated input as an entity.
-   */
-  public static HttpEntity deflateBytes(final byte[] bytes) {
-    // We would like to use DeflaterInputStream here, but it's minSDK=9, and we
-    // still target 8. It would also force us to use chunked Transfer-Encoding,
-    // so perhaps it's for the best!
-
-    final byte[] out = new byte[deflateBound(bytes.length)];
-    final int outLength = deflate(bytes, out);
-    return new BoundedByteArrayEntity(out, 0, outLength);
-  }
-
-  public static HttpEntity deflateBody(final String payload) {
-    final byte[] bytes;
-    try {
-      bytes = payload.getBytes("UTF-8");
-    } catch (UnsupportedEncodingException ex) {
-      // This will never happen. Thanks, Java!
-      throw new RuntimeException(ex);
-    }
-    return deflateBytes(bytes);
-  }
-}
\ No newline at end of file
--- a/mobile/android/base/background/common/GlobalConstants.java
+++ b/mobile/android/base/background/common/GlobalConstants.java
@@ -18,25 +18,16 @@ public class GlobalConstants {
    * Bug 800244: this signing-level permission protects broadcast intents that
    * should be received only by the Firefox versions with the given Android
    * package name.
    */
   public static final String PER_ANDROID_PACKAGE_PERMISSION = AppConstants.ANDROID_PACKAGE_NAME + ".permission.PER_ANDROID_PACKAGE";
 
   public static final int SHARED_PREFERENCES_MODE = 0;
 
-  // These are used to ask Fennec (via reflection) to send
-  // us a pref notification. This avoids us having to guess
-  // Fennec's prefs branch and pref name.
-  // Eventually Fennec might listen to startup notifications and
-  // do this automatically, but this will do for now. See Bug 800244.
-  public static String GECKO_PREFERENCES_CLASS = "org.mozilla.gecko.preferences.GeckoPreferences";
-  public static String GECKO_BROADCAST_HEALTHREPORT_UPLOAD_PREF_METHOD  = "broadcastHealthReportUploadPref";
-  public static String GECKO_BROADCAST_HEALTHREPORT_PRUNE_METHOD = "broadcastHealthReportPrune";
-
   // Common time values.
   public static final long MILLISECONDS_PER_DAY = 24 * 60 * 60 * 1000;
   public static final long MILLISECONDS_PER_SIX_MONTHS = 180 * MILLISECONDS_PER_DAY;
 
   // Acceptable cipher suites.
   /**
    * We support only a very limited range of strong cipher suites and protocols:
    * no SSLv3 or TLSv1.0 (if we can), no DHE ciphers that might be vulnerable to Logjam
deleted file mode 100644
--- a/mobile/android/base/background/healthreport/AndroidConfigurationProvider.java
+++ /dev/null
@@ -1,76 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-package org.mozilla.gecko.background.healthreport;
-
-import org.mozilla.gecko.background.healthreport.Environment.UIType;
-import org.mozilla.gecko.background.healthreport.EnvironmentBuilder.ConfigurationProvider;
-import org.mozilla.gecko.util.HardwareUtils;
-
-import android.content.Context;
-import android.content.res.Configuration;
-import android.content.res.Resources;
-import android.util.DisplayMetrics;
-
-public class AndroidConfigurationProvider implements ConfigurationProvider {
-  private static final float MILLIMETERS_PER_INCH = 25.4f;
-
-  private final Configuration configuration;
-  private final DisplayMetrics displayMetrics;
-
-  public AndroidConfigurationProvider(final Context context) {
-    final Resources resources = context.getResources();
-    this.configuration = resources.getConfiguration();
-    this.displayMetrics = resources.getDisplayMetrics();
-
-    HardwareUtils.init(context);
-  }
-
-  @Override
-  public boolean hasHardwareKeyboard() {
-    return configuration.keyboard != Configuration.KEYBOARD_NOKEYS;
-  }
-
-  @Override
-  public UIType getUIType() {
-    if (HardwareUtils.isLargeTablet()) {
-      return UIType.LARGE_TABLET;
-    }
-
-    if (HardwareUtils.isSmallTablet()) {
-      return UIType.SMALL_TABLET;
-    }
-
-    return UIType.DEFAULT;
-  }
-
-  @Override
-  public int getUIModeType() {
-    return configuration.uiMode & Configuration.UI_MODE_TYPE_MASK;
-  }
-
-  @Override
-  public int getScreenLayoutSize() {
-    return configuration.screenLayout & Configuration.SCREENLAYOUT_SIZE_MASK;
-  }
-
-  /**
-   * Calculate screen horizontal width, in millimeters.
-   * This is approximate, will be wrong on some devices, and
-   * most likely doesn't include screen area that the app doesn't own.
-   * http://stackoverflow.com/questions/2193457/is-there-a-way-to-determine-android-physical-screen-height-in-cm-or-inches
-   */
-  @Override
-  public int getScreenXInMM() {
-    return Math.round((displayMetrics.widthPixels / displayMetrics.xdpi) * MILLIMETERS_PER_INCH);
-  }
-
-  /**
-   * @see #getScreenXInMM() for caveats.
-   */
-  @Override
-  public int getScreenYInMM() {
-    return Math.round((displayMetrics.heightPixels / displayMetrics.ydpi) * MILLIMETERS_PER_INCH);
-  }
-}
deleted file mode 100644
--- a/mobile/android/base/background/healthreport/Environment.java
+++ /dev/null
@@ -1,98 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-package org.mozilla.gecko.background.healthreport;
-
-/**
- * This captures all of the details that define an 'environment' for FHR's purposes.
- * Whenever this format changes, it'll be changing with a build ID, so no migration
- * of values is needed.
- *
- * Unless you remove the build descriptors from the set, of course.
- *
- * Or store these in a database.
- *
- * Instances of this class should be considered "effectively immutable": control their
- * scope such that clear creation/sharing boundaries exist. Once you've populated and
- * registered an <code>Environment</code>, don't do so again; start from scratch.
- *
- */
-public abstract class Environment extends EnvironmentV2 {
-  // Version 2 adds osLocale, appLocale, acceptLangSet, and distribution.
-  // Version 3 adds device characteristics.
-  public static final int CURRENT_VERSION = 3;
-
-  public static enum UIType {
-    // Corresponds to the typical phone interface.
-    DEFAULT("default"),
-
-    // Corresponds to a device for which Fennec is displaying the large tablet UI.
-    LARGE_TABLET("largetablet"),
-
-    // Corresponds to a device for which Fennec is displaying the small tablet UI.
-    SMALL_TABLET("smalltablet");
-
-    private final String label;
-
-    private UIType(final String label) {
-      this.label = label;
-    }
-
-    public String toString() {
-      return this.label;
-    }
-
-    public static UIType fromLabel(final String label) {
-      for (UIType type : UIType.values()) {
-        if (type.label.equals(label)) {
-          return type;
-        }
-      }
-
-      throw new IllegalArgumentException("Bad enum value: " + label);
-    }
-  }
-
-  public UIType uiType = UIType.DEFAULT;
-
-  /**
-   * Mask of Configuration#uiMode. E.g., UI_MODE_TYPE_CAR.
-   */
-  public int uiMode = 0;     // UI_MODE_TYPE_UNDEFINED = 0
-
-  /**
-   * Computed physical dimensions in millimeters.
-   */
-  public int screenXInMM;
-  public int screenYInMM;
-
-  /**
-   * One of the Configuration#SCREENLAYOUT_SIZE_* constants.
-   */
-  public int screenLayout = 0;  // SCREENLAYOUT_SIZE_UNDEFINED = 0
-
-  public boolean hasHardwareKeyboard;
-
-  public Environment() {
-    this(Environment.HashAppender.class);
-  }
-
-  public Environment(Class<? extends EnvironmentAppender> appenderClass) {
-    super(appenderClass);
-    version = CURRENT_VERSION;
-  }
-
-  @Override
-  protected void appendHash(EnvironmentAppender appender) {
-    super.appendHash(appender);
-
-    // v3.
-    appender.append(hasHardwareKeyboard ? 1 : 0);
-    appender.append(uiType.toString());
-    appender.append(uiMode);
-    appender.append(screenLayout);
-    appender.append(screenXInMM);
-    appender.append(screenYInMM);
-  }
-}
deleted file mode 100644
--- a/mobile/android/base/background/healthreport/EnvironmentBuilder.java
+++ /dev/null
@@ -1,189 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-package org.mozilla.gecko.background.healthreport;
-
-import java.util.Iterator;
-
-import org.json.JSONObject;
-import org.mozilla.gecko.AppConstants;
-import org.mozilla.gecko.SysInfo;
-import org.mozilla.gecko.background.common.GlobalConstants;
-import org.mozilla.gecko.background.common.log.Logger;
-import org.mozilla.gecko.background.healthreport.Environment.UIType;
-
-import android.content.ContentProvider;
-import android.content.ContentProviderClient;
-import android.content.ContentResolver;
-import android.content.Context;
-
-/**
- * Construct a HealthReport environment from the current running system.
- */
-public class EnvironmentBuilder {
-  private static final String LOG_TAG = "GeckoEnvBuilder";
-
-  public static ContentProviderClient getContentProviderClient(Context context) {
-    ContentResolver cr = context.getContentResolver();
-    return cr.acquireContentProviderClient(HealthReportConstants.HEALTH_AUTHORITY);
-  }
-
-  /**
-   * Fetch the storage object associated with the provided
-   * {@link ContentProviderClient}. If no storage instance can be found --
-   * perhaps because the {@link ContentProvider} is running in a different
-   * process -- returns <code>null</code>. On success, the returned
-   * {@link HealthReportDatabaseStorage} instance is owned by the underlying
-   * {@link HealthReportProvider} and thus does not need to be closed by the
-   * caller.
-   *
-   * If the provider is not a {@link HealthReportProvider}, throws a
-   * {@link ClassCastException}, because that would be disastrous.
-   */
-  public static HealthReportDatabaseStorage getStorage(ContentProviderClient cpc,
-                                                       String profilePath) {
-    ContentProvider pr = cpc.getLocalContentProvider();
-    if (pr == null) {
-      Logger.error(LOG_TAG, "Unable to retrieve local content provider. Running in a different process?");
-      return null;
-    }
-    try {
-      return ((HealthReportProvider) pr).getProfileStorage(profilePath);
-    } catch (ClassCastException ex) {
-      Logger.error(LOG_TAG, "ContentProvider not a HealthReportProvider!", ex);
-      throw ex;
-    }
-  }
-
-  public static interface ProfileInformationProvider {
-    public boolean isBlocklistEnabled();
-    public boolean isTelemetryEnabled();
-    public boolean isAcceptLangUserSet();
-    public long getProfileCreationTime();
-
-    public String getDistributionString();
-    public String getOSLocale();
-    public String getAppLocale();
-
-    public JSONObject getAddonsJSON();
-  }
-
-  public static interface ConfigurationProvider {
-    public boolean hasHardwareKeyboard();
-
-    public UIType getUIType();
-    public int getUIModeType();
-
-    public int getScreenLayoutSize();
-    public int getScreenXInMM();
-    public int getScreenYInMM();
-  }
-
-  protected static void populateEnvironment(Environment e,
-                                            ProfileInformationProvider info,
-                                            ConfigurationProvider config) {
-    e.cpuCount = SysInfo.getCPUCount();
-    e.memoryMB = SysInfo.getMemSize();
-
-    e.appName = AppConstants.MOZ_APP_NAME;
-    e.appID = AppConstants.MOZ_APP_ID;
-    e.appVersion = AppConstants.MOZ_APP_VERSION;
-    e.appBuildID = AppConstants.MOZ_APP_BUILDID;
-    e.updateChannel = AppConstants.MOZ_UPDATE_CHANNEL;
-    e.vendor = AppConstants.MOZ_APP_VENDOR;
-    e.platformVersion = AppConstants.MOZILLA_VERSION;
-    e.platformBuildID = AppConstants.MOZ_APP_BUILDID;
-    e.xpcomabi = AppConstants.TARGET_XPCOM_ABI;
-    e.os = "Android";
-    e.architecture = SysInfo.getArchABI();       // Not just "arm".
-    e.sysName = SysInfo.getName();
-    e.sysVersion = SysInfo.getReleaseVersion();
-
-    e.profileCreation = (int) (info.getProfileCreationTime() / GlobalConstants.MILLISECONDS_PER_DAY);
-
-    // Corresponds to Gecko pref "extensions.blocklist.enabled".
-    e.isBlocklistEnabled = (info.isBlocklistEnabled() ? 1 : 0);
-
-    // Corresponds to Gecko pref "toolkit.telemetry.enabled".
-    e.isTelemetryEnabled = (info.isTelemetryEnabled() ? 1 : 0);
-
-    e.extensionCount = 0;
-    e.pluginCount = 0;
-    e.themeCount = 0;
-
-    JSONObject addons = info.getAddonsJSON();
-    if (addons != null) {
-      @SuppressWarnings("unchecked")
-      Iterator<String> it = addons.keys();
-      while (it.hasNext()) {
-        String key = it.next();
-        try {
-          JSONObject addon = addons.getJSONObject(key);
-          String type = addon.optString("type");
-          Logger.pii(LOG_TAG, "Add-on " + key + " is a " + type);
-          if ("extension".equals(type)) {
-            ++e.extensionCount;
-          } else if ("plugin".equals(type)) {
-            ++e.pluginCount;
-          } else if ("theme".equals(type)) {
-            ++e.themeCount;
-          } else if ("service".equals(type)) {
-            // Later.
-          } else {
-            Logger.debug(LOG_TAG, "Unknown add-on type: " + type);
-          }
-        } catch (Exception ex) {
-          Logger.warn(LOG_TAG, "Failed to process add-on " + key, ex);
-        }
-      }
-    }
-
-    e.addons = addons;
-
-    // v2 environment fields.
-    e.distribution = info.getDistributionString();
-    e.osLocale = info.getOSLocale();
-    e.appLocale = info.getAppLocale();
-    e.acceptLangSet = info.isAcceptLangUserSet() ? 1 : 0;
-
-    // v3 environment fields.
-    e.hasHardwareKeyboard = config.hasHardwareKeyboard();
-    e.uiType = config.getUIType();
-    e.uiMode = config.getUIModeType();
-    e.screenLayout = config.getScreenLayoutSize();
-    e.screenXInMM = config.getScreenXInMM();
-    e.screenYInMM = config.getScreenYInMM();
-  }
-
-  /**
-   * Returns an {@link Environment} not linked to a storage instance, but
-   * populated with current field values.
-   *
-   * @param info a source of profile data
-   * @return the new {@link Environment}
-   */
-  public static Environment getCurrentEnvironment(ProfileInformationProvider info, ConfigurationProvider config) {
-    Environment e = new Environment() {
-      @Override
-      public int register() {
-        return 0;
-      }
-    };
-    populateEnvironment(e, info, config);
-    return e;
-  }
-
-  /**
-   * @return the current environment's ID in the provided storage layer
-   */
-  public static int registerCurrentEnvironment(final HealthReportStorage storage,
-                                               final ProfileInformationProvider info,
-                                               final ConfigurationProvider config) {
-    Environment e = storage.getEnvironment();
-    populateEnvironment(e, info, config);
-    e.register();
-    Logger.debug(LOG_TAG, "Registering current environment: " + e.getHash() + " = " + e.id);
-    return e.id;
-  }
-}
deleted file mode 100644
--- a/mobile/android/base/background/healthreport/EnvironmentV1.java
+++ /dev/null
@@ -1,270 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-package org.mozilla.gecko.background.healthreport;
-
-import java.io.UnsupportedEncodingException;
-import java.security.NoSuchAlgorithmException;
-import java.util.Iterator;
-import java.util.SortedSet;
-
-import org.json.JSONException;
-import org.json.JSONObject;
-import org.mozilla.apache.commons.codec.binary.Base64;
-import org.mozilla.gecko.background.common.log.Logger;
-import org.mozilla.gecko.background.nativecode.NativeCrypto;
-
-public abstract class EnvironmentV1 {
-  private static final String LOG_TAG = "GeckoEnvironment";
-  private static final int VERSION = 1;
-
-  protected final Class<? extends EnvironmentAppender> appenderClass;
-
-  protected volatile String hash = null;
-  protected volatile int id = -1;
-
-  public int version = VERSION;
-
-  // org.mozilla.profile.age.
-  public int profileCreation;
-
-  // org.mozilla.sysinfo.sysinfo.
-  public int cpuCount;
-  public int memoryMB;
-  public String architecture;
-  public String sysName;
-  public String sysVersion;       // Kernel.
-
-  // geckoAppInfo.
-  public String vendor;
-  public String appName;
-  public String appID;
-  public String appVersion;
-  public String appBuildID;
-  public String platformVersion;
-  public String platformBuildID;
-  public String os;
-  public String xpcomabi;
-  public String updateChannel;
-
-  // appinfo.
-  public int isBlocklistEnabled;
-  public int isTelemetryEnabled;
-
-  // org.mozilla.addons.active.
-  public JSONObject addons = null;
-
-  // org.mozilla.addons.counts.
-  public int extensionCount;
-  public int pluginCount;
-  public int themeCount;
-
-  /**
-   * We break out this interface in order to allow for testing -- pass in your
-   * own appender that just records strings, for example.
-   */
-  public static abstract class EnvironmentAppender {
-    public abstract void append(String s);
-    public abstract void append(int v);
-  }
-
-  public static class HashAppender extends EnvironmentAppender {
-    private final StringBuilder builder;
-
-    public HashAppender() throws NoSuchAlgorithmException {
-      builder = new StringBuilder();
-    }
-
-    @Override
-    public void append(String s) {
-      builder.append((s == null) ? "null" : s);
-    }
-
-    @Override
-    public void append(int profileCreation) {
-      append(Integer.toString(profileCreation, 10));
-    }
-
-    @Override
-    public String toString() {
-      // We *could* use ASCII85… but the savings would be negated by the
-      // inclusion of JSON-unsafe characters like double-quote.
-      final byte[] inputBytes;
-      try {
-        inputBytes = builder.toString().getBytes("UTF-8");
-      } catch (UnsupportedEncodingException e) {
-        Logger.warn(LOG_TAG, "Invalid charset String passed to getBytes", e);
-        return null;
-      }
-
-      // Note to the security-minded reader: we deliberately use SHA-1 here, not
-      // a stronger hash. These identifiers don't strictly need a cryptographic
-      // hash function, because there is negligible value in attacking the hash.
-      // We use SHA-1 because it's *shorter* -- the exact same reason that Git
-      // chose SHA-1.
-      final byte[] hash = NativeCrypto.sha1(inputBytes);
-      return new Base64(-1, null, false).encodeAsString(hash);
-    }
-  }
-
-  /**
-   * Ensure that the {@link Environment} has been registered with its
-   * storage layer, and can be used to annotate events.
-   *
-   * It's safe to call this method more than once, and each time you'll
-   * get the same ID.
-   *
-   * @return the integer ID to use in subsequent DB insertions.
-   */
-  public abstract int register();
-
-  protected EnvironmentAppender getAppender() {
-    EnvironmentAppender appender = null;
-    try {
-      appender = appenderClass.newInstance();
-    } catch (InstantiationException | IllegalAccessException ex) {
-      // Should never happen, but...
-      Logger.warn(LOG_TAG,  "Could not compute hash.", ex);
-    }
-
-    return appender;
-  }
-
-  protected void appendHash(EnvironmentAppender appender) {
-    appender.append(profileCreation);
-    appender.append(cpuCount);
-    appender.append(memoryMB);
-    appender.append(architecture);
-    appender.append(sysName);
-    appender.append(sysVersion);
-    appender.append(vendor);
-    appender.append(appName);
-    appender.append(appID);
-    appender.append(appVersion);
-    appender.append(appBuildID);
-    appender.append(platformVersion);
-    appender.append(platformBuildID);
-    appender.append(os);
-    appender.append(xpcomabi);
-    appender.append(updateChannel);
-    appender.append(isBlocklistEnabled);
-    appender.append(isTelemetryEnabled);
-    appender.append(extensionCount);
-    appender.append(pluginCount);
-    appender.append(themeCount);
-
-    // We need sorted values.
-    if (addons != null) {
-      appendSortedAddons(getNonIgnoredAddons(), appender);
-    }
-  }
-
-  /**
-   * Compute the stable hash of the configured environment.
-   *
-   * @return the hash in base34, or null if there was a problem.
-   */
-  public String getHash() {
-    // It's never unset, so we only care about partial reads. volatile is enough.
-    if (hash != null) {
-      return hash;
-    }
-
-    EnvironmentAppender appender = getAppender();
-    if (appender == null) {
-      return null;
-    }
-
-    appendHash(appender);
-    return hash = appender.toString();
-  }
-
-  public EnvironmentV1(Class<? extends EnvironmentAppender> appenderClass) {
-    super();
-    this.appenderClass = appenderClass;
-  }
-
-  public JSONObject getNonIgnoredAddons() {
-    if (addons == null) {
-      return null;
-    }
-    JSONObject out = new JSONObject();
-    @SuppressWarnings("unchecked")
-    Iterator<String> keys = addons.keys();
-    while (keys.hasNext()) {
-      try {
-        final String key = keys.next();
-        final Object obj = addons.get(key);
-        if (obj != null &&
-            obj instanceof JSONObject &&
-            ((JSONObject) obj).optBoolean("ignore", false)) {
-          continue;
-        }
-        out.put(key, obj);
-      } catch (JSONException ex) {
-        // Do nothing.
-      }
-    }
-    return out;
-  }
-
-  /**
-   * Take a collection of add-on descriptors, appending a consistent string
-   * to the provided builder.
-   */
-  public static void appendSortedAddons(JSONObject addons, final EnvironmentAppender builder) {
-    final SortedSet<String> keys = HealthReportUtils.sortedKeySet(addons);
-
-    // For each add-on, produce a consistent, sorted mapping of its descriptor.
-    for (String key : keys) {
-      try {
-        JSONObject addon = addons.getJSONObject(key);
-
-        // Now produce the output for this add-on.
-        builder.append(key);
-        builder.append("={");
-
-        for (String addonKey : HealthReportUtils.sortedKeySet(addon)) {
-          builder.append(addonKey);
-          builder.append("==");
-          try {
-            builder.append(addon.get(addonKey).toString());
-          } catch (JSONException e) {
-            builder.append("_e_");
-          }
-        }
-
-        builder.append("}");
-      } catch (Exception e) {
-        // Muffle.
-        Logger.warn(LOG_TAG, "Invalid add-on for ID " + key);
-      }
-    }
-  }
-
-  public void setJSONForAddons(byte[] json) throws Exception {
-    setJSONForAddons(new String(json, "UTF-8"));
-  }
-
-  public void setJSONForAddons(String json) throws Exception {
-    if (json == null || "null".equals(json)) {
-      addons = null;
-      return;
-    }
-    addons = new JSONObject(json);
-  }
-
-  public void setJSONForAddons(JSONObject json) {
-    addons = json;
-  }
-
-  /**
-   * Includes ignored add-ons.
-   */
-  public String getNormalizedAddonsJSON() {
-    // We trust that our input will already be normalized. If that assumption
-    // is invalidated, then we'll be sorry.
-    return (addons == null) ? "null" : addons.toString();
-  }
-}
deleted file mode 100644
--- a/mobile/android/base/background/healthreport/EnvironmentV2.java
+++ /dev/null
@@ -1,30 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-package org.mozilla.gecko.background.healthreport;
-
-public abstract class EnvironmentV2 extends EnvironmentV1 {
-  private static final int VERSION = 2;
-
-  public String osLocale;
-  public String appLocale;
-  public int acceptLangSet;
-  public String distribution;
-
-  public EnvironmentV2(Class<? extends EnvironmentAppender> appenderClass) {
-    super(appenderClass);
-    version = VERSION;
-  }
-
-  @Override
-  protected void appendHash(EnvironmentAppender appender) {
-    super.appendHash(appender);
-
-    // v2.
-    appender.append(osLocale);
-    appender.append(appLocale);
-    appender.append(acceptLangSet);
-    appender.append(distribution);
-  }
-}
\ No newline at end of file
deleted file mode 100644
--- a/mobile/android/base/background/healthreport/HealthReportBroadcastReceiver.java
+++ /dev/null
@@ -1,41 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-package org.mozilla.gecko.background.healthreport;
-
-import org.mozilla.gecko.background.common.log.Logger;
-
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-
-/**
- * Watch for notifications to start Health Report background services.
- *
- * Some observations:
- *
- * From the Android documentation: "Also note that as of Android 3.0 the user
- * needs to have started the application at least once before your application
- * can receive android.intent.action.BOOT_COMPLETED events."
- *
- * We really do want to launch on BOOT_COMPLETED, since it's possible for a user
- * to run Firefox, shut down the phone, then power it on again on the same day.
- * We want to submit a health report in this case, even though they haven't
- * launched Firefox since boot.
- */
-public class HealthReportBroadcastReceiver extends BroadcastReceiver {
-  public static final String LOG_TAG = HealthReportBroadcastReceiver.class.getSimpleName();
-
-  /**
-   * Forward the intent to an IntentService to do background processing.
-   */
-  @Override
-  public void onReceive(Context context, Intent intent) {
-    Logger.debug(LOG_TAG, "Received intent - forwarding to BroadcastService.");
-    Intent service = new Intent(context, HealthReportBroadcastService.class);
-    service.putExtras(intent);
-    service.setAction(intent.getAction());
-    context.startService(service);
-  }
-}
deleted file mode 100644
--- a/mobile/android/base/background/healthreport/HealthReportBroadcastService.java
+++ /dev/null
@@ -1,260 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-package org.mozilla.gecko.background.healthreport;
-
-import org.mozilla.gecko.background.BackgroundService;
-import org.mozilla.gecko.background.common.GlobalConstants;
-import org.mozilla.gecko.background.common.log.Logger;
-import org.mozilla.gecko.background.healthreport.prune.HealthReportPruneService;
-import org.mozilla.gecko.background.healthreport.upload.HealthReportUploadService;
-import org.mozilla.gecko.background.healthreport.upload.ObsoleteDocumentTracker;
-
-import android.app.AlarmManager;
-import android.app.PendingIntent;
-import android.content.Context;
-import android.content.Intent;
-import android.content.SharedPreferences;
-import android.content.SharedPreferences.Editor;
-
-/**
- * A service which listens to broadcast intents from the system and from the
- * browser, registering or unregistering the background health report services with the
- * {@link AlarmManager}.
- */
-public class HealthReportBroadcastService extends BackgroundService {
-  public static final String LOG_TAG = HealthReportBroadcastService.class.getSimpleName();
-  public static final String WORKER_THREAD_NAME = LOG_TAG + "Worker";
-
-  public HealthReportBroadcastService() {
-    super(WORKER_THREAD_NAME);
-  }
-
-  protected SharedPreferences getSharedPreferences() {
-    return this.getSharedPreferences(HealthReportConstants.PREFS_BRANCH, GlobalConstants.SHARED_PREFERENCES_MODE);
-  }
-
-  public long getSubmissionPollInterval() {
-    return getSharedPreferences().getLong(HealthReportConstants.PREF_SUBMISSION_INTENT_INTERVAL_MSEC, HealthReportConstants.DEFAULT_SUBMISSION_INTENT_INTERVAL_MSEC);
-  }
-
-  public void setSubmissionPollInterval(final long interval) {
-    getSharedPreferences().edit().putLong(HealthReportConstants.PREF_SUBMISSION_INTENT_INTERVAL_MSEC, interval).commit();
-  }
-
-  public long getPrunePollInterval() {
-    return getSharedPreferences().getLong(HealthReportConstants.PREF_PRUNE_INTENT_INTERVAL_MSEC,
-        HealthReportConstants.DEFAULT_PRUNE_INTENT_INTERVAL_MSEC);
-  }
-
-  public void setPrunePollInterval(final long interval) {
-    getSharedPreferences().edit().putLong(HealthReportConstants.PREF_PRUNE_INTENT_INTERVAL_MSEC,
-        interval).commit();
-  }
-
-  /**
-   * Set or cancel an alarm to submit data for a profile.
-   *
-   * @param context
-   *          Android context.
-   * @param profileName
-   *          to submit data for.
-   * @param profilePath
-   *          to submit data for.
-   * @param enabled
-   *          whether the user has enabled submitting health report data for
-   *          this profile.
-   * @param serviceEnabled
-   *          whether submitting should be scheduled. If the user turns off
-   *          submitting, <code>enabled</code> could be false but we could need
-   *          to delete so <code>serviceEnabled</code> could be true.
-   */
-  protected void toggleSubmissionAlarm(final Context context, String profileName, String profilePath,
-      boolean enabled, boolean serviceEnabled) {
-    final Class<?> serviceClass = HealthReportUploadService.class;
-    Logger.info(LOG_TAG, (serviceEnabled ? "R" : "Unr") + "egistering " +
-        serviceClass.getSimpleName() + ".");
-
-    // PendingIntents are compared without reference to their extras. Therefore
-    // even though we pass the profile details to the action, different
-    // profiles will share the *same* pending intent. In a multi-profile future,
-    // this will need to be addressed.  See Bug 882182.
-    final Intent service = new Intent(context, serviceClass);
-    service.setAction("upload"); // PendingIntents "lose" their extras if no action is set.
-    service.putExtra("uploadEnabled", enabled);
-    service.putExtra("profileName", profileName);
-    service.putExtra("profilePath", profilePath);
-    final PendingIntent pending = PendingIntent.getService(context, 0, service, PendingIntent.FLAG_CANCEL_CURRENT);
-
-    if (!serviceEnabled) {
-      cancelAlarm(pending);
-      return;
-    }
-
-    final long pollInterval = getSubmissionPollInterval();
-    scheduleAlarm(pollInterval, pending);
-  }
-
-  @Override
-  protected void onHandleIntent(Intent intent) {
-    Logger.setThreadLogTag(HealthReportConstants.GLOBAL_LOG_TAG);
-
-    // Intent can be null. Bug 1025937.
-    if (intent == null) {
-      Logger.debug(LOG_TAG, "Short-circuiting on null intent.");
-      return;
-    }
-
-    // The same intent can be handled by multiple methods so do not short-circuit evaluate.
-    boolean handled = attemptHandleIntentForUpload(intent);
-    handled = attemptHandleIntentForPrune(intent) || handled;
-
-    if (!handled) {
-      Logger.warn(LOG_TAG, "Unhandled intent with action " + intent.getAction() + ".");
-    }
-  }
-
-  /**
-   * Attempts to handle the given intent for FHR document upload. If it cannot, false is returned.
-   *
-   * @param intent must be non-null.
-   */
-  private boolean attemptHandleIntentForUpload(final Intent intent) {
-    if (HealthReportConstants.UPLOAD_FEATURE_DISABLED) {
-      Logger.debug(LOG_TAG, "Health report upload feature is compile-time disabled; not handling intent.");
-      return false;
-    }
-
-    final String action = intent.getAction();
-    Logger.debug(LOG_TAG, "Health report upload feature is compile-time enabled; attempting to " +
-        "handle intent with action " + action + ".");
-
-    if (HealthReportConstants.ACTION_HEALTHREPORT_UPLOAD_PREF.equals(action)) {
-      handleUploadPrefIntent(intent);
-      return true;
-    }
-
-    if (Intent.ACTION_BOOT_COMPLETED.equals(action) ||
-        Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE.equals(action)) {
-      BackgroundService.reflectContextToFennec(this,
-          GlobalConstants.GECKO_PREFERENCES_CLASS,
-          GlobalConstants.GECKO_BROADCAST_HEALTHREPORT_UPLOAD_PREF_METHOD);
-      return true;
-    }
-
-    return false;
-  }
-
-  /**
-   * Handle the intent sent by the browser when it wishes to notify us
-   * of the value of the user preference. Look at the value and toggle the
-   * alarm service accordingly.
-   *
-   * @param intent must be non-null.
-   */
-  private void handleUploadPrefIntent(Intent intent) {
-    if (!intent.hasExtra("enabled")) {
-      Logger.warn(LOG_TAG, "Got " + HealthReportConstants.ACTION_HEALTHREPORT_UPLOAD_PREF + " intent without enabled. Ignoring.");
-      return;
-    }
-
-    final boolean enabled = intent.getBooleanExtra("enabled", true);
-    Logger.debug(LOG_TAG, intent.getStringExtra("branch") + "/" +
-                          intent.getStringExtra("pref")   + " = " +
-                          (intent.hasExtra("enabled") ? enabled : ""));
-
-    String profileName = intent.getStringExtra("profileName");
-    String profilePath = intent.getStringExtra("profilePath");
-
-    if (profileName == null || profilePath == null) {
-      Logger.warn(LOG_TAG, "Got " + HealthReportConstants.ACTION_HEALTHREPORT_UPLOAD_PREF + " intent without profilePath or profileName. Ignoring.");
-      return;
-    }
-
-    Logger.pii(LOG_TAG, "Updating health report upload alarm for profile " + profileName + " at " +
-        profilePath + ".");
-
-    final SharedPreferences sharedPrefs = getSharedPreferences();
-    final ObsoleteDocumentTracker tracker = new ObsoleteDocumentTracker(sharedPrefs);
-    final boolean hasObsoleteIds = tracker.hasObsoleteIds();
-
-    if (!enabled) {
-      final Editor editor = sharedPrefs.edit();
-      editor.remove(HealthReportConstants.PREF_LAST_UPLOAD_DOCUMENT_ID);
-
-      if (hasObsoleteIds) {
-        Logger.debug(LOG_TAG, "Health report upload disabled; scheduling deletion of " + tracker.numberOfObsoleteIds() + " documents.");
-        tracker.limitObsoleteIds();
-      } else {
-        // Primarily intended for debugging and testing.
-        Logger.debug(LOG_TAG, "Health report upload disabled and no deletes to schedule: clearing prefs.");
-        editor.remove(HealthReportConstants.PREF_FIRST_RUN);
-        editor.remove(HealthReportConstants.PREF_NEXT_SUBMISSION);
-      }
-
-      editor.commit();
-    }
-
-    // The user can toggle us off or on, or we can have obsolete documents to
-    // remove.
-    final boolean serviceEnabled = hasObsoleteIds || enabled;
-    toggleSubmissionAlarm(this, profileName, profilePath, enabled, serviceEnabled);
-  }
-
-  /**
-   * Attempts to handle the given intent for FHR data pruning. If it cannot, false is returned.
-   *
-   * @param intent must be non-null.
-   */
-  private boolean attemptHandleIntentForPrune(final Intent intent) {
-    final String action = intent.getAction();
-    Logger.debug(LOG_TAG, "Prune: Attempting to handle intent with action, " + action + ".");
-
-    if (HealthReportConstants.ACTION_HEALTHREPORT_PRUNE.equals(action)) {
-      handlePruneIntent(intent);
-      return true;
-    }
-
-    if (Intent.ACTION_BOOT_COMPLETED.equals(action) ||
-        Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE.equals(action)) {
-      BackgroundService.reflectContextToFennec(this,
-          GlobalConstants.GECKO_PREFERENCES_CLASS,
-          GlobalConstants.GECKO_BROADCAST_HEALTHREPORT_PRUNE_METHOD);
-      return true;
-    }
-
-    return false;
-  }
-
-  /**
-   * @param intent must be non-null.
-   */
-  private void handlePruneIntent(final Intent intent) {
-    final String profileName = intent.getStringExtra("profileName");
-    final String profilePath = intent.getStringExtra("profilePath");
-
-    if (profileName == null || profilePath == null) {
-      Logger.warn(LOG_TAG, "Got " + HealthReportConstants.ACTION_HEALTHREPORT_PRUNE + " intent " +
-          "without profilePath or profileName. Ignoring.");
-      return;
-    }
-
-    final Class<?> serviceClass = HealthReportPruneService.class;
-    final Intent service = new Intent(this, serviceClass);
-    service.setAction("prune"); // Intents without actions have their extras removed.
-    service.putExtra("profileName", profileName);
-    service.putExtra("profilePath", profilePath);
-    final PendingIntent pending = PendingIntent.getService(this, 0, service,
-        PendingIntent.FLAG_CANCEL_CURRENT);
-
-    // Set a regular alarm to start PruneService. Since the various actions that PruneService can
-    // take occur on irregular intervals, we can be more efficient by only starting the Service
-    // when one of these time limits runs out.  However, subsequent Service invocations must then
-    // be registered by the PruneService itself, which would fail if the PruneService crashes.
-    // Thus, we set this regular (and slightly inefficient) alarm.
-    Logger.info(LOG_TAG, "Registering " + serviceClass.getSimpleName() + ".");
-    final long pollInterval = getPrunePollInterval();
-    scheduleAlarm(pollInterval, pending);
-  }
-}
deleted file mode 100644
--- a/mobile/android/base/background/healthreport/HealthReportConstants.java
+++ /dev/null
@@ -1,128 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-package org.mozilla.gecko.background.healthreport;
-
-import org.mozilla.gecko.background.common.GlobalConstants;
-import org.mozilla.gecko.AppConstants;
-
-public class HealthReportConstants {
-  public static final String HEALTH_AUTHORITY = AppConstants.ANDROID_PACKAGE_NAME + ".health";
-  public static final String GLOBAL_LOG_TAG = "GeckoHealth";
-
-  public static final String USER_AGENT = "Firefox-Android-HealthReport/" + AppConstants.MOZ_APP_VERSION + " (" + AppConstants.MOZ_APP_DISPLAYNAME + ")";
-
-  /**
-   * The earliest allowable value for the last ping time, corresponding to May 2nd 2013.
-   * Used for sanity checks.
-   */
-  public static final long EARLIEST_LAST_PING = 1367500000000L;
-
-  // Not `final` so we have the option to turn this on at runtime with a magic addon.
-  public static boolean UPLOAD_FEATURE_DISABLED = false;
-
-  // Android SharedPreferences branch where global (not per-profile) uploader
-  // settings are stored.
-  public static final String PREFS_BRANCH = "background";
-
-  // How frequently the submission and prune policies are ticked over. This is how frequently our
-  // intent is scheduled to be called by the Android Alarm Manager, not how frequently we
-  // actually submit. These values are set as preferences rather than constants so that testing
-  // addons can change their values.
-  public static final String PREF_SUBMISSION_INTENT_INTERVAL_MSEC = "healthreport_submission_intent_interval_msec";
-  public static final long DEFAULT_SUBMISSION_INTENT_INTERVAL_MSEC = GlobalConstants.MILLISECONDS_PER_DAY / 24;
-  public static final String PREF_PRUNE_INTENT_INTERVAL_MSEC = "healthreport_prune_intent_interval_msec";
-  public static final long DEFAULT_PRUNE_INTENT_INTERVAL_MSEC = GlobalConstants.MILLISECONDS_PER_DAY;
-
-  public static final String ACTION_HEALTHREPORT_UPLOAD_PREF = AppConstants.ANDROID_PACKAGE_NAME + ".HEALTHREPORT_UPLOAD_PREF";
-  public static final String ACTION_HEALTHREPORT_PRUNE = AppConstants.ANDROID_PACKAGE_NAME + ".HEALTHREPORT_PRUNE";
-
-  public static final String PREF_MINIMUM_TIME_BETWEEN_UPLOADS = "healthreport_time_between_uploads";
-  public static final long DEFAULT_MINIMUM_TIME_BETWEEN_UPLOADS = GlobalConstants.MILLISECONDS_PER_DAY;
-
-  public static final String PREF_MINIMUM_TIME_BEFORE_FIRST_SUBMISSION = "healthreport_time_before_first_submission";
-  public static final long DEFAULT_MINIMUM_TIME_BEFORE_FIRST_SUBMISSION = GlobalConstants.MILLISECONDS_PER_DAY;
-
-  public static final String PREF_MINIMUM_TIME_AFTER_FAILURE = "healthreport_time_after_failure";
-  public static final long DEFAULT_MINIMUM_TIME_AFTER_FAILURE = DEFAULT_SUBMISSION_INTENT_INTERVAL_MSEC;
-
-  public static final String PREF_MAXIMUM_FAILURES_PER_DAY = "healthreport_maximum_failures_per_day";
-  public static final long DEFAULT_MAXIMUM_FAILURES_PER_DAY = 2;
-
-  // Authoritative.
-  public static final String PREF_FIRST_RUN = "healthreport_first_run";
-  public static final String PREF_NEXT_SUBMISSION = "healthreport_next_submission";
-  public static final String PREF_CURRENT_DAY_FAILURE_COUNT = "healthreport_current_day_failure_count";
-  public static final String PREF_CURRENT_DAY_RESET_TIME = "healthreport_current_day_reset_time";
-
-  // Forensic.
-  public static final String PREF_LAST_UPLOAD_REQUESTED = "healthreport_last_upload_requested";
-  public static final String PREF_LAST_UPLOAD_SUCCEEDED = "healthreport_last_upload_succeeded";
-  public static final String PREF_LAST_UPLOAD_FAILED = "healthreport_last_upload_failed";
-
-  // Preferences for deleting obsolete documents.
-  public static final String PREF_MINIMUM_TIME_BETWEEN_DELETES = "healthreport_time_between_deletes";
-  public static final long DEFAULT_MINIMUM_TIME_BETWEEN_DELETES = DEFAULT_SUBMISSION_INTENT_INTERVAL_MSEC;
-
-  public static final String PREF_OBSOLETE_DOCUMENT_IDS_TO_DELETION_ATTEMPTS_REMAINING = "healthreport_obsolete_document_ids_to_deletions_remaining";
-
-  // We don't want to try to delete forever, but we also don't want to orphan
-  // obsolete document IDs from devices that fail to reach the server for a few
-  // days.  This tries to delete document IDs for at least one week (of upload
-  // failures).  Note that if the device is really offline, no upload is
-  // performed and our count of attempts is not altered.
-  public static final long DELETION_ATTEMPTS_PER_OBSOLETE_DOCUMENT_ID = (DEFAULT_MAXIMUM_FAILURES_PER_DAY + 1) * 7;
-
-  // If we absolutely know that a document ID reached the server, we really
-  // don't want to orphan it.  This tries to delete document IDs that will
-  // definitely be orphaned for at least six weeks (of upload failures).  Note
-  // that if the device is really offline, no upload is performed and our count
-  // of attempts is not altered.
-  public static final long DELETION_ATTEMPTS_PER_KNOWN_TO_BE_ON_SERVER_DOCUMENT_ID = (DEFAULT_MAXIMUM_FAILURES_PER_DAY + 1) * 7 * 6;
-
-  // We don't want to allocate unbounded storage for obsolete IDs, but we also
-  // don't want to orphan obsolete document IDs from devices that fail to delete
-  // for a few days.  This stores as many IDs as are expected to be generated in
-  // a month.  Note that if the device is really offline, no upload is performed
-  // and our count of attempts is not altered.
-  public static final long MAXIMUM_STORED_OBSOLETE_DOCUMENT_IDS = (DEFAULT_MAXIMUM_FAILURES_PER_DAY + 1) * 30;
-
-  // Forensic.
-  public static final String PREF_LAST_DELETE_REQUESTED = "healthreport_last_delete_requested";
-  public static final String PREF_LAST_DELETE_SUCCEEDED = "healthreport_last_delete_succeeded";
-  public static final String PREF_LAST_DELETE_FAILED = "healthreport_last_delete_failed";
-
-  // Preferences for upload client.
-  public static final String PREF_LAST_UPLOAD_LOCAL_TIME  = "healthreport_last_upload_local_time";
-  public static final String PREF_LAST_UPLOAD_DOCUMENT_ID  = "healthreport_last_upload_document_id";
-
-  public static final String PREF_DOCUMENT_SERVER_URI = "healthreport_document_server_uri";
-  public static final String DEFAULT_DOCUMENT_SERVER_URI = "https://fhr.data.mozilla.com/";
-
-  public static final String PREF_DOCUMENT_SERVER_NAMESPACE = "healthreport_document_server_namespace";
-  public static final String DEFAULT_DOCUMENT_SERVER_NAMESPACE = "metrics";
-
-  // One UUID is 36 characters (like e56542e0-e4d2-11e2-a28f-0800200c9a66), so
-  // we limit the number of obsolete IDs passed so that each request is not a
-  // large upload (and therefore more likely to fail).  We also don't want to
-  // push Bagheera to make too many deletes, since we don't know how the cluster
-  // will handle such API usage.  This obsoletes 2 days worth of old documents
-  // at a time.
-  public static final int MAXIMUM_DELETIONS_PER_POST = ((int) DEFAULT_MAXIMUM_FAILURES_PER_DAY + 1) * 2;
-
-  public static final String PREF_PRUNE_BY_SIZE_TIME = "healthreport_prune_by_size_time";
-  public static final long MINIMUM_TIME_BETWEEN_PRUNE_BY_SIZE_CHECKS_MILLIS =
-      GlobalConstants.MILLISECONDS_PER_DAY;
-  public static final int MAX_ENVIRONMENT_COUNT = 50;
-  public static final int ENVIRONMENT_COUNT_AFTER_PRUNE = 35;
-  public static final int MAX_EVENT_COUNT = 10000;
-  public static final int EVENT_COUNT_AFTER_PRUNE = 8000;
-
-  public static final String PREF_EXPIRATION_TIME = "healthreport_expiration_time";
-  public static final long MINIMUM_TIME_BETWEEN_EXPIRATION_CHECKS_MILLIS = GlobalConstants.MILLISECONDS_PER_DAY * 7;
-  public static final long EVENT_EXISTENCE_DURATION = GlobalConstants.MILLISECONDS_PER_SIX_MONTHS;
-
-  public static final String PREF_CLEANUP_TIME = "healthreport_cleanup_time";
-  public static final long MINIMUM_TIME_BETWEEN_CLEANUP_CHECKS_MILLIS = GlobalConstants.MILLISECONDS_PER_DAY * 30;
-}
deleted file mode 100644
--- a/mobile/android/base/background/healthreport/HealthReportDatabaseStorage.java
+++ /dev/null
@@ -1,1678 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-package org.mozilla.gecko.background.healthreport;
-
-import java.io.File;
-import java.util.Collection;
-import java.util.HashMap;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.Executor;
-import java.util.concurrent.Executors;
-
-import org.json.JSONObject;
-import org.mozilla.gecko.background.common.DateUtils;
-import org.mozilla.gecko.background.common.log.Logger;
-import org.mozilla.gecko.background.healthreport.HealthReportStorage.MeasurementFields.FieldSpec;
-
-import android.content.ContentValues;
-import android.content.Context;
-import android.content.ContextWrapper;
-import android.database.Cursor;
-import android.database.SQLException;
-import android.database.sqlite.SQLiteConstraintException;
-import android.database.sqlite.SQLiteDatabase;
-import android.database.sqlite.SQLiteOpenHelper;
-import android.util.SparseArray;
-
-/**
- * <code>HealthReportDatabaseStorage</code> provides an interface on top of
- * SQLite storage for Health Report data. It exposes methods for management of
- * environments, measurements, fields, and values, and a cursor-based API for
- * querying.
- *
- * Health Report data is structured as follows.
- *
- * Records are primarily broken down by date, at day granularity. Each day's data
- * is then split according to environment. An environment is a collection of
- * constant attributes, such as version and processor; if one of these attributes
- * changes, a new environment becomes active.
- *
- * Environments are identified by a stable hash of their attributes.
- *
- * The database includes a persistent numeric identifier for each environment. Create
- * or fetch this identifier via:
- *
- * <pre>
- *  final Environment environment = storage.getEnvironment();
- *  // Init the environment now.
- *  String envHash = environment.getHash();
- *  int env = environment.register();
- * </pre>
- *
- * You can safely cache this environment identifier for the life of the database.
- *
- * Orthogonal to environments are measurements. Each measurement is a named and
- * versioned scope for a collection of fields. It is assumed that if a measurement
- * with the given name and version is known to the database, then all of its fields
- * are also known; if you change the collection of fields in the measurement, the
- * measurement's version must be incremented, too.
- *
- * As with environments, measurements have an internal numeric identifier.
- *
- * Calling code should initialize its measurements as follows:
- *
- * <pre>
- *   public static class FooFieldsV1 implements MeasurementFields {
- *     {@literal @}Override
- *     public Iterable<String> getFields() {
- *       ArrayList<String> fields = new ArrayList<String>();
- *       fields.add("bar");
- *       fields.add("baz");
- *       return fields;
- *     }
- *   }
- *
- *   storage.beginInitialization();
- *
- *   try {
- *     storage.ensureMeasurementInitialized("org.mozilla.fooProvider.fooMeasurement",
- *                                          1, new FooFieldsV1());
- *     storage.finishInitialization();
- *   } catch (Exception e) {
- *     storage.abortInitialization();
- *   }
- * </pre>
- *
- * Measurements have fields. Fields can conceptually be divided into "daily last"
- * (we only care about the last value), "daily counter" (increments per day),
- * "daily discrete" (multiple records per day). Simply call the correct method for each.
- *
- * To do so you need a field ID, to avoid constant costly string lookups. You can get
- * this value from storage:
- *
- * <pre>
- *   Field field = storage.getField("org.mozilla.fooProvider.fooMeasurement", 1, "bar");
- *   int fieldID = field.getID();
- * </pre>
- *
- * This lookup is cached, and so is relatively inexpensive.
- *
- * You can then do something like the following:
- *
- * <pre>
- *   storage.recordDailyLast(storage.getDay(), env, fieldID, "last value");
- * </pre>
- *
- * or equivalently for numeric values, discrete or counters, etc.
- *
- * To retrieve values, use {@link #getRawEventsSince(long)}.
- *
- * For safety, perform operations on the storage executor thread:
- *
- * <pre>
- *   storage.enqueueOperation(runnable);
- * </pre>
- */
-public class HealthReportDatabaseStorage implements HealthReportStorage {
-
-  private static final String WHERE_DATE_AND_ENV_AND_FIELD = "date = ? AND env = ? AND field = ?";
-
-  public static final String[] COLUMNS_HASH = new String[] {"hash"};
-  public static final String[] COLUMNS_DATE_ENV_FIELD_VALUE = new String[] {"date", "env", "field", "value"};
-  public static final String[] COLUMNS_DATE_ENVSTR_M_MV_F_VALUE = new String[] {
-    "date", "environment", "measurement_name", "measurement_version",
-    "field_name", "field_flags", "value"
-  };
-
-  private static final String[] COLUMNS_ENVIRONMENT_DETAILS = new String[] {
-      "id", "version", "hash",
-      "profileCreation", "cpuCount", "memoryMB",
-
-      "isBlocklistEnabled", "isTelemetryEnabled", "extensionCount",
-      "pluginCount", "themeCount",
-
-      "architecture", "sysName", "sysVersion", "vendor", "appName", "appID",
-      "appVersion", "appBuildID", "platformVersion", "platformBuildID", "os",
-      "xpcomabi", "updateChannel",
-
-      "distribution", "osLocale", "appLocale", "acceptLangSet",
-
-      // Joined to the add-ons table.
-      "addonsBody",
-
-      // v3.
-      "hasHardwareKeyboard",
-      "uiMode", "uiType",
-      "screenLayout", "screenXInMM", "screenYInMM"
-  };
-
-  public static final String[] COLUMNS_MEASUREMENT_DETAILS = new String[] {"id", "name", "version"};
-  public static final String[] COLUMNS_MEASUREMENT_AND_FIELD_DETAILS =
-      new String[] {"measurement_name", "measurement_id", "measurement_version",
-                    "field_name", "field_id", "field_flags"};
-
-  private static final String[] COLUMNS_VALUE = new String[] {"value"};
-  private static final String[] COLUMNS_ID = new String[] {"id"};
-
-  private static final String EVENTS_TEXTUAL = "events_textual";
-  private static final String EVENTS_INTEGER = "events_integer";
-
-  protected static final String DB_NAME = "health.db";
-
-  private static final String LOG_TAG = "HealthReportStorage";
-
-  private final Executor executor = Executors.newSingleThreadExecutor();
-
-  @Override
-  public void enqueueOperation(Runnable runnable) {
-    executor.execute(runnable);
-  }
-
-  public HealthReportDatabaseStorage(final Context context,
-                                     final File profileDirectory) {
-    this.helper = new HealthReportSQLiteOpenHelper(context, profileDirectory,
-                                                   DB_NAME);
-    executor.execute(new Runnable() {
-      @Override
-      public void run() {
-        Logger.setThreadLogTag(HealthReportConstants.GLOBAL_LOG_TAG);
-        Logger.debug(LOG_TAG, "Creating HealthReportDatabaseStorage.");
-      }
-    });
-  }
-
-  @Override
-  public void close() {
-    this.helper.close();
-    this.fields.clear();
-    this.envs.clear();
-    this.measurementVersions.clear();
-  }
-
-  protected final HealthReportSQLiteOpenHelper helper;
-
-  public static class HealthReportSQLiteOpenHelper extends SQLiteOpenHelper {
-    public static final int CURRENT_VERSION = 7;
-    public static final String LOG_TAG = "HealthReportSQL";
-
-    /**
-     * A little helper to avoid SQLiteOpenHelper misbehaving on Android 2.1.
-     * Partly cribbed from
-     * <http://stackoverflow.com/questions/5332328/sqliteopenhelper-problem-with-fully-qualified-db-path-name>.
-     */
-    public static class AbsolutePathContext extends ContextWrapper {
-      private final File parent;
-
-      public AbsolutePathContext(Context base, File parent) {
-        super(base);
-        this.parent = parent;
-      }
-
-      @Override
-      public File getDatabasePath(String name) {
-        return new File(getAbsolutePath(parent, name));
-      }
-
-      // Won't be called after API v11, but we can't override the version that
-      // *is* called and still support v8.
-      // Instead we check the version code in the HealthReportSQLiteOpenHelper
-      // constructor, and only use this workaround if we need to.
-      @Override
-      public SQLiteDatabase openOrCreateDatabase(String name,
-                                                 int mode,
-                                                 SQLiteDatabase.CursorFactory factory) {
-        final File path = getDatabasePath(name);
-        Logger.pii(LOG_TAG, "Opening database through absolute path " + path.getAbsolutePath());
-        return SQLiteDatabase.openOrCreateDatabase(path, null);
-      }
-    }
-
-    public static String getAbsolutePath(File parent, String name) {
-      return parent.getAbsolutePath() + File.separator + name;
-    }
-
-    public HealthReportSQLiteOpenHelper(Context context, File profileDirectory, String name) {
-      this(context, profileDirectory, name, CURRENT_VERSION);
-    }
-
-    // For testing DBs of different versions.
-    public HealthReportSQLiteOpenHelper(Context context, File profileDirectory, String name, int version) {
-      super(context, getAbsolutePath(profileDirectory, name), null, version);
-      Logger.pii(LOG_TAG, "Opening: " + getAbsolutePath(profileDirectory, name));
-    }
-
-    @Override
-    public void onCreate(SQLiteDatabase db) {
-      db.execSQL("CREATE TABLE addons (id INTEGER PRIMARY KEY AUTOINCREMENT, " +
-                 "                     body TEXT, " +
-                 "                     UNIQUE (body) " +
-                 ")");
-
-      // N.B., hash collisions can occur across versions. In that case, the system
-      // is likely to persist the original environment version.
-      db.execSQL("CREATE TABLE environments (id INTEGER PRIMARY KEY AUTOINCREMENT, " +
-                 "                           version INTEGER, " +
-                 "                           hash TEXT, " +
-                 "                           profileCreation INTEGER, " +
-                 "                           cpuCount        INTEGER, " +
-                 "                           memoryMB        INTEGER, " +
-                 "                           isBlocklistEnabled INTEGER, " +
-                 "                           isTelemetryEnabled INTEGER, " +
-                 "                           extensionCount     INTEGER, " +
-                 "                           pluginCount        INTEGER, " +
-                 "                           themeCount         INTEGER, " +
-                 "                           architecture    TEXT, " +
-                 "                           sysName         TEXT, " +
-                 "                           sysVersion      TEXT, " +
-                 "                           vendor          TEXT, " +
-                 "                           appName         TEXT, " +
-                 "                           appID           TEXT, " +
-                 "                           appVersion      TEXT, " +
-                 "                           appBuildID      TEXT, " +
-                 "                           platformVersion TEXT, " +
-                 "                           platformBuildID TEXT, " +
-                 "                           os              TEXT, " +
-                 "                           xpcomabi        TEXT, " +
-                 "                           updateChannel   TEXT, " +
-
-                 "                           distribution    TEXT, " +
-                 "                           osLocale        TEXT, " +
-                 "                           appLocale       TEXT, " +
-                 "                           acceptLangSet   INTEGER, " +
-
-                 "                           addonsID        INTEGER, " +
-
-                 "                           hasHardwareKeyboard INTEGER, " +
-                 "                           uiMode          INTEGER, " +
-                 "                           uiType          TEXT, " +
-                 "                           screenLayout    INTEGER, " +
-                 "                           screenXInMM     INTEGER, " +
-                 "                           screenYInMM     INTEGER, " +
-
-                 "                           FOREIGN KEY (addonsID) REFERENCES addons(id) ON DELETE RESTRICT, " +
-                 "                           UNIQUE (hash) " +
-                 ")");
-
-      db.execSQL("CREATE TABLE measurements (id INTEGER PRIMARY KEY AUTOINCREMENT, " +
-                 "                           name TEXT, " +
-                 "                           version INTEGER, " +
-                 "                           UNIQUE (name, version) " +
-                 ")");
-
-      db.execSQL("CREATE TABLE fields (id INTEGER PRIMARY KEY AUTOINCREMENT, " +
-                 "                     measurement INTEGER, " +
-                 "                     name TEXT, " +
-                 "                     flags INTEGER, " +
-                 "                     FOREIGN KEY (measurement) REFERENCES measurements(id) ON DELETE CASCADE, " +
-                 "                     UNIQUE (measurement, name)" +
-                 ")");
-
-      db.execSQL("CREATE TABLE " + EVENTS_INTEGER + "(" +
-                 "                 date  INTEGER, " +
-                 "                 env   INTEGER, " +
-                 "                 field INTEGER, " +
-                 "                 value INTEGER, " +
-                 "                 FOREIGN KEY (field) REFERENCES fields(id) ON DELETE CASCADE, " +
-                 "                 FOREIGN KEY (env) REFERENCES environments(id) ON DELETE CASCADE" +
-                 ")");
-
-      db.execSQL("CREATE TABLE " + EVENTS_TEXTUAL + "(" +
-                 "                 date  INTEGER, " +
-                 "                 env   INTEGER, " +
-                 "                 field INTEGER, " +
-                 "                 value TEXT, " +
-                 "                 FOREIGN KEY (field) REFERENCES fields(id) ON DELETE CASCADE, " +
-                 "                 FOREIGN KEY (env) REFERENCES environments(id) ON DELETE CASCADE" +
-                 ")");
-
-      db.execSQL("CREATE INDEX idx_events_integer_date_env_field ON events_integer (date, env, field)");
-      db.execSQL("CREATE INDEX idx_events_textual_date_env_field ON events_textual (date, env, field)");
-
-      db.execSQL("CREATE VIEW events AS " +
-                 "SELECT date, env, field, value FROM " + EVENTS_INTEGER + " " +
-                 "UNION ALL " +
-                 "SELECT date, env, field, value FROM " + EVENTS_TEXTUAL);
-
-      db.execSQL("CREATE VIEW named_events AS " +
-                 "SELECT date, " +
-                 "       environments.hash AS environment, " +
-                 "       measurements.name AS measurement_name, " +
-                 "       measurements.version AS measurement_version, " +
-                 "       fields.name AS field_name, " +
-                 "       fields.flags AS field_flags, " +
-                 "       value FROM " +
-                 "events JOIN environments ON events.env = environments.id " +
-                 "       JOIN fields ON events.field = fields.id " +
-                 "       JOIN measurements ON fields.measurement = measurements.id");
-
-      db.execSQL("CREATE VIEW named_fields AS " +
-                 "SELECT measurements.name AS measurement_name, " +
-                 "       measurements.id AS measurement_id, " +
-                 "       measurements.version AS measurement_version, " +
-                 "       fields.name AS field_name, " +
-                 "       fields.id AS field_id, " +
-                 "       fields.flags AS field_flags " +
-                 "FROM fields JOIN measurements ON fields.measurement = measurements.id");
-
-      db.execSQL("CREATE VIEW current_measurements AS " +
-                 "SELECT name, MAX(version) AS version FROM measurements GROUP BY name");
-
-      createAddonsEnvironmentsView(db);
-    }
-
-    @Override
-    public void onOpen(SQLiteDatabase db) {
-      if (!db.isReadOnly()) {
-        db.execSQL("PRAGMA foreign_keys=ON;");
-      }
-    }
-
-    private void createAddonsEnvironmentsView(SQLiteDatabase db) {
-      db.execSQL("CREATE VIEW environments_with_addons AS " +
-          "SELECT e.id AS id, " +
-          "       e.version AS version, " +
-          "       e.hash AS hash, " +
-          "       e.profileCreation AS profileCreation, " +
-          "       e.cpuCount AS cpuCount, " +
-          "       e.memoryMB AS memoryMB, " +
-          "       e.isBlocklistEnabled AS isBlocklistEnabled, " +
-          "       e.isTelemetryEnabled AS isTelemetryEnabled, " +
-          "       e.extensionCount AS extensionCount, " +
-          "       e.pluginCount AS pluginCount, " +
-          "       e.themeCount AS themeCount, " +
-          "       e.architecture AS architecture, " +
-          "       e.sysName AS sysName, " +
-          "       e.sysVersion AS sysVersion, " +
-          "       e.vendor AS vendor, " +
-          "       e.appName AS appName, " +
-          "       e.appID AS appID, " +
-          "       e.appVersion AS appVersion, " +
-          "       e.appBuildID AS appBuildID, " +
-          "       e.platformVersion AS platformVersion, " +
-          "       e.platformBuildID AS platformBuildID, " +
-          "       e.os AS os, " +
-          "       e.xpcomabi AS xpcomabi, " +
-          "       e.updateChannel AS updateChannel, " +
-          "       e.distribution AS distribution, " +
-          "       e.osLocale AS osLocale, " +
-          "       e.appLocale AS appLocale, " +
-          "       e.acceptLangSet AS acceptLangSet, " +
-          "       addons.body AS addonsBody, " +
-
-          "       e.hasHardwareKeyboard AS hasHardwareKeyboard, " +
-          "       e.uiMode AS uiMode, " +
-          "       e.uiType AS uiType, " +
-          "       e.screenLayout AS screenLayout, " +
-          "       e.screenXInMM AS screenXInMM, " +
-          "       e.screenYInMM AS screenYInMM " +
-
-          "FROM environments AS e, addons " +
-          "WHERE e.addonsID = addons.id");
-    }
-
-    private void upgradeDatabaseFrom2To3(SQLiteDatabase db) {
-      db.execSQL("CREATE TABLE addons (id INTEGER PRIMARY KEY AUTOINCREMENT, " +
-                 "                     body TEXT, " +
-                 "                     UNIQUE (body) " +
-                 ")");
-
-      db.execSQL("ALTER TABLE environments ADD COLUMN addonsID INTEGER REFERENCES addons(id) ON DELETE RESTRICT");
-
-      // No need to create view: we're just going to upgrade again.
-    }
-
-    private void upgradeDatabaseFrom3To4(SQLiteDatabase db) {
-      // Update search measurements to use a different type.
-      db.execSQL("UPDATE OR IGNORE fields SET flags = " + Field.TYPE_COUNTED_STRING_DISCRETE +
-                 " WHERE measurement IN (SELECT id FROM measurements WHERE name = 'org.mozilla.searches.counts')");
-    }
-
-    private void upgradeDatabaseFrom4to5(SQLiteDatabase db) {
-      // Delete NULL in addons.body, which appeared as a result of Bug 886156. Note that the
-      // foreign key constraint, "ON DELETE RESTRICT", may be violated, but since onOpen() is
-      // called after this method, foreign keys are not yet enabled and constraints can be broken.
-      db.delete("addons", "body IS NULL", null);
-
-      // Purge any data inconsistent with foreign key references (which may have appeared before
-      // foreign keys were enabled in Bug 900289).
-      db.delete("fields", "measurement NOT IN (SELECT id FROM measurements)", null);
-      db.delete("environments", "addonsID NOT IN (SELECT id from addons)", null);
-      db.delete(EVENTS_INTEGER, "env NOT IN (SELECT id FROM environments)", null);
-      db.delete(EVENTS_TEXTUAL, "env NOT IN (SELECT id FROM environments)", null);
-      db.delete(EVENTS_INTEGER, "field NOT IN (SELECT id FROM fields)", null);
-      db.delete(EVENTS_TEXTUAL, "field NOT IN (SELECT id FROM fields)", null);
-    }
-
-    private void upgradeDatabaseFrom5to6(SQLiteDatabase db) {
-      db.execSQL("DROP VIEW IF EXISTS environments_with_addons");
-
-      // Add version to environment (default to 1).
-      db.execSQL("ALTER TABLE environments ADD COLUMN version INTEGER DEFAULT 1");
-
-      // Add fields to environment (default to empty string).
-      db.execSQL("ALTER TABLE environments ADD COLUMN distribution TEXT DEFAULT ''");
-      db.execSQL("ALTER TABLE environments ADD COLUMN osLocale TEXT DEFAULT ''");
-      db.execSQL("ALTER TABLE environments ADD COLUMN appLocale TEXT DEFAULT ''");
-      db.execSQL("ALTER TABLE environments ADD COLUMN acceptLangSet INTEGER DEFAULT 0");
-
-      // No need to recreate view -- we're just going to upgrade to v7 next.
-    }
-
-    private void upgradeDatabaseFrom6to7(SQLiteDatabase db) {
-      db.execSQL("DROP VIEW IF EXISTS environments_with_addons");
-
-      // Add fields to environment (default to empty string and 0).
-      db.execSQL("ALTER TABLE environments ADD COLUMN hasHardwareKeyboard INTEGER DEFAULT 0");
-      db.execSQL("ALTER TABLE environments ADD COLUMN uiMode INTEGER DEFAULT 0");
-      db.execSQL("ALTER TABLE environments ADD COLUMN uiType TEXT DEFAULT ''");
-      db.execSQL("ALTER TABLE environments ADD COLUMN screenLayout INTEGER DEFAULT 0");
-      db.execSQL("ALTER TABLE environments ADD COLUMN screenXInMM INTEGER DEFAULT 0");
-      db.execSQL("ALTER TABLE environments ADD COLUMN screenYInMM INTEGER DEFAULT 0");
-
-      // Recreate view.
-      createAddonsEnvironmentsView(db);
-    }
-
-    @Override
-    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
-      if (oldVersion >= newVersion) {
-        return;
-      }
-
-      Logger.info(LOG_TAG, "onUpgrade: from " + oldVersion + " to " + newVersion + ".");
-      try {
-        switch (oldVersion) {
-        case 2:
-          upgradeDatabaseFrom2To3(db);
-        case 3:
-          upgradeDatabaseFrom3To4(db);
-        case 4:
-          upgradeDatabaseFrom4to5(db);
-        case 5:
-          upgradeDatabaseFrom5to6(db);
-        case 6:
-          upgradeDatabaseFrom6to7(db);
-        }
-      } catch (Exception e) {
-        Logger.error(LOG_TAG, "Failure in onUpgrade.", e);
-        throw new RuntimeException(e);
-      }
-   }
-
-    public void deleteEverything() {
-      final SQLiteDatabase db = this.getWritableDatabase();
-
-      Logger.info(LOG_TAG, "Deleting everything.");
-      db.beginTransaction();
-      try {
-        // Cascade will clear the rest.
-        db.delete("measurements", null, null);
-        db.delete("environments", null, null);
-        db.delete("addons", null, null);
-        db.setTransactionSuccessful();
-        Logger.info(LOG_TAG, "Deletion successful.");
-      } finally {
-        db.endTransaction();
-      }
-    }
-  }
-
-  public class DatabaseField extends Field {
-    public DatabaseField(String mName, int mVersion, String fieldName) {
-      this(mName, mVersion, fieldName, UNKNOWN_TYPE_OR_FIELD_ID, UNKNOWN_TYPE_OR_FIELD_ID);
-    }
-
-    public DatabaseField(String mName, int mVersion, String fieldName, int flags) {
-      this(mName, mVersion, fieldName, UNKNOWN_TYPE_OR_FIELD_ID, flags);
-    }
-
-    public DatabaseField(String mName, int mVersion, String fieldName, int fieldID, int flags) {
-      super(mName, mVersion, fieldName, flags);
-      this.fieldID = fieldID;
-    }
-
-    private void loadFlags() {
-      if (this.flags == UNKNOWN_TYPE_OR_FIELD_ID) {
-        if (this.fieldID == UNKNOWN_TYPE_OR_FIELD_ID) {
-          this.getID();
-        }
-        this.flags = integerQuery("fields", "flags", "id = ?", new String[] { Integer.toString(this.fieldID, 10) }, -1);
-      }
-    }
-
-    @Override
-    public synchronized boolean isIntegerField() {
-      loadFlags();
-      return super.isIntegerField();
-    }
-
-    @Override
-    public synchronized boolean isStringField() {
-      loadFlags();
-      return super.isStringField();
-    }
-
-    @Override
-    public synchronized boolean isDiscreteField() {
-      loadFlags();
-      return super.isDiscreteField();
-    }
-
-    @Override
-    public synchronized int getID() throws IllegalStateException {
-      if (this.fieldID == UNKNOWN_TYPE_OR_FIELD_ID) {
-        this.fieldID = integerQuery("named_fields", "field_id",
-                                    "measurement_name = ? AND measurement_version = ? AND field_name = ?",
-                                    new String[] {measurementName, measurementVersion, fieldName},
-                                    UNKNOWN_TYPE_OR_FIELD_ID);
-        if (this.fieldID == UNKNOWN_TYPE_OR_FIELD_ID) {
-          throw new IllegalStateException("No field with name " + fieldName +
-                                          " (" + measurementName + ", " + measurementVersion + ")");
-        }
-      }
-      return this.fieldID;
-    }
-  }
-
-  // `envs` and `fields` look similar, but they are touched differently and
-  // store differently stable kinds of data, hence type difference.
-  // Note that we don't pre-populate the environment cache. We'll typically only
-  // handle one per session.
-  //
-  // protected for testing purposes only.
-  protected final ConcurrentHashMap<String, Integer> envs = new ConcurrentHashMap<String, Integer>();
-
-  /**
-   * An {@link Environment} that knows how to persist to and from our database.
-   */
-  public static class DatabaseEnvironment extends Environment {
-    protected final HealthReportDatabaseStorage storage;
-
-    @Override
-    public int register() {
-      final String h = getHash();
-      if (storage.envs.containsKey(h)) {
-        this.id = storage.envs.get(h);
-        return this.id;
-      }
-
-      // Otherwise, add data and hash to the DB.
-      ContentValues v = new ContentValues();
-      v.put("version", version);
-      v.put("hash", h);
-      v.put("profileCreation", profileCreation);
-      v.put("cpuCount", cpuCount);
-      v.put("memoryMB", memoryMB);
-      v.put("isBlocklistEnabled", isBlocklistEnabled);
-      v.put("isTelemetryEnabled", isTelemetryEnabled);
-      v.put("extensionCount", extensionCount);
-      v.put("pluginCount", pluginCount);
-      v.put("themeCount", themeCount);
-      v.put("architecture", architecture);
-      v.put("sysName", sysName);
-      v.put("sysVersion", sysVersion);
-      v.put("vendor", vendor);
-      v.put("appName", appName);
-      v.put("appID", appID);
-      v.put("appVersion", appVersion);
-      v.put("appBuildID", appBuildID);
-      v.put("platformVersion", platformVersion);
-      v.put("platformBuildID", platformBuildID);
-      v.put("os", os);
-      v.put("xpcomabi", xpcomabi);
-      v.put("updateChannel", updateChannel);
-      v.put("distribution", distribution);
-      v.put("osLocale", osLocale);
-      v.put("appLocale", appLocale);
-      v.put("acceptLangSet", acceptLangSet);
-      v.put("hasHardwareKeyboard", hasHardwareKeyboard ? 1 : 0);
-      v.put("uiMode", uiMode);
-      v.put("uiType", uiType.toString());
-      v.put("screenLayout", screenLayout);
-      v.put("screenXInMM", screenXInMM);
-      v.put("screenYInMM", screenYInMM);
-
-      final SQLiteDatabase db = storage.helper.getWritableDatabase();
-
-      // If we're not already, we want all of our inserts to be in a transaction.
-      boolean newTransaction = !db.inTransaction();
-
-      // Insert, with a little error handling to populate the cache in case of
-      // omission and consequent collision.
-      //
-      // We would like to hang a trigger off a view here, and just use that for
-      // inserts. But triggers don't seem to correctly update the last inserted
-      // ID, so Android's insertOrThrow method returns -1.
-      //
-      // Instead, we go without the trigger, simply running the inserts ourselves.
-      //
-      // insertWithOnConflict doesn't work as documented: <http://stackoverflow.com/questions/11328877/android-sqllite-on-conflict-ignore-is-ignored-in-ics/11424150>.
-      // So we do this the hard way.
-      // We presume that almost every get will hit the cache (except for the first, obviously), so we
-      // bias here towards inserts for the environments.
-      // For add-ons we assume that different environments will share add-ons, so we query first.
-
-      final String addonsJSON = getNormalizedAddonsJSON();
-      if (newTransaction) {
-        db.beginTransaction();
-      }
-
-      try {
-        int addonsID = ensureAddons(db, addonsJSON);
-        v.put("addonsID", addonsID);
-
-        try {
-          int inserted = (int) db.insertOrThrow("environments", null, v);
-          Logger.debug(LOG_TAG, "Inserted ID: " + inserted + " for hash " + h);
-          if (inserted == -1) {
-            throw new SQLException("Insert returned -1!");
-          }
-          this.id = inserted;
-          storage.envs.put(h, this.id);
-          if (newTransaction) {
-            db.setTransactionSuccessful();
-          }
-          return inserted;
-        } catch (SQLException e) {
-          // The inserter should take care of updating `envs`. But if it
-          // doesn't...
-          Cursor c = db.query("environments", COLUMNS_ID, "hash = ?",
-                              new String[] { h }, null, null, null);
-          try {
-            if (!c.moveToFirst()) {
-              throw e;
-            }
-            this.id = (int) c.getLong(0);
-            Logger.debug(LOG_TAG, "Found " + this.id + " for hash " + h);
-            storage.envs.put(h, this.id);
-            if (newTransaction) {
-              db.setTransactionSuccessful();
-            }
-            return this.id;
-          } finally {
-            c.close();
-          }
-        }
-      } finally {
-        if (newTransaction) {
-          db.endTransaction();
-        }
-      }
-    }
-
-    protected static int ensureAddons(SQLiteDatabase db, String json) {
-      Cursor c = db.query("addons", COLUMNS_ID, "body = ?",
-                          new String[] { (json == null) ? "null" : json }, null, null, null);
-      try {
-        if (c.moveToFirst()) {
-          return c.getInt(0);
-        }
-        ContentValues values = new ContentValues();
-        values.put("body", json);
-        return (int) db.insert("addons", null, values);
-      } finally {
-        c.close();
-      }
-    }
-
-    public void init(ContentValues v) {
-      version         = v.containsKey("version") ? v.getAsInteger("version") : Environment.CURRENT_VERSION;
-
-      Logger.debug(LOG_TAG, "Initializing environment with version " + version);
-
-      profileCreation = v.getAsInteger("profileCreation");
-      cpuCount        = v.getAsInteger("cpuCount");
-      memoryMB        = v.getAsInteger("memoryMB");
-
-      isBlocklistEnabled = v.getAsInteger("isBlocklistEnabled");
-      isTelemetryEnabled = v.getAsInteger("isTelemetryEnabled");
-      extensionCount     = v.getAsInteger("extensionCount");
-      pluginCount        = v.getAsInteger("pluginCount");
-      themeCount         = v.getAsInteger("themeCount");
-
-      architecture    = v.getAsString("architecture");
-      sysName         = v.getAsString("sysName");
-      sysVersion      = v.getAsString("sysVersion");
-      vendor          = v.getAsString("vendor");
-      appName         = v.getAsString("appName");
-      appID           = v.getAsString("appID");
-      appVersion      = v.getAsString("appVersion");
-      appBuildID      = v.getAsString("appBuildID");
-      platformVersion = v.getAsString("platformVersion");
-      platformBuildID = v.getAsString("platformBuildID");
-      os              = v.getAsString("os");
-      xpcomabi        = v.getAsString("xpcomabi");
-      updateChannel   = v.getAsString("updateChannel");
-
-      distribution    = v.getAsString("distribution");
-      osLocale        = v.getAsString("osLocale");
-      appLocale       = v.getAsString("appLocale");
-      acceptLangSet   = v.getAsInteger("acceptLangSet");
-
-      try {
-        setJSONForAddons(v.getAsString("addonsBody"));
-      } catch (Exception e) {
-        // Nothing we can do.
-      }
-
-      if (version >= 3) {
-        hasHardwareKeyboard = v.getAsInteger("hasHardwareKeyboard") != 0;
-        uiMode = v.getAsInteger("uiMode");
-        uiType = UIType.fromLabel(v.getAsString("uiType"));
-        screenLayout = v.getAsInteger("screenLayout");
-        screenXInMM = v.getAsInteger("screenXInMM");
-        screenYInMM = v.getAsInteger("screenYInMM");
-      }
-
-      this.hash = null;
-      this.id = -1;
-    }
-
-    /**
-     * Fill ourselves with data from the DB, then advance the cursor.
-     *
-     * @param cursor a {@link Cursor} pointing at a record to load.
-     * @return true if the cursor was successfully advanced.
-     */
-    public boolean init(Cursor cursor) {
-      int i = 0;
-      this.id         = cursor.getInt(i++);
-      this.version    = cursor.getInt(i++);
-      this.hash       = cursor.getString(i++);
-
-      profileCreation = cursor.getInt(i++);
-      cpuCount        = cursor.getInt(i++);
-      memoryMB        = cursor.getInt(i++);
-
-      isBlocklistEnabled = cursor.getInt(i++);
-      isTelemetryEnabled = cursor.getInt(i++);
-      extensionCount     = cursor.getInt(i++);
-      pluginCount        = cursor.getInt(i++);
-      themeCount         = cursor.getInt(i++);
-
-      architecture    = cursor.getString(i++);
-      sysName         = cursor.getString(i++);
-      sysVersion      = cursor.getString(i++);
-      vendor          = cursor.getString(i++);
-      appName         = cursor.getString(i++);
-      appID           = cursor.getString(i++);
-      appVersion      = cursor.getString(i++);
-      appBuildID      = cursor.getString(i++);
-      platformVersion = cursor.getString(i++);
-      platformBuildID = cursor.getString(i++);
-      os              = cursor.getString(i++);
-      xpcomabi        = cursor.getString(i++);
-      updateChannel   = cursor.getString(i++);
-
-      distribution    = cursor.getString(i++);
-      osLocale        = cursor.getString(i++);
-      appLocale       = cursor.getString(i++);
-      acceptLangSet   = cursor.getInt(i++);
-
-      try {
-        setJSONForAddons(cursor.getBlob(i++));
-      } catch (Exception e) {
-        // Nothing we can do.
-      }
-
-      if (this.version >= 3) {
-        hasHardwareKeyboard = cursor.getInt(i++) != 0;
-        uiMode = cursor.getInt(i++);
-        uiType = UIType.fromLabel(cursor.getString(i++));
-        screenLayout = cursor.getInt(i++);
-        screenXInMM = cursor.getInt(i++);
-        screenYInMM = cursor.getInt(i++);
-      }
-
-      return cursor.moveToNext();
-    }
-
-    public DatabaseEnvironment(HealthReportDatabaseStorage storage, Class<? extends EnvironmentAppender> appender) {
-      super(appender);
-      this.storage = storage;
-    }
-
-    public DatabaseEnvironment(HealthReportDatabaseStorage storage) {
-      this.storage = storage;
-    }
-  }
-
-  /**
-   * Factory method. Returns a new {@link Environment} that callers can
-   * populate and then register.
-   */
-  @Override
-  public DatabaseEnvironment getEnvironment() {
-    return new DatabaseEnvironment(this);
-  }
-
-  @Override
-  public SparseArray<Environment> getEnvironmentRecordsByID() {
-    final SQLiteDatabase db = this.helper.getReadableDatabase();
-    Cursor c = db.query("environments_with_addons", COLUMNS_ENVIRONMENT_DETAILS, null, null, null, null, null);
-    try {
-      SparseArray<Environment> results = new SparseArray<Environment>();
-      if (!c.moveToFirst()) {
-        return results;
-      }
-
-      DatabaseEnvironment e = getEnvironment();
-      while (e.init(c)) {
-        results.put(e.id, e);
-        e = getEnvironment();
-      }
-      results.put(e.id, e);
-      return results;
-    } finally {
-      c.close();
-    }
-  }
-
-  /**
-   * Reverse lookup for an env. Only really used for tests: document generation
-   * fetches all environments at once, and insertion only uses the integer key
-   * that's returned during insertion.
-   *
-   * @param id
-   *          the identifier for the environment.
-   * @return a cursor over its details.
-   */
-  @Override
-  public Cursor getEnvironmentRecordForID(int id) {
-    final SQLiteDatabase db = this.helper.getReadableDatabase();
-    return db.query("environments_with_addons", COLUMNS_ENVIRONMENT_DETAILS, "id = " + id, null, null, null, null);
-  }
-
-  @Override
-  public SparseArray<String> getEnvironmentHashesByID() {
-    final SQLiteDatabase db = this.helper.getReadableDatabase();
-    Cursor c = db.query("environments", new String[] {"id", "hash"}, null, null, null, null, null);
-    try {
-      SparseArray<String> results = new SparseArray<String>();
-      if (!c.moveToFirst()) {
-        return results;
-      }
-
-      while (!c.isAfterLast()) {
-        results.put(c.getInt(0), c.getString(1));
-        c.moveToNext();
-      }
-      return results;
-    } finally {
-      c.close();
-    }
-  }
-
-  /**
-   * Cache the lookup from measurement and field specifier to field instance.
-   * This allows us to memoize the field ID lookup, too.
-   */
-  private final HashMap<String, Field> fields = new HashMap<String, Field>();
-  private boolean fieldsCacheUpdated = false;
-
-  private void invalidateFieldsCache() {
-    synchronized (this.fields) {
-      fieldsCacheUpdated = false;
-    }
-  }
-
-  private String getFieldKey(String mName, int mVersion, String fieldName) {
-    return mVersion + "." + mName + "/" + fieldName;
-  }
-
-  @Override
-  public Field getField(String mName, int mVersion, String fieldName) {
-    final String key = getFieldKey(mName, mVersion, fieldName);
-    synchronized (fields) {
-      if (fields.containsKey(key)) {
-        return fields.get(key);
-      }
-      Field f = new DatabaseField(mName, mVersion, fieldName);
-      fields.put(key, f);
-      return f;
-    }
-  }
-
-  private void populateFieldCache() {
-    synchronized (fields) {
-      if (fieldsCacheUpdated) {
-        return;
-      }
-
-      fields.clear();
-      Cursor c = getFieldVersions();
-      try {
-        if (!c.moveToFirst()) {
-          return;
-        }
-        do {
-          // We don't use the measurement ID here, so column 1 is unused.
-          final String mName = c.getString(0);
-          final int mVersion = c.getInt(2);
-          final String fieldName = c.getString(3);
-          final int fieldID = c.getInt(4);
-          final int flags = c.getInt(5);
-          final String key = getFieldKey(mName, mVersion, fieldName);
-
-          Field f = new DatabaseField(mName, mVersion, fieldName, fieldID, flags);
-          fields.put(key, f);
-        } while (c.moveToNext());
-        fieldsCacheUpdated = true;
-      } finally {
-        c.close();
-      }
-    }
-  }
-
-  /**
-   * Return mappings from field ID to Field instance. Do so by looking in the DB.
-   */
-  @Override
-  public SparseArray<Field> getFieldsByID() {
-    final SparseArray<Field> out = new SparseArray<Field>();
-    synchronized (fields) {
-      populateFieldCache();
-      Collection<Field> values = fields.values();
-      for (Field field : values) {
-        // Cache is up-to-date at this point, so we don't need to hit the DB.
-        out.put(field.getID(), field);
-      }
-    }
-    return out;
-  }
-
-  private final HashMap<String, Integer> measurementVersions = new HashMap<String, Integer>();
-
-  private void populateMeasurementVersionsCache(SQLiteDatabase db) {
-    HashMap<String, Integer> results = getIntegers(db, "current_measurements", "name", "version");
-    if (results == null) {
-      measurementVersions.clear();
-      return;
-    }
-    synchronized (measurementVersions) {
-      measurementVersions.clear();
-      measurementVersions.putAll(results);
-    }
-  }
-
-  /**
-   * Return the version of the measurement for which the DB is currently configured, or
-   * 0 if unknown.
-   * @param measurement String measurement identifier.
-   * @return Current version.
-   */
-  private int getMeasurementVersion(String measurement) {
-    synchronized (measurementVersions) {
-      if (measurementVersions.containsKey(measurement)) {
-        return measurementVersions.get(measurement);
-      }
-
-      // This should never be necessary, unless the measurement does not exist.
-      int value = integerQuery("measurements", "version", "name = ?", new String[] {measurement}, 0);
-      measurementVersions.put(measurement, value);
-      return value;
-    }
-  }
-
-  /**
-   * Inform the storage layer that fields for the given measurement have been updated
-   * to this version.
-   *
-   * This should be one of the final calls in a configuration transaction.
-   * Always call this inside a transaction.
-   */
-  private void notifyMeasurementVersionUpdated(String measurement, int version) {
-    Logger.info(LOG_TAG, "Measurement " + measurement + " now at " + version);
-
-    final SQLiteDatabase db = this.helper.getWritableDatabase();
-    final ContentValues values = new ContentValues();
-    values.put("name", measurement);
-    values.put("version", version);
-
-    synchronized (measurementVersions) {
-      measurementVersions.put(measurement, version);
-    }
-
-    db.insertWithOnConflict("measurements", null, values, SQLiteDatabase.CONFLICT_IGNORE);
-  }
-
-  /**
-   * Call in a transaction.
-   * This method could race with other accesses, but (a) it's within a transaction,
-   * (b) configuration should be single-threaded, (c) we initialize the cache up-front.
-   */
-  @Override
-  public void ensureMeasurementInitialized(String measurement, int version, MeasurementFields fields) {
-    final int currentVersion = getMeasurementVersion(measurement);
-    Logger.info(LOG_TAG, "Initializing measurement " + measurement + " to " +
-                         version + " (current " + currentVersion + ")");
-
-    if (currentVersion == version) {
-      Logger.info(LOG_TAG, "Measurement " + measurement + " already at v" + version);
-      return;
-    }
-
-    final SQLiteDatabase db = this.helper.getWritableDatabase();
-    if (!db.inTransaction()) {
-      Logger.warn(LOG_TAG, "ensureMeasurementInitialized should be called within a transaction.");
-    }
-
-    final ContentValues mv = new ContentValues();
-    mv.put("name", measurement);
-    mv.put("version", version);
-
-    final int measurementID = (int) db.insert("measurements", null, mv);
-
-    final ContentValues v = new ContentValues();
-    v.put("measurement", measurementID);
-    for (FieldSpec field : fields.getFields()) {
-      v.put("name", field.name);
-      v.put("flags", field.type);
-      Logger.debug(LOG_TAG, "M: " + measurementID + " F: " + field.name + " (" + field.type + ")");
-      db.insert("fields", null, v);
-    }
-
-    notifyMeasurementVersionUpdated(measurement, version);
-
-    // Let's be easy for now.
-    invalidateFieldsCache();
-  }
-
-  /**
-   * Return a cursor over the measurements and fields in the DB.
-   * Columns are {@link HealthReportDatabaseStorage#COLUMNS_MEASUREMENT_AND_FIELD_DETAILS}.
-   */
-  @Override
-  public Cursor getFieldVersions() {
-    final SQLiteDatabase db = this.helper.getReadableDatabase();
-    return db.query("named_fields", COLUMNS_MEASUREMENT_AND_FIELD_DETAILS,
-                    null, null, null, null, "measurement_name, measurement_version, field_name");
-  }
-
-  @Override
-  public Cursor getFieldVersions(String measurement, int measurementVersion) {
-    final SQLiteDatabase db = this.helper.getReadableDatabase();
-    return db.query("named_fields", COLUMNS_MEASUREMENT_AND_FIELD_DETAILS,
-                    "measurement_name = ? AND measurement_version = ?",
-                    new String[] {measurement, Integer.toString(measurementVersion)},
-                    null, null, "field_name");
-  }
-
-  @Override
-  public Cursor getMeasurementVersions() {
-    final SQLiteDatabase db = this.helper.getReadableDatabase();
-    return db.query("measurements", COLUMNS_MEASUREMENT_DETAILS,
-                    null, null, null, null, "name, version");
-  }
-
-  /**
-   * A thin wrapper around the database transactional semantics. Clients can
-   * use this to more efficiently ensure that measurements are initialized.
-   *
-   * Note that caches are also initialized here.
-   */
-  public void beginInitialization() {
-    SQLiteDatabase db = this.helper.getWritableDatabase();
-    db.beginTransaction();
-    populateMeasurementVersionsCache(db);
-  }
-
-  public void finishInitialization() {
-    SQLiteDatabase db = this.helper.getWritableDatabase();
-    db.setTransactionSuccessful();
-    db.endTransaction();
-  }
-
-  public void abortInitialization() {
-    this.helper.getWritableDatabase().endTransaction();
-  }
-
-  protected int getIntFromQuery(final String sql, final String[] selectionArgs) {
-    final SQLiteDatabase db = this.helper.getReadableDatabase();
-    final Cursor c = db.rawQuery(sql, selectionArgs);
-    try {
-      if (!c.moveToFirst()) {
-        throw new IllegalStateException("Cursor is empty.");
-      }
-      return c.getInt(0);
-    } finally {
-      c.close();
-    }
-  }
-
-  @Override
-  public int getDay(long time) {
-    return DateUtils.getDay(time);
-  }
-
-  @Override
-  public int getDay() {
-    return this.getDay(System.currentTimeMillis());
-  }
-
-  private void recordDailyLast(int env, int day, int field, Object value, String table) {
-    if (env == -1) {
-      Logger.warn(LOG_TAG, "Refusing to record with environment = -1.");
-      return;
-    }
-
-    final SQLiteDatabase db = this.helper.getWritableDatabase();
-
-    final String envString = Integer.toString(env);
-    final String fieldIDString = Integer.toString(field, 10);
-    final String dayString = Integer.toString(day, 10);
-
-    // Java, your capacity for abstraction leaves me wanting.
-    final ContentValues v = new ContentValues();
-    putValue(v, value);
-
-    // If we used a separate table, such that we could have a
-    // UNIQUE(env, field, day) constraint for daily-last values, then we could
-    // use INSERT OR REPLACE.
-    final int updated = db.update(table, v, WHERE_DATE_AND_ENV_AND_FIELD,
-                                  new String[] {dayString, envString, fieldIDString});
-    if (0 == updated) {
-      v.put("env", env);
-      v.put("field", field);
-      v.put("date", day);
-      try {
-        db.insertOrThrow(table, null, v);
-      } catch (SQLiteConstraintException e) {
-        throw new IllegalStateException("Event did not reference existing an environment or field.", e);
-      }
-    }
-  }
-
-  @Override
-  public void recordDailyLast(int env, int day, int field, JSONObject value) {
-    this.recordDailyLast(env, day, field, value == null ? "null" : value.toString(), EVENTS_TEXTUAL);
-  }
-
-  @Override
-  public void recordDailyLast(int env, int day, int field, String value) {
-    this.recordDailyLast(env, day, field, value, EVENTS_TEXTUAL);
-  }
-
-  @Override
-  public void recordDailyLast(int env, int day, int field, int value) {
-    this.recordDailyLast(env, day, field, value, EVENTS_INTEGER);
-  }
-
-  private void recordDailyDiscrete(int env, int day, int field, Object value, String table) {
-    if (env == -1) {
-      Logger.warn(LOG_TAG, "Refusing to record with environment = -1.");
-      return;
-    }
-
-    final ContentValues v = new ContentValues();
-    v.put("env", env);
-    v.put("field", field);
-    v.put("date", day);
-
-    final SQLiteDatabase db = this.helper.getWritableDatabase();
-    putValue(v, value);
-
-    // Using SQLiteDatabase.insertOrThrow throws SQLiteConstraintException we cannot catch for
-    // unknown reasons (bug 961526 comment 13). We believe these are thrown because we attempt to
-    // record events using environment IDs removed from the database by the prune service. We
-    // invalidate the currentEnvironment ID after pruning, preventing further propagation,
-    // however, any event recording waiting for the prune service to complete on the background
-    // thread may carry an invalid ID: we expect an insertion failure and drop these events here.
-    final long res = db.insert(table, null, v);
-    if (res == -1) {
-      Logger.error(LOG_TAG, "Unable to record daily discrete event. Ignoring.");
-    }
-  }
-
-  @Override
-  public void recordDailyDiscrete(int env, int day, int field, JSONObject value) {
-    this.recordDailyDiscrete(env, day, field, value == null ? "null" : value.toString(), EVENTS_TEXTUAL);
-  }
-
-  @Override
-  public void recordDailyDiscrete(int env, int day, int field, String value) {
-    this.recordDailyDiscrete(env, day, field, value, EVENTS_TEXTUAL);
-  }
-
-  @Override
-  public void recordDailyDiscrete(int env, int day, int field, int value) {
-    this.recordDailyDiscrete(env, day, field, value, EVENTS_INTEGER);
-  }
-
-  /**
-   * Increment the specified field value by the specified amount. Counts start
-   * at zero.
-   *
-   * Note that this method can misbehave or throw if not executed within a
-   * transaction, because correct behavior involves querying then
-   * insert-or-update, and a race condition can otherwise occur.
-   *
-   * @param env the environment ID
-   * @param day the current day, in days since epoch
-   * @param field the field ID
-   * @param by how much to increment the counter.
-   */
-  @Override
-  public void incrementDailyCount(int env, int day, int field, int by) {
-    if (env == -1) {
-      Logger.warn(LOG_TAG, "Refusing to record with environment = -1.");
-      return;
-    }
-
-    final SQLiteDatabase db = this.helper.getWritableDatabase();
-    final String envString = Integer.toString(env);
-    final String fieldIDString = Integer.toString(field, 10);
-    final String dayString = Integer.toString(day, 10);
-
-    // Can't run a complex UPDATE and get the number of changed rows, so we'll
-    // do this the hard way.
-    // This relies on being called within a transaction.
-    final String[] args = new String[] {dayString, envString, fieldIDString};
-    final Cursor c = db.query(EVENTS_INTEGER,
-                              COLUMNS_VALUE,
-                              WHERE_DATE_AND_ENV_AND_FIELD,
-                              args, null, null, null, "1");
-
-    boolean present = false;
-    try {
-      present = c.moveToFirst();
-    } finally {
-      c.close();
-    }
-
-    if (present) {
-      // It's an int, so safe to concatenate. Avoids us having to mess with args.
-      db.execSQL("UPDATE " + EVENTS_INTEGER + " SET value = value + " + by + " WHERE " +
-                 WHERE_DATE_AND_ENV_AND_FIELD,
-                 args);
-    } else {
-      final ContentValues v = new ContentValues();
-      v.put("env", env);
-      v.put("value", by);
-      v.put("field", field);
-      v.put("date", day);
-      try {
-        db.insertOrThrow(EVENTS_INTEGER, null, v);
-      } catch (SQLiteConstraintException e) {
-        throw new IllegalStateException("Event did not reference existing an environment or field.", e);
-      }
-    }
-  }
-
-  @Override
-  public void incrementDailyCount(int env, int day, int field) {
-    this.incrementDailyCount(env, day, field, 1);
-  }
-
-  /**
-   * Are there events recorded on or after <code>time</code>?
-   *
-   * @param time milliseconds since epoch. Will be converted by {@link #getDay(long)}.
-   * @return true if such events exist, false otherwise.
-   */
-  @Override
-  public boolean hasEventSince(long time) {
-    final int start = this.getDay(time);
-    final SQLiteDatabase db = this.helper.getReadableDatabase();
-    final String dayString = Integer.toString(start, 10);
-    Cursor cur = db.query("events", COLUMNS_DATE_ENV_FIELD_VALUE,
-        "date >= ?", new String[] {dayString}, null, null, null, "1");
-    if (cur == null) {
-      // Something is horribly wrong; let the caller who actually reads the
-      // events deal with it.
-      return true;
-    }
-    try {
-      return cur.getCount() > 0;
-    } finally {
-      cur.close();
-    }
-  }
-
-  /**
-   * Returns a cursor over field events in the database. The results will be
-   * strictly ordered first by date, then by environment, and finally by field.
-   *
-   * Each row includes columns in {@link #COLUMNS_DATE_ENV_FIELD_VALUE}:
-   * "date", "env", "field", "value".
-   *
-   * @param time milliseconds since epoch. Will be converted by {@link #getDay(long)}.
-   * @return a cursor. The caller is responsible for closing this.
-   */
-  @Override
-  public Cursor getRawEventsSince(long time) {
-    final int start = this.getDay(time);
-    final SQLiteDatabase db = this.helper.getReadableDatabase();
-    final String dayString = Integer.toString(start, 10);
-    return db.query("events", COLUMNS_DATE_ENV_FIELD_VALUE,
-                    "date >= ?", new String[] {dayString}, null, null, "date, env, field");
-  }
-
-  /**
-   * Returns a cursor over field events in the database. The results will be
-   * strictly ordered first by date, then by environment, and finally by field.
-   *
-   * Each row includes columns in {@link #COLUMNS_DATE_ENVSTR_M_MV_F_VALUE}:
-   * "date", "environment" (as a String), "measurement_name", "measurement_version",
-   * "field_name", "field_flags", "value".
-   *
-   * @param time milliseconds since epoch. Will be converted by {@link #getDay(long)}.
-   * @return a cursor. The caller is responsible for closing this.
-   */
-  @Override
-  public Cursor getEventsSince(long time) {
-    final int start = this.getDay(time);
-    final SQLiteDatabase db = this.helper.getReadableDatabase();
-    final String dayString = Integer.toString(start, 10);
-    return db.query("named_events", COLUMNS_DATE_ENVSTR_M_MV_F_VALUE,
-                    "date >= ?", new String[] {dayString}, null, null,
-                    "date, environment, measurement_name, measurement_version, field_name");
-  }
-
-  @Override
-  public int getEventCount() {
-    return getRowCount("events");
-  }
-
-  @Override
-  public int getEnvironmentCount() {
-    return getRowCount("environments");
-  }
-
-  private int getRowCount(String table) {
-    // table should be parameterized, but SQL throws a compilation error if the table in unknown
-    // in advance.
-    return getIntFromQuery("SELECT COUNT(*) from " + table, null);
-  }
-
-  /**
-   * Deletes all environments, addons, and events from the database before the given time. If this
-   * data does not have recorded dates but are no longer referenced by other fields, they are also
-   * removed (with exception to the current environment).
-   *
-   * @param time milliseconds since epoch. Will be converted by {@link #getDay(long)}.
-   * @param curEnv The ID of the current environment.
-   * @return The number of environments and addon entries deleted.
-   */
-  @Override
-  public int deleteDataBefore(final long time, final int curEnv) {
-    final SQLiteDatabase db = this.helper.getWritableDatabase();
-    db.beginTransaction();
-    int numRowsDeleted = 0;
-    try {
-      numRowsDeleted += deleteEnvAndEventsBefore(db, time, curEnv);
-      numRowsDeleted += deleteOrphanedAddons(db);
-      db.setTransactionSuccessful();
-    } finally {
-      db.endTransaction();
-    }
-    return numRowsDeleted;
-  }
-
-  /**
-   * Deletes environments and their referring events recorded before the given time. Environments
-   * referenced by no events are deleted, except for the current environment.
-   *
-   * @param time milliseconds since epoch. Will be converted by {@link #getDay(long)}.
-   * @param curEnv The ID of the current environment.
-   * @return The number of environments (not events) deleted.
-   */
-  protected int deleteEnvAndEventsBefore(final long time, final int curEnv) {
-    final SQLiteDatabase db = this.helper.getWritableDatabase();
-    return deleteEnvAndEventsBefore(db, time, curEnv);
-  }
-
-  // Called internally only to ensure the same db instance is used.
-  protected int deleteEnvAndEventsBefore(final SQLiteDatabase db, final long time, final int curEnv) {
-    // Delete environments only referenced by events occuring before the given time. Cascade
-    // delete these events.
-    String whereClause =
-        "(SELECT COUNT(*) FROM events WHERE date >= ? " +
-        "    AND events.env = environments.id) = 0 " +
-        "AND id IN (SELECT DISTINCT env FROM events WHERE date < ?)";
-    final int day = this.getDay(time);
-    final String dayString = Integer.toString(day, 10);
-    String[] whereArgs = new String[] {dayString, dayString};
-
-    int numEnvDeleted = 0;
-    db.beginTransaction();
-    try {
-      numEnvDeleted += db.delete("environments", whereClause, whereArgs);
-      numEnvDeleted += deleteOrphanedEnv(db, curEnv);
-      // We can't get the number of events deleted through cascading deletions so we do not record
-      // the number of events deleted here.
-      deleteEventsBefore(db, dayString);
-      db.setTransactionSuccessful();
-    } finally {
-      db.endTransaction();
-    }
-    return numEnvDeleted;
-  }
-
-  /**
-   * Deletes environments not referenced by any events except for the given current environment.
-   */
-  protected int deleteOrphanedEnv(final int curEnv) {
-    final SQLiteDatabase db = this.helper.getWritableDatabase();
-    return deleteOrphanedEnv(db, curEnv);
-  }
-
-  // Called internally only to ensure the same db instance is used.
-  @SuppressWarnings("static-method")
-  protected int deleteOrphanedEnv(final SQLiteDatabase db, final int curEnv) {
-    final String whereClause =
-        "id != ? AND " +
-        "id NOT IN (SELECT env FROM events)";
-    final String[] whereArgs = new String[] {Integer.toString(curEnv)};
-    return db.delete("environments", whereClause, whereArgs);
-  }
-
-  protected int deleteEventsBefore(final String dayString) {
-    final SQLiteDatabase db = this.helper.getWritableDatabase();
-    return deleteEventsBefore(db, dayString);
-  }
-
-  // Called internally only to ensure the same db instance is used.
-  @SuppressWarnings("static-method")
-  protected int deleteEventsBefore(final SQLiteDatabase db, final String dayString) {
-    final String whereClause = "date < ?";
-    final String[] whereArgs = new String[] {dayString};
-    int numEventsDeleted = 0;
-    db.beginTransaction();
-    try {
-      numEventsDeleted += db.delete("events_integer", whereClause, whereArgs);
-      numEventsDeleted += db.delete("events_textual", whereClause, whereArgs);
-      db.setTransactionSuccessful();
-    } finally {
-      db.endTransaction();
-    }
-    return numEventsDeleted;
-  }
-
-  /**
-   * Deletes addons not referenced by any environments.
-   */
-  protected int deleteOrphanedAddons() {
-    final SQLiteDatabase db = this.helper.getWritableDatabase();
-    return deleteOrphanedAddons(db);
-  }
-
-  // Called internally only to ensure the same db instance is used.
-  @SuppressWarnings("static-method")
-  protected int deleteOrphanedAddons(final SQLiteDatabase db) {
-    final String whereClause = "id NOT IN (SELECT addonsID FROM environments)";
-    return db.delete("addons", whereClause, null);
-  }
-
-  /**
-   * Retrieve a mapping from a table. Keys should be unique; only one key-value
-   * pair will be returned for each key.
-   */
-  private static HashMap<String, Integer> getIntegers(SQLiteDatabase db, String table, String columnA, String columnB) {
-    Cursor c = db.query(table, new String[] {columnA, columnB}, null, null, null, null, null);
-    try {
-      if (!c.moveToFirst()) {
-        return null;
-      }
-
-      HashMap<String, Integer> results = new HashMap<String, Integer>();
-      while (!c.isAfterLast()) {
-        results.put(c.getString(0), c.getInt(1));
-        c.moveToNext();
-      }
-      return results;
-    } finally {
-      c.close();
-    }
-  }
-
-  /**
-   * Retrieve a single value from a mapping table.
-   */
-  private int integerQuery(String table, String column, String where, String[] args, int defaultValue) {
-    final SQLiteDatabase db = this.helper.getReadableDatabase();
-    Cursor c = db.query(table, new String[] {column}, where, args, null, null, column + " DESC", "1");
-    try {
-      if (!c.moveToFirst()) {
-        return defaultValue;
-      }
-      return c.getInt(0);
-    } finally {
-      c.close();
-    }
-  }
-
-  /**
-   * Helper to allow us to avoid excessive code duplication.
-   *
-   * @param v
-   *          the destination <code>ContentValues</code>.
-   * @param value
-   *          either a <code>String</code> or an <code>Integer</code>. No type
-   *          checking is performed.
-   */
-  private static final void putValue(final ContentValues v, Object value) {
-    if (value instanceof String) {
-      v.put("value", (String) value);
-    } else {
-      v.put("value", (Integer) value);
-    }
-  }
-
-  @Override
-  public void deleteEverything() {
-    this.helper.deleteEverything();
-  }
-
-  @Override
-  public void deleteEnvironments() {
-    final SQLiteDatabase db = this.helper.getWritableDatabase();
-    db.beginTransaction();
-    try {
-      // Cascade will clear the rest.
-      db.delete("environments", null, null);
-      db.setTransactionSuccessful();
-    } finally {
-      db.endTransaction();
-    }
-  }
-
-  @Override
-  public void deleteMeasurements() {
-    final SQLiteDatabase db = this.helper.getWritableDatabase();
-    db.beginTransaction();
-    try {
-      // Cascade will clear the rest.
-      db.delete("measurements", null, null);
-
-      // Clear measurements and fields cache, because some of their IDs are now invalid.
-      invalidateFieldsCache(); // Let it repopulate on its own.
-      populateMeasurementVersionsCache(db); // Performed at Storage init so repopulate now.
-
-      db.setTransactionSuccessful();
-    } finally {
-      db.endTransaction();
-    }
-  }
-
-  /**
-   * Prunes the given number of least-recently used environments. Note that orphaned environments
-   * are not removed and the environment cache is cleared.
-   */
-  @Override
-  public void pruneEnvironments(final int numToPrune) {
-    final SQLiteDatabase db = this.helper.getWritableDatabase();
-    db.beginTransaction();
-    try {
-      db.delete("environments",
-          "id in (SELECT env " +
-          "       FROM events " +
-          "       GROUP BY env " +
-          "       ORDER BY MAX(date), env " +
-          "       LIMIT " + numToPrune + ")",
-          null);
-      db.setTransactionSuccessful();
-
-      // Clear environment cache, because some of their IDs are now invalid.
-      this.envs.clear();
-    } finally {
-      db.endTransaction();
-    }
-  }
-
-  /**
-   * Prunes up to a maximum of the given number of the oldest events. While it is more correct to
-   * prune the exact given amount, there is no unique identifier among events so we cannot be so
-   * precise. Instead, we prune on date, deleting all events up to the day before our count of
-   * events reaches the given maximum. Note that this technicality means this method cannot be
-   * used to delete all events.
-   */
-  @Override
-  public void pruneEvents(final int maxNumToPrune) {
-    final SQLiteDatabase db = this.helper.getWritableDatabase();
-
-    final Cursor c = db.rawQuery(
-        "SELECT MAX(date) " +
-        "FROM (SELECT date " +
-        "      FROM events " +
-        "      ORDER BY date " +
-        "      LIMIT " + maxNumToPrune + ")",
-        null);
-    long pruneDate = -1;
-    try {
-      if (!c.moveToFirst()) {
-        Logger.debug(LOG_TAG, "No max date found in events: table is likely empty. Not pruning " +
-            "events.");
-        return;
-      }
-      pruneDate = c.getLong(0);
-    } finally {
-      c.close();
-    }
-
-    final String selection = "date < " + pruneDate;
-    db.beginTransaction();
-    try {
-      db.delete(EVENTS_INTEGER, selection, null);
-      db.delete(EVENTS_TEXTUAL, selection, null);
-      db.setTransactionSuccessful();
-    } finally {
-      db.endTransaction();
-    }
-  }
-
-  public void vacuum() {
-    final SQLiteDatabase db = this.helper.getWritableDatabase();
-    db.execSQL("vacuum");
-  }
-
-  /**
-   * Disables auto_vacuuming. Changes may only take effect after a "vacuum" command.
-   */
-  public void disableAutoVacuuming() {
-    final SQLiteDatabase db = this.helper.getWritableDatabase();
-    db.execSQL("PRAGMA auto_vacuum=0");
-  }
-}
deleted file mode 100644
--- a/mobile/android/base/background/healthreport/HealthReportDatabases.java
+++ /dev/null
@@ -1,53 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-package org.mozilla.gecko.background.healthreport;
-
-import java.io.File;
-import java.util.HashMap;
-
-import org.mozilla.gecko.background.common.log.Logger;
-
-import android.content.Context;
-
-/**
- * Manages a set of per-profile Health Report storage helpers.
- */
-public class HealthReportDatabases {
-  private static final String LOG_TAG = "HealthReportDatabases";
-
-  private final Context context;
-  private final HashMap<File, HealthReportDatabaseStorage> storages = new HashMap<File, HealthReportDatabaseStorage>();
-
-
-  public HealthReportDatabases(final Context context) {
-    this.context = context;
-  }
-
-  public synchronized HealthReportDatabaseStorage getDatabaseHelperForProfile(final File profileDir) {
-    if (profileDir == null) {
-      throw new IllegalArgumentException("No profile provided.");
-    }
-
-    if (this.storages.containsKey(profileDir)) {
-      return this.storages.get(profileDir);
-    }
-
-    final HealthReportDatabaseStorage helper;
-    helper = new HealthReportDatabaseStorage(this.context, profileDir);
-    this.storages.put(profileDir, helper);
-    return helper;
-  }
-
-  public synchronized void closeDatabaseHelpers() {
-    for (HealthReportDatabaseStorage helper : storages.values()) {
-      try {
-        helper.close();
-      } catch (Exception e) {
-        Logger.warn(LOG_TAG, "Failed to close database helper.", e);
-      }
-    }
-    storages.clear();
-  }
-}
deleted file mode 100644
--- a/mobile/android/base/background/healthreport/HealthReportGenerator.java
+++ /dev/null
@@ -1,710 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-package org.mozilla.gecko.background.healthreport;
-
-import java.util.HashSet;
-import java.util.Iterator;
-import java.util.Set;
-
-import org.json.JSONException;
-import org.json.JSONObject;
-import org.mozilla.gecko.background.common.DateUtils.DateFormatter;
-import org.mozilla.gecko.background.common.log.Logger;
-import org.mozilla.gecko.background.healthreport.EnvironmentBuilder.ConfigurationProvider;
-import org.mozilla.gecko.background.healthreport.HealthReportStorage.Field;
-
-import android.database.Cursor;
-import android.util.SparseArray;
-
-public class HealthReportGenerator {
-  private static final int PAYLOAD_VERSION = 3;
-
-  private static final String LOG_TAG = "GeckoHealthGen";
-
-  private final HealthReportStorage storage;
-  private final DateFormatter dateFormatter;
-
-  public HealthReportGenerator(HealthReportStorage storage) {
-    this.storage = storage;
-    this.dateFormatter = new DateFormatter();
-  }
-
-  @SuppressWarnings("static-method")
-  protected long now() {
-    return System.currentTimeMillis();
-  }
-
-  /**
-   * Ensure that you have initialized the Locale to your satisfaction
-   * prior to calling this method.
-   *
-   * @return null if no environment could be computed, or else the resulting document.
-   * @throws JSONException if there was an error adding environment data to the resulting document.
-   */
-  public JSONObject generateDocument(long since, long lastPingTime, String profilePath, ConfigurationProvider config) throws JSONException {
-    Logger.info(LOG_TAG, "Generating FHR document from " + since + "; last ping " + lastPingTime);
-    Logger.pii(LOG_TAG, "Generating for profile " + profilePath);
-
-    ProfileInformationCache cache = new ProfileInformationCache(profilePath);
-    if (!cache.restoreUnlessInitialized()) {
-      Logger.warn(LOG_TAG, "Not enough profile information to compute current environment.");
-      return null;
-    }
-
-    Environment current = EnvironmentBuilder.getCurrentEnvironment(cache, config);
-    return generateDocument(since, lastPingTime, current);
-  }
-
-  /**
-   * The document consists of:
-   *
-   *<ul>
-   *<li>Basic metadata: last ping time, current ping time, version.</li>
-   *<li>A map of environments: <code>current</code> and others named by hash. <code>current</code> is fully specified,
-   * and others are deltas from current.</li>
-   *<li>A <code>data</code> object. This includes <code>last</code> and <code>days</code>.</li>
-   *</ul>
-   *
-   * <code>days</code> is a map from date strings to <tt>{hash: {measurement: {_v: version, fields...}}}</tt>.
-   * @throws JSONException if there was an error adding environment data to the resulting document.
-   */
-  public JSONObject generateDocument(long since, long lastPingTime, Environment currentEnvironment) throws JSONException {
-    final String currentHash = currentEnvironment.getHash();
-
-    Logger.debug(LOG_TAG, "Current environment hash: " + currentHash);
-    if (currentHash == null) {
-      Logger.warn(LOG_TAG, "Current hash is null; aborting.");
-      return null;
-    }
-
-    // We want to map field IDs to some strings as we go.
-    SparseArray<Environment> envs = storage.getEnvironmentRecordsByID();
-
-    JSONObject document = new JSONObject();
-
-    if (lastPingTime >= HealthReportConstants.EARLIEST_LAST_PING) {
-      document.put("lastPingDate", dateFormatter.getDateString(lastPingTime));
-    }
-
-    document.put("thisPingDate", dateFormatter.getDateString(now()));
-    document.put("version", PAYLOAD_VERSION);
-
-    document.put("environments", getEnvironmentsJSON(currentEnvironment, envs));
-    document.put("data", getDataJSON(currentEnvironment, envs, since));
-
-    return document;
-  }
-
-  protected JSONObject getDataJSON(Environment currentEnvironment,
-                                   SparseArray<Environment> envs, long since) throws JSONException {
-    SparseArray<Field> fields = storage.getFieldsByID();
-
-    JSONObject days = getDaysJSON(currentEnvironment, envs, fields, since);
-
-    JSONObject last = new JSONObject();
-
-    JSONObject data = new JSONObject();
-    data.put("days", days);
-    data.put("last", last);
-    return data;
-  }
-
-  protected JSONObject getDaysJSON(Environment currentEnvironment, SparseArray<Environment> envs, SparseArray<Field> fields, long since) throws JSONException {
-    if (Logger.shouldLogVerbose(LOG_TAG)) {
-      for (int i = 0; i < envs.size(); ++i) {
-        Logger.trace(LOG_TAG, "Days environment " + envs.keyAt(i) + ": " + envs.get(envs.keyAt(i)).getHash());
-      }
-    }
-
-    JSONObject days = new JSONObject();
-    Cursor cursor = storage.getRawEventsSince(since);
-    try {
-      if (!cursor.moveToFirst()) {
-        return days;
-      }
-
-      // A classic walking partition.
-      // Columns are "date", "env", "field", "value".
-      // Note that we care about the type (integer, string) and kind
-      // (last/counter, discrete) of each field.
-      // Each field will be accessed once for each date/env pair, so
-      // Field memoizes these facts.
-      // We also care about which measurement contains each field.
-      int lastDate  = -1;
-      int lastEnv   = -1;
-      JSONObject dateObject = null;
-      JSONObject envObject = null;
-
-      while (!cursor.isAfterLast()) {
-        int cEnv = cursor.getInt(1);
-        if (cEnv == -1 ||
-            (cEnv != lastEnv &&
-             envs.indexOfKey(cEnv) < 0)) {
-          Logger.warn(LOG_TAG, "Invalid environment " + cEnv + " in cursor. Skipping.");
-          cursor.moveToNext();
-          continue;
-        }
-
-        int cDate  = cursor.getInt(0);
-        int cField = cursor.getInt(2);
-
-        Logger.trace(LOG_TAG, "Event row: " + cDate + ", " + cEnv + ", " + cField);
-        boolean dateChanged = cDate != lastDate;
-        boolean envChanged = cEnv != lastEnv;
-
-        if (dateChanged) {
-          if (dateObject != null) {
-            days.put(dateFormatter.getDateStringForDay(lastDate), dateObject);
-          }
-          dateObject = new JSONObject();
-          lastDate = cDate;
-        }
-
-        if (dateChanged || envChanged) {
-          envObject = new JSONObject();
-          // This is safe because we checked above that cEnv is valid.
-          dateObject.put(envs.get(cEnv).getHash(), envObject);
-          lastEnv = cEnv;
-        }
-
-        final Field field = fields.get(cField);
-        JSONObject measurement = envObject.optJSONObject(field.measurementName);
-        if (measurement == null) {
-          // We will never have more than one measurement version within a
-          // single environment -- to do so involves changing the build ID. And
-          // even if we did, we have no way to represent it. So just build the
-          // output object once.
-          measurement = new JSONObject();
-          measurement.put("_v", field.measurementVersion);
-          envObject.put(field.measurementName, measurement);
-        }
-
-        // How we record depends on the type of the field, so we
-        // break this out into a separate method for clarity.
-        recordMeasurementFromCursor(field, measurement, cursor);
-
-        cursor.moveToNext();
-        continue;
-      }
-      days.put(dateFormatter.getDateStringForDay(lastDate), dateObject);
-    } finally {
-      cursor.close();
-    }
-    return days;
-  }
-
-  /**
-   * Return the {@link JSONObject} parsed from the provided index of the given
-   * cursor, or {@link JSONObject#NULL} if either SQL <code>NULL</code> or
-   * string <code>"null"</code> is present at that index.
-   */
-  private static Object getJSONAtIndex(Cursor cursor, int index) throws JSONException {
-    if (cursor.isNull(index)) {
-      return JSONObject.NULL;
-    }
-    final String value = cursor.getString(index);
-    if ("null".equals(value)) {
-      return JSONObject.NULL;
-    }
-    return new JSONObject(value);
-  }
-
-  protected static void recordMeasurementFromCursor(final Field field,
-                                             JSONObject measurement,
-                                             Cursor cursor)
-                                                           throws JSONException {
-    if (field.isDiscreteField()) {
-      // Discrete counted. Increment the named counter.
-      if (field.isCountedField()) {
-        if (!field.isStringField()) {
-          throw new IllegalStateException("Unable to handle non-string counted types.");
-        }
-        HealthReportUtils.count(measurement, field.fieldName, cursor.getString(3));
-        return;
-      }
-
-      // Discrete string or integer. Append it.
-      if (field.isStringField()) {
-        HealthReportUtils.append(measurement, field.fieldName, cursor.getString(3));
-        return;
-      }
-      if (field.isJSONField()) {
-        HealthReportUtils.append(measurement, field.fieldName, getJSONAtIndex(cursor, 3));
-        return;
-      }
-      if (field.isIntegerField()) {
-        HealthReportUtils.append(measurement, field.fieldName, cursor.getLong(3));
-        return;
-      }
-      throw new IllegalStateException("Unknown field type: " + field.flags);
-    }
-
-    // Non-discrete -- must be LAST or COUNTER, so just accumulate the value.
-    if (field.isStringField()) {
-      measurement.put(field.fieldName, cursor.getString(3));
-      return;
-    }
-    if (field.isJSONField()) {
-      measurement.put(field.fieldName, getJSONAtIndex(cursor, 3));
-      return;
-    }
-    measurement.put(field.fieldName, cursor.getLong(3));
-  }
-
-  public static JSONObject getEnvironmentsJSON(Environment currentEnvironment,
-                                               SparseArray<Environment> envs) throws JSONException {
-    JSONObject environments = new JSONObject();
-
-    // Always do this, even if it hasn't recorded anything in the DB.
-    environments.put("current", jsonify(currentEnvironment, null));
-
-    String currentHash = currentEnvironment.getHash();
-    for (int i = 0; i < envs.size(); i++) {
-      Environment e = envs.valueAt(i);
-      if (currentHash.equals(e.getHash())) {
-        continue;
-      }
-      environments.put(e.getHash(), jsonify(e, currentEnvironment));
-    }
-    return environments;
-  }
-
-  public static JSONObject jsonify(Environment e, Environment current) throws JSONException {
-    JSONObject age = getProfileAge(e, current);
-    JSONObject sysinfo = getSysInfo(e, current);
-    JSONObject gecko = getGeckoInfo(e, current);
-    JSONObject appinfo = getAppInfo(e, current);
-    JSONObject counts = getAddonCounts(e, current);
-    JSONObject config = getDeviceConfig(e, current);
-
-    JSONObject out = new JSONObject();
-    if (age != null)
-      out.put("org.mozilla.profile.age", age);
-    if (sysinfo != null)
-      out.put("org.mozilla.sysinfo.sysinfo", sysinfo);
-    if (gecko != null)
-      out.put("geckoAppInfo", gecko);
-    if (appinfo != null)
-      out.put("org.mozilla.appInfo.appinfo", appinfo);
-    if (counts != null)
-      out.put("org.mozilla.addons.counts", counts);
-
-    JSONObject active = getActiveAddons(e, current);
-    if (active != null)
-      out.put("org.mozilla.addons.active", active);
-
-    if (config != null)
-      out.put("org.mozilla.device.config", config);
-
-    if (current == null) {
-      out.put("hash", e.getHash());
-    }
-    return out;
-  }
-
-  // v3 environment fields.
-  private static JSONObject getDeviceConfig(Environment e, Environment current) throws JSONException {
-    JSONObject config = new JSONObject();
-    int changes = 0;
-    if (e.version < 3) {
-      return null;
-    }
-
-    if (current != null && current.version < 3) {
-      return getDeviceConfig(e, null);
-    }
-
-    if (current == null || current.hasHardwareKeyboard != e.hasHardwareKeyboard) {
-      config.put("hasHardwareKeyboard", e.hasHardwareKeyboard);
-      changes++;
-    }
-
-    if (current == null || current.screenLayout != e.screenLayout) {
-      config.put("screenLayout", e.screenLayout);
-      changes++;
-    }
-
-    if (current == null || current.screenXInMM != e.screenXInMM) {
-      config.put("screenXInMM", e.screenXInMM);
-      changes++;
-    }
-
-    if (current == null || current.screenYInMM != e.screenYInMM) {
-      config.put("screenYInMM", e.screenYInMM);
-      changes++;
-    }
-
-    if (current == null || current.uiType != e.uiType) {
-      config.put("uiType", e.uiType.toString());
-      changes++;
-    }
-
-    if (current == null || current.uiMode != e.uiMode) {
-      config.put("uiMode", e.uiMode);
-      changes++;
-    }
-
-    if (current != null && changes == 0) {
-      return null;
-    }
-
-    config.put("_v", 1);
-    return config;
-  }
-
-  private static JSONObject getProfileAge(Environment e, Environment current) throws JSONException {
-    JSONObject age = new JSONObject();
-    int changes = 0;
-    if (current == null || current.profileCreation != e.profileCreation) {
-      age.put("profileCreation", e.profileCreation);
-      changes++;
-    }
-    if (current != null && changes == 0) {
-      return null;
-    }
-    age.put("_v", 1);
-    return age;
-  }
-
-  private static JSONObject getSysInfo(Environment e, Environment current) throws JSONException {
-    JSONObject sysinfo = new JSONObject();
-    int changes = 0;
-    if (current == null || current.cpuCount != e.cpuCount) {
-      sysinfo.put("cpuCount", e.cpuCount);
-      changes++;
-    }
-    if (current == null || current.memoryMB != e.memoryMB) {
-      sysinfo.put("memoryMB", e.memoryMB);
-      changes++;
-    }
-    if (current == null || !current.architecture.equals(e.architecture)) {
-      sysinfo.put("architecture", e.architecture);
-      changes++;
-    }
-    if (current == null || !current.sysName.equals(e.sysName)) {
-      sysinfo.put("name", e.sysName);
-      changes++;
-    }
-    if (current == null || !current.sysVersion.equals(e.sysVersion)) {
-      sysinfo.put("version", e.sysVersion);
-      changes++;
-    }
-    if (current != null && changes == 0) {
-      return null;
-    }
-    sysinfo.put("_v", 1);
-    return sysinfo;
-  }
-
-  private static JSONObject getGeckoInfo(Environment e, Environment current) throws JSONException {
-    JSONObject gecko = new JSONObject();
-    int changes = 0;
-    if (current == null || !current.vendor.equals(e.vendor)) {
-      gecko.put("vendor", e.vendor);
-      changes++;
-    }
-    if (current == null || !current.appName.equals(e.appName)) {
-      gecko.put("name", e.appName);
-      changes++;
-    }
-    if (current == null || !current.appID.equals(e.appID)) {
-      gecko.put("id", e.appID);
-      changes++;
-    }
-    if (current == null || !current.appVersion.equals(e.appVersion)) {
-      gecko.put("version", e.appVersion);
-      changes++;
-    }
-    if (current == null || !current.appBuildID.equals(e.appBuildID)) {
-      gecko.put("appBuildID", e.appBuildID);
-      changes++;
-    }
-    if (current == null || !current.platformVersion.equals(e.platformVersion)) {
-      gecko.put("platformVersion", e.platformVersion);
-      changes++;
-    }
-    if (current == null || !current.platformBuildID.equals(e.platformBuildID)) {
-      gecko.put("platformBuildID", e.platformBuildID);
-      changes++;
-    }
-    if (current == null || !current.os.equals(e.os)) {
-      gecko.put("os", e.os);
-      changes++;
-    }
-    if (current == null || !current.xpcomabi.equals(e.xpcomabi)) {
-      gecko.put("xpcomabi", e.xpcomabi);
-      changes++;
-    }
-    if (current == null || !current.updateChannel.equals(e.updateChannel)) {
-      gecko.put("updateChannel", e.updateChannel);
-      changes++;
-    }
-    if (current != null && changes == 0) {
-      return null;
-    }
-    gecko.put("_v", 1);
-    return gecko;
-  }
-
-  // Null-safe string comparison.
-  private static boolean stringsDiffer(final String a, final String b) {
-    if (a == null) {
-      return b != null;
-    }
-    return !a.equals(b);
-  }
-
-  private static JSONObject getAppInfo(Environment e, Environment current) throws JSONException {
-    JSONObject appinfo = new JSONObject();
-
-    Logger.debug(LOG_TAG, "Generating appinfo for v" + e.version + " env " + e.hash);
-
-    // Is the environment in question newer than the diff target, or is
-    // there no diff target?
-    final boolean outdated = current == null ||
-                             e.version > current.version;
-
-    // Is the environment in question a different version (lower or higher),
-    // or is there no diff target?
-    final boolean differ = outdated || current.version > e.version;
-
-    // Always produce an output object if there's a version mismatch or this
-    // isn't a diff. Otherwise, track as we go if there's any difference.
-    boolean changed = differ;
-
-    switch (e.version) {
-    // There's a straightforward correspondence between environment versions
-    // and appinfo versions.
-    case 3:
-    case 2:
-      appinfo.put("_v", 3);
-      break;
-    case 1:
-      appinfo.put("_v", 2);
-      break;
-    default:
-      Logger.warn(LOG_TAG, "Unknown environment version: " + e.version);
-      return appinfo;
-    }
-
-    switch (e.version) {
-    case 3:
-    case 2:
-      if (populateAppInfoV2(appinfo, e, current, outdated)) {
-        changed = true;
-      }
-      // Fall through.
-
-    case 1:
-      // There is no older version than v1, so don't check outdated.
-      if (populateAppInfoV1(e, current, appinfo)) {
-        changed = true;
-      }
-    }
-
-    if (!changed) {
-      return null;
-    }
-
-    return appinfo;
-  }
-
-  private static boolean populateAppInfoV1(Environment e,
-                                           Environment current,
-                                           JSONObject appinfo)
-    throws JSONException {
-    boolean changes = false;
-    if (current == null || current.isBlocklistEnabled != e.isBlocklistEnabled) {
-      appinfo.put("isBlocklistEnabled", e.isBlocklistEnabled);
-      changes = true;
-    }
-
-    if (current == null || current.isTelemetryEnabled != e.isTelemetryEnabled) {
-      appinfo.put("isTelemetryEnabled", e.isTelemetryEnabled);
-      changes = true;
-    }
-
-    return changes;
-  }
-
-  private static boolean populateAppInfoV2(JSONObject appinfo,
-                                           Environment e,
-                                           Environment current,
-                                           final boolean outdated)
-    throws JSONException {
-    boolean changes = false;
-    if (outdated ||
-        stringsDiffer(current.osLocale, e.osLocale)) {
-      appinfo.put("osLocale", e.osLocale);
-      changes = true;
-    }
-
-    if (outdated ||
-        stringsDiffer(current.appLocale, e.appLocale)) {
-      appinfo.put("appLocale", e.appLocale);
-      changes = true;
-    }
-
-    if (outdated ||
-        stringsDiffer(current.distribution, e.distribution)) {
-      appinfo.put("distribution", e.distribution);
-      changes = true;
-    }
-
-    if (outdated ||
-        current.acceptLangSet != e.acceptLangSet) {
-      appinfo.put("acceptLangIsUserSet", e.acceptLangSet);
-      changes = true;
-    }
-    return changes;
-  }
-
-  private static JSONObject getAddonCounts(Environment e, Environment current) throws JSONException {
-    JSONObject counts = new JSONObject();
-    int changes = 0;
-    if (current == null || current.extensionCount != e.extensionCount) {
-      counts.put("extension", e.extensionCount);
-      changes++;
-    }
-    if (current == null || current.pluginCount != e.pluginCount) {
-      counts.put("plugin", e.pluginCount);
-      changes++;
-    }
-    if (current == null || current.themeCount != e.themeCount) {
-      counts.put("theme", e.themeCount);
-      changes++;
-    }
-    if (current != null && changes == 0) {
-      return null;
-    }
-    counts.put("_v", 1);
-    return counts;
-  }
-
-  /**
-   * Compute the *tree* difference set between the two objects. If the two
-   * objects are identical, returns <code>null</code>. If <code>from</code> is
-   * <code>null</code>, returns <code>to</code>. If <code>to</code> is
-   * <code>null</code>, behaves as if <code>to</code> were an empty object.
-   *
-   * (Note that this method does not check for {@link JSONObject#NULL}, because
-   * by definition it can't be provided as input to this method.)
-   *
-   * This behavior is intended to simplify life for callers: a missing object
-   * can be viewed as (and behaves as) an empty map, to a useful extent, rather
-   * than throwing an exception.
-   *
-   * @param from
-   *          a JSONObject.
-   * @param to
-   *          a JSONObject.
-   * @param includeNull
-   *          if true, keys present in <code>from</code> but not in
-   *          <code>to</code> are included as {@link JSONObject#NULL} in the
-   *          output.
-   *
-   * @return a JSONObject, or null if the two objects are identical.
-   * @throws JSONException
-   *           should not occur, but...
-   */
-  public static JSONObject diff(JSONObject from,
-                                JSONObject to,
-                                boolean includeNull) throws JSONException {
-    if (from == null) {
-      return to;
-    }
-
-    if (to == null) {
-      return diff(from, new JSONObject(), includeNull);
-    }
-
-    JSONObject out = new JSONObject();
-
-    HashSet<String> toKeys = includeNull ? new HashSet<String>(to.length())
-                                         : null;
-
-    @SuppressWarnings("unchecked")
-    Iterator<String> it = to.keys();
-    while (it.hasNext()) {
-      String key = it.next();
-
-      // Track these as we go if we'll need them later.
-      if (includeNull) {
-        toKeys.add(key);
-      }
-
-      Object value = to.get(key);
-      if (!from.has(key)) {
-        // It must be new.
-        out.put(key, value);
-        continue;
-      }
-
-      // Not new? Then see if it changed.
-      Object old = from.get(key);
-
-      // Two JSONObjects should be diffed.
-      if (old instanceof JSONObject && value instanceof JSONObject) {
-        JSONObject innerDiff = diff(((JSONObject) old), ((JSONObject) value),
-                                    includeNull);
-        // No change? No output.
-        if (innerDiff == null) {
-          continue;
-        }
-
-        // Otherwise include the diff.
-        out.put(key, innerDiff);
-        continue;
-      }
-
-      // A regular value, or a type change. Only skip if they're the same.
-      if (value.equals(old)) {
-        continue;
-      }
-      out.put(key, value);
-    }
-
-    // Now -- if requested -- include any removed keys.
-    if (includeNull) {
-      Set<String> fromKeys = HealthReportUtils.keySet(from);
-      fromKeys.removeAll(toKeys);
-      for (String notPresent : fromKeys) {
-        out.put(notPresent, JSONObject.NULL);
-      }
-    }
-
-    if (out.length() == 0) {
-      return null;
-    }
-    return out;
-  }
-
-  private static JSONObject getActiveAddons(Environment e, Environment current) throws JSONException {
-    // Just return the current add-on set, with a version annotation.
-    // To do so requires copying.
-    if (current == null) {
-      JSONObject out = e.getNonIgnoredAddons();
-      if (out == null) {
-        Logger.warn(LOG_TAG, "Null add-ons to return in FHR document. Returning {}.");
-        out = new JSONObject();        // So that we always return something.
-      }
-      out.put("_v", 1);
-      return out;
-    }
-
-    // Otherwise, return the diff.
-    JSONObject diff = diff(current.getNonIgnoredAddons(), e.getNonIgnoredAddons(), true);
-    if (diff == null) {
-      return null;
-    }
-    if (diff == e.addons) {
-      // Again, needs to copy.
-      return getActiveAddons(e, null);
-    }
-
-    diff.put("_v", 1);
-    return diff;
-  }
-}
deleted file mode 100644
--- a/mobile/android/base/background/healthreport/HealthReportProvider.java
+++ /dev/null
@@ -1,301 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-package org.mozilla.gecko.background.healthreport;
-
-import java.io.File;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Map.Entry;
-
-import org.mozilla.gecko.background.healthreport.HealthReportDatabaseStorage.DatabaseEnvironment;
-import org.mozilla.gecko.background.healthreport.HealthReportStorage.Field;
-import org.mozilla.gecko.background.healthreport.HealthReportStorage.MeasurementFields;
-import org.mozilla.gecko.background.healthreport.HealthReportStorage.MeasurementFields.FieldSpec;
-
-import android.content.ContentProvider;
-import android.content.ContentUris;
-import android.content.ContentValues;
-import android.content.UriMatcher;
-import android.database.Cursor;
-import android.net.Uri;
-
-/**
- * This is a {@link ContentProvider} wrapper around a database-backed Health
- * Report storage layer.
- *
- * It stores environments, fields, and measurements, and events which refer to
- * each of these by integer ID.
- *
- * Insert = daily discrete.
- * content://org.mozilla.gecko.health/events/env/measurement/v/field
- *
- * Update = daily last or daily counter
- * content://org.mozilla.gecko.health/events/env/measurement/v/field/counter
- * content://org.mozilla.gecko.health/events/env/measurement/v/field/last
- *
- * Delete = drop today's row
- * content://org.mozilla.gecko.health/events/env/measurement/v/field/
- *
- * Query, of course: content://org.mozilla.gecko.health/events/?since
- *
- * Each operation accepts an optional `time` query parameter, formatted as
- * milliseconds since epoch. If omitted, it defaults to the current time.
- *
- * Each operation also accepts mandatory `profilePath` and `env` arguments.
- *
- * TODO: document measurements.
- */
-public class HealthReportProvider extends ContentProvider {
-  private HealthReportDatabases databases;
-  private static final UriMatcher uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
-
-  public static final String HEALTH_AUTHORITY = HealthReportConstants.HEALTH_AUTHORITY;
-
-  // URI matches.
-  private static final int ENVIRONMENTS_ROOT    = 10;
-  private static final int EVENTS_ROOT          = 11;
-  private static final int EVENTS_RAW_ROOT      = 12;
-  private static final int FIELDS_ROOT          = 13;
-  private static final int MEASUREMENTS_ROOT    = 14;
-
-  private static final int EVENTS_FIELD_GENERIC = 20;
-  private static final int EVENTS_FIELD_COUNTER = 21;
-  private static final int EVENTS_FIELD_LAST    = 22;
-
-  private static final int ENVIRONMENT_DETAILS  = 30;
-  private static final int FIELDS_MEASUREMENT   = 31;
-
-  static {
-    uriMatcher.addURI(HEALTH_AUTHORITY, "environments/", ENVIRONMENTS_ROOT);
-    uriMatcher.addURI(HEALTH_AUTHORITY, "events/", EVENTS_ROOT);
-    uriMatcher.addURI(HEALTH_AUTHORITY, "rawevents/", EVENTS_RAW_ROOT);
-    uriMatcher.addURI(HEALTH_AUTHORITY, "fields/", FIELDS_ROOT);
-    uriMatcher.addURI(HEALTH_AUTHORITY, "measurements/", MEASUREMENTS_ROOT);
-
-    uriMatcher.addURI(HEALTH_AUTHORITY, "events/#/*/#/*", EVENTS_FIELD_GENERIC);
-    uriMatcher.addURI(HEALTH_AUTHORITY, "events/#/*/#/*/counter", EVENTS_FIELD_COUNTER);
-    uriMatcher.addURI(HEALTH_AUTHORITY, "events/#/*/#/*/last", EVENTS_FIELD_LAST);
-
-    uriMatcher.addURI(HEALTH_AUTHORITY, "environments/#", ENVIRONMENT_DETAILS);
-    uriMatcher.addURI(HEALTH_AUTHORITY, "fields/*/#", FIELDS_MEASUREMENT);
-  }
-
-  /**
-   * So we can bypass the ContentProvider layer.
-   */
-  public HealthReportDatabaseStorage getProfileStorage(final String profilePath) {
-    if (profilePath == null) {
-      throw new IllegalArgumentException("profilePath must be provided.");
-    }
-    return databases.getDatabaseHelperForProfile(new File(profilePath));
-  }
-
-  private HealthReportDatabaseStorage getProfileStorageForUri(Uri uri) {
-    final String profilePath = uri.getQueryParameter("profilePath");
-    return getProfileStorage(profilePath);
-  }
-
-  @Override
-  public void onLowMemory() {
-    // While we could prune the database here, it wouldn't help - it would restore disk space
-    // rather then lower our RAM usage. Additionally, pruning the database may use even more
-    // memory and take too long to run in this method.
-    super.onLowMemory();
-    databases.closeDatabaseHelpers();
-  }
-
-  @Override
-  public String getType(Uri uri) {
-    return null;
-  }
-
-  @Override
-  public boolean onCreate() {
-    databases = new HealthReportDatabases(getContext());
-    return true;
-  }
-
-  @Override
-  public Uri insert(Uri uri, ContentValues values) {
-    int match = uriMatcher.match(uri);
-    HealthReportDatabaseStorage storage = getProfileStorageForUri(uri);
-    switch (match) {
-    case FIELDS_MEASUREMENT:
-      // The keys of this ContentValues are field names.
-      List<String> pathSegments = uri.getPathSegments();
-      String measurement = pathSegments.get(1);
-      int v = Integer.parseInt(pathSegments.get(2));
-      storage.ensureMeasurementInitialized(measurement, v, getFieldSpecs(values));
-      return uri;
-
-    case ENVIRONMENTS_ROOT:
-      DatabaseEnvironment environment = storage.getEnvironment();
-      environment.init(values);
-      return ContentUris.withAppendedId(uri, environment.register());
-
-    case EVENTS_FIELD_GENERIC:
-      long time = getTimeFromUri(uri);
-      int day = storage.getDay(time);
-      int env = getEnvironmentFromUri(uri);
-      Field field = getFieldFromUri(storage, uri);
-
-      if (!values.containsKey("value")) {
-        throw new IllegalArgumentException("Must provide ContentValues including 'value' key.");
-      }
-
-      Object object = values.get("value");
-      if (object instanceof Integer ||
-          object instanceof Long) {
-        storage.recordDailyDiscrete(env, day, field.getID(), ((Integer) object).intValue());
-      } else if (object instanceof String) {
-        storage.recordDailyDiscrete(env, day, field.getID(), (String) object);
-      } else {
-        storage.recordDailyDiscrete(env, day, field.getID(), object.toString());
-      }
-
-      // TODO: eventually we might want to return something more useful than
-      // the input URI.
-      return uri;
-    default:
-      throw new IllegalArgumentException("Unknown insert URI");
-    }
-  }
-
-  @Override
-  public int update(Uri uri, ContentValues values, String selection,
-                    String[] selectionArgs) {
-
-    int match = uriMatcher.match(uri);
-    if (match != EVENTS_FIELD_COUNTER &&
-        match != EVENTS_FIELD_LAST) {
-      throw new IllegalArgumentException("Must provide operation for update.");
-    }
-
-    HealthReportStorage storage = getProfileStorageForUri(uri);
-    long time = getTimeFromUri(uri);
-    int day = storage.getDay(time);
-    int env = getEnvironmentFromUri(uri);
-    Field field = getFieldFromUri(storage, uri);
-
-    switch (match) {
-    case EVENTS_FIELD_COUNTER:
-      int by = values.containsKey("value") ? values.getAsInteger("value") : 1;
-      storage.incrementDailyCount(env, day, field.getID(), by);
-      return 1;
-
-    case EVENTS_FIELD_LAST:
-      Object object = values.get("value");
-      if (object instanceof Integer ||
-          object instanceof Long) {
-        storage.recordDailyLast(env, day, field.getID(), (Integer) object);
-      } else if (object instanceof String) {
-        storage.recordDailyLast(env, day, field.getID(), (String) object);
-      } else {
-        storage.recordDailyLast(env, day, field.getID(), object.toString());
-      }
-      return 1;
-
-    default:
-        // javac's flow control analysis sucks.
-        return 0;
-    }
-  }
-
-  @Override
-  public int delete(Uri uri, String selection, String[] selectionArgs) {
-    int match = uriMatcher.match(uri);
-    HealthReportStorage storage = getProfileStorageForUri(uri);
-    switch (match) {
-    case MEASUREMENTS_ROOT:
-      storage.deleteMeasurements();
-      return 1;
-    case ENVIRONMENTS_ROOT:
-      storage.deleteEnvironments();
-      return 1;
-    default:
-      throw new IllegalArgumentException();
-    }
-
-    // TODO: more
-  }
-
-  @Override
-  public Cursor query(Uri uri, String[] projection, String selection,
-                      String[] selectionArgs, String sortOrder) {
-    int match = uriMatcher.match(uri);
-
-    HealthReportStorage storage = getProfileStorageForUri(uri);
-    switch (match) {
-    case EVENTS_ROOT:
-      return storage.getEventsSince(getTimeFromUri(uri));
-    case EVENTS_RAW_ROOT:
-      return storage.getRawEventsSince(getTimeFromUri(uri));
-    case MEASUREMENTS_ROOT:
-      return storage.getMeasurementVersions();
-    case FIELDS_ROOT:
-      return storage.getFieldVersions();
-    }
-    List<String> pathSegments = uri.getPathSegments();
-    switch (match) {
-    case ENVIRONMENT_DETAILS:
-      return storage.getEnvironmentRecordForID(Integer.parseInt(pathSegments.get(1), 10));
-    case FIELDS_MEASUREMENT:
-      String measurement = pathSegments.get(1);
-      int v = Integer.parseInt(pathSegments.get(2));
-      return storage.getFieldVersions(measurement, v);
-    default:
-    return null;
-    }
-  }
-
-  private static long getTimeFromUri(final Uri uri) {
-    String t = uri.getQueryParameter("time");
-    if (t == null) {
-      return System.currentTimeMillis();
-    } else {
-      return Long.parseLong(t, 10);
-    }
-  }
-
-  private static int getEnvironmentFromUri(final Uri uri) {
-    return Integer.parseInt(uri.getPathSegments().get(1), 10);
-  }
-
-  /**
-   * Assumes a URI structured like:
-   *
-   * <code>content://org.mozilla.gecko.health/events/env/measurement/v/field</code>
-   *
-   * @param uri a URI formatted as expected.
-   * @return a {@link Field} instance.
-   */
-  private static Field getFieldFromUri(HealthReportStorage storage, final Uri uri) {
-    String measurement;
-    String field;
-    int measurementVersion;
-
-    List<String> pathSegments = uri.getPathSegments();
-    measurement = pathSegments.get(2);
-    measurementVersion = Integer.parseInt(pathSegments.get(3), 10);
-    field = pathSegments.get(4);
-
-    return storage.getField(measurement, measurementVersion, field);
-  }
-
-  private MeasurementFields getFieldSpecs(ContentValues values) {
-    final ArrayList<FieldSpec> specs = new ArrayList<FieldSpec>(values.size());
-    for (Entry<String, Object> entry : values.valueSet()) {
-      specs.add(new FieldSpec(entry.getKey(), (Integer) entry.getValue()));
-    }
-
-    return new MeasurementFields() {
-      @Override
-      public Iterable<FieldSpec> getFields() {
-        return specs;
-      }
-    };
-  }
-
-}
deleted file mode 100644
--- a/mobile/android/base/background/healthreport/HealthReportStorage.java
+++ /dev/null
@@ -1,238 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-package org.mozilla.gecko.background.healthreport;
-
-import org.json.JSONObject;
-
-import android.database.Cursor;
-import android.util.SparseArray;
-
-/**
- * Abstraction over storage for Firefox Health Report on Android.
- */
-public interface HealthReportStorage {
-  // Right now we only care about the name of the field.
-  public interface MeasurementFields {
-    public class FieldSpec {
-      public final String name;
-      public final int type;
-      public FieldSpec(String name, int type) {
-        this.name = name;
-        this.type = type;
-      }
-    }
-    Iterable<FieldSpec> getFields();
-  }
-
-  public abstract class Field {
-    protected static final int UNKNOWN_TYPE_OR_FIELD_ID = -1;
-
-    protected static final int FLAG_INTEGER  = 1 << 0;
-    protected static final int FLAG_STRING   = 1 << 1;
-    protected static final int FLAG_JSON     = 1 << 2;
-
-    protected static final int FLAG_DISCRETE = 1 << 8;
-    protected static final int FLAG_LAST     = 1 << 9;
-    protected static final int FLAG_COUNTER  = 1 << 10;
-
-    protected static final int FLAG_COUNTED  = 1 << 14;
-
-    public static final int TYPE_INTEGER_DISCRETE = FLAG_INTEGER | FLAG_DISCRETE;
-    public static final int TYPE_INTEGER_LAST     = FLAG_INTEGER | FLAG_LAST;
-    public static final int TYPE_INTEGER_COUNTER  = FLAG_INTEGER | FLAG_COUNTER;
-
-    public static final int TYPE_STRING_DISCRETE  = FLAG_STRING | FLAG_DISCRETE;
-    public static final int TYPE_STRING_LAST      = FLAG_STRING | FLAG_LAST;
-
-    public static final int TYPE_JSON_DISCRETE    = FLAG_JSON | FLAG_DISCRETE;
-    public static final int TYPE_JSON_LAST        = FLAG_JSON | FLAG_LAST;
-
-    public static final int TYPE_COUNTED_STRING_DISCRETE = FLAG_COUNTED | TYPE_STRING_DISCRETE;
-
-    protected int fieldID = UNKNOWN_TYPE_OR_FIELD_ID;
-    protected int flags;
-
-    protected final String measurementName;
-    protected final String measurementVersion;
-    protected final String fieldName;
-
-    public Field(String mName, int mVersion, String fieldName, int type) {
-      this.measurementName = mName;
-      this.measurementVersion = Integer.toString(mVersion, 10);
-      this.fieldName = fieldName;
-      this.flags = type;
-    }
-
-    /**
-     * @return the ID for this <code>Field</code>
-     * @throws IllegalStateException if this field is not found in storage
-     */
-    public abstract int getID() throws IllegalStateException;
-
-    public boolean isIntegerField() {
-      return (this.flags & FLAG_INTEGER) > 0;
-    }
-
-    public boolean isStringField() {
-      return (this.flags & FLAG_STRING) > 0;
-    }
-
-    public boolean isJSONField() {
-      return (this.flags & FLAG_JSON) > 0;
-    }
-
-    public boolean isStoredAsString() {
-      return (this.flags & (FLAG_JSON | FLAG_STRING)) > 0;
-    }
-
-    public boolean isDiscreteField() {
-      return (this.flags & FLAG_DISCRETE) > 0;
-    }
-
-    /**
-     * True if the accrued values are intended to be bucket-counted. For strings,
-     * each discrete value will name a bucket, with the number of instances per
-     * day being the value in the bucket.
-     */
-    public boolean isCountedField() {
-      return (this.flags & FLAG_COUNTED) > 0;
-    }
-  }
-
-  /**
-   * Close open storage handles and otherwise finish up.
-   */
-  public void close();
-
-  /**
-   * Return the day integer corresponding to the provided time.
-   *
-   * @param time
-   *          milliseconds since Unix epoch.
-   * @return an integer day.
-   */
-  public int getDay(long time);
-
-  /**
-   * Return the day integer corresponding to the current time.
-   *
-   * @return an integer day.
-   */
-  public int getDay();
-
-  /**
-   * Return a new {@link Environment}, suitable for being populated, hashed, and
-   * registered.
-   *
-   * @return a new {@link Environment} instance.
-   */
-  public Environment getEnvironment();
-
-  /**
-   * @return a mapping from environment IDs to hashes, suitable for use in
-   *         payload generation.
-   */
-  public SparseArray<String> getEnvironmentHashesByID();
-
-  /**
-   * @return a mapping from environment IDs to registered {@link Environment}
-   *         records, suitable for use in payload generation.
-   */
-  public SparseArray<Environment> getEnvironmentRecordsByID();
-
-  /**
-   * @param id
-   *          the environment ID, as returned by {@link Environment#register()}.
-   * @return a cursor for the record.
-   */
-  public Cursor getEnvironmentRecordForID(int id);
-
-  /**
-   * @param measurement
-   *          the name of a measurement, such as "org.mozilla.appInfo.appInfo".
-   * @param measurementVersion
-   *          the version of a measurement, such as '3'.
-   * @param fieldName
-   *          the name of a field, such as "platformVersion".
-   *
-   * @return a {@link Field} instance corresponding to the provided values.
-   */
-  public Field getField(String measurement, int measurementVersion,
-                        String fieldName);
-
-  /**
-   * @return a mapping from field IDs to {@link Field} instances, suitable for
-   *         use in payload generation.
-   */
-  public SparseArray<Field> getFieldsByID();
-
-  public void recordDailyLast(int env, int day, int field, JSONObject value);
-  public void recordDailyLast(int env, int day, int field, String value);
-  public void recordDailyLast(int env, int day, int field, int value);
-  public void recordDailyDiscrete(int env, int day, int field, JSONObject value);
-  public void recordDailyDiscrete(int env, int day, int field, String value);
-  public void recordDailyDiscrete(int env, int day, int field, int value);
-  public void incrementDailyCount(int env, int day, int field, int by);
-  public void incrementDailyCount(int env, int day, int field);
-
-  /**
-   * Return true if events exist that were recorded on or after <code>time</code>.
-   */
-  boolean hasEventSince(long time);
-
-  /**
-   * Obtain a cursor over events that were recorded since <code>time</code>.
-   * This cursor exposes 'raw' events, with integer identifiers for values.
-   */
-  public Cursor getRawEventsSince(long time);
-
-  /**
-   * Obtain a cursor over events that were recorded since <code>time</code>.
-   *
-   * This cursor exposes 'friendly' events, with string names and full
-   * measurement metadata.
-   */
-  public Cursor getEventsSince(long time);
-
-  /**
-   * Ensure that a measurement and all of its fields are registered with the DB.
-   * No fields will be processed if the measurement exists with the specified
-   * version.
-   *
-   * @param measurement
-   *          a measurement name, such as "org.mozilla.appInfo.appInfo".
-   * @param version
-   *          a version number, such as '3'.
-   * @param fields
-   *          a {@link MeasurementFields} instance, consisting of a collection
-   *          of field names.
-   */
-  public void ensureMeasurementInitialized(String measurement,
-                                           int version,
-                                           MeasurementFields fields);
-  public Cursor getMeasurementVersions();
-  public Cursor getFieldVersions();
-  public Cursor getFieldVersions(String measurement, int measurementVersion);
-
-  public void deleteEverything();
-  public void deleteEnvironments();
-  public void deleteMeasurements();
-  /**
-   * Deletes all environments, addons, and events from the database before the given time.
-   *
-   * @param time milliseconds since epoch.
-   * @param curEnv The ID of the current environment.
-   * @return The number of environments and addon entries deleted.
-   */
-  public int deleteDataBefore(final long time, final int curEnv);
-
-  public int getEventCount();
-  public int getEnvironmentCount();
-
-  public void pruneEvents(final int num);
-  public void pruneEnvironments(final int num);
-
-  public void enqueueOperation(Runnable runnable);
-}
deleted file mode 100644
--- a/mobile/android/base/background/healthreport/HealthReportUtils.java
+++ /dev/null
@@ -1,136 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-package org.mozilla.gecko.background.healthreport;
-
-import java.util.HashSet;
-import java.util.Iterator;
-import java.util.Set;
-import java.util.SortedSet;
-import java.util.TreeSet;
-import java.util.UUID;
-
-import org.json.JSONArray;
-import org.json.JSONException;
-import org.json.JSONObject;
-import org.mozilla.apache.commons.codec.digest.DigestUtils;
-
-import android.content.ContentUris;
-import android.net.Uri;
-
-public class HealthReportUtils {
-  public static final String LOG_TAG = HealthReportUtils.class.getSimpleName();
-
-  public static String getEnvironmentHash(final String input) {
-    return DigestUtils.shaHex(input);
-  }
-
-  /**
-   * Take an environment URI (one that identifies an environment) and produce an
-   * event URI.
-   *
-   * That this is needed is tragic.
-   *
-   * @param environmentURI
-   *          the {@link Uri} returned by an environment operation.
-   * @return a {@link Uri} to which insertions can be dispatched.
-   */
-  public static Uri getEventURI(Uri environmentURI) {
-    return environmentURI.buildUpon().path("/events/" + ContentUris.parseId(environmentURI) + "/").build();
-  }
-
-  /**
-   * Copy the keys from the provided {@link JSONObject} into the provided {@link Set}.
-   */
-  private static <T extends Set<String>> T intoKeySet(T keys, JSONObject o) {
-    if (o == null || o == JSONObject.NULL) {
-      return keys;
-    }
-
-    @SuppressWarnings("unchecked")
-    Iterator<String> it = o.keys();
-    while (it.hasNext()) {
-      keys.add(it.next());
-    }
-    return keys;
-  }
-
-  /**
-   * Produce a {@link SortedSet} containing the string keys of the provided
-   * object.
-   *
-   * @param o a {@link JSONObject} with string keys.
-   * @return a sorted set.
-   */
-  public static SortedSet<String> sortedKeySet(JSONObject o) {
-    return intoKeySet(new TreeSet<String>(), o);
-  }
-
-  /**
-   * Produce a {@link Set} containing the string keys of the provided object.
-   * @param o a {@link JSONObject} with string keys.
-   * @return an unsorted set.
-   */
-  public static Set<String> keySet(JSONObject o) {
-    return intoKeySet(new HashSet<String>(), o);
-  }
-
-  /**
-   * Just like {@link JSONObject#accumulate(String, Object)}, but doesn't do the wrong thing for single values.
-   * @throws JSONException 
-   */
-  public static void append(JSONObject o, String key, Object value) throws JSONException {
-    if (!o.has(key)) {
-      JSONArray arr = new JSONArray();
-      arr.put(value);
-      o.put(key, arr);
-      return;
-    }
-    Object dest = o.get(key);
-    if (dest instanceof JSONArray) {
-      ((JSONArray) dest).put(value);
-      return;
-    }
-    JSONArray arr = new JSONArray();
-    arr.put(dest);
-    arr.put(value);
-    o.put(key, arr);
-  }
-
-  /**
-   * Accumulate counts for how often each provided value occurs.
-   *
-   * <code>
-   *   HealthReportUtils.count(o, "foo", "bar");
-   * </code>
-   *
-   * will change
-   *
-   * <pre>
-   *   {"foo", {"bar": 1}}
-   * </pre>
-   *
-   * into
-   *
-   * <pre>
-   *   {"foo", {"bar": 2}}
-   * </pre>
-   *
-   */
-  public static void count(JSONObject o, String key,
-                           String value) throws JSONException {
-    if (!o.has(key)) {
-      JSONObject counts = new JSONObject();
-      counts.put(value, 1);
-      o.put(key, counts);
-      return;
-    }
-    JSONObject dest = o.getJSONObject(key);
-    dest.put(value, dest.optInt(value, 0) + 1);
-  }
-
-  public static String generateDocumentId() {
-    return UUID.randomUUID().toString();
-  }
-}
deleted file mode 100644
--- a/mobile/android/base/background/healthreport/ProfileInformationCache.java
+++ /dev/null
@@ -1,386 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-package org.mozilla.gecko.background.healthreport;
-
-import java.io.File;
-import java.io.FileNotFoundException;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.OutputStreamWriter;
-import java.nio.charset.Charset;
-import java.util.Locale;
-import java.util.Scanner;
-
-import org.json.JSONException;
-import org.json.JSONObject;
-import org.mozilla.gecko.background.common.log.Logger;
-import org.mozilla.gecko.background.healthreport.EnvironmentBuilder.ProfileInformationProvider;
-
-/**
- * There are some parts of the FHR environment that can't be readily computed
- * without a running Gecko -- add-ons, for example. In order to make this
- * information available without launching Gecko, we persist it on Fennec
- * startup. This class is the notepad in which we write.
- */
-public class ProfileInformationCache implements ProfileInformationProvider {
-  private static final String LOG_TAG = "GeckoProfileInfo";
-  private static final String CACHE_FILE = "profile_info_cache.json";
-
-  /*
-   * FORMAT_VERSION history:
-   *   -: No version number; implicit v1.
-   *   1: Add versioning (Bug 878670).
-   *   2: Bump to regenerate add-on set after landing Bug 900694 (Bug 901622).
-   *   3: Add distribution, osLocale, appLocale.
-   *   4: Add experiments as add-ons.
-   */
-  public static final int FORMAT_VERSION = 4;
-
-  protected boolean initialized = false;
-  protected boolean needsWrite = false;
-
-  protected final File file;
-
-  private volatile boolean blocklistEnabled = true;
-  private volatile boolean telemetryEnabled = false;
-  private volatile boolean isAcceptLangUserSet = false;
-
-  private volatile long profileCreationTime = 0;
-  private volatile String distribution = "";
-
-  // There are really four kinds of locale in play:
-  //
-  // * The OS
-  // * The Android environment of the app (setDefault)
-  // * The Gecko locale
-  // * The requested content locale (Accept-Language).
-  //
-  // We track only the first two, assuming that the Gecko locale will typically
-  // be the same as the app locale.
-  //
-  // The app locale is fetched from the PIC because it can be modified at
-  // runtime -- it won't necessarily be what Locale.getDefaultLocale() returns
-  // in a fresh non-browser profile.
-  //
-  // We also track the OS locale here for the same reason -- we need to store
-  // the default (OS) value before the locale-switching code takes effect!
-  private volatile String osLocale = "";
-  private volatile String appLocale = "";
-
-  private volatile JSONObject addons = null;
-
-  protected ProfileInformationCache(final File f) {
-    file = f;
-    Logger.pii(LOG_TAG, "Using " + file.getAbsolutePath() + " for profile information cache.");
-  }
-
-  public ProfileInformationCache(final String profilePath) {
-    this(new File(profilePath + File.separator + CACHE_FILE));
-  }
-
-  public synchronized void beginInitialization() {
-    initialized = false;
-    needsWrite = true;
-  }
-
-  public JSONObject toJSON() {
-    JSONObject object = new JSONObject();
-    try {
-      object.put("version", FORMAT_VERSION);
-      object.put("blocklist", blocklistEnabled);
-      object.put("telemetry", telemetryEnabled);
-      object.put("isAcceptLangUserSet", isAcceptLangUserSet);
-      object.put("profileCreated", profileCreationTime);
-      object.put("osLocale", osLocale);
-      object.put("appLocale", appLocale);
-      object.put("distribution", distribution);
-      object.put("addons", addons);
-    } catch (JSONException e) {
-      // There isn't much we can do about this.
-      // Let's just quietly muffle.
-      return null;
-    }
-    return object;
-  }
-
-  /**
-   * Attempt to restore this object from a JSON blob. If there is a version mismatch, there has
-   * likely been an upgrade to the cache format. The cache can be reconstructed without data loss
-   * so rather than migrating, we invalidate the cache by refusing to store the given JSONObject
-   * and returning false.
-   *
-   * @return false if there's a version mismatch or an error, true on success.
-   */
-  private boolean fromJSON(JSONObject object) throws JSONException {
-    if (object == null) {
-      Logger.debug(LOG_TAG, "Can't load restore PIC from null JSON object.");
-      return false;
-    }
-
-    int version = object.optInt("version", 1);
-    switch (version) {
-    case FORMAT_VERSION:
-      blocklistEnabled = object.getBoolean("blocklist");
-      telemetryEnabled = object.getBoolean("telemetry");
-      isAcceptLangUserSet = object.getBoolean("isAcceptLangUserSet");
-      profileCreationTime = object.getLong("profileCreated");
-      addons = object.getJSONObject("addons");
-      distribution = object.getString("distribution");
-      osLocale = object.getString("osLocale");
-      appLocale = object.getString("appLocale");
-      return true;
-    default:
-      Logger.warn(LOG_TAG, "Unable to restore from version " + version + " PIC file: expecting " + FORMAT_VERSION);
-      return false;
-    }
-  }
-
-  protected JSONObject readFromFile() throws FileNotFoundException, JSONException {
-    Scanner scanner = null;
-    try {
-      scanner = new Scanner(file, "UTF-8").useDelimiter("\\A");
-      if (!scanner.hasNext()) {
-        return null;
-      }
-      return new JSONObject(scanner.next());
-    } finally {
-      if (scanner != null) {
-        scanner.close();
-      }
-    }
-  }
-
-  protected void writeToFile(JSONObject object) throws IOException {
-    Logger.debug(LOG_TAG, "Writing profile information.");
-    Logger.pii(LOG_TAG, "Writing to file: " + file.getAbsolutePath());
-    FileOutputStream stream = new FileOutputStream(file);
-    OutputStreamWriter writer = new OutputStreamWriter(stream, Charset.forName("UTF-8"));
-    try {
-      writer.append(object.toString());
-      needsWrite = false;
-    } finally {
-      writer.close();
-    }
-  }
-
-  /**
-   * Call this <b>on a background thread</b> when you're done adding things.
-   * @throws IOException if there was a problem serializing or writing the cache to disk.
-   */
-  public synchronized void completeInitialization() throws IOException {
-    initialized = true;
-    if (!needsWrite) {
-      Logger.debug(LOG_TAG, "No write needed.");
-      return;
-    }
-
-    JSONObject object = toJSON();
-    if (object == null) {
-      throw new IOException("Couldn't serialize JSON.");
-    }
-
-    writeToFile(object);
-  }
-
-  /**
-   * Call this if you're interested in reading.
-   *
-   * You should be doing so on a background thread.
-   *
-   * @return true if this object was initialized correctly.
-   */
-  public synchronized boolean restoreUnlessInitialized() {
-    if (initialized) {
-      return true;
-    }
-
-    if (!file.exists()) {
-      return false;
-    }
-
-    // One-liner for file reading in Java. So sorry.
-    Logger.info(LOG_TAG, "Restoring ProfileInformationCache from file.");
-    Logger.pii(LOG_TAG, "Restoring from file: " + file.getAbsolutePath());
-
-    try {
-      if (!fromJSON(readFromFile())) {
-        // No need to blow away the file; the caller can eventually overwrite it.
-        return false;
-      }
-      initialized = true;
-      needsWrite = false;
-      return true;
-    } catch (FileNotFoundException e) {
-      return false;
-    } catch (JSONException e) {
-      Logger.warn(LOG_TAG, "Malformed ProfileInformationCache. Not restoring.");
-      return false;
-    }
-  }
-
-  private void ensureInitialized() {
-    if (!initialized) {
-      throw new IllegalStateException("Not initialized.");
-    }
-  }
-
-  @Override
-  public boolean isBlocklistEnabled() {
-    ensureInitialized();
-    return blocklistEnabled;
-  }
-
-  public void setBlocklistEnabled(boolean value) {
-    Logger.debug(LOG_TAG, "Setting blocklist enabled: " + value);
-    blocklistEnabled = value;
-    needsWrite = true;
-  }
-
-  @Override
-  public boolean isTelemetryEnabled() {
-    ensureInitialized();
-    return telemetryEnabled;
-  }
-
-  public void setTelemetryEnabled(boolean value) {
-    Logger.debug(LOG_TAG, "Setting telemetry enabled: " + value);
-    telemetryEnabled = value;
-    needsWrite = true;
-  }
-
-  @Override
-  public boolean isAcceptLangUserSet() {
-    ensureInitialized();
-    return isAcceptLangUserSet;
-  }
-
-  public void setAcceptLangUserSet(boolean value) {
-    Logger.debug(LOG_TAG, "Setting accept-lang as user-set: " + value);
-    isAcceptLangUserSet = value;
-    needsWrite = true;
-  }
-
-  @Override
-  public long getProfileCreationTime() {
-    ensureInitialized();
-    return profileCreationTime;
-  }
-
-  public void setProfileCreationTime(long value) {
-    Logger.debug(LOG_TAG, "Setting profile creation time: " + value);
-    profileCreationTime = value;
-    needsWrite = true;
-  }
-
-  @Override
-  public String getDistributionString() {
-    ensureInitialized();
-    return distribution;
-  }
-
-  /**
-   * Ensure that your arguments are non-null.
-   */
-  public void setDistributionString(String distributionID, String distributionVersion) {
-    Logger.debug(LOG_TAG, "Setting distribution: " + distributionID + ", " + distributionVersion);
-    distribution = distributionID + ":" + distributionVersion;
-    needsWrite = true;
-  }
-
-  @Override
-  public String getAppLocale() {
-    ensureInitialized();
-    return appLocale;
-  }
-
-  public void setAppLocale(String value) {
-    if (value.equalsIgnoreCase(appLocale)) {
-      return;
-    }
-    Logger.debug(LOG_TAG, "Setting app locale: " + value);
-    appLocale = value.toLowerCase(Locale.US);
-    needsWrite = true;
-  }
-
-  @Override
-  public String getOSLocale() {
-    ensureInitialized();
-    return osLocale;
-  }
-
-  public void setOSLocale(String value) {
-    if (value.equalsIgnoreCase(osLocale)) {
-      return;
-    }
-    Logger.debug(LOG_TAG, "Setting OS locale: " + value);
-    osLocale = value.toLowerCase(Locale.US);
-    needsWrite = true;
-  }
-
-  /**
-   * Update the PIC, if necessary, to match the current locale environment.
-   *
-   * @return true if the PIC needed to be updated.
-   */
-  public boolean updateLocales(String osLocale, String appLocale) {
-    if (this.osLocale.equalsIgnoreCase(osLocale) &&
-        (appLocale == null || this.appLocale.equalsIgnoreCase(appLocale))) {
-      return false;
-    }
-    this.setOSLocale(osLocale);
-    if (appLocale != null) {
-      this.setAppLocale(appLocale);
-    }
-    return true;
-  }
-
-  @Override
-  public JSONObject getAddonsJSON() {
-    ensureInitialized();
-    return addons;
-  }
-
-  public void updateJSONForAddon(String id, String json) throws Exception {
-    addons.put(id, new JSONObject(json));
-    needsWrite = true;
-  }
-
-  public void removeAddon(String id) {
-    if (null != addons.remove(id)) {
-      needsWrite = true;
-    }
-  }
-
-  /**
-   * Will throw if you haven't done a full update at least once.
-   */
-  public void updateJSONForAddon(String id, JSONObject json) {
-    if (addons == null) {
-      throw new IllegalStateException("Cannot incrementally update add-ons without first initializing.");
-    }
-    try {
-      addons.put(id, json);
-      needsWrite = true;
-    } catch (Exception e) {
-      // Why would this happen?
-      Logger.warn(LOG_TAG, "Unexpected failure updating JSON for add-on.", e);
-    }
-  }
-
-  /**
-   * Update the cached set of add-ons. Throws on invalid input.
-   *
-   * @param json a valid add-ons JSON string.
-   */
-  public void setJSONForAddons(String json) throws Exception {
-    addons = new JSONObject(json);
-    needsWrite = true;
-  }
-
-  public void setJSONForAddons(JSONObject json) {
-    addons = json;
-    needsWrite = true;
-  }
-}
deleted file mode 100644
--- a/mobile/android/base/background/healthreport/prune/HealthReportPruneService.java
+++ /dev/null
@@ -1,90 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-package org.mozilla.gecko.background.healthreport.prune;
-
-import org.mozilla.gecko.background.BackgroundService;
-import org.mozilla.gecko.background.common.GlobalConstants;
-import org.mozilla.gecko.background.common.log.Logger;
-import org.mozilla.gecko.background.healthreport.HealthReportConstants;
-
-import android.content.Intent;
-import android.content.SharedPreferences;
-import android.os.IBinder;
-
-/**
- * A <code>Service</code> to prune unnecessary or excessive health report data.
- *
- * We extend <code>IntentService</code>, rather than just <code>Service</code>,
- * because this gives us a worker thread to avoid excessive main-thread disk access.
- */
-public class HealthReportPruneService extends BackgroundService {
-  public static final String LOG_TAG = HealthReportPruneService.class.getSimpleName();
-  public static final String WORKER_THREAD_NAME = LOG_TAG + "Worker";
-
-  public HealthReportPruneService() {
-    super(WORKER_THREAD_NAME);
-  }
-
-  @Override
-  public IBinder onBind(Intent intent) {
-    return null;
-  }
-
-  protected SharedPreferences getSharedPreferences() {
-    return this.getSharedPreferences(HealthReportConstants.PREFS_BRANCH, GlobalConstants.SHARED_PREFERENCES_MODE);
-  }
-
-  @Override
-  public void onHandleIntent(Intent intent) {
-    Logger.setThreadLogTag(HealthReportConstants.GLOBAL_LOG_TAG);
-
-    // Intent can be null. Bug 1025937.
-    if (intent == null) {
-      Logger.debug(LOG_TAG, "Short-circuiting on null intent.");
-      return;
-    }
-
-    Logger.debug(LOG_TAG, "Handling prune intent.");
-
-    if (!isIntentValid(intent)) {
-      Logger.warn(LOG_TAG, "Intent not valid - returning.");
-      return;
-    }
-
-    final String profileName = intent.getStringExtra("profileName");
-    final String profilePath = intent.getStringExtra("profilePath");
-    Logger.debug(LOG_TAG, "Ticking for profile " + profileName + " at " + profilePath + ".");
-    final PrunePolicy policy = getPrunePolicy(profilePath);
-    policy.tick(System.currentTimeMillis());
-  }
-
-  // Generator function wraps constructor for testing purposes.
-  protected PrunePolicy getPrunePolicy(final String profilePath) {
-    final PrunePolicyStorage storage = new PrunePolicyDatabaseStorage(this, profilePath);
-    return new PrunePolicy(storage, getSharedPreferences());
-  }
-
-  /**
-   * @param intent must be non-null.
-   * @return true if the supplied intent contains both profileName and profilePath.
-   */
-  private static boolean isIntentValid(final Intent intent) {
-    boolean isValid = true;
-
-    final String profileName = intent.getStringExtra("profileName");
-    if (profileName == null) {
-      Logger.warn(LOG_TAG, "Got intent without profileName.");
-      isValid = false;
-    }
-
-    final String profilePath = intent.getStringExtra("profilePath");
-    if (profilePath == null) {
-      Logger.warn(LOG_TAG, "Got intent without profilePath.");
-      isValid = false;
-    }
-
-    return isValid;
-  }
-}
deleted file mode 100644
--- a/mobile/android/base/background/healthreport/prune/PrunePolicy.java
+++ /dev/null
@@ -1,233 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-package org.mozilla.gecko.background.healthreport.prune;
-
-import org.mozilla.gecko.background.common.log.Logger;
-import org.mozilla.gecko.background.healthreport.HealthReportConstants;
-
-import android.content.SharedPreferences;
-
-/**
- * Manages scheduling of the pruning of old Firefox Health Report data.
- *
- * There are three main actions that take place:
- *   1) Excessive storage pruning: The recorded data is taking up an unreasonable amount of space.
- *   2) Expired data pruning: Data that is kept around longer than is useful.
- *   3) Cleanup: To deal with storage maintenance (e.g. bloat and fragmentation)
- *
- * (1) and (2) are performed periodically on their own schedules. (3) will activate after a
- * certain duration but only after (1) or (2) is performed.
- */
-public class PrunePolicy {
-  public static final String LOG_TAG = PrunePolicy.class.getSimpleName();
-
-  protected final PrunePolicyStorage storage;
-  protected final SharedPreferences sharedPreferences;
-  protected final Editor editor;
-
-  public PrunePolicy(final PrunePolicyStorage storage, final SharedPreferences sharedPrefs) {
-    this.storage = storage;
-    this.sharedPreferences = sharedPrefs;
-    this.editor = new Editor(this.sharedPreferences.edit());
-  }
-
-  protected SharedPreferences getSharedPreferences() {
-    return this.sharedPreferences;
-  }
-
-  public void tick(final long time) {
-    try {
-      try {
-        boolean pruned = attemptPruneBySize(time);
-        pruned = attemptExpiration(time) || pruned;
-        // We only need to cleanup after a large pruning.
-        if (pruned) {
-          attemptStorageCleanup(time);
-        }
-      } catch (Exception e) {
-        // While catching Exception is ordinarily bad form, this Service runs in the same process
-        // as Fennec so if we crash, it crashes. Additionally, this Service runs regularly so
-        // these crashes could be regular. Thus, we choose to quietly fail instead.
-        Logger.error(LOG_TAG, "Got exception pruning document.", e);
-      } finally {
-        editor.commit();
-      }
-    } catch (Exception e) {
-      Logger.error(LOG_TAG, "Got exception committing to SharedPreferences.", e);
-    } finally {
-      storage.close();
-    }
-  }
-
-  protected boolean attemptPruneBySize(final long time) {
-    final long nextPrune = getNextPruneBySizeTime();
-    if (nextPrune < 0) {
-      Logger.debug(LOG_TAG, "Initializing prune-by-size time.");
-      editor.setNextPruneBySizeTime(time + getMinimumTimeBetweenPruneBySizeChecks());
-      return false;
-    }
-
-    // If the system clock is skewed into the past, making the time between prunes too long, reset
-    // the clock.
-    if (nextPrune > getMinimumTimeBetweenPruneBySizeChecks() + time) {
-      Logger.debug(LOG_TAG, "Clock skew detected - resetting prune-by-size time.");
-      editor.setNextPruneBySizeTime(time + getMinimumTimeBetweenPruneBySizeChecks());
-      return false;
-    }
-
-    if (nextPrune > time) {
-      Logger.debug(LOG_TAG, "Skipping prune-by-size - wait period has not yet elapsed.");
-      return false;
-    }
-
-    Logger.debug(LOG_TAG, "Attempting prune-by-size.");
-
-    // Prune environments first because their cascading deletions may delete some events. These
-    // environments are pruned in order of least-recently used first. Note that orphaned
-    // environments are ignored here and should be removed elsewhere.
-    final int environmentCount = storage.getEnvironmentCount();
-    if (environmentCount > getMaxEnvironmentCount()) {
-      final int environmentPruneCount = environmentCount - getEnvironmentCountAfterPrune();
-      Logger.debug(LOG_TAG, "Pruning " + environmentPruneCount + " environments.");
-      storage.pruneEnvironments(environmentPruneCount);
-    }
-
-    final int eventCount = storage.getEventCount();
-    if (eventCount > getMaxEventCount()) {
-      final int eventPruneCount = eventCount - getEventCountAfterPrune();
-      Logger.debug(LOG_TAG, "Pruning up to " + eventPruneCount + " events.");
-      storage.pruneEvents(eventPruneCount);
-    }
-    editor.setNextPruneBySizeTime(time + getMinimumTimeBetweenPruneBySizeChecks());
-    return true;
-  }
-
-  protected boolean attemptExpiration(final long time) {
-    final long nextPrune = getNextExpirationTime();
-    if (nextPrune < 0) {
-      Logger.debug(LOG_TAG, "Initializing expiration time.");
-      editor.setNextExpirationTime(time + getMinimumTimeBetweenExpirationChecks());
-      return false;
-    }
-
-    // If the system clock is skewed into the past, making the time between prunes too long, reset
-    // the clock.
-    if (nextPrune > getMinimumTimeBetweenExpirationChecks() + time) {
-      Logger.debug(LOG_TAG, "Clock skew detected - resetting expiration time.");
-      editor.setNextExpirationTime(time + getMinimumTimeBetweenExpirationChecks());
-      return false;
-    }
-
-    if (nextPrune > time) {
-      Logger.debug(LOG_TAG, "Skipping expiration - wait period has not yet elapsed.");
-      return false;
-    }
-
-    final long oldEventTime = time - getEventExistenceDuration();
-    Logger.debug(LOG_TAG, "Pruning data older than " + oldEventTime + ".");
-    storage.deleteDataBefore(oldEventTime);
-    editor.setNextExpirationTime(time + getMinimumTimeBetweenExpirationChecks());
-    return true;
-  }
-
-  protected boolean attemptStorageCleanup(final long time) {
-    // Cleanup if max duration since last cleanup is exceeded.
-    final long nextCleanup = getNextCleanupTime();
-    if (nextCleanup < 0) {
-      Logger.debug(LOG_TAG, "Initializing cleanup time.");
-      editor.setNextCleanupTime(time + getMinimumTimeBetweenCleanupChecks());
-      return false;
-    }
-
-    // If the system clock is skewed into the past, making the time between cleanups too long,
-    // reset the clock.
-    if (nextCleanup > getMinimumTimeBetweenCleanupChecks() + time) {
-      Logger.debug(LOG_TAG, "Clock skew detected - resetting cleanup time.");
-      editor.setNextCleanupTime(time + getMinimumTimeBetweenCleanupChecks());
-      return false;
-    }
-
-    if (nextCleanup > time) {
-      Logger.debug(LOG_TAG, "Skipping cleanup - wait period has not yet elapsed.");
-      return false;
-    }
-
-    editor.setNextCleanupTime(time + getMinimumTimeBetweenCleanupChecks());
-    Logger.debug(LOG_TAG, "Cleaning up storage.");
-    storage.cleanup();
-    return true;
-  }
-
-  protected static class Editor {
-    protected final SharedPreferences.Editor editor;
-
-    public Editor(final SharedPreferences.Editor editor) {
-      this.editor = editor;
-    }
-
-    public void commit() {
-      editor.commit();
-    }
-
-    public Editor setNextExpirationTime(final long time) {
-      editor.putLong(HealthReportConstants.PREF_EXPIRATION_TIME, time);
-      return this;
-    }
-
-    public Editor setNextPruneBySizeTime(final long time) {
-      editor.putLong(HealthReportConstants.PREF_PRUNE_BY_SIZE_TIME, time);
-      return this;
-    }
-
-    public Editor setNextCleanupTime(final long time) {
-      editor.putLong(HealthReportConstants.PREF_CLEANUP_TIME, time);
-      return this;
-    }
-  }
-
-  private long getNextExpirationTime() {
-    return getSharedPreferences().getLong(HealthReportConstants.PREF_EXPIRATION_TIME, -1L);
-  }
-
-  private long getEventExistenceDuration() {
-    return HealthReportConstants.EVENT_EXISTENCE_DURATION;
-  }
-
-  private long getMinimumTimeBetweenExpirationChecks() {
-    return HealthReportConstants.MINIMUM_TIME_BETWEEN_EXPIRATION_CHECKS_MILLIS;
-  }
-
-  private long getNextPruneBySizeTime() {
-    return getSharedPreferences().getLong(HealthReportConstants.PREF_PRUNE_BY_SIZE_TIME, -1L);
-  }
-
-  private long getMinimumTimeBetweenPruneBySizeChecks() {
-    return HealthReportConstants.MINIMUM_TIME_BETWEEN_PRUNE_BY_SIZE_CHECKS_MILLIS;
-  }
-
-  private int getMaxEnvironmentCount() {
-    return HealthReportConstants.MAX_ENVIRONMENT_COUNT;
-  }
-
-  private int getEnvironmentCountAfterPrune() {
-    return HealthReportConstants.ENVIRONMENT_COUNT_AFTER_PRUNE;
-  }
-
-  private int getMaxEventCount() {
-    return HealthReportConstants.MAX_EVENT_COUNT;
-  }
-
-  private int getEventCountAfterPrune() {
-    return HealthReportConstants.EVENT_COUNT_AFTER_PRUNE;
-  }
-
-  private long getNextCleanupTime() {
-    return getSharedPreferences().getLong(HealthReportConstants.PREF_CLEANUP_TIME, -1L);
-  }
-
-  private long getMinimumTimeBetweenCleanupChecks() {
-    return HealthReportConstants.MINIMUM_TIME_BETWEEN_CLEANUP_CHECKS_MILLIS;
-  }
-}
deleted file mode 100644
--- a/mobile/android/base/background/healthreport/prune/PrunePolicyDatabaseStorage.java
+++ /dev/null
@@ -1,147 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-package org.mozilla.gecko.background.healthreport.prune;
-
-import org.mozilla.gecko.background.common.log.Logger;
-import org.mozilla.gecko.background.healthreport.AndroidConfigurationProvider;
-import org.mozilla.gecko.background.healthreport.Environment;
-import org.mozilla.gecko.background.healthreport.EnvironmentBuilder;
-import org.mozilla.gecko.background.healthreport.EnvironmentBuilder.ConfigurationProvider;
-import org.mozilla.gecko.background.healthreport.HealthReportDatabaseStorage;
-import org.mozilla.gecko.background.healthreport.ProfileInformationCache;
-
-import android.content.ContentProviderClient;
-import android.content.Context;
-
-/**
- * Abstracts over the Storage instance behind the PrunePolicy. The underlying storage instance is
- * a {@link HealthReportDatabaseStorage} instance. Since our cleanup routine vacuums, auto_vacuum
- * can be disabled. It is enabled by default, however, turning it off requires an expensive vacuum
- * so we wait until our first {@link cleanup} call since we are vacuuming anyway.
- */
-public class PrunePolicyDatabaseStorage implements PrunePolicyStorage {
-  public static final String LOG_TAG = PrunePolicyDatabaseStorage.class.getSimpleName();
-
-  private final Context context;
-  private final String profilePath;
-  private final ConfigurationProvider config;
-
-  private ContentProviderClient client;
-  private HealthReportDatabaseStorage storage;
-
-  private int currentEnvironmentID; // So we don't prune the current environment.
-
-  public PrunePolicyDatabaseStorage(final Context context, final String profilePath) {
-    this.context = context;
-    this.profilePath = profilePath;
-    this.config = new AndroidConfigurationProvider(context);
-
-    this.currentEnvironmentID = -1;
-  }
-
-  @Override
-  public void pruneEvents(final int count) {
-    getStorage().pruneEvents(count);
-  }
-
-  @Override
-  public void pruneEnvironments(final int count) {
-    getStorage().pruneEnvironments(count);
-
-    // Re-populate the DB and environment cache with the current environment in the unlikely event
-    // that it was deleted.
-    this.currentEnvironmentID = -1;
-    getCurrentEnvironmentID();
-  }
-
-  /**
-   * Deletes data recorded before the given time. Note that if this method fails to retrieve the
-   * current environment from the profile cache, it will not delete data so be sure to prune by
-   * other methods (e.g. {@link pruneEvents}) as well.
-   */
-  @Override
-  public int deleteDataBefore(final long time) {
-    return getStorage().deleteDataBefore(time, getCurrentEnvironmentID());
-  }
-
-  @Override
-  public void cleanup() {
-    final HealthReportDatabaseStorage storage = getStorage();
-    // The change to auto_vacuum will only take affect after a vacuum.
-    storage.disableAutoVacuuming();
-    storage.vacuum();
-  }
-
-  @Override
-  public int getEventCount() {
-    return getStorage().getEventCount();
-  }
-
-  @Override
-  public int getEnvironmentCount() {
-    return getStorage().getEnvironmentCount();
-  }
-
-  @Override
-  public void close() {
-    if (client != null) {
-      client.release();
-      client = null;
-    }
-  }
-
-  /**
-   * Retrieves the {@link HealthReportDatabaseStorage} associated with the profile of the policy.
-   * For efficiency, the underlying {@link ContentProviderClient} and
-   * {@link HealthReportDatabaseStorage} are cached for later invocations. However, this means a
-   * call to this method MUST be accompanied by a call to {@link close}. Throws
-   * {@link IllegalStateException} if the storage instance could not be retrieved - note that the
-   * {@link ContentProviderClient} instance will not be closed in this case and
-   * {@link releaseClient} should still be called.
-   */
-  protected HealthReportDatabaseStorage getStorage() {
-    if (storage != null) {
-      return storage;
-    }
-
-    client = EnvironmentBuilder.getContentProviderClient(context);
-    if (client == null) {
-      // TODO: Record prune failures and submit as part of FHR upload.
-      Logger.warn(LOG_TAG, "Unable to get ContentProviderClient - throwing.");
-      throw new IllegalStateException("Unable to get ContentProviderClient.");
-    }
-
-    try {
-      storage = EnvironmentBuilder.getStorage(client, profilePath);
-      if (storage == null) {
-        // TODO: Record prune failures and submit as part of FHR upload.
-        Logger.warn(LOG_TAG,"Unable to get HealthReportDatabaseStorage for " + profilePath +
-            " - throwing.");
-        throw new IllegalStateException("Unable to get HealthReportDatabaseStorage for " +
-            profilePath + " (== null).");
-      }
-    } catch (ClassCastException ex) {
-      // TODO: Record prune failures and submit as part of FHR upload.
-      Logger.warn(LOG_TAG,"Unable to get HealthReportDatabaseStorage for " + profilePath +
-          profilePath + " (ClassCastException).");
-      throw new IllegalStateException("Unable to get HealthReportDatabaseStorage for " +
-          profilePath + ".", ex);
-    }
-
-    return storage;
-  }
-
-  protected int getCurrentEnvironmentID() {
-    if (currentEnvironmentID < 0) {
-      final ProfileInformationCache cache = new ProfileInformationCache(profilePath);
-      if (!cache.restoreUnlessInitialized()) {
-        throw new IllegalStateException("Current environment unknown.");
-      }
-      final Environment env = EnvironmentBuilder.getCurrentEnvironment(cache, config);
-      currentEnvironmentID = env.register();
-    }
-    return currentEnvironmentID;
-  }
-}
deleted file mode 100644
--- a/mobile/android/base/background/healthreport/prune/PrunePolicyStorage.java
+++ /dev/null
@@ -1,26 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-package org.mozilla.gecko.background.healthreport.prune;
-
-/**
- * Abstracts over the Storage instance behind the PrunePolicy.
- */
-public interface PrunePolicyStorage {
-  public void pruneEvents(final int count);
-  public void pruneEnvironments(final int count);
-
-  public int deleteDataBefore(final long time);
-
-  public void cleanup();
-
-  public int getEventCount();
-  public int getEnvironmentCount();
-
-  /**
-   * Release the resources owned by this helper. MUST be called before this helper is garbage
-   * collected.
-   */
-  public void close();
-}
deleted file mode 100644
--- a/mobile/android/base/background/healthreport/upload/AndroidSubmissionClient.java
+++ /dev/null
@@ -1,471 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-package org.mozilla.gecko.background.healthreport.upload;
-
-import java.io.IOException;
-import java.io.UnsupportedEncodingException;
-import java.net.URISyntaxException;
-import java.util.ArrayList;
-import java.util.Collection;
-
-import org.json.JSONException;
-import org.json.JSONObject;
-import org.mozilla.gecko.BrowserLocaleManager;
-import org.mozilla.gecko.background.bagheera.BagheeraClient;
-import org.mozilla.gecko.background.bagheera.BagheeraRequestDelegate;
-import org.mozilla.gecko.background.common.GlobalConstants;
-import org.mozilla.gecko.background.common.log.Logger;
-import org.mozilla.gecko.background.healthreport.AndroidConfigurationProvider;
-import org.mozilla.gecko.background.healthreport.Environment;
-import org.mozilla.gecko.background.healthreport.EnvironmentBuilder;
-import org.mozilla.gecko.background.healthreport.EnvironmentBuilder.ConfigurationProvider;
-import org.mozilla.gecko.background.healthreport.HealthReportConstants;
-import org.mozilla.gecko.background.healthreport.HealthReportDatabaseStorage;
-import org.mozilla.gecko.background.healthreport.HealthReportGenerator;
-import org.mozilla.gecko.background.healthreport.HealthReportStorage;
-import org.mozilla.gecko.background.healthreport.HealthReportStorage.Field;
-import org.mozilla.gecko.background.healthreport.HealthReportStorage.MeasurementFields;
-import org.mozilla.gecko.background.healthreport.ProfileInformationCache;
-import org.mozilla.gecko.sync.net.BaseResource;
-
-import android.content.ContentProviderClient;
-import android.content.Context;
-import android.content.SharedPreferences;
-import ch.boye.httpclientandroidlib.HttpResponse;
-
-public class AndroidSubmissionClient implements SubmissionClient {
-  protected static final String LOG_TAG = AndroidSubmissionClient.class.getSimpleName();
-
-  private static final String MEASUREMENT_NAME_SUBMISSIONS = "org.mozilla.healthreport.submissions";
-  private static final int MEASUREMENT_VERSION_SUBMISSIONS = 1;
-
-  protected final Context context;
-  protected final SharedPreferences sharedPreferences;
-  protected final String profilePath;
-  protected final ConfigurationProvider config;
-
-  public AndroidSubmissionClient(Context context, SharedPreferences sharedPreferences, String profilePath) {
-    this(context, sharedPreferences, profilePath, new AndroidConfigurationProvider(context));
-  }
-
-  public AndroidSubmissionClient(Context context, SharedPreferences sharedPreferences, String profilePath, ConfigurationProvider config) {
-    this.context = context;
-    this.sharedPreferences = sharedPreferences;
-    this.profilePath = profilePath;
-    this.config = config;
-  }
-
-  public SharedPreferences getSharedPreferences() {
-    return sharedPreferences;
-  }
-
-  public String getDocumentServerURI() {
-    return getSharedPreferences().getString(HealthReportConstants.PREF_DOCUMENT_SERVER_URI, HealthReportConstants.DEFAULT_DOCUMENT_SERVER_URI);
-  }
-
-  public String getDocumentServerNamespace() {
-    return getSharedPreferences().getString(HealthReportConstants.PREF_DOCUMENT_SERVER_NAMESPACE, HealthReportConstants.DEFAULT_DOCUMENT_SERVER_NAMESPACE);
-  }
-
-  public long getLastUploadLocalTime() {
-    return getSharedPreferences().getLong(HealthReportConstants.PREF_LAST_UPLOAD_LOCAL_TIME, 0L);
-  }
-
-  public String getLastUploadDocumentId() {
-    return getSharedPreferences().getString(HealthReportConstants.PREF_LAST_UPLOAD_DOCUMENT_ID, null);
-  }
-
-  public boolean hasUploadBeenRequested() {
-    return getSharedPreferences().contains(HealthReportConstants.PREF_LAST_UPLOAD_REQUESTED);
-  }
-
-  public void setLastUploadLocalTimeAndDocumentId(long localTime, String id) {
-    getSharedPreferences().edit()
-      .putLong(HealthReportConstants.PREF_LAST_UPLOAD_LOCAL_TIME, localTime)
-      .putString(HealthReportConstants.PREF_LAST_UPLOAD_DOCUMENT_ID, id)
-      .commit();
-  }
-
-  protected HealthReportDatabaseStorage getStorage(final ContentProviderClient client) {
-    return EnvironmentBuilder.getStorage(client, profilePath);
-  }
-
-  protected JSONObject generateDocument(final long localTime, final long last,
-      final SubmissionsTracker tracker) throws JSONException {
-    final long since = localTime - GlobalConstants.MILLISECONDS_PER_SIX_MONTHS;
-    final HealthReportGenerator generator = tracker.getGenerator();
-    return generator.generateDocument(since, last, profilePath, config);
-  }
-
-  protected void uploadPayload(String id, String payload, Collection<String> oldIds, BagheeraRequestDelegate uploadDelegate) {
-    final BagheeraClient client = new BagheeraClient(getDocumentServerURI());
-
-    Logger.pii(LOG_TAG, "New health report has id " + id +
-        "and obsoletes " + (oldIds != null ? Integer.toString(oldIds.size()) : "no") + " old ids.");
-
-    try {
-      client.uploadJSONDocument(getDocumentServerNamespace(),
-          id,
-          payload,
-          oldIds,
-          uploadDelegate);
-    } catch (Exception e) {
-      uploadDelegate.handleError(e);
-    }
-  }
-
-  @Override
-  public void upload(long localTime, String id, Collection<String> oldIds, Delegate delegate) {
-    // We abuse the life-cycle of an Android ContentProvider slightly by holding
-    // onto a ContentProviderClient while we generate a payload. This keeps our
-    // database storage alive, and may also allow us to share a database
-    // connection with a BrowserHealthRecorder from Fennec.  The ContentProvider
-    // owns all underlying Storage instances, so we don't need to explicitly
-    // close them.
-    ContentProviderClient client = EnvironmentBuilder.getContentProviderClient(context);
-    if (client == null) {
-      // TODO: Bug 910898 - Store client failure in SharedPrefs so we can increment next time with storage.
-      delegate.onHardFailure(localTime, null, "Could not fetch content provider client.", null);
-      return;
-    }
-
-    try {
-      // Storage instance is owned by HealthReportProvider, so we don't need to
-      // close it. It's worth noting that this call will fail if called
-      // out-of-process.
-      final HealthReportDatabaseStorage storage = getStorage(client);
-      if (storage == null) {
-        // TODO: Bug 910898 - Store error in SharedPrefs so we can increment next time with storage.
-        delegate.onHardFailure(localTime, null, "No storage when generating report.", null);
-        return;
-      }
-
-      long last = Math.max(getLastUploadLocalTime(), HealthReportConstants.EARLIEST_LAST_PING);
-      if (!storage.hasEventSince(last)) {
-        delegate.onHardFailure(localTime, null, "No new events in storage.", null);
-        return;
-      }
-
-      initializeStorageForUploadProviders(storage);
-
-      final SubmissionsTracker tracker =
-          getSubmissionsTracker(storage, localTime, hasUploadBeenRequested());
-      try {
-        // TODO: Bug 910898 - Add errors from sharedPrefs to tracker.
-        final JSONObject document = generateDocument(localTime, last, tracker);
-        if (document == null) {
-          delegate.onHardFailure(localTime, null, "Generator returned null document.", null);
-          return;
-        }
-
-        final BagheeraRequestDelegate uploadDelegate = tracker.getDelegate(delegate, localTime,
-            true, id);
-        this.uploadPayload(id, document.toString(), oldIds, uploadDelegate);
-      } catch (Exception e) {
-        // Incrementing the failure count here could potentially cause the failure count to be
-        // incremented twice, but this helper class checks and prevents this.
-        tracker.incrementUploadClientFailureCount();
-        throw e;
-      }
-    } catch (Exception e) {
-      // TODO: Bug 910898 - Store client failure in SharedPrefs so we can increment next time with storage.
-      Logger.warn(LOG_TAG, "Got exception generating document.", e);
-      delegate.onHardFailure(localTime, null, "Got exception uploading.", e);
-      return;
-    } finally {
-      client.release();
-    }
-  }
-
-  protected SubmissionsTracker getSubmissionsTracker(final HealthReportStorage storage,
-      final long localTime, final boolean hasUploadBeenRequested) {
-    return new SubmissionsTracker(storage, localTime, hasUploadBeenRequested);
-  }
-
-  @Override
-  public void delete(final long localTime, final String id, Delegate delegate) {
-    final BagheeraClient client = new BagheeraClient(getDocumentServerURI());
-
-    Logger.pii(LOG_TAG, "Deleting health report with id " + id + ".");
-
-    BagheeraRequestDelegate deleteDelegate = new RequestDelegate(delegate, localTime, false, id);
-    try {
-      client.deleteDocument(getDocumentServerNamespace(), id, deleteDelegate);
-    } catch (Exception e) {
-      deleteDelegate.handleError(e);
-    }
-  }
-
-  protected class RequestDelegate implements BagheeraRequestDelegate {
-    protected final Delegate delegate;
-    protected final boolean isUpload;
-    protected final String methodString;
-    protected final long localTime;
-    protected final String id;
-
-    public RequestDelegate(Delegate delegate, long localTime, boolean isUpload, String id) {
-      this.delegate = delegate;
-      this.localTime = localTime;
-      this.isUpload = isUpload;
-      this.methodString = this.isUpload ? "upload" : "delete";
-      this.id = id;
-    }
-
-    @Override
-    public String getUserAgent() {
-      return HealthReportConstants.USER_AGENT;
-    }
-
-    @Override
-    public void handleSuccess(int status, String namespace, String id, HttpResponse response) {
-      BaseResource.consumeEntity(response);
-      if (isUpload) {
-        setLastUploadLocalTimeAndDocumentId(localTime, id);
-      }
-      Logger.debug(LOG_TAG, "Successful " + methodString + " at " + localTime + ".");
-      delegate.onSuccess(localTime, id);
-    }
-
-    /**
-     * Bagheera status codes:
-     *
-     * 403 Forbidden - Violated access restrictions. Most likely because of the method used.
-     * 413 Request Too Large - Request payload was larger than the configured maximum.
-     * 400 Bad Request - Returned if the POST/PUT failed validation in some manner.
-     * 404 Not Found - Returned if the URI path doesn't exist or if the URI was not in the proper format.
-     * 500 Server Error - General server error. Someone with access should look at the logs for more details.
-     */
-    @Override
-    public void handleFailure(int status, String namespace, HttpResponse response) {
-      BaseResource.consumeEntity(response);
-      Logger.debug(LOG_TAG, "Failed " + methodString + " at " + localTime + ".");
-      if (status >= 500) {
-        delegate.onSoftFailure(localTime, id, "Got status " + status + " from server.", null);
-        return;
-      }
-      // Things are either bad locally (bad payload format, too much data) or
-      // bad remotely (badly configured server, temporarily unavailable). Try
-      // again tomorrow.
-      delegate.onHardFailure(localTime, id, "Got status " + status + " from server.", null);
-    }
-
-    @Override
-    public void handleError(Exception e) {
-      Logger.debug(LOG_TAG, "Exception during " + methodString + " at " + localTime + ".", e);
-      if (e instanceof IOException) {
-        // Let's assume IO exceptions are Android dropping the network.
-        delegate.onSoftFailure(localTime, id, "Got exception during " + methodString + ".", e);
-        return;
-      }
-      delegate.onHardFailure(localTime, id, "Got exception during " + methodString + ".", e);
-    }
-  };
-
-  private void initializeStorageForUploadProviders(HealthReportDatabaseStorage storage) {
-    storage.beginInitialization();
-    try {
-      initializeSubmissionsProvider(storage);
-      storage.finishInitialization();
-    } catch (Exception e) {
-      // TODO: Bug 910898 - Store error in SharedPrefs so we can increment next time with storage.
-      storage.abortInitialization();
-      throw new IllegalStateException("Could not initialize storage for upload provider.", e);
-    }
-  }
-
-  private void initializeSubmissionsProvider(HealthReportDatabaseStorage storage) {
-    storage.ensureMeasurementInitialized(
-        MEASUREMENT_NAME_SUBMISSIONS,
-        MEASUREMENT_VERSION_SUBMISSIONS,
-        new MeasurementFields() {
-          @Override
-          public Iterable<FieldSpec> getFields() {
-            final ArrayList<FieldSpec> out = new ArrayList<FieldSpec>();
-            for (SubmissionsFieldName fieldName : SubmissionsFieldName.values()) {
-              FieldSpec spec = new FieldSpec(fieldName.getName(), Field.TYPE_INTEGER_COUNTER);
-              out.add(spec);
-            }
-            return out;
-          }
-        });
-  }
-
-  public static enum SubmissionsFieldName {
-    FIRST_ATTEMPT("firstDocumentUploadAttempt"),
-    CONTINUATION_ATTEMPT("continuationDocumentUploadAttempt"),
-    SUCCESS("uploadSuccess"),
-    TRANSPORT_FAILURE("uploadTransportFailure"),
-    SERVER_FAILURE("uploadServerFailure"),
-    CLIENT_FAILURE("uploadClientFailure");
-
-    private final String name;
-
-    SubmissionsFieldName(String name) {
-      this.name = name;
-    }
-
-    public String getName() {
-      return name;
-    }
-
-    public int getID(HealthReportStorage storage) {
-      final Field field = storage.getField(MEASUREMENT_NAME_SUBMISSIONS,
-                                           MEASUREMENT_VERSION_SUBMISSIONS,
-                                           name);
-      return field.getID();
-    }
-  }
-
-  /**
-   * Encapsulates the counting mechanisms for submissions status counts. Ensures multiple failures
-   * and successes are not recorded for a single instance.
-   */
-  public class SubmissionsTracker {
-    private final HealthReportStorage storage;
-    private final ProfileInformationCache profileCache;
-    private final int day;
-    private final int envID;
-
-    private boolean isUploadStatusCountIncremented;
-
-    public SubmissionsTracker(final HealthReportStorage storage, final long localTime,
-        final boolean hasUploadBeenRequested) throws IllegalStateException {
-      this.storage = storage;
-      this.profileCache = getProfileInformationCache();
-      this.day = storage.getDay(localTime);
-      this.envID = registerCurrentEnvironment();
-
-      this.isUploadStatusCountIncremented = false;
-
-      if (!hasUploadBeenRequested) {
-        incrementFirstUploadAttemptCount();
-      } else {
-        incrementContinuationAttemptCount();
-      }
-    }
-
-    protected ProfileInformationCache getProfileInformationCache() {
-      final ProfileInformationCache profileCache = new ProfileInformationCache(profilePath);
-      if (!profileCache.restoreUnlessInitialized()) {
-        Logger.warn(LOG_TAG, "Not enough profile information to compute current environment.");
-        throw new IllegalStateException("Could not retrieve current environment.");
-      }
-      return profileCache;
-    }
-
-    protected int registerCurrentEnvironment() {
-      return EnvironmentBuilder.registerCurrentEnvironment(storage, profileCache, config);
-    }
-
-    protected void incrementFirstUploadAttemptCount() {
-      Logger.debug(LOG_TAG, "Incrementing first upload attempt field.");
-      storage.incrementDailyCount(envID, day, SubmissionsFieldName.FIRST_ATTEMPT.getID(storage));
-    }
-
-    protected void incrementContinuationAttemptCount() {
-      Logger.debug(LOG_TAG, "Incrementing continuation upload attempt field.");
-      storage.incrementDailyCount(envID, day, SubmissionsFieldName.CONTINUATION_ATTEMPT.getID(storage));
-    }
-
-    public void incrementUploadSuccessCount() {
-      incrementStatusCount(SubmissionsFieldName.SUCCESS.getID(storage), "success");
-    }
-
-    public void incrementUploadClientFailureCount() {
-      incrementStatusCount(SubmissionsFieldName.CLIENT_FAILURE.getID(storage), "client failure");
-    }
-
-    public void incrementUploadTransportFailureCount() {
-      incrementStatusCount(SubmissionsFieldName.TRANSPORT_FAILURE.getID(storage), "transport failure");
-    }
-
-    public void incrementUploadServerFailureCount() {
-      incrementStatusCount(SubmissionsFieldName.SERVER_FAILURE.getID(storage), "server failure");
-    }
-
-    private void incrementStatusCount(final int fieldID, final String countType) {
-      if (!isUploadStatusCountIncremented) {
-        Logger.debug(LOG_TAG, "Incrementing upload attempt " + countType + " count.");
-        storage.incrementDailyCount(envID, day, fieldID);
-        isUploadStatusCountIncremented = true;
-      } else {
-        Logger.warn(LOG_TAG, "Upload status count already incremented - not incrementing " +
-            countType + " count.");
-      }
-    }
-
-    public TrackingGenerator getGenerator() {
-      return new TrackingGenerator();
-    }
-
-    public class TrackingGenerator extends HealthReportGenerator {
-      public TrackingGenerator() {
-        super(storage);
-      }
-
-      @Override
-      public JSONObject generateDocument(long since, long lastPingTime,
-          String generationProfilePath, ConfigurationProvider providedConfig) throws JSONException {
-
-        // Let's make sure we have an accurate locale.
-        BrowserLocaleManager.getInstance().getAndApplyPersistedLocale(context);
-
-        final JSONObject document;
-        // If the given profilePath matches the one we cached for the tracker, use the cached env.
-        if (profilePath != null && profilePath.equals(generationProfilePath)) {
-          final Environment environment = getCurrentEnvironment();
-          document = super.generateDocument(since, lastPingTime, environment);
-        } else {
-          document = super.generateDocument(since, lastPingTime, generationProfilePath, providedConfig);
-        }
-
-        if (document == null) {
-          incrementUploadClientFailureCount();
-        }
-        return document;
-      }
-
-      protected Environment getCurrentEnvironment() {
-        return EnvironmentBuilder.getCurrentEnvironment(profileCache, config);
-      }
-    }
-
-    public TrackingRequestDelegate getDelegate(final Delegate delegate, final long localTime,
-        final boolean isUpload, final String id) {
-      return new TrackingRequestDelegate(delegate, localTime, isUpload, id);
-    }
-
-    public class TrackingRequestDelegate extends RequestDelegate {
-      public TrackingRequestDelegate(final Delegate delegate, final long localTime,
-          final boolean isUpload, final String id) {
-        super(delegate, localTime, isUpload, id);
-      }
-
-      @Override
-      public void handleSuccess(int status, String namespace, String id, HttpResponse response) {
-        super.handleSuccess(status, namespace, id, response);
-        incrementUploadSuccessCount();
-      }
-
-      @Override
-      public void handleFailure(int status, String namespace, HttpResponse response) {
-        super.handleFailure(status, namespace, response);
-        incrementUploadServerFailureCount();
-      }
-
-      @Override
-      public void handleError(Exception e) {
-        super.handleError(e);
-        if (e instanceof IllegalArgumentException ||
-            e instanceof UnsupportedEncodingException ||
-            e instanceof URISyntaxException) {
-          incrementUploadClientFailureCount();
-        } else {
-          incrementUploadTransportFailureCount();
-        }
-      }
-    }
-  }
-}
deleted file mode 100644
--- a/mobile/android/base/background/healthreport/upload/HealthReportUploadService.java
+++ /dev/null
@@ -1,94 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-package org.mozilla.gecko.background.healthreport.upload;
-
-import org.mozilla.gecko.background.BackgroundService;
-import org.mozilla.gecko.background.common.GlobalConstants;
-import org.mozilla.gecko.background.common.log.Logger;
-import org.mozilla.gecko.background.healthreport.HealthReportConstants;
-
-import android.content.Intent;
-import android.content.SharedPreferences;
-import android.os.IBinder;
-
-/**
- * A <code>Service</code> to manage and upload health report data.
- *
- * We extend <code>IntentService</code>, rather than just <code>Service</code>,
- * because this gives us a worker thread to avoid main-thread networking.
- *
- * Yes, even though we're in an alarm-triggered service, it still counts as
- * main-thread.
- */
-public class HealthReportUploadService extends BackgroundService {
-  public static final String LOG_TAG = HealthReportUploadService.class.getSimpleName();
-  public static final String WORKER_THREAD_NAME = LOG_TAG + "Worker";
-
-  public HealthReportUploadService() {
-    super(WORKER_THREAD_NAME);
-  }
-
-  @Override
-  public IBinder onBind(Intent intent) {
-    return null;
-  }
-
-  protected SharedPreferences getSharedPreferences() {
-    return this.getSharedPreferences(HealthReportConstants.PREFS_BRANCH, GlobalConstants.SHARED_PREFERENCES_MODE);
-  }
-
-  @Override
-  public void onHandleIntent(Intent intent) {
-    Logger.setThreadLogTag(HealthReportConstants.GLOBAL_LOG_TAG);
-
-    // Intent can be null. Bug 1025937.
-    if (intent == null) {
-      Logger.debug(LOG_TAG, "Short-circuiting on null intent.");
-      return;
-    }
-
-    if (HealthReportConstants.UPLOAD_FEATURE_DISABLED) {
-      Logger.debug(LOG_TAG, "Health report upload feature is compile-time disabled; not handling upload intent.");
-      return;
-    }
-
-    Logger.debug(LOG_TAG, "Health report upload feature is compile-time enabled; handling upload intent.");
-
-    String profileName = intent.getStringExtra("profileName");
-    String profilePath = intent.getStringExtra("profilePath");
-
-    if (profileName == null || profilePath == null) {
-      Logger.warn(LOG_TAG, "Got intent without profilePath or profileName. Ignoring.");
-      return;
-    }
-
-    if (!intent.hasExtra("uploadEnabled")) {
-      Logger.warn(LOG_TAG, "Got intent without uploadEnabled. Ignoring.");
-      return;
-    }
-
-    // We disabled Health Report uploads in Bug 1230206, because the service is being decommissioned.
-    // We chose this specific place to turn uploads off because we wish to preserve deletions in the
-    // interim, and this is the tested code path for when a user turns off upload, but still expects
-    // deletions to work.
-    boolean uploadEnabled = false;
-
-    // Don't do anything if the device can't talk to the server.
-    if (!backgroundDataIsEnabled()) {
-      Logger.debug(LOG_TAG, "Background data is not enabled; skipping.");
-      return;
-    }
-
-    Logger.pii(LOG_TAG, "Ticking policy for profile " + profileName + " at " + profilePath + ".");
-
-    final SharedPreferences sharedPrefs = getSharedPreferences();
-    final ObsoleteDocumentTracker tracker = new ObsoleteDocumentTracker(sharedPrefs);
-    SubmissionClient client = new AndroidSubmissionClient(this, sharedPrefs, profilePath);
-    SubmissionPolicy policy = new SubmissionPolicy(sharedPrefs, client, tracker, uploadEnabled);
-
-    final long now = System.currentTimeMillis();
-    policy.tick(now);
-  }
-}
deleted file mode 100644
--- a/mobile/android/base/background/healthreport/upload/ObsoleteDocumentTracker.java
+++ /dev/null
@@ -1,245 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-package org.mozilla.gecko.background.healthreport.upload;
-
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map.Entry;
-import java.util.Set;
-
-import org.mozilla.gecko.background.common.log.Logger;
-import org.mozilla.gecko.background.healthreport.HealthReportConstants;
-import org.mozilla.gecko.sync.ExtendedJSONObject;
-
-import android.content.SharedPreferences;
-
-public class ObsoleteDocumentTracker {
-  public static final String LOG_TAG = ObsoleteDocumentTracker.class.getSimpleName();
-
-  protected final SharedPreferences sharedPrefs;
-
-  public ObsoleteDocumentTracker(SharedPreferences sharedPrefs) {
-    this.sharedPrefs = sharedPrefs;
-  }
-
-  protected ExtendedJSONObject getObsoleteIds() {
-    String s = sharedPrefs.getString(HealthReportConstants.PREF_OBSOLETE_DOCUMENT_IDS_TO_DELETION_ATTEMPTS_REMAINING, null);
-    if (s == null) {
-      // It's possible we're migrating an old profile forward.
-      String lastId = sharedPrefs.getString(HealthReportConstants.PREF_LAST_UPLOAD_DOCUMENT_ID, null);
-      if (lastId == null) {
-        return new ExtendedJSONObject();
-      }
-      ExtendedJSONObject ids = new ExtendedJSONObject();
-      ids.put(lastId, HealthReportConstants.DELETION_ATTEMPTS_PER_OBSOLETE_DOCUMENT_ID);
-      setObsoleteIds(ids);
-      return ids;
-    }
-    try {
-      return ExtendedJSONObject.parseJSONObject(s);
-    } catch (Exception e) {
-      Logger.warn(LOG_TAG, "Got exception getting obsolete ids.", e);
-      return new ExtendedJSONObject();
-    }
-  }
-
-  /**
-   * Write obsolete ids to disk.
-   *
-   * @param ids to write.
-   */
-  protected void setObsoleteIds(ExtendedJSONObject ids) {
-    sharedPrefs
-      .edit()
-      .putString(HealthReportConstants.PREF_OBSOLETE_DOCUMENT_IDS_TO_DELETION_ATTEMPTS_REMAINING, ids.toString())
-      .commit();
-  }
-
-  /**
-   * Remove id from set of obsolete document ids tracked for deletion.
-   *
-   * Public for testing.
-   *
-   * @param id to stop tracking.
-   */
-  public void removeObsoleteId(String id) {
-    ExtendedJSONObject ids = getObsoleteIds();
-    ids.remove(id);
-    setObsoleteIds(ids);
-  }
-
-  protected void decrementObsoleteId(ExtendedJSONObject ids, String id) {
-    if (!ids.containsKey(id)) {
-      return;
-    }
-    try {
-      Long attempts = ids.getLong(id);
-      if (attempts == null || --attempts < 1) {
-        ids.remove(id);
-      } else {
-        ids.put(id, attempts);
-      }
-    } catch (ClassCastException e) {
-      ids.remove(id);
-      Logger.info(LOG_TAG, "Got exception decrementing obsolete ids counter.", e);
-    }
-  }
-
-  /**
-   * Decrement attempts remaining for id in set of obsolete document ids tracked
-   * for deletion.
-   *
-   * Public for testing.
-   *
-   * @param id to decrement attempts.
-   */
-  public void decrementObsoleteIdAttempts(String id) {
-    ExtendedJSONObject ids = getObsoleteIds();
-    decrementObsoleteId(ids, id);
-    setObsoleteIds(ids);
-  }
-
-  public void purgeObsoleteIds(Collection<String> oldIds) {
-    ExtendedJSONObject ids = getObsoleteIds();
-    for (String oldId : oldIds) {
-      ids.remove(oldId);
-    }
-    setObsoleteIds(ids);
-  }
-
-  public void decrementObsoleteIdAttempts(Collection<String> oldIds) {
-    ExtendedJSONObject ids = getObsoleteIds();
-    for (String oldId : oldIds) {
-      decrementObsoleteId(ids, oldId);
-    }
-    setObsoleteIds(ids);
-  }
-
-  /**
-   * Sort Longs in decreasing order, moving null and non-Longs to the front.
-   *
-   * Public for testing only.
-   */
-  public static class PairComparator implements Comparator<Entry<String, Object>> {
-    @Override
-    public int compare(Entry<String, Object> lhs, Entry<String, Object> rhs) {
-      Object l = lhs.getValue();
-      Object r = rhs.getValue();
-      if (!(l instanceof Long)) {
-        if (!(r instanceof Long)) {
-          return 0;
-        }
-        return -1;
-      }
-      if (!(r instanceof Long)) {
-        return 1;
-      }
-      return ((Long) r).compareTo((Long) l);
-    }
-  }
-
-  /**
-   * Return a batch of obsolete document IDs that should be deleted next.
-   *
-   * Document IDs are long and sending too many in a single request might
-   * increase the likelihood of POST failures, so we delete a (deterministic)
-   * subset here.
-   *
-   * @return a non-null collection.
-   */
-  public Collection<String> getBatchOfObsoleteIds() {
-    ExtendedJSONObject ids = getObsoleteIds();
-    // Sort by increasing order of key values.
-    List<Entry<String, Object>> pairs = new ArrayList<Entry<String,Object>>(ids.entrySet());
-    Collections.sort(pairs, new PairComparator());
-    List<String> batch = new ArrayList<String>(HealthReportConstants.MAXIMUM_DELETIONS_PER_POST);
-    int i = 0;
-    while (batch.size() < HealthReportConstants.MAXIMUM_DELETIONS_PER_POST && i < pairs.size()) {
-      batch.add(pairs.get(i++).getKey());
-    }
-    return batch;
-  }
-
-  /**
-   * Track the given document ID for eventual obsolescence and deletion.
-   * Obsolete IDs are not known to have been uploaded to the server, so we just
-   * give a best effort attempt at deleting them
-   *
-   * @param id to eventually delete.
-   */
-  public void addObsoleteId(String id) {
-    ExtendedJSONObject ids = getObsoleteIds();
-    if (ids.size() >= HealthReportConstants.MAXIMUM_STORED_OBSOLETE_DOCUMENT_IDS) {
-      // Remove the one that's been tried the most and is least likely to be
-      // known to be on the server. Since the comparator orders in decreasing
-      // order, we take the max.
-      ids.remove(Collections.max(ids.entrySet(), new PairComparator()).getKey());
-    }
-    ids.put(id, HealthReportConstants.DELETION_ATTEMPTS_PER_OBSOLETE_DOCUMENT_ID);
-    setObsoleteIds(ids);
-  }
-
-  /**
-   * Track the given document ID for eventual obsolescence and deletion, and
-   * give it priority since we know this ID has made it to the server, and we
-   * definitely don't want to orphan it.
-   *
-   * @param id to eventually delete.
-   */
-  public void markIdAsUploaded(String id) {
-    ExtendedJSONObject ids = getObsoleteIds();
-    ids.put(id, HealthReportConstants.DELETION_ATTEMPTS_PER_KNOWN_TO_BE_ON_SERVER_DOCUMENT_ID);
-    setObsoleteIds(ids);
-  }
-
-  public boolean hasObsoleteIds() {
-    return getObsoleteIds().size() > 0;
-  }
-
-  public int numberOfObsoleteIds() {
-    return getObsoleteIds().size();
-  }
-
-  public String getNextObsoleteId() {
-    ExtendedJSONObject ids = getObsoleteIds();
-    if (ids.size() < 1) {
-      return null;
-    }
-    try {
-      // Delete the one that's most likely to be known to be on the server, and
-      // that's not been tried as much. Since the comparator orders in
-      // decreasing order, we take the min.
-      return Collections.min(ids.entrySet(), new PairComparator()).getKey();
-    } catch (Exception e) {
-      Logger.warn(LOG_TAG, "Got exception picking obsolete id to delete.", e);
-      return null;
-    }
-  }
-
-  /**
-   * We want cleaning up documents on the server to be best effort. Purge badly
-   * formed IDs and cap the number of times we try to delete so that the queue
-   * doesn't take too long.
-   */
-  public void limitObsoleteIds() {
-    ExtendedJSONObject ids = getObsoleteIds();
-
-    Set<String> keys = new HashSet<String>(ids.keySet()); // Avoid invalidating an iterator.
-    for (String key : keys) {
-      Object o = ids.get(key);
-      if (!(o instanceof Long)) {
-        continue;
-      }
-      if ((Long) o > HealthReportConstants.DELETION_ATTEMPTS_PER_OBSOLETE_DOCUMENT_ID) {
-        ids.put(key, HealthReportConstants.DELETION_ATTEMPTS_PER_OBSOLETE_DOCUMENT_ID);
-      }
-    }
-    setObsoleteIds(ids);
-  }
-}
deleted file mode 100644
--- a/mobile/android/base/background/healthreport/upload/SubmissionClient.java
+++ /dev/null
@@ -1,42 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-package org.mozilla.gecko.background.healthreport.upload;
-
-import java.util.Collection;
-
-public interface SubmissionClient {
-  public interface Delegate {
-    /**
-     * Called in the event of a temporary failure; we should try again soon.
-     *
-     * @param localTime milliseconds since the epoch.
-     * @param id if known; may be null.
-     * @param reason for failure.
-     * @param e if there was an exception; may be null.
-     */
-    public void onSoftFailure(long localTime, String id, String reason, Exception e);
-
-    /**
-     * Called in the event of a failure; we should try again, but not today.
-     *
-     * @param localTime milliseconds since the epoch.
-     * @param id if known; may be null.
-     * @param reason for failure.
-     * @param e if there was an exception; may be null.
-     */
-    public void onHardFailure(long localTime, String id, String reason, Exception e);
-
-    /**
-     * Success!
-     *
-     * @param localTime milliseconds since the epoch.
-     * @param id is always known; not null.
-     */
-    public void onSuccess(long localTime, String id);
-  }
-
-  public void upload(long localTime, String id, Collection<String> oldIds, Delegate delegate);
-  public void delete(long localTime, String id, Delegate delegate);
-}
deleted file mode 100644
--- a/mobile/android/base/background/healthreport/upload/SubmissionPolicy.java
+++ /dev/null
@@ -1,462 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-package org.mozilla.gecko.background.healthreport.upload;
-
-import java.net.MalformedURLException;
-import java.net.SocketException;
-import java.net.UnknownHostException;
-import java.util.Collection;
-
-import org.mozilla.gecko.background.common.log.Logger;
-import org.mozilla.gecko.background.healthreport.HealthReportConstants;
-import org.mozilla.gecko.background.healthreport.HealthReportUtils;
-import org.mozilla.gecko.background.healthreport.upload.SubmissionClient.Delegate;
-
-import android.content.SharedPreferences;
-
-/**
- * Manages scheduling of Firefox Health Report data submission.
- *
- * The rules of data submission are as follows:
- *
- * 1. Do not submit data more than once every 24 hours.
- *
- * 2. Try to submit as close to 24 hours apart as possible.
- *
- * 3. Do not submit too soon after application startup so as to not negatively
- * impact performance at startup.
- *
- * 4. Before first ever data submission, the user should be notified about data
- * collection practices.
- *
- * 5. User should have opportunity to react to this notification before data
- * submission.
- *
- * 6. Display of notification without any explicit user action constitutes
- * implicit consent after a certain duration of time.
- *
- * 7. If data submission fails, try at most 2 additional times before giving up
- * on that day's submission.
- *
- * On Android, items 4, 5, and 6 are addressed by displaying an Android
- * notification on first run.
- */
-public class SubmissionPolicy {
-  public static final String LOG_TAG = SubmissionPolicy.class.getSimpleName();
-
-  protected final SharedPreferences sharedPreferences;
-  protected final SubmissionClient client;
-  protected final boolean uploadEnabled;
-  protected final ObsoleteDocumentTracker tracker;
-
-  public SubmissionPolicy(final SharedPreferences sharedPreferences,
-      final SubmissionClient client,
-      final ObsoleteDocumentTracker tracker,
-      boolean uploadEnabled) {
-    if (sharedPreferences == null) {
-      throw new IllegalArgumentException("sharedPreferences must not be null");
-    }
-    this.sharedPreferences = sharedPreferences;
-    this.client = client;
-    this.tracker = tracker;
-    this.uploadEnabled = uploadEnabled;
-  }
-
-  /**
-   * Check what action must happen, advance counters and timestamps, and
-   * possibly spawn a request to the server.
-   *
-   * @param localTime now.
-   * @return true if a request was spawned; false otherwise.
-   */
-  public boolean tick(final long localTime) {
-    final long nextUpload = getNextSubmission();
-
-    // If the system clock were ever set to a time in the distant future,
-    // it's possible our next schedule date is far out as well. We know
-    // we shouldn't schedule for more than a day out, so we reset the next
-    // scheduled date appropriately. 3 days was chosen to match desktop's
-    // arbitrary choice.
-    if (nextUpload >= localTime + 3 * getMinimumTimeBetweenUploads()) {
-      Logger.warn(LOG_TAG, "Next upload scheduled far in the future; system clock reset? " + nextUpload + " > " + localTime);
-      // Things are strange, we want to start again but we don't want to stampede.
-      editor()
-        .setNextSubmission(localTime + getMinimumTimeBetweenUploads())
-        .commit();
-      return false;
-    }
-
-    // Don't upload unless an interval has elapsed.
-    if (localTime < nextUpload) {
-      Logger.debug(LOG_TAG, "We uploaded less than an interval ago; skipping. " + nextUpload + " > " + localTime);
-      return false;
-    }
-
-    if (!uploadEnabled) {
-      // We only delete (rather than mark as obsolete during upload) when
-      // uploading is disabled. We try to delete aggressively, since the volume
-      // of deletes should be very low. But we don't want to send too many
-      // delete requests at the same time, so we process these one at a time. In
-      // the future (Bug 872756), we will be able to delete multiple documents
-      // with one request.
-      final String obsoleteId = tracker.getNextObsoleteId();
-      if (obsoleteId == null) {
-        Logger.debug(LOG_TAG, "Upload disabled and nothing to delete.");
-        return false;
-      }
-
-      Logger.info(LOG_TAG, "Upload disabled. Deleting obsolete document.");
-      Editor editor = editor();
-      editor.setLastDeleteRequested(localTime); // Write committed by delegate.
-      client.delete(localTime, obsoleteId, new DeleteDelegate(editor));
-      return true;
-    }
-
-    long firstRun = getFirstRunLocalTime();
-    if (firstRun < 0) {
-      firstRun = localTime;
-      // Make sure we start clean and as soon as possible.
-      editor()
-        .setFirstRunLocalTime(firstRun)
-        .setNextSubmission(localTime + getMinimumTimeBeforeFirstSubmission())
-        .setCurrentDayFailureCount(0)
-        .commit();
-    }
-
-    // This case will occur if the nextSubmission time is not set (== -1) but firstRun is.
-    if (localTime < firstRun + getMinimumTimeBeforeFirstSubmission()) {
-      Logger.info(LOG_TAG, "Need to wait " + getMinimumTimeBeforeFirstSubmission() + " before first upload.");
-      return false;
-    }
-
-    // The first upload attempt for a given document submission begins a 24-hour period in which
-    // the upload will retry upon a soft failure. At the end of this period, the submission
-    // failure count is reset, ensuring each day's first submission attempt has a zeroed failure
-    // count. A period may also end on upload success or hard failure.
-    if (localTime >= getCurrentDayResetTime()) {
-      editor()
-        .setCurrentDayResetTime(localTime + getMinimumTimeBetweenUploads())
-        .setCurrentDayFailureCount(0)
-        .commit();
-    }
-
-    String id = HealthReportUtils.generateDocumentId();
-    Collection<String> oldIds = tracker.getBatchOfObsoleteIds();
-    tracker.addObsoleteId(id);
-
-    Editor editor = editor();
-    editor.setLastUploadRequested(localTime); // Write committed by delegate.
-    client.upload(localTime, id, oldIds, new UploadDelegate(editor, oldIds));
-    return true;
-  }
-
-  /**
-   * Return true if the upload that produced <code>e</code> definitely did not
-   * produce a new record on the remote server.
-   *
-   * @param e
-   *          <code>Exception</code> that upload produced.
-   * @return true if the server could not have a new record.
-   */
-  protected boolean isLocalException(Exception e) {
-    return (e instanceof MalformedURLException) ||
-           (e instanceof SocketException) ||
-           (e instanceof UnknownHostException);
-  }
-
-  protected class UploadDelegate implements Delegate {
-    protected final Editor editor;
-    protected final Collection<String> oldIds;
-
-    public UploadDelegate(Editor editor, Collection<String> oldIds) {
-      this.editor = editor;
-      this.oldIds = oldIds;
-    }
-
-    @Override
-    public void onSuccess(long localTime, String id) {
-      long next = localTime + getMinimumTimeBetweenUploads();
-      tracker.markIdAsUploaded(id);
-      tracker.purgeObsoleteIds(oldIds);
-      editor
-        .setNextSubmission(next)
-        .setLastUploadSucceeded(localTime)
-        .setCurrentDayFailureCount(0)
-        .clearCurrentDayResetTime() // Set again on the next submission's first upload attempt.
-        .commit();
-      if (Logger.LOG_PERSONAL_INFORMATION) {
-        Logger.pii(LOG_TAG, "Successful upload with id " + id + " obsoleting "
-            + oldIds.size() + " old records reported at " + localTime + "; next upload at " + next + ".");
-      } else {
-        Logger.info(LOG_TAG, "Successful upload obsoleting " + oldIds.size()
-            + " old records reported at " + localTime + "; next upload at " + next + ".");
-      }
-    }
-
-    @Override
-    public void onHardFailure(long localTime, String id, String reason, Exception e) {
-      long next = localTime + getMinimumTimeBetweenUploads();
-      if (isLocalException(e)) {
-        Logger.info(LOG_TAG, "Hard failure caused by local exception; not tracking id and not decrementing attempts.");
-        tracker.removeObsoleteId(id);
-      } else {
-        tracker.decrementObsoleteIdAttempts(oldIds);
-      }
-      editor
-        .setNextSubmission(next)
-        .setLastUploadFailed(localTime)
-        .setCurrentDayFailureCount(0)
-        .clearCurrentDayResetTime() // Set again on the next submission's first upload attempt.
-        .commit();
-      Logger.warn(LOG_TAG, "Hard failure reported at " + localTime + ": " + reason + " Next upload at " + next + ".", e);
-    }
-
-    @Override
-    public void onSoftFailure(long localTime, String id, String reason, Exception e) {
-      int failuresToday = getCurrentDayFailureCount();
-      Logger.warn(LOG_TAG, "Soft failure reported at " + localTime + ": " + reason + " Previously failed " + failuresToday + " time(s) today.");
-
-      if (failuresToday >= getMaximumFailuresPerDay()) {
-        onHardFailure(localTime, id, "Reached the limit of daily upload attempts: " + failuresToday, e);
-        return;
-      }
-
-      long next = localTime + getMinimumTimeAfterFailure();
-      if (isLocalException(e)) {
-        Logger.info(LOG_TAG, "Soft failure caused by local exception; not tracking id and not decrementing attempts.");
-        tracker.removeObsoleteId(id);
-      } else {
-        tracker.decrementObsoleteIdAttempts(oldIds);
-      }
-      editor
-        .setNextSubmission(next)
-        .setLastUploadFailed(localTime)
-        .setCurrentDayFailureCount(failuresToday + 1)
-        .commit();
-      Logger.info(LOG_TAG, "Retrying upload at " + next + ".");
-    }
-  }
-
-  protected class DeleteDelegate implements Delegate {
-    protected final Editor editor;
-
-    public DeleteDelegate(Editor editor) {
-      this.editor = editor;
-    }
-
-    @Override
-    public void onSoftFailure(final long localTime, String id, String reason, Exception e) {
-      long next = localTime + getMinimumTimeBetweenDeletes();
-      if (isLocalException(e)) {
-        Logger.info(LOG_TAG, "Soft failure caused by local exception; not decrementing attempts.");
-      } else {
-        tracker.decrementObsoleteIdAttempts(id);
-      }
-      editor
-        .setNextSubmission(next)
-        .setLastDeleteFailed(localTime)
-        .commit();
-
-      if (Logger.LOG_PERSONAL_INFORMATION) {
-        Logger.info(LOG_TAG, "Got soft failure at " + localTime + " deleting obsolete document with id " + id + ": " + reason + " Trying again later.");
-      } else {
-        Logger.info(LOG_TAG, "Got soft failure at " + localTime + " deleting obsolete document: " + reason + " Trying again later.");
-      }
-    }
-
-    @Override
-    public void onHardFailure(final long localTime, String id, String reason, Exception e) {
-      // We're never going to be able to delete this id, so don't keep trying.
-      long next = localTime + getMinimumTimeBetweenDeletes();
-      tracker.removeObsoleteId(id);
-      editor
-        .setNextSubmission(next)
-        .setLastDeleteFailed(localTime)
-        .commit();
-
-      if (Logger.LOG_PERSONAL_INFORMATION) {
-        Logger.warn(LOG_TAG, "Got hard failure at " + localTime + " deleting obsolete document with id " + id + ": " + reason + " Abandoning delete request.", e);
-      } else {
-        Logger.warn(LOG_TAG, "Got hard failure at " + localTime + " deleting obsolete document: " + reason + " Abandoning delete request.", e);
-      }
-    }
-
-    @Override
-    public void onSuccess(final long localTime, String id) {
-      long next = localTime + getMinimumTimeBetweenDeletes();
-      tracker.removeObsoleteId(id);
-      editor
-        .setNextSubmission(next)
-        .setLastDeleteSucceeded(localTime)
-        .commit();
-
-      if (Logger.LOG_PERSONAL_INFORMATION) {
-        Logger.pii(LOG_TAG, "Deleted an obsolete document with id " + id + " at " + localTime + ".");
-      } else {
-        Logger.info(LOG_TAG, "Deleted an obsolete document at " + localTime + ".");
-      }
-    }
-  }
-
-  public SharedPreferences getSharedPreferences() {
-    return this.sharedPreferences;
-  }
-
-  public long getMinimumTimeBetweenUploads() {
-    return getSharedPreferences().getLong(HealthReportConstants.PREF_MINIMUM_TIME_BETWEEN_UPLOADS, HealthReportConstants.DEFAULT_MINIMUM_TIME_BETWEEN_UPLOADS);
-  }
-
-  public long getMinimumTimeBeforeFirstSubmission() {
-    return getSharedPreferences().getLong(HealthReportConstants.PREF_MINIMUM_TIME_BEFORE_FIRST_SUBMISSION, HealthReportConstants.DEFAULT_MINIMUM_TIME_BEFORE_FIRST_SUBMISSION);
-  }
-
-  public long getMinimumTimeAfterFailure() {
-    return getSharedPreferences().getLong(HealthReportConstants.PREF_MINIMUM_TIME_AFTER_FAILURE, HealthReportConstants.DEFAULT_MINIMUM_TIME_AFTER_FAILURE);
-  }
-
-  public long getMaximumFailuresPerDay() {
-    return getSharedPreferences().getLong(HealthReportConstants.PREF_MAXIMUM_FAILURES_PER_DAY, HealthReportConstants.DEFAULT_MAXIMUM_FAILURES_PER_DAY);
-  }
-
-  // Authoritative.
-  public long getFirstRunLocalTime() {
-    return getSharedPreferences().getLong(HealthReportConstants.PREF_FIRST_RUN, -1);
-  }
-
-  // Authoritative.
-  public long getNextSubmission() {
-    return getSharedPreferences().getLong(HealthReportConstants.PREF_NEXT_SUBMISSION, -1);
-  }
-
-  // Authoritative.
-  public int getCurrentDayFailureCount() {
-    return getSharedPreferences().getInt(HealthReportConstants.PREF_CURRENT_DAY_FAILURE_COUNT, 0);
-  }
-
-  // Authoritative.
-  public long getCurrentDayResetTime() {
-    return getSharedPreferences().getLong(HealthReportConstants.PREF_CURRENT_DAY_RESET_TIME, -1);
-  }
-
-  /**
-   * To avoid writing to disk multiple times, we encapsulate writes in a
-   * helper class. Be sure to call <code>commit</code> to flush to disk!
-   */
-  protected Editor editor() {
-    return new Editor(getSharedPreferences().edit());
-  }
-
-  protected static class Editor {
-    protected final SharedPreferences.Editor editor;
-
-    public Editor(SharedPreferences.Editor editor) {
-      this.editor = editor;
-    }
-
-    public void commit() {
-      editor.commit();
-    }
-
-    // Authoritative.
-    public Editor setFirstRunLocalTime(long localTime) {
-      editor.putLong(HealthReportConstants.PREF_FIRST_RUN, localTime);
-      return this;
-    }
-
-    // Authoritative.
-    public Editor setNextSubmission(long localTime) {
-      editor.putLong(HealthReportConstants.PREF_NEXT_SUBMISSION, localTime);
-      return this;
-    }
-
-    // Authoritative.
-    public Editor setCurrentDayFailureCount(int failureCount) {
-      editor.putInt(HealthReportConstants.PREF_CURRENT_DAY_FAILURE_COUNT, failureCount);
-      return this;
-    }
-
-    // Authoritative.
-    public Editor setCurrentDayResetTime(long resetTime) {
-      editor.putLong(HealthReportConstants.PREF_CURRENT_DAY_RESET_TIME, resetTime);
-      return this;
-    }
-
-    // Authoritative.
-    public Editor clearCurrentDayResetTime() {
-      editor.putLong(HealthReportConstants.PREF_CURRENT_DAY_RESET_TIME, -1);
-      return this;
-    }
-
-    // Authoritative.
-    public Editor setLastUploadRequested(long localTime) {
-      editor.putLong(HealthReportConstants.PREF_LAST_UPLOAD_REQUESTED, localTime);
-      return this;
-    }
-
-    // Forensics only.
-    public Editor setLastUploadSucceeded(long localTime) {
-      editor.putLong(HealthReportConstants.PREF_LAST_UPLOAD_SUCCEEDED, localTime);
-      return this;
-    }
-
-    // Forensics only.
-    public Editor setLastUploadFailed(long localTime) {
-      editor.putLong(HealthReportConstants.PREF_LAST_UPLOAD_FAILED, localTime);
-      return this;
-    }
-
-    // Forensics only.
-    public Editor setLastDeleteRequested(long localTime) {
-      editor.putLong(HealthReportConstants.PREF_LAST_DELETE_REQUESTED, localTime);
-      return this;
-    }
-
-    // Forensics only.
-    public Editor setLastDeleteSucceeded(long localTime) {
-      editor.putLong(HealthReportConstants.PREF_LAST_DELETE_SUCCEEDED, localTime);
-      return this;
-    }
-
-    // Forensics only.
-    public Editor setLastDeleteFailed(long localTime) {
-      editor.putLong(HealthReportConstants.PREF_LAST_DELETE_FAILED, localTime);
-      return this;
-    }
-  }
-
-  // Authoritative.
-  public long getLastUploadRequested() {
-    return getSharedPreferences().getLong(HealthReportConstants.PREF_LAST_UPLOAD_REQUESTED, -1);
-  }
-
-  // Forensics only.
-  public long getLastUploadSucceeded() {
-    return getSharedPreferences().getLong(HealthReportConstants.PREF_LAST_UPLOAD_SUCCEEDED, -1);
-  }
-
-  // Forensics only.
-  public long getLastUploadFailed() {
-    return getSharedPreferences().getLong(HealthReportConstants.PREF_LAST_UPLOAD_FAILED, -1);
-  }
-
-  // Forensics only.
-  public long getLastDeleteRequested() {
-    return getSharedPreferences().getLong(HealthReportConstants.PREF_LAST_DELETE_REQUESTED, -1);
-  }
-
-  // Forensics only.
-  public long getLastDeleteSucceeded() {
-    return getSharedPreferences().getLong(HealthReportConstants.PREF_LAST_DELETE_SUCCEEDED, -1);
-  }
-
-  // Forensics only.
-  public long getLastDeleteFailed() {
-    return getSharedPreferences().getLong(HealthReportConstants.PREF_LAST_DELETE_FAILED, -1);
-  }
-
-  public long getMinimumTimeBetweenDeletes() {
-    return getSharedPreferences().getLong(HealthReportConstants.PREF_MINIMUM_TIME_BETWEEN_DELETES, HealthReportConstants.DEFAULT_MINIMUM_TIME_BETWEEN_DELETES);
-  }
-}
deleted file mode 100644
--- a/mobile/android/services/manifests/HealthReportAndroidManifest_activities.xml.in
+++ /dev/null
@@ -1,27 +0,0 @@
-        <provider android:name="org.mozilla.gecko.background.healthreport.HealthReportProvider"
-                  android:authorities="@ANDROID_PACKAGE_NAME@.health"
-                  android:permission="@ANDROID_PACKAGE_NAME@.permissions.HEALTH_PROVIDER">
-        </provider>
-
-        <!-- BroadcastReceiver is a thin receiver whose purpose is to
-             start the background service in response to external events,
-             some sent by the system and some particular to Health Report.
-          -->
-        <receiver android:name="org.mozilla.gecko.background.healthreport.HealthReportBroadcastReceiver" >
-            <intent-filter>
-                <!-- Startup. -->
-                <action android:name="android.intent.action.BOOT_COMPLETED" />
-            </intent-filter>
-            <intent-filter>
-                <!-- SD card remounted. -->
-                <action android:name="android.intent.action.EXTERNAL_APPLICATIONS_AVAILABLE" />
-            </intent-filter>
-            <intent-filter >
-                <!-- Toggle Health Report upload service alarm (based on preferences value) -->
-                <action android:name="@ANDROID_PACKAGE_NAME@.HEALTHREPORT_UPLOAD_PREF" />
-            </intent-filter>
-            <intent-filter >
-                <!-- Enable Health Report prune service alarm -->
-                <action android:name="@ANDROID_PACKAGE_NAME@.HEALTHREPORT_PRUNE" />
-            </intent-filter>
-        </receiver>
deleted file mode 100644
--- a/mobile/android/services/manifests/HealthReportAndroidManifest_permissions.xml.in
+++ /dev/null
@@ -1,5 +0,0 @@
-    <!-- So we can start our service. -->
-    <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
-
-    <!-- So we can receive messages from Fennec. -->
-    <uses-permission android:name="@ANDROID_PACKAGE_NAME@.permission.PER_ANDROID_PACKAGE" />
deleted file mode 100644
--- a/mobile/android/services/manifests/HealthReportAndroidManifest_services.xml.in
+++ /dev/null
@@ -1,17 +0,0 @@
-        <!-- BroadcastService responds to external events and starts
-             the other background services. We don't export any of
-             these services, since they are only started by components
-             internal to the Fennec package.
-          -->
-        <service
-            android:exported="false"
-            android:name="org.mozilla.gecko.background.healthreport.HealthReportBroadcastService" >
-        </service>
-        <service
-            android:exported="false"
-            android:name="org.mozilla.gecko.background.healthreport.upload.HealthReportUploadService" >
-        </service>
-        <service
-            android:exported="false"
-            android:name="org.mozilla.gecko.background.healthreport.prune.HealthReportPruneService" >
-        </service>
--- a/mobile/android/services/manifests/SyncAndroidManifest_permissions.xml.in
+++ b/mobile/android/services/manifests/SyncAndroidManifest_permissions.xml.in
@@ -24,8 +24,10 @@
          GlobalConstants.PER_ANDROID_PACKAGE_PERMISSION.
 
          This is not Sync-specific, but we don't have a better place
          to put generic background service manifest snippets, so here
          is expedient. -->
     <permission
         android:name="@ANDROID_PACKAGE_NAME@.permission.PER_ANDROID_PACKAGE"
         android:protectionLevel="signature"/>
+
+    <uses-permission android:name="@ANDROID_PACKAGE_NAME@.permission.PER_ANDROID_PACKAGE" />
--- a/mobile/android/tests/background/junit3/background_junit3_sources.mozbuild
+++ b/mobile/android/tests/background/junit3/background_junit3_sources.mozbuild
@@ -21,32 +21,16 @@ background_junit3_sources = [
     'src/org/mozilla/gecko/background/db/TestFennecTabsRepositorySession.java',
     'src/org/mozilla/gecko/background/db/TestFennecTabsStorage.java',
     'src/org/mozilla/gecko/background/db/TestFormHistoryRepositorySession.java',
     'src/org/mozilla/gecko/background/db/TestPasswordsRepository.java',
     'src/org/mozilla/gecko/background/fxa/authenticator/TestAccountPickler.java',
     'src/org/mozilla/gecko/background/fxa/TestAccountLoader.java',
     'src/org/mozilla/gecko/background/fxa/TestBrowserIDKeyPairGeneration.java',
     'src/org/mozilla/gecko/background/fxa/TestFirefoxAccounts.java',
-    'src/org/mozilla/gecko/background/healthreport/MockDatabaseEnvironment.java',
-    'src/org/mozilla/gecko/background/healthreport/MockHealthReportDatabaseStorage.java',
-    'src/org/mozilla/gecko/background/healthreport/MockHealthReportSQLiteOpenHelper.java',
-    'src/org/mozilla/gecko/background/healthreport/MockProfileInformationCache.java',
-    'src/org/mozilla/gecko/background/healthreport/prune/TestHealthReportPruneService.java',
-    'src/org/mozilla/gecko/background/healthreport/prune/TestPrunePolicyDatabaseStorage.java',
-    'src/org/mozilla/gecko/background/healthreport/TestEnvironmentBuilder.java',
-    'src/org/mozilla/gecko/background/healthreport/TestEnvironmentV1HashAppender.java',
-    'src/org/mozilla/gecko/background/healthreport/TestHealthReportBroadcastService.java',
-    'src/org/mozilla/gecko/background/healthreport/TestHealthReportDatabaseStorage.java',
-    'src/org/mozilla/gecko/background/healthreport/TestHealthReportGenerator.java',
-    'src/org/mozilla/gecko/background/healthreport/TestHealthReportProvider.java',
-    'src/org/mozilla/gecko/background/healthreport/TestHealthReportSQLiteOpenHelper.java',
-    'src/org/mozilla/gecko/background/healthreport/TestProfileInformationCache.java',
-    'src/org/mozilla/gecko/background/healthreport/upload/TestAndroidSubmissionClient.java',
-    'src/org/mozilla/gecko/background/healthreport/upload/TestHealthReportUploadService.java',
     'src/org/mozilla/gecko/background/helpers/AndroidSyncTestCase.java',
     'src/org/mozilla/gecko/background/helpers/BackgroundServiceTestCase.java',
     'src/org/mozilla/gecko/background/helpers/DBHelpers.java',
     'src/org/mozilla/gecko/background/helpers/DBProviderTestCase.java',
     'src/org/mozilla/gecko/background/helpers/FakeProfileTestCase.java',
     'src/org/mozilla/gecko/background/nativecode/test/TestNativeCrypto.java',
     'src/org/mozilla/gecko/background/sync/AndroidSyncTestCaseWithAccounts.java',
     'src/org/mozilla/gecko/background/sync/helpers/BookmarkHelpers.java',
@@ -100,12 +84,11 @@ background_junit3_sources = [
     'src/org/mozilla/gecko/background/testhelpers/MockAbstractNonRepositorySyncStage.java',
     'src/org/mozilla/gecko/background/testhelpers/MockClientsDatabaseAccessor.java',
     'src/org/mozilla/gecko/background/testhelpers/MockClientsDataDelegate.java',
     'src/org/mozilla/gecko/background/testhelpers/MockGlobalSession.java',
     'src/org/mozilla/gecko/background/testhelpers/MockPrefsGlobalSession.java',
     'src/org/mozilla/gecko/background/testhelpers/MockRecord.java',
     'src/org/mozilla/gecko/background/testhelpers/MockServerSyncStage.java',
     'src/org/mozilla/gecko/background/testhelpers/MockSharedPreferences.java',
-    'src/org/mozilla/gecko/background/testhelpers/StubDelegate.java',
     'src/org/mozilla/gecko/background/testhelpers/WaitHelper.java',
     'src/org/mozilla/gecko/background/testhelpers/WBORepository.java',
 ]
--- a/mobile/android/tests/background/junit3/instrumentation.ini
+++ b/mobile/android/tests/background/junit3/instrumentation.ini
@@ -14,28 +14,16 @@ subsuite = background
 [src/org/mozilla/gecko/background/db/TestClientsDatabase.java]
 [src/org/mozilla/gecko/background/db/TestClientsDatabaseAccessor.java]
 [src/org/mozilla/gecko/background/db/TestFennecTabsRepositorySession.java]
 [src/org/mozilla/gecko/background/db/TestFennecTabsStorage.java]
 [src/org/mozilla/gecko/background/db/TestFormHistoryRepositorySession.java]
 [src/org/mozilla/gecko/background/db/TestPasswordsRepository.java]
 [src/org/mozilla/gecko/background/fxa/TestBrowserIDKeyPairGeneration.java]
 [src/org/mozilla/gecko/background/fxa/authenticator/TestAccountPickler.java]
-[src/org/mozilla/gecko/background/healthreport/TestEnvironmentBuilder.java]
-[src/org/mozilla/gecko/background/healthreport/TestEnvironmentV1HashAppender.java]
-[src/org/mozilla/gecko/background/healthreport/TestHealthReportBroadcastService.java]
-[src/org/mozilla/gecko/background/healthreport/TestHealthReportDatabaseStorage.java]
-[src/org/mozilla/gecko/background/healthreport/TestHealthReportGenerator.java]
-[src/org/mozilla/gecko/background/healthreport/TestHealthReportProvider.java]
-[src/org/mozilla/gecko/background/healthreport/TestHealthReportSQLiteOpenHelper.java]
-[src/org/mozilla/gecko/background/healthreport/TestProfileInformationCache.java]
-[src/org/mozilla/gecko/background/healthreport/prune/TestHealthReportPruneService.java]
-[src/org/mozilla/gecko/background/healthreport/prune/TestPrunePolicyDatabaseStorage.java]
-[src/org/mozilla/gecko/background/healthreport/upload/TestAndroidSubmissionClient.java]
-[src/org/mozilla/gecko/background/healthreport/upload/TestHealthReportUploadService.java]
 [src/org/mozilla/gecko/background/nativecode/test/TestNativeCrypto.java]
 [src/org/mozilla/gecko/background/sync/TestAccountPickler.java]
 [src/org/mozilla/gecko/background/sync/TestClientsStage.java]
 [src/org/mozilla/gecko/background/sync/TestConfigurationMigrator.java]
 [src/org/mozilla/gecko/background/sync/TestResetting.java]
 [src/org/mozilla/gecko/background/sync/TestSendTabData.java]
 [src/org/mozilla/gecko/background/sync/TestStoreTracking.java]
 [src/org/mozilla/gecko/background/sync/TestSyncAccounts.java]
deleted file mode 100644
--- a/mobile/android/tests/background/junit3/src/org/mozilla/gecko/background/healthreport/MockDatabaseEnvironment.java
+++ /dev/null
@@ -1,76 +0,0 @@
-/* Any copyright is dedicated to the Public Domain.
-   http://creativecommons.org/publicdomain/zero/1.0/ */
-
-package org.mozilla.gecko.background.healthreport;
-
-import org.mozilla.gecko.background.healthreport.HealthReportDatabaseStorage;
-import org.mozilla.gecko.background.healthreport.HealthReportDatabaseStorage.DatabaseEnvironment;
-
-public class MockDatabaseEnvironment extends DatabaseEnvironment {
-  public MockDatabaseEnvironment(HealthReportDatabaseStorage storage, Class<? extends EnvironmentAppender> appender) {
-    super(storage, appender);
-  }
-
-  public MockDatabaseEnvironment(HealthReportDatabaseStorage storage) {
-    super(storage);
-  }
-
-  public static class MockEnvironmentAppender extends EnvironmentAppender {
-    public StringBuilder appended = new StringBuilder();
-
-    public MockEnvironmentAppender() {
-      super();
-    }
-
-    @Override
-    public void append(String s) {
-      appended.append(s);
-    }
-
-    @Override
-    public void append(int v) {
-      appended.append(v);
-    }
-
-    @Override
-    public String toString() {
-      return appended.toString();
-    }
-  }
-
-  public MockDatabaseEnvironment mockInit(String appVersion) {
-    profileCreation = 1234;
-    cpuCount        = 2;
-    memoryMB        = 512;
-
-    isBlocklistEnabled = 1;
-    isTelemetryEnabled = 1;
-    extensionCount     = 0;
-    pluginCount        = 0;
-    themeCount         = 0;
-
-    architecture    = "";
-    sysName         = "";
-    sysVersion      = "";
-    vendor          = "";
-    appName         = "";
-    appID           = "";
-    this.appVersion = appVersion;
-    appBuildID      = "";
-    platformVersion = "";
-    platformBuildID = "";
-    os              = "";
-    xpcomabi        = "";
-    updateChannel   = "";
-
-    // v2 fields.
-    distribution  = "";
-    appLocale     = "";
-    osLocale      = "";
-    acceptLangSet = 0;
-
-    version       = Environment.CURRENT_VERSION;
-
-    return this;
-  }
-}
deleted file mode 100644
--- a/mobile/android/tests/background/junit3/src/org/mozilla/gecko/background/healthreport/MockHealthReportDatabaseStorage.java
+++ /dev/null
@@ -1,280 +0,0 @@
-/* Any copyright is dedicated to the Public Domain.
-   http://creativecommons.org/publicdomain/zero/1.0/ */
-
-package org.mozilla.gecko.background.healthreport;
-
-import java.io.File;
-import java.util.ArrayList;
-import java.util.concurrent.ConcurrentHashMap;
-
-import org.json.JSONObject;
-import org.mozilla.gecko.background.common.GlobalConstants;
-import org.mozilla.gecko.background.healthreport.HealthReportDatabaseStorage;
-import org.mozilla.gecko.background.healthreport.HealthReportStorage.MeasurementFields.FieldSpec;
-
-import android.content.ContentValues;
-import android.content.Context;
-import android.database.sqlite.SQLiteDatabase;
-
-public class MockHealthReportDatabaseStorage extends HealthReportDatabaseStorage {
-  public long now = System.currentTimeMillis();
-
-  public long getOneDayAgo() {
-    return now - GlobalConstants.MILLISECONDS_PER_DAY;
-  }
-
-  public int getYesterday() {
-    return super.getDay(this.getOneDayAgo());
-  }
-
-  public int getToday() {
-    return super.getDay(now);
-  }
-
-  public int getTomorrow() {
-    return super.getDay(now + GlobalConstants.MILLISECONDS_PER_DAY);
-  }
-
-  public int getGivenDaysAgo(int numDays) {
-    return super.getDay(this.getGivenDaysAgoMillis(numDays));
-  }
-
-  public long getGivenDaysAgoMillis(int numDays) {
-    return now - numDays * GlobalConstants.MILLISECONDS_PER_DAY;
-  }
-
-  public ConcurrentHashMap<String, Integer> getEnvironmentCache() {
-    return this.envs;
-  }
-
-  public MockHealthReportDatabaseStorage(Context context, File fakeProfileDirectory) {
-    super(context, fakeProfileDirectory);
-  }
-
-  public SQLiteDatabase getDB() {
-    return this.helper.getWritableDatabase();
-  }
-
-  @Override
-  public MockDatabaseEnvironment getEnvironment() {
-    return new MockDatabaseEnvironment(this);
-  }
-
-  @Override
-  public int deleteEnvAndEventsBefore(long time, int curEnv) {
-    return super.deleteEnvAndEventsBefore(time, curEnv);
-  }
-
-  @Override
-  public int deleteOrphanedEnv(int curEnv) {
-    return super.deleteOrphanedEnv(curEnv);
-  }
-
-  @Override
-  public int deleteEventsBefore(String dayString) {
-    return super.deleteEventsBefore(dayString);
-  }
-
-  @Override
-  public int deleteOrphanedAddons() {
-    return super.deleteOrphanedAddons();
-  }
-
-  @Override
-  public int getIntFromQuery(final String sql, final String[] selectionArgs) {
-    return super.getIntFromQuery(sql, selectionArgs);
-  }
-
-  /**
-   * A storage instance prepopulated with dummy data to be used for testing.
-   *
-   * Modifying this data directly will cause tests relying on it to fail so use the versioned
-   * constructor to change the data if it's the desired version. Example:
-   * <pre>
-   *  if (version >= 3) {
-   *    addVersion3Stuff();
-   *  }
-   *  if (version >= 2) {
-   *    addVersion2Stuff();
-   *  }
-   *  addVersion1Stuff();
-   * </pre>
-   *
-   * Don't forget to increment the {@link MAX_VERSION_USED} constant.
-   *
-   * Note that all instances of this class use the same underlying database and so each newly
-   * created instance will share the same data.
-   */
-  public static class PrepopulatedMockHealthReportDatabaseStorage extends MockHealthReportDatabaseStorage {
-    // A constant to enforce which version constructor is the maximum used so far.
-    private int MAX_VERSION_USED = 2;
-
-    public String[] measurementNames;
-    public int[] measurementVers;
-    public FieldSpecContainer[] fieldSpecContainers;
-    public int env;
-    private final JSONObject addonJSON = new JSONObject(
-        "{ " +
-        "\"amznUWL2@amazon.com\": { " +
-        "  \"userDisabled\": false, " +
-        "  \"appDisabled\": false, " +
-        "  \"version\": \"1.10\", " +
-        "  \"type\": \"extension\", " +
-        "  \"scope\": 1, " +
-        "  \"foreignInstall\": false, " +
-        "  \"hasBinaryComponents\": false, " +
-        "  \"installDay\": 15269, " +
-        "  \"updateDay\": 15602 " +
-        "}, " +
-        "\"jid0-qBnIpLfDFa4LpdrjhAC6vBqN20Q@jetpack\": { " +
-        "  \"userDisabled\": false, " +
-        "  \"appDisabled\": false, " +
-        "  \"version\": \"1.12.1\", " +
-        "  \"type\": \"extension\", " +
-        "  \"scope\": 1, " +
-        "  \"foreignInstall\": false, " +
-        "  \"hasBinaryComponents\": false, " +
-        "  \"installDay\": 15062, " +
-        "  \"updateDay\": 15580 " +
-        "} " +
-        "} ");
-
-    public static class FieldSpecContainer {
-      public final FieldSpec counter;
-      public final FieldSpec discrete;
-      public final FieldSpec last;
-
-      public FieldSpecContainer(FieldSpec counter, FieldSpec discrete, FieldSpec last) {
-        this.counter = counter;
-        this.discrete = discrete;
-        this.last = last;
-      }
-
-      public ArrayList<FieldSpec> asList() {
-        final ArrayList<FieldSpec> out = new ArrayList<FieldSpec>(3);
-        out.add(counter);
-        out.add(discrete);
-        out.add(last);
-        return out;
-      }
-    }
-
-    public PrepopulatedMockHealthReportDatabaseStorage(Context context, File fakeProfileDirectory) throws Exception {
-      this(context, fakeProfileDirectory, 1);
-    }
-
-    public PrepopulatedMockHealthReportDatabaseStorage(Context context, File fakeProfileDirectory, int version) throws Exception {
-      super(context, fakeProfileDirectory);
-
-      if (version > MAX_VERSION_USED || version < 1) {
-        throw new IllegalStateException("Invalid version number! Check " +
-            "PrepopulatedMockHealthReportDatabaseStorage.MAX_VERSION_USED!");
-      }
-
-      measurementNames = new String[2];
-      measurementNames[0] = "a_string_measurement";
-      measurementNames[1] = "b_integer_measurement";
-
-      measurementVers = new int[2];
-      measurementVers[0] = 1;
-      measurementVers[1] = 2;
-
-      fieldSpecContainers = new FieldSpecContainer[2];
-      fieldSpecContainers[0] = new FieldSpecContainer(
-          new FieldSpec("a_counter_integer_field", Field.TYPE_INTEGER_COUNTER),
-          new FieldSpec("a_discrete_string_field", Field.TYPE_STRING_DISCRETE),
-          new FieldSpec("a_last_string_field", Field.TYPE_STRING_LAST));
-      fieldSpecContainers[1] = new FieldSpecContainer(
-          new FieldSpec("b_counter_integer_field", Field.TYPE_INTEGER_COUNTER),
-          new FieldSpec("b_discrete_integer_field", Field.TYPE_INTEGER_DISCRETE),
-          new FieldSpec("b_last_integer_field", Field.TYPE_INTEGER_LAST));
-
-      final MeasurementFields[] measurementFields =
-          new MeasurementFields[fieldSpecContainers.length];
-      for (int i = 0; i < fieldSpecContainers.length; i++) {
-        final FieldSpecContainer fieldSpecContainer = fieldSpecContainers[i];
-        measurementFields[i] = new MeasurementFields() {
-          @Override
-          public Iterable<FieldSpec> getFields() {
-            return fieldSpecContainer.asList();
-          }
-        };
-      }
-
-      this.beginInitialization();
-      for (int i = 0; i < measurementNames.length; i++) {
-        this.ensureMeasurementInitialized(measurementNames[i], measurementVers[i],
-            measurementFields[i]);
-      }
-      this.finishInitialization();
-
-      MockDatabaseEnvironment environment = this.getEnvironment();
-      environment.mockInit("v123");
-      environment.setJSONForAddons(addonJSON);
-      env = environment.register();
-
-      String mName = measurementNames[0];
-      int mVer = measurementVers[0];
-      FieldSpecContainer fieldSpecCont = fieldSpecContainers[0];
-      int fieldID = this.getField(mName, mVer, fieldSpecCont.counter.name).getID();
-      this.incrementDailyCount(env, this.getGivenDaysAgo(7), fieldID, 1);
-      this.incrementDailyCount(env, this.getGivenDaysAgo(4), fieldID, 2);
-      this.incrementDailyCount(env, this.getToday(), fieldID, 3);
-      fieldID = this.getField(mName, mVer, fieldSpecCont.discrete.name).getID();
-      this.recordDailyDiscrete(env, this.getGivenDaysAgo(5), fieldID, "five");
-      this.recordDailyDiscrete(env, this.getGivenDaysAgo(5), fieldID, "five-two");
-      this.recordDailyDiscrete(env, this.getGivenDaysAgo(2), fieldID, "two");
-      this.recordDailyDiscrete(env, this.getToday(), fieldID, "zero");
-      fieldID = this.getField(mName, mVer, fieldSpecCont.last.name).getID();
-      this.recordDailyLast(env, this.getGivenDaysAgo(6), fieldID, "six");
-      this.recordDailyLast(env, this.getGivenDaysAgo(3), fieldID, "three");
-      this.recordDailyLast(env, this.getToday(), fieldID, "zero");
-
-      mName = measurementNames[1];
-      mVer = measurementVers[1];
-      fieldSpecCont = fieldSpecContainers[1];
-      fieldID = this.getField(mName, mVer, fieldSpecCont.counter.name).getID();
-      this.incrementDailyCount(env, this.getGivenDaysAgo(2), fieldID, 2);
-      fieldID = this.getField(mName, mVer, fieldSpecCont.discrete.name).getID();
-      this.recordDailyDiscrete(env, this.getToday(), fieldID, 0);
-      this.recordDailyDiscrete(env, this.getToday(), fieldID, 1);
-      fieldID = this.getField(mName, mVer, fieldSpecCont.last.name).getID();
-      this.recordDailyLast(env, this.getYesterday(), fieldID, 1);
-
-      if (version >= 2) {
-        // Insert more diverse environments.
-        for (int i = 1; i <= 3; i++) {
-          environment = this.getEnvironment();
-          environment.mockInit("v" + i);
-          env = environment.register();
-          this.recordDailyLast(env, this.getGivenDaysAgo(7 * i + 1), fieldID, 13);
-        }
-        environment = this.getEnvironment();
-        environment.mockInit("v4");
-        env = environment.register();
-        this.recordDailyLast(env, this.getGivenDaysAgo(1000), fieldID, 14);
-        this.recordDailyLast(env, this.getToday(), fieldID, 15);
-      }
-    }
-
-    public void insertTextualEvents(final int count) {
-      final ContentValues v = new ContentValues();
-      v.put("env", env);
-      final int fieldID = this.getField(measurementNames[0], measurementVers[0],
-          fieldSpecContainers[0].discrete.name).getID();
-      v.put("field", fieldID);
-      v.put("value", "data");
-      final SQLiteDatabase db = this.helper.getWritableDatabase();
-      db.beginTransaction();
-      try {
-        for (int i = 1; i <= count; i++) {
-          v.put("date", i);
-          db.insertOrThrow("events_textual", null, v);
-        }
-        db.setTransactionSuccessful();
-      } finally {
-        db.endTransaction();
-      }
-    }
-  }
-}
deleted file mode 100644
--- a/mobile/android/tests/background/junit3/src/org/mozilla/gecko/background/healthreport/MockHealthReportSQLiteOpenHelper.java
+++ /dev/null
@@ -1,172 +0,0 @@
-/* Any copyright is dedicated to the Public Domain.
-   http://creativecommons.org/publicdomain/zero/1.0/ */
-
-package org.mozilla.gecko.background.healthreport;
-
-import java.io.File;
-
-import org.mozilla.gecko.background.healthreport.HealthReportDatabaseStorage.HealthReportSQLiteOpenHelper;
-
-import android.content.Context;
-import android.database.sqlite.SQLiteDatabase;
-
-public class MockHealthReportSQLiteOpenHelper extends HealthReportSQLiteOpenHelper {
-  private int version;
-
-  public MockHealthReportSQLiteOpenHelper(Context context, File fakeProfileDirectory, String name) {
-    super(context, fakeProfileDirectory, name);
-    version = HealthReportSQLiteOpenHelper.CURRENT_VERSION;
-  }
-
-  public MockHealthReportSQLiteOpenHelper(Context context, File fakeProfileDirectory, String name, int version) {
-    super(context, fakeProfileDirectory, name, version);
-    this.version = version;
-  }
-
-  @Override
-  public void onCreate(SQLiteDatabase db) {
-    if (version == HealthReportSQLiteOpenHelper.CURRENT_VERSION) {
-      super.onCreate(db);
-    } else if (version == 4) {
-      onCreateSchemaVersion4(db);
-    } else {
-      throw new IllegalStateException("Unknown version number, " + version + ".");
-    }
-  }
-
-  // Copy-pasta from HealthReportDatabaseStorage.onCreate from v4.
-  public void onCreateSchemaVersion4(SQLiteDatabase db) {
-    db.beginTransaction();
-    try {
-      db.execSQL("CREATE TABLE addons (id INTEGER PRIMARY KEY AUTOINCREMENT, " +
-                 "                     body TEXT, " +
-                 "                     UNIQUE (body) " +
-                 ")");
-
-      db.execSQL("CREATE TABLE environments (id INTEGER PRIMARY KEY AUTOINCREMENT, " +
-                 "                           hash TEXT, " +
-                 "                           profileCreation INTEGER, " +
-                 "                           cpuCount        INTEGER, " +
-                 "                           memoryMB        INTEGER, " +
-                 "                           isBlocklistEnabled INTEGER, " +
-                 "                           isTelemetryEnabled INTEGER, " +
-                 "                           extensionCount     INTEGER, " +
-                 "                           pluginCount        INTEGER, " +
-                 "                           themeCount         INTEGER, " +
-                 "                           architecture    TEXT, " +
-                 "                           sysName         TEXT, " +
-                 "                           sysVersion      TEXT, " +
-                 "                           vendor          TEXT, " +
-                 "                           appName         TEXT, " +
-                 "                           appID           TEXT, " +
-                 "                           appVersion      TEXT, " +
-                 "                           appBuildID      TEXT, " +
-                 "                           platformVersion TEXT, " +
-                 "                           platformBuildID TEXT, " +
-                 "                           os              TEXT, " +
-                 "                           xpcomabi        TEXT, " +
-                 "                           updateChannel   TEXT, " +
-                 "                           addonsID        INTEGER, " +
-                 "                           FOREIGN KEY (addonsID) REFERENCES addons(id) ON DELETE RESTRICT, " +
-                 "                           UNIQUE (hash) " +
-                 ")");
-
-      db.execSQL("CREATE TABLE measurements (id INTEGER PRIMARY KEY AUTOINCREMENT, " +
-                 "                           name TEXT, " +
-                 "                           version INTEGER, " +
-                 "                           UNIQUE (name, version) " +
-                 ")");
-
-      db.execSQL("CREATE TABLE fields (id INTEGER PRIMARY KEY AUTOINCREMENT, " +
-                 "                     measurement INTEGER, " +
-                 "                     name TEXT, " +
-                 "                     flags INTEGER, " +
-                 "                     FOREIGN KEY (measurement) REFERENCES measurements(id) ON DELETE CASCADE, " +
-                 "                     UNIQUE (measurement, name)" +
-                 ")");
-
-      db.execSQL("CREATE TABLE events_integer (" +
-                 "                 date  INTEGER, " +
-                 "                 env   INTEGER, " +
-                 "                 field INTEGER, " +
-                 "                 value INTEGER, " +
-                 "                 FOREIGN KEY (field) REFERENCES fields(id) ON DELETE CASCADE, " +
-                 "                 FOREIGN KEY (env) REFERENCES environments(id) ON DELETE CASCADE" +
-                 ")");
-
-      db.execSQL("CREATE TABLE events_textual (" +
-                 "                 date  INTEGER, " +
-                 "                 env   INTEGER, " +
-                 "                 field INTEGER, " +
-                 "                 value TEXT, " +
-                 "                 FOREIGN KEY (field) REFERENCES fields(id) ON DELETE CASCADE, " +
-                 "                 FOREIGN KEY (env) REFERENCES environments(id) ON DELETE CASCADE" +
-                 ")");
-
-      db.execSQL("CREATE INDEX idx_events_integer_date_env_field ON events_integer (date, env, field)");
-      db.execSQL("CREATE INDEX idx_events_textual_date_env_field ON events_textual (date, env, field)");
-
-      db.execSQL("CREATE VIEW events AS " +
-                 "SELECT date, env, field, value FROM events_integer " +
-                 "UNION ALL " +
-                 "SELECT date, env, field, value FROM events_textual");
-
-      db.execSQL("CREATE VIEW named_events AS " +
-                 "SELECT date, " +
-                 "       environments.hash AS environment, " +
-                 "       measurements.name AS measurement_name, " +
-                 "       measurements.version AS measurement_version, " +
-                 "       fields.name AS field_name, " +
-                 "       fields.flags AS field_flags, " +
-                 "       value FROM " +
-                 "events JOIN environments ON events.env = environments.id " +
-                 "       JOIN fields ON events.field = fields.id " +
-                 "       JOIN measurements ON fields.measurement = measurements.id");
-
-      db.execSQL("CREATE VIEW named_fields AS " +
-                 "SELECT measurements.name AS measurement_name, " +
-                 "       measurements.id AS measurement_id, " +
-                 "       measurements.version AS measurement_version, " +
-                 "       fields.name AS field_name, " +
-                 "       fields.id AS field_id, " +
-                 "       fields.flags AS field_flags " +
-                 "FROM fields JOIN measurements ON fields.measurement = measurements.id");
-
-      db.execSQL("CREATE VIEW current_measurements AS " +
-                 "SELECT name, MAX(version) AS version FROM measurements GROUP BY name");
-
-      // createAddonsEnvironmentsView(db):
-      db.execSQL("CREATE VIEW environments_with_addons AS " +
-          "SELECT e.id AS id, " +
-          "       e.hash AS hash, " +
-          "       e.profileCreation AS profileCreation, " +
-          "       e.cpuCount AS cpuCount, " +
-          "       e.memoryMB AS memoryMB, " +
-          "       e.isBlocklistEnabled AS isBlocklistEnabled, " +
-          "       e.isTelemetryEnabled AS isTelemetryEnabled, " +
-          "       e.extensionCount AS extensionCount, " +
-          "       e.pluginCount AS pluginCount, " +
-          "       e.themeCount AS themeCount, " +
-          "       e.architecture AS architecture, " +
-          "       e.sysName AS sysName, " +
-          "       e.sysVersion AS sysVersion, " +
-          "       e.vendor AS vendor, " +
-          "       e.appName AS appName, " +
-          "       e.appID AS appID, " +
-          "       e.appVersion AS appVersion, " +
-          "       e.appBuildID AS appBuildID, " +
-          "       e.platformVersion AS platformVersion, " +
-          "       e.platformBuildID AS platformBuildID, " +
-          "       e.os AS os, " +
-          "       e.xpcomabi AS xpcomabi, " +
-          "       e.updateChannel AS updateChannel, " +
-          "       addons.body AS addonsBody " +
-          "FROM environments AS e, addons " +
-          "WHERE e.addonsID = addons.id");
-
-      db.setTransactionSuccessful();
-    } finally {
-      db.endTransaction();
-    }
-  }
-}
deleted file mode 100644
--- a/mobile/android/tests/background/junit3/src/org/mozilla/gecko/background/healthreport/MockProfileInformationCache.java
+++ /dev/null
@@ -1,44 +0,0 @@
-/* Any copyright is dedicated to the Public Domain.
-   http://creativecommons.org/publicdomain/zero/1.0/ */
-
-package org.mozilla.gecko.background.healthreport;
-
-import java.io.File;
-import java.io.FileNotFoundException;
-import java.io.IOException;
-
-import org.json.JSONException;
-import org.json.JSONObject;
-import org.mozilla.gecko.background.healthreport.ProfileInformationCache;
-
-public class MockProfileInformationCache extends ProfileInformationCache {
-  public MockProfileInformationCache(String profilePath) {
-    super(profilePath);
-  }
-
-  public MockProfileInformationCache(File mockFile) {
-      super(mockFile);
-  }
-
-  public boolean isInitialized() {
-    return this.initialized;
-  }
-  public boolean needsWrite() {
-    return this.needsWrite;
-  }
-  public File getFile() {
-    return this.file;
-  }
-
-  public void writeJSON(JSONObject toWrite) throws IOException {
-    writeToFile(toWrite);
-  }
-
-  public JSONObject readJSON() throws FileNotFoundException, JSONException {
-    return readFromFile();
-  }
-
-  public void setInitialized(final boolean initialized) {
-    this.initialized = initialized;
-  }
-}
deleted file mode 100644
--- a/mobile/android/tests/background/junit3/src/org/mozilla/gecko/background/healthreport/TestEnvironmentBuilder.java
+++ /dev/null
@@ -1,83 +0,0 @@
-/* Any copyright is dedicated to the Public Domain.
-   http://creativecommons.org/publicdomain/zero/1.0/ */
-
-package org.mozilla.gecko.background.healthreport;
-
-import java.io.File;
-import java.io.IOException;
-
-import org.json.JSONException;
-import org.json.JSONObject;
-import org.mozilla.gecko.AppConstants;
-import org.mozilla.gecko.background.common.GlobalConstants;
-import org.mozilla.gecko.background.helpers.FakeProfileTestCase;
-
-public class TestEnvironmentBuilder extends FakeProfileTestCase {
-  public static void testIgnoringAddons() throws JSONException {
-    Environment env = new Environment() {
-      @Override
-      public int register() {
-        return 0;
-      }
-    };
-
-    JSONObject addons = new JSONObject();
-    JSONObject foo = new JSONObject();
-    foo.put("a", 1);
-    foo.put("b", "c");
-    addons.put("foo", foo);
-    JSONObject ignore = new JSONObject();
-    ignore.put("ignore", true);
-    addons.put("ig", ignore);
-
-    env.setJSONForAddons(addons);
-
-    JSONObject kept = env.getNonIgnoredAddons();
-    assertTrue(kept.has("foo"));
-    assertFalse(kept.has("ig"));
-    JSONObject fooCopy = kept.getJSONObject("foo");
-    assertSame(foo, fooCopy);
-  }
-
-  public void testSanity() throws IOException {
-    File subdir = new File(this.fakeProfileDirectory.getAbsolutePath() +
-                           File.separator + "testPersisting");
-    subdir.mkdir();
-    long now = System.currentTimeMillis();
-    int expectedDays = (int) (now / GlobalConstants.MILLISECONDS_PER_DAY);
-
-    MockProfileInformationCache cache = new MockProfileInformationCache(subdir.getAbsolutePath());
-    assertFalse(cache.getFile().exists());
-    cache.beginInitialization();
-    cache.setBlocklistEnabled(true);
-    cache.setTelemetryEnabled(false);
-    cache.setProfileCreationTime(now);
-    cache.completeInitialization();
-    assertTrue(cache.getFile().exists());
-
-    final AndroidConfigurationProvider configProvider = new AndroidConfigurationProvider(context);
-    Environment environment = EnvironmentBuilder.getCurrentEnvironment(cache, configProvider);
-    assertEquals(AppConstants.MOZ_APP_BUILDID, environment.appBuildID);
-    assertEquals("Android", environment.os);
-    assertTrue(100 < environment.memoryMB); // Seems like a sane lower bound...
-    assertTrue(environment.cpuCount >= 1);
-    assertEquals(1, environment.isBlocklistEnabled);
-    assertEquals(0, environment.isTelemetryEnabled);
-    assertEquals(expectedDays, environment.profileCreation);
-    assertEquals(EnvironmentBuilder.getCurrentEnvironment(cache, configProvider).getHash(),
-                 environment.getHash());
-
-    // v3 sanity.
-    assertEquals(configProvider.hasHardwareKeyboard(), environment.hasHardwareKeyboard);
-    assertEquals(configProvider.getScreenXInMM(), environment.screenXInMM);
-    assertTrue(1 < environment.screenXInMM);
-    assertTrue(2000 > environment.screenXInMM);
-
-    cache.beginInitialization();
-    cache.setBlocklistEnabled(false);
-    cache.completeInitialization();
-
-    assertFalse(EnvironmentBuilder.getCurrentEnvironment(cache, configProvider).getHash()
-                                  .equals(environment.getHash()));
-  }
-}
deleted file mode 100644
--- a/mobile/android/tests/background/junit3/src/org/mozilla/gecko/background/healthreport/TestEnvironmentV1HashAppender.java
+++ /dev/null
@@ -1,146 +0,0 @@
-/* Any copyright is dedicated to the Public Domain.
-   http://creativecommons.org/publicdomain/zero/1.0/ */
-
-package org.mozilla.gecko.background.healthreport;
-
-import java.io.UnsupportedEncodingException;
-import java.security.MessageDigest;
-import java.security.NoSuchAlgorithmException;
-import java.util.Arrays;
-import java.util.LinkedList;
-
-import org.mozilla.apache.commons.codec.binary.Base64;
-import org.mozilla.gecko.background.healthreport.EnvironmentV1.EnvironmentAppender;
-import org.mozilla.gecko.background.healthreport.EnvironmentV1.HashAppender;
-import org.mozilla.gecko.background.helpers.FakeProfileTestCase;
-import org.mozilla.gecko.sync.Utils;
-
-/**
- * Tests the HashAppender functionality. Note that these tests must be run on an Android
- * device because the SHA-1 native library needs to be loaded.
- */
-public class TestEnvironmentV1HashAppender extends FakeProfileTestCase {
-  // input and expected values via: http://oauth.googlecode.com/svn/code/c/liboauth/src/sha1.c
-  private final static String[] INPUTS = new String[] {
-    "abc",
-    "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq",
-    "" // To be filled in below.
-  };
-  static {
-    final String baseStr = "01234567";
-    final int repetitions = 80;
-    final StringBuilder builder = new StringBuilder(baseStr.length() * repetitions);
-    for (int i = 0; i < 80; ++i) {
-      builder.append(baseStr);
-    }
-    INPUTS[2] = builder.toString();
-  }
-
-  private final static String[] EXPECTEDS = new String[] {
-    "a9993e364706816aba3e25717850c26c9cd0d89d",
-    "84983e441c3bd26ebaae4aa1f95129e5e54670f1",
-    "dea356a2cddd90c7a7ecedc5ebb563934f460452"
-  };
-  static {
-    for (int i = 0; i < EXPECTEDS.length; ++i) {
-      EXPECTEDS[i] = new Base64(-1, null, false).encodeAsString(Utils.hex2Byte(EXPECTEDS[i]));
-    }
-  }
-
-  public void testSHA1Hashing() throws Exception {
-    for (int i = 0; i < INPUTS.length; ++i) {
-      final String input = INPUTS[i];
-      final String expected = EXPECTEDS[i];
-
-      final HashAppender appender = new HashAppender();
-      addStringToAppenderInParts(appender, input);
-      final String result = appender.toString();
-
-      assertEquals(expected, result);
-    }
-  }
-
-  /**
-   * Tests to ensure output is the same as the former MessageDigest implementation (bug 959652).
-   */
-  public void testAgainstMessageDigestImpl() throws Exception {
-    // List.add doesn't allow add(null) so we make a LinkedList here.
-    final LinkedList<String> inputs = new LinkedList<String>(Arrays.asList(INPUTS));
-    inputs.add(null);
-
-    for (final String input : inputs) {
-      final HashAppender hAppender = new HashAppender();
-      final MessageDigestHashAppender mdAppender = new MessageDigestHashAppender();
-
-      hAppender.append(input);
-      mdAppender.append(input);
-
-      final String hResult = hAppender.toString();
-      final String mdResult = mdAppender.toString();
-      assertEquals(mdResult, hResult);
-    }
-  }
-
-  public void testIntegersAgainstMessageDigestImpl() throws Exception {
-    final int[] INPUTS = {Integer.MIN_VALUE, -1337, -42, 0, 42, 1337, Integer.MAX_VALUE};
-    for (final int input : INPUTS) {
-      final HashAppender hAppender = new HashAppender();
-      final MessageDigestHashAppender mdAppender = new MessageDigestHashAppender();
-
-      hAppender.append(input);
-      mdAppender.append(input);
-
-      final String hResult = hAppender.toString();
-      final String mdResult = mdAppender.toString();
-      assertEquals(mdResult, hResult);
-    }
-  }
-
-  private void addStringToAppenderInParts(final EnvironmentAppender appender, final String input) {
-    int substrInd = 0;
-    int substrLength = 1;
-    while (substrInd < input.length()) {
-      final int endInd = Math.min(substrInd + substrLength, input.length());
-
-      appender.append(input.substring(substrInd, endInd));
-
-      substrInd = endInd;
-      ++substrLength;
-    }
-  }
-
-  // --- COPY-PASTA'D CODE, FOR TESTING PURPOSES. ---
-  public static class MessageDigestHashAppender extends EnvironmentAppender {
-    final MessageDigest hasher;
-
-    public MessageDigestHashAppender() throws NoSuchAlgorithmException {
-      // Note to the security-minded reader: we deliberately use SHA-1 here, not
-      // a stronger hash. These identifiers don't strictly need a cryptographic
-      // hash function, because there is negligible value in attacking the hash.
-      // We use SHA-1 because it's *shorter* -- the exact same reason that Git
-      // chose SHA-1.
-      hasher = MessageDigest.getInstance("SHA-1");
-    }
-
-    @Override
-    public void append(String s) {
-      try {
-        hasher.update(((s == null) ? "null" : s).getBytes("UTF-8"));
-      } catch (UnsupportedEncodingException e) {
-        // This can never occur. Thanks, Java.
-      }
-    }
-
-    @Override
-    public void append(int profileCreation) {
-      append(Integer.toString(profileCreation, 10));
-    }
-
-    @Override
-    public String toString() {
-      // We *could* use ASCII85… but the savings would be negated by the
-      // inclusion of JSON-unsafe characters like double-quote.
-      return new Base64(-1, null, false).encodeAsString(hasher.digest());
-    }
-  }
-}
deleted file mode 100644
--- a/mobile/android/tests/background/junit3/src/org/mozilla/gecko/background/healthreport/TestHealthReportBroadcastService.java
+++ /dev/null
@@ -1,145 +0,0 @@
-/* Any copyright is dedicated to the Public Domain.
-   http://creativecommons.org/publicdomain/zero/1.0/ */
-
-package org.mozilla.gecko.background.healthreport;
-
-import java.util.concurrent.BrokenBarrierException;
-
-import org.mozilla.gecko.background.common.GlobalConstants;
-import org.mozilla.gecko.background.healthreport.prune.HealthReportPruneService;
-import org.mozilla.gecko.background.healthreport.upload.HealthReportUploadService;
-import org.mozilla.gecko.background.helpers.BackgroundServiceTestCase;
-
-import android.content.Intent;
-import android.content.SharedPreferences;
-
-public class TestHealthReportBroadcastService
-    extends BackgroundServiceTestCase<TestHealthReportBroadcastService.MockHealthReportBroadcastService> {
-  public static class MockHealthReportBroadcastService extends HealthReportBroadcastService {
-    @Override
-    protected SharedPreferences getSharedPreferences() {
-      return this.getSharedPreferences(sharedPrefsName, GlobalConstants.SHARED_PREFERENCES_MODE);
-    }
-
-    @Override
-    protected void onHandleIntent(Intent intent) {
-      super.onHandleIntent(intent);
-      try {
-        barrier.await();
-      } catch (InterruptedException e) {
-        fail("Awaiting Service thread should not be interrupted.");
-      } catch (BrokenBarrierException e) {
-        // This will happen on timeout - do nothing.
-      }
-    }
-  }
-
-  public TestHealthReportBroadcastService() {
-    super(MockHealthReportBroadcastService.class);
-  }
-
-  @Override
-  public void setUp() throws Exception {
-    super.setUp();
-    // We can't mock AlarmManager since it has a package-private constructor, so instead we reset
-    // the alarm by hand.
-    cancelAlarm(getUploadIntent());
-  }
-
-  @Override
-  public void tearDown() throws Exception {
-    cancelAlarm(getUploadIntent());
-    super.tearDown();
-  }
-
-  protected Intent getUploadIntent() {
-    final Intent intent = new Intent(getContext(), HealthReportUploadService.class);
-    intent.setAction("upload");
-    return intent;
-  }
-
-  protected Intent getPruneIntent() {
-    final Intent intent = new Intent(getContext(), HealthReportPruneService.class);
-    intent.setAction("prune");
-    return intent;
-  }
-
-  public void testIgnoredUploadPrefIntents() throws Exception {
-    // Intent without "upload" extra is ignored.
-    intent.setAction(HealthReportConstants.ACTION_HEALTHREPORT_UPLOAD_PREF)
-        .putExtra("profileName", "profileName")
-        .putExtra("profilePath", "profilePath");
-    startService(intent);
-    await();
-
-    assertFalse(isServiceAlarmSet(getUploadIntent()));
-    barrier.reset();
-
-    // No "profileName" extra.
-    intent.putExtra("enabled", true)
-        .removeExtra("profileName");
-    startService(intent);
-    await();
-
-    assertFalse(isServiceAlarmSet(getUploadIntent()));
-    barrier.reset();
-
-    // No "profilePath" extra.
-    intent.putExtra("profileName", "profileName")
-        .removeExtra("profilePath");
-    startService(intent);
-    await();
-
-    assertFalse(isServiceAlarmSet(getUploadIntent()));
-  }
-
-  public void testUploadPrefIntentDisabled() throws Exception {
-    intent.setAction(HealthReportConstants.ACTION_HEALTHREPORT_UPLOAD_PREF)
-        .putExtra("enabled", false)
-        .putExtra("profileName", "profileName")
-        .putExtra("profilePath", "profilePath");
-    startService(intent);
-    await();
-
-    assertFalse(isServiceAlarmSet(getUploadIntent()));
-  }
-
-  public void testUploadPrefIntentEnabled() throws Exception {
-    intent.setAction(HealthReportConstants.ACTION_HEALTHREPORT_UPLOAD_PREF)
-        .putExtra("enabled", true)
-        .putExtra("profileName", "profileName")
-        .putExtra("profilePath", "profilePath");
-    startService(intent);
-    await();
-
-    assertTrue(isServiceAlarmSet(getUploadIntent()));
-  }
-
-  public void testUploadServiceCancelled() throws Exception {
-    intent.setAction(HealthReportConstants.ACTION_HEALTHREPORT_UPLOAD_PREF)
-        .putExtra("enabled", true)
-        .putExtra("profileName", "profileName")
-        .putExtra("profilePath", "profilePath");
-    startService(intent);
-    await();
-
-    assertTrue(isServiceAlarmSet(getUploadIntent()));
-    barrier.reset();
-
-    intent.putExtra("enabled", false);
-    startService(intent);
-    await();
-
-    assertFalse(isServiceAlarmSet(getUploadIntent()));
-  }
-
-  public void testPruneService() throws Exception {
-    intent.setAction(HealthReportConstants.ACTION_HEALTHREPORT_PRUNE)
-        .putExtra("profileName", "profileName")
-        .putExtra("profilePath", "profilePath");
-    startService(intent);
-    await();
-    assertTrue(isServiceAlarmSet(getPruneIntent()));
-    barrier.reset();
-  }
-}
deleted file mode 100644
--- a/mobile/android/tests/background/junit3/src/org/mozilla/gecko/background/healthreport/TestHealthReportDatabaseStorage.java
+++ /dev/null
@@ -1,662 +0,0 @@
-/* Any copyright is dedicated to the Public Domain.
-   http://creativecommons.org/publicdomain/zero/1.0/ */
-
-package org.mozilla.gecko.background.healthreport;
-
-import java.util.ArrayList;
-
-import org.json.JSONException;
-import org.json.JSONObject;
-import org.mozilla.gecko.background.common.GlobalConstants;
-import org.mozilla.gecko.background.healthreport.HealthReportStorage;
-import org.mozilla.gecko.background.healthreport.HealthReportStorage.Field;
-import org.mozilla.gecko.background.healthreport.HealthReportStorage.MeasurementFields;
-import org.mozilla.gecko.background.healthreport.MockHealthReportDatabaseStorage.PrepopulatedMockHealthReportDatabaseStorage;
-import org.mozilla.gecko.background.helpers.DBHelpers;
-import org.mozilla.gecko.background.helpers.FakeProfileTestCase;
-
-import android.content.ContentValues;
-import android.database.Cursor;
-import android.database.sqlite.SQLiteConstraintException;
-import android.database.sqlite.SQLiteDatabase;
-
-public class TestHealthReportDatabaseStorage extends FakeProfileTestCase {
-  private String[] TABLE_NAMES = {
-    "addons",
-    "environments",
-    "measurements",
-    "fields",
-    "events_integer",
-    "events_textual"
-  };
-
-  public static class MockMeasurementFields implements MeasurementFields {
-    @Override
-    public Iterable<FieldSpec> getFields() {
-      ArrayList<FieldSpec> fields = new ArrayList<FieldSpec>();
-      fields.add(new FieldSpec("testfield1", Field.TYPE_INTEGER_COUNTER));
-      fields.add(new FieldSpec("testfield2", Field.TYPE_INTEGER_COUNTER));
-      return fields;
-    }
-  }
-
-  public void testInitializingProvider() {
-    MockHealthReportDatabaseStorage storage = new MockHealthReportDatabaseStorage(context, fakeProfileDirectory);
-    storage.beginInitialization();
-
-    // Two providers with the same measurement and field names. Shouldn't conflict.
-    storage.ensureMeasurementInitialized("testpA.testm", 1, new MockMeasurementFields());
-    storage.ensureMeasurementInitialized("testpB.testm", 2, new MockMeasurementFields());
-    storage.finishInitialization();
-
-    // Now make sure our stuff is in the DB.
-    SQLiteDatabase db = storage.getDB();
-    Cursor c = db.query("measurements", new String[] {"id", "name", "version"}, null, null, null, null, "name");
-    assertTrue(c.moveToFirst());
-    assertEquals(2, c.getCount());
-
-    Object[][] expected = new Object[][] {
-        {null, "testpA.testm", 1},
-        {null, "testpB.testm", 2},
-    };
-
-    DBHelpers.assertCursorContains(expected, c);
-    c.close();
-  }
-
-  private static final JSONObject EXAMPLE_ADDONS = safeJSONObject(
-            "{ " +
-            "\"amznUWL2@amazon.com\": { " +
-            "  \"userDisabled\": false, " +
-            "  \"appDisabled\": false, " +
-            "  \"version\": \"1.10\", " +
-            "  \"type\": \"extension\", " +
-            "  \"scope\": 1, " +
-            "  \"foreignInstall\": false, " +
-            "  \"hasBinaryComponents\": false, " +
-            "  \"installDay\": 15269, " +
-            "  \"updateDay\": 15602 " +
-            "}, " +
-            "\"jid0-qBnIpLfDFa4LpdrjhAC6vBqN20Q@jetpack\": { " +
-            "  \"userDisabled\": false, " +
-            "  \"appDisabled\": false, " +
-            "  \"version\": \"1.12.1\", " +
-            "  \"type\": \"extension\", " +
-            "  \"scope\": 1, " +
-            "  \"foreignInstall\": false, " +
-            "  \"hasBinaryComponents\": false, " +
-            "  \"installDay\": 15062, " +
-            "  \"updateDay\": 15580 " +
-            "} " +
-            "} ");
-
-  private static JSONObject safeJSONObject(String s) {
-    try {
-      return new JSONObject(s);
-    } catch (JSONException e) {
-      return null;
-    }
-  }
-
-  public void testEnvironmentsAndFields() throws Exception {
-    MockHealthReportDatabaseStorage storage = new MockHealthReportDatabaseStorage(context, fakeProfileDirectory);
-    storage.beginInitialization();
-    storage.ensureMeasurementInitialized("testpA.testm", 1, new MockMeasurementFields());
-    storage.ensureMeasurementInitialized("testpB.testn", 1, new MockMeasurementFields());
-    storage.finishInitialization();
-
-    MockDatabaseEnvironment environmentA = storage.getEnvironment();
-    environmentA.mockInit("v123");
-    environmentA.setJSONForAddons(EXAMPLE_ADDONS);
-    final int envA = environmentA.register();
-    assertEquals(envA, environmentA.register());
-
-    // getField memoizes.
-    assertSame(storage.getField("foo", 2, "bar"),
-               storage.getField("foo", 2, "bar"));
-
-    // It throws if you refer to a non-existent field.
-    try {
-      storage.getField("foo", 2, "bar").getID();
-      fail("Should throw.");
-    } catch (IllegalStateException ex) {
-      // Expected.
-    }
-
-    // It returns the field ID for a valid field.
-    Field field = storage.getField("testpA.testm", 1, "testfield1");
-    assertTrue(field.getID() >= 0);
-
-    // These IDs are stable.
-    assertEquals(field.getID(), field.getID());
-    int fieldID = field.getID();
-
-    // Before inserting, no events.
-    assertFalse(storage.hasEventSince(0));
-    assertFalse(storage.hasEventSince(storage.now));
-
-    // Store some data for two environments across two days.
-    storage.incrementDailyCount(envA, storage.getYesterday(), fieldID, 4);
-    storage.incrementDailyCount(envA, storage.getYesterday(), fieldID, 1);
-    storage.incrementDailyCount(envA, storage.getToday(), fieldID, 2);
-
-    // After inserting, we have events.
-    assertTrue(storage.hasEventSince(storage.now - GlobalConstants.MILLISECONDS_PER_DAY));
-    assertTrue(storage.hasEventSince(storage.now));
-    // But not in the future.
-    assertFalse(storage.hasEventSince(storage.now + GlobalConstants.MILLISECONDS_PER_DAY));
-
-    MockDatabaseEnvironment environmentB = storage.getEnvironment();
-    environmentB.mockInit("v234");
-    environmentB.setJSONForAddons(EXAMPLE_ADDONS);
-    final int envB = environmentB.register();
-    assertFalse(envA == envB);
-
-    storage.incrementDailyCount(envB, storage.getToday(), fieldID, 6);
-    storage.incrementDailyCount(envB, storage.getToday(), fieldID, 2);
-
-    // Let's make sure everything's there.
-    Cursor c = storage.getRawEventsSince(storage.getOneDayAgo());
-    try {
-      assertTrue(c.moveToFirst());
-      assertTrue(assertRowEquals(c, storage.getYesterday(), envA, fieldID, 5));
-      assertTrue(assertRowEquals(c, storage.getToday(), envA, fieldID, 2));
-      assertFalse(assertRowEquals(c, storage.getToday(), envB, fieldID, 8));
-    } finally {
-      c.close();
-    }
-
-    // The stored environment has the provided JSON add-ons bundle.
-    Cursor e = storage.getEnvironmentRecordForID(envA);
-    e.moveToFirst();
-    assertEquals(EXAMPLE_ADDONS.toString(), e.getString(e.getColumnIndex("addonsBody")));
-    e.close();
-
-    e = storage.getEnvironmentRecordForID(envB);
-    e.moveToFirst();
-    assertEquals(EXAMPLE_ADDONS.toString(), e.getString(e.getColumnIndex("addonsBody")));
-    e.close();
-
-    // There's only one add-ons bundle in the DB, despite having two environments.
-    Cursor addons = storage.getDB().query("addons", null, null, null, null, null, null);
-    assertEquals(1, addons.getCount());
-    addons.close();
-  }
-
-  /**
-   * Asserts validity for a storage cursor. Returns whether there is another row to process.
-   */
-  private static boolean assertRowEquals(Cursor c, int day, int env, int field, int value) {
-    assertEquals(day,   c.getInt(0));
-    assertEquals(env,   c.getInt(1));
-    assertEquals(field, c.getInt(2));
-    assertEquals(value, c.getLong(3));
-    return c.moveToNext();
-  }
-
-  /**
-   * Test robust insertions. This also acts as a test for the getPrepopulatedStorage method,
-   * allowing faster debugging if this fails and other tests relying on getPrepopulatedStorage
-   * also fail.
-   */
-  public void testInsertions() throws Exception {
-    final PrepopulatedMockHealthReportDatabaseStorage storage =
-        new PrepopulatedMockHealthReportDatabaseStorage(context, fakeProfileDirectory);
-    assertNotNull(storage);
-  }
-
-  public void testForeignKeyConstraints() throws Exception {
-    final PrepopulatedMockHealthReportDatabaseStorage storage =
-        new PrepopulatedMockHealthReportDatabaseStorage(context, fakeProfileDirectory);
-    final SQLiteDatabase db = storage.getDB();
-
-    final int envID = storage.getEnvironment().register();
-    final int counterFieldID = storage.getField(storage.measurementNames[0], storage.measurementVers[0],
-        storage.fieldSpecContainers[0].counter.name).getID();
-    final int discreteFieldID = storage.getField(storage.measurementNames[0], storage.measurementVers[0],
-        storage.fieldSpecContainers[0].discrete.name).getID();
-
-    final int nonExistentEnvID = DBHelpers.getNonExistentID(db, "environments");
-    final int nonExistentFieldID = DBHelpers.getNonExistentID(db, "fields");
-    final int nonExistentAddonID = DBHelpers.getNonExistentID(db, "addons");
-    final int nonExistentMeasurementID = DBHelpers.getNonExistentID(db, "measurements");
-
-    ContentValues v = new ContentValues();
-    v.put("field", counterFieldID);
-    v.put("env", nonExistentEnvID);
-    try {
-      db.insertOrThrow("events_integer", null, v);
-      fail("Should throw - events_integer(env) is referencing non-existent environments(id)");
-    } catch (SQLiteConstraintException e) { }
-    v.put("field", discreteFieldID);
-    try {
-      db.insertOrThrow("events_textual", null, v);
-      fail("Should throw - events_textual(env) is referencing non-existent environments(id)");
-    } catch (SQLiteConstraintException e) { }
-
-    v.put("field", nonExistentFieldID);
-    v.put("env", envID);
-    try {
-      db.insertOrThrow("events_integer", null, v);
-      fail("Should throw - events_integer(field) is referencing non-existent fields(id)");
-    } catch (SQLiteConstraintException e) { }
-    try {
-      db.insertOrThrow("events_textual", null, v);
-      fail("Should throw - events_textual(field) is referencing non-existent fields(id)");
-    } catch (SQLiteConstraintException e) { }
-
-    v = new ContentValues();
-    v.put("addonsID", nonExistentAddonID);
-    try {
-      db.insertOrThrow("environments", null, v);
-      fail("Should throw - environments(addonsID) is referencing non-existent addons(id).");
-    } catch (SQLiteConstraintException e) { }
-
-    v = new ContentValues();
-    v.put("measurement", nonExistentMeasurementID);
-    try {
-      db.insertOrThrow("fields", null, v);
-      fail("Should throw - fields(measurement) is referencing non-existent measurements(id).");
-    } catch (SQLiteConstraintException e) { }
-  }
-
-  private int getTotalEventCount(HealthReportStorage storage) {
-    final Cursor c = storage.getEventsSince(0);
-    try {
-      return c.getCount();
-    } finally {
-      c.close();
-    }
-  }
-
-  public void testCascadingDeletions() throws Exception {
-    PrepopulatedMockHealthReportDatabaseStorage storage =
-        new PrepopulatedMockHealthReportDatabaseStorage(context, fakeProfileDirectory);
-    SQLiteDatabase db = storage.getDB();
-    db.delete("environments", null, null);
-    assertEquals(0, DBHelpers.getRowCount(db, "events_integer"));
-    assertEquals(0, DBHelpers.getRowCount(db, "events_textual"));
-
-    storage = new PrepopulatedMockHealthReportDatabaseStorage(context, fakeProfileDirectory);
-    db = storage.getDB();
-    db.delete("measurements", null, null);
-    assertEquals(0, DBHelpers.getRowCount(db, "fields"));
-    assertEquals(0, DBHelpers.getRowCount(db, "events_integer"));
-    assertEquals(0, DBHelpers.getRowCount(db, "events_textual"));
-  }
-
-  public void testRestrictedDeletions() throws Exception {
-    final PrepopulatedMockHealthReportDatabaseStorage storage =
-        new PrepopulatedMockHealthReportDatabaseStorage(context, fakeProfileDirectory);
-    SQLiteDatabase db = storage.getDB();
-    try {
-      db.delete("addons", null, null);
-      fail("Should throw - environment references addons and thus addons cannot be deleted.");
-    } catch (SQLiteConstraintException e) { }
-  }
-
-  public void testDeleteEverything() throws Exception {
-    final PrepopulatedMockHealthReportDatabaseStorage storage =
-        new PrepopulatedMockHealthReportDatabaseStorage(context, fakeProfileDirectory);
-    storage.deleteEverything();
-
-    final SQLiteDatabase db = storage.getDB();
-    for (String table : TABLE_NAMES) {
-      if (DBHelpers.getRowCount(db, table) != 0) {
-        fail("Not everything has been deleted for table " + table + ".");
-      }
-    }
-  }
-
-  public void testMeasurementRecordingConstraintViolation() throws Exception {
-    final PrepopulatedMockHealthReportDatabaseStorage storage =
-        new PrepopulatedMockHealthReportDatabaseStorage(context, fakeProfileDirectory);
-    final SQLiteDatabase db = storage.getDB();
-
-    final int envID = storage.getEnvironment().register();
-    final int counterFieldID = storage.getField(storage.measurementNames[0], storage.measurementVers[0],
-        storage.fieldSpecContainers[0].counter.name).getID();
-    final int discreteFieldID = storage.getField(storage.measurementNames[0], storage.measurementVers[0],
-        storage.fieldSpecContainers[0].discrete.name).getID();
-
-    final int nonExistentEnvID = DBHelpers.getNonExistentID(db, "environments");
-    final int nonExistentFieldID = DBHelpers.getNonExistentID(db, "fields");
-
-    try {
-      storage.incrementDailyCount(nonExistentEnvID, storage.getToday(), counterFieldID);
-      fail("Should throw - event_integer(env) references environments(id), which is given as a non-existent value.");
-    } catch (IllegalStateException e) { }
-    try {
-      storage.recordDailyLast(nonExistentEnvID, storage.getToday(), discreteFieldID, "iu");
-      fail("Should throw - event_textual(env) references environments(id), which is given as a non-existent value.");
-    } catch (IllegalStateException e) { }
-
-    try {
-      storage.incrementDailyCount(envID, storage.getToday(), nonExistentFieldID);
-      fail("Should throw - event_integer(field) references fields(id), which is given as a non-existent value.");
-    } catch (IllegalStateException e) { }
-    try {
-      storage.recordDailyLast(envID, storage.getToday(), nonExistentFieldID, "iu");
-      fail("Should throw - event_textual(field) references fields(id), which is given as a non-existent value.");
-    } catch (IllegalStateException e) { }
-
-    // Test dropped events due to constraint violations that do not throw (see bug 961526).
-    final String eventValue = "a value not in the database";
-    assertFalse(isEventInDB(db, eventValue)); // Better safe than sorry.
-
-    storage.recordDailyDiscrete(nonExistentEnvID, storage.getToday(), discreteFieldID, eventValue);
-    assertFalse(isEventInDB(db, eventValue));
-
-    storage.recordDailyDiscrete(envID, storage.getToday(), nonExistentFieldID, "iu");
-    assertFalse(isEventInDB(db, eventValue));
-  }
-
-  private static boolean isEventInDB(final SQLiteDatabase db, final String value) {
-    final Cursor c = db.query("events_textual", new String[] {"value"}, "value = ?",
-        new String[] {value}, null, null, null);
-    try {
-      return c.getCount() > 0;
-    } finally {
-      c.close();
-    }
-  }
-
-  // Largely taken from testDeleteEnvAndEventsBefore and testDeleteOrphanedAddons.
-  public void testDeleteDataBefore() throws Exception {
-    final PrepopulatedMockHealthReportDatabaseStorage storage =
-        new PrepopulatedMockHealthReportDatabaseStorage(context, fakeProfileDirectory);
-    final SQLiteDatabase db = storage.getDB();
-
-    // Insert (and delete) an environment not referenced by any events.
-    ContentValues v = new ContentValues();
-    v.put("hash", "I really hope this is a unique hash! ^_^");
-    v.put("addonsID", DBHelpers.getExistentID(db, "addons"));
-    db.insertOrThrow("environments", null, v);
-    v.put("hash", "Another unique hash!");
-    final int curEnv = (int) db.insertOrThrow("environments", null, v);
-    final ContentValues addonV = new ContentValues();
-    addonV.put("body", "addon1");
-    db.insertOrThrow("addons", null, addonV);
-    // 2 = 1 addon + 1 env.
-    assertEquals(2, storage.deleteDataBefore(storage.getGivenDaysAgoMillis(8), curEnv));
-    assertEquals(1, storage.deleteDataBefore(storage.getGivenDaysAgoMillis(8),
-          DBHelpers.getNonExistentID(db, "environments")));
-    assertEquals(1, DBHelpers.getRowCount(db, "addons"));
-
-    // Insert (and delete) new environment and referencing events.
-    final long envID = db.insertOrThrow("environments", null, v);
-    v = new ContentValues();
-    v.put("date", storage.getGivenDaysAgo(9));
-    v.put("env", envID);
-    v.put("field", DBHelpers.getExistentID(db, "fields"));
-    db.insertOrThrow("events_integer", null, v);
-    db.insertOrThrow("events_integer", null, v);
-    assertEquals(16, getTotalEventCount(storage));
-    final int nonExistentEnvID = DBHelpers.getNonExistentID(db, "environments");
-    assertEquals(1, storage.deleteDataBefore(storage.getGivenDaysAgoMillis(8), nonExistentEnvID));
-    assertEquals(14, getTotalEventCount(storage));
-
-    // Assert only pre-populated storage is stored.
-    assertEquals(1, DBHelpers.getRowCount(db, "environments"));
-
-    assertEquals(0, storage.deleteDataBefore(storage.getGivenDaysAgoMillis(5), nonExistentEnvID));
-    assertEquals(12, getTotalEventCount(storage));
-
-    assertEquals(0, storage.deleteDataBefore(storage.getGivenDaysAgoMillis(4), nonExistentEnvID));
-    assertEquals(10, getTotalEventCount(storage));
-
-    assertEquals(0, storage.deleteDataBefore(storage.now, nonExistentEnvID));
-    assertEquals(5, getTotalEventCount(storage));
-    assertEquals(1, DBHelpers.getRowCount(db, "addons"));
-
-    // 2 = 1 addon + 1 env.
-    assertEquals(2, storage.deleteDataBefore(storage.now + GlobalConstants.MILLISECONDS_PER_DAY,
-          nonExistentEnvID));
-    assertEquals(0, getTotalEventCount(storage));
-    assertEquals(0, DBHelpers.getRowCount(db, "addons"));
-  }
-
-  // Largely taken from testDeleteOrphanedEnv and testDeleteEventsBefore.
-  public void testDeleteEnvAndEventsBefore() throws Exception {
-    final PrepopulatedMockHealthReportDatabaseStorage storage =
-        new PrepopulatedMockHealthReportDatabaseStorage(context, fakeProfileDirectory);
-    final SQLiteDatabase db = storage.getDB();
-
-    // Insert (and delete) an environment not referenced by any events.
-    ContentValues v = new ContentValues();
-    v.put("hash", "I really hope this is a unique hash! ^_^");
-    v.put("addonsID", DBHelpers.getExistentID(db, "addons"));
-    db.insertOrThrow("environments", null, v);
-    v.put("hash", "Another unique hash!");
-    final int curEnv = (int) db.insertOrThrow("environments", null, v);
-    assertEquals(1, storage.deleteEnvAndEventsBefore(storage.getGivenDaysAgoMillis(8), curEnv));
-    assertEquals(1, storage.deleteEnvAndEventsBefore(storage.getGivenDaysAgoMillis(8),
-          DBHelpers.getNonExistentID(db, "environments")));
-
-    // Insert (and delete) new environment and referencing events.
-    final long envID = db.insertOrThrow("environments", null, v);
-    v = new ContentValues();
-    v.put("date", storage.getGivenDaysAgo(9));
-    v.put("env", envID);
-    v.put("field", DBHelpers.getExistentID(db, "fields"));
-    db.insertOrThrow("events_integer", null, v);
-    db.insertOrThrow("events_integer", null, v);
-    assertEquals(16, getTotalEventCount(storage));
-    final int nonExistentEnvID = DBHelpers.getNonExistentID(db, "environments");
-    assertEquals(1, storage.deleteEnvAndEventsBefore(storage.getGivenDaysAgoMillis(8), nonExistentEnvID));
-    assertEquals(14, getTotalEventCount(storage));
-
-    // Assert only pre-populated storage is stored.
-    assertEquals(1, DBHelpers.getRowCount(db, "environments"));
-
-    assertEquals(0, storage.deleteEnvAndEventsBefore(storage.getGivenDaysAgoMillis(5), nonExistentEnvID));
-    assertEquals(12, getTotalEventCount(storage));
-
-    assertEquals(0, storage.deleteEnvAndEventsBefore(storage.getGivenDaysAgoMillis(4), nonExistentEnvID));
-    assertEquals(10, getTotalEventCount(storage));
-
-    assertEquals(0, storage.deleteEnvAndEventsBefore(storage.now, nonExistentEnvID));
-    assertEquals(5, getTotalEventCount(storage));
-
-    assertEquals(1, storage.deleteEnvAndEventsBefore(storage.now + GlobalConstants.MILLISECONDS_PER_DAY,
-          nonExistentEnvID));
-    assertEquals(0, getTotalEventCount(storage));
-  }
-
-  public void testDeleteOrphanedEnv() throws Exception {
-    final PrepopulatedMockHealthReportDatabaseStorage storage =
-        new PrepopulatedMockHealthReportDatabaseStorage(context, fakeProfileDirectory);
-    final SQLiteDatabase db = storage.getDB();
-
-    final ContentValues v = new ContentValues();
-    v.put("addonsID", DBHelpers.getExistentID(db, "addons"));
-    v.put("hash", "unique");
-    final int envID = (int) db.insert("environments", null, v);
-
-    assertEquals(0, storage.deleteOrphanedEnv(envID));
-    assertEquals(1, storage.deleteOrphanedEnv(storage.env));
-    this.deleteEvents(db);
-    assertEquals(1, storage.deleteOrphanedEnv(envID));
-  }
-
-  private void deleteEvents(final SQLiteDatabase db) throws Exception {
-    db.beginTransaction();
-    try {
-      db.delete("events_integer", null, null);
-      db.delete("events_textual", null, null);
-      db.setTransactionSuccessful();
-    } finally {
-      db.endTransaction();
-    }
-  }
-
-  public void testDeleteEventsBefore() throws Exception {
-    final PrepopulatedMockHealthReportDatabaseStorage storage =
-        new PrepopulatedMockHealthReportDatabaseStorage(context, fakeProfileDirectory);
-    assertEquals(2, storage.deleteEventsBefore(Integer.toString(storage.getGivenDaysAgo(5))));
-    assertEquals(12, getTotalEventCount(storage));
-
-    assertEquals(2, storage.deleteEventsBefore(Integer.toString(storage.getGivenDaysAgo(4))));
-    assertEquals(10, getTotalEventCount(storage));
-
-    assertEquals(5, storage.deleteEventsBefore(Integer.toString(storage.getToday())));
-    assertEquals(5, getTotalEventCount(storage));
-
-    assertEquals(5, storage.deleteEventsBefore(Integer.toString(storage.getTomorrow())));
-    assertEquals(0, getTotalEventCount(storage));
-  }
-
-  public void testDeleteOrphanedAddons() throws Exception {
-    final PrepopulatedMockHealthReportDatabaseStorage storage =
-        new PrepopulatedMockHealthReportDatabaseStorage(context, fakeProfileDirectory);
-    final SQLiteDatabase db = storage.getDB();
-
-    final ArrayList<Integer> nonOrphanIDs = new ArrayList<Integer>();
-    final Cursor c = db.query("addons", new String[] {"id"}, null, null, null, null, null);
-    try {
-      assertTrue(c.moveToFirst());
-      do {
-        nonOrphanIDs.add(c.getInt(0));
-      } while (c.moveToNext());
-    } finally {
-      c.close();
-    }
-
-    // Ensure we don't delete non-orphans.
-    assertEquals(0, storage.deleteOrphanedAddons());
-
-    // Insert orphans.
-    final long[] orphanIDs = new long[2];
-    final ContentValues v = new ContentValues();
-    v.put("body", "addon1");
-    orphanIDs[0] = db.insertOrThrow("addons", null, v);
-    v.put("body", "addon2");
-    orphanIDs[1] = db.insertOrThrow("addons", null, v);
-    assertEquals(2, storage.deleteOrphanedAddons());
-    assertEquals(0, DBHelpers.getRowCount(db, "addons", "ID = ? OR ID = ?",
-        new String[] {Long.toString(orphanIDs[0]), Long.toString(orphanIDs[1])}));
-
-    // Orphan all addons.
-    db.delete("environments", null, null);
-    assertEquals(nonOrphanIDs.size(), storage.deleteOrphanedAddons());
-    assertEquals(0, DBHelpers.getRowCount(db, "addons"));
-  }
-
-  public void testGetEventCount() throws Exception {
-    final PrepopulatedMockHealthReportDatabaseStorage storage =
-        new PrepopulatedMockHealthReportDatabaseStorage(context, fakeProfileDirectory);
-    assertEquals(14, storage.getEventCount());
-    final SQLiteDatabase db = storage.getDB();
-    this.deleteEvents(db);
-    assertEquals(0, storage.getEventCount());
-  }
-
-  public void testGetEnvironmentCount() throws Exception {
-    final PrepopulatedMockHealthReportDatabaseStorage storage =
-        new PrepopulatedMockHealthReportDatabaseStorage(context, fakeProfileDirectory);
-    assertEquals(1, storage.getEnvironmentCount());
-    final SQLiteDatabase db = storage.getDB();
-    db.delete("environments", null, null);
-    assertEquals(0, storage.getEnvironmentCount());
-  }
-
-  public void testPruneEnvironments() throws Exception {
-    final PrepopulatedMockHealthReportDatabaseStorage storage =
-        new PrepopulatedMockHealthReportDatabaseStorage(context, fakeProfileDirectory, 2);
-    final SQLiteDatabase db = storage.getDB();
-    assertEquals(5, DBHelpers.getRowCount(db, "environments"));
-    assertEquals(5, storage.getEnvironmentCache().size());
-
-    storage.pruneEnvironments(1);
-    assertEquals(0, storage.getEnvironmentCache().size());
-    assertTrue(!getEnvAppVersions(db).contains("v3"));
-    storage.pruneEnvironments(2);
-    assertTrue(!getEnvAppVersions(db).contains("v2"));
-    assertTrue(!getEnvAppVersions(db).contains("v1"));
-    storage.pruneEnvironments(1);
-    assertTrue(!getEnvAppVersions(db).contains("v123"));
-    storage.pruneEnvironments(1);
-    assertTrue(!getEnvAppVersions(db).contains("v4"));
-  }
-
-  private ArrayList<String> getEnvAppVersions(final SQLiteDatabase db) {
-    ArrayList<String> out = new ArrayList<String>();
-    Cursor c = null;
-    try {
-      c = db.query(true, "environments", new String[] {"appVersion"}, null, null, null, null, null, null);
-      while (c.moveToNext()) {
-        out.add(c.getString(0));
-      }
-    } finally {
-      if (c != null) {
-        c.close();
-      }
-    }
-    return out;
-  }
-
-  public void testPruneEvents() throws Exception {
-    final PrepopulatedMockHealthReportDatabaseStorage storage =
-        new PrepopulatedMockHealthReportDatabaseStorage(context, fakeProfileDirectory);
-    SQLiteDatabase db = storage.getDB();
-    assertEquals(14, DBHelpers.getRowCount(db, "events"));
-    storage.pruneEvents(1); // Delete < 7 days ago.
-    assertEquals(14, DBHelpers.getRowCount(db, "events"));
-    storage.pruneEvents(2); // Delete < 5 days ago.
-    assertEquals(13, DBHelpers.getRowCount(db, "events"));
-    storage.pruneEvents(5); // Delete < 2 days ago.
-    assertEquals(9, DBHelpers.getRowCount(db, "events"));
-    storage.pruneEvents(14); // Delete < today.
-    assertEquals(5, DBHelpers.getRowCount(db, "events"));
-  }
-
-  public void testVacuum() throws Exception {
-    final PrepopulatedMockHealthReportDatabaseStorage storage =
-        new PrepopulatedMockHealthReportDatabaseStorage(context, fakeProfileDirectory);
-    final SQLiteDatabase db = storage.getDB();
-    // Need to disable auto_vacuum to allow free page fragmentation. Note that the pragma changes
-    // only after a vacuum command.
-    db.execSQL("PRAGMA auto_vacuum=0");
-    db.execSQL("vacuum");
-    assertTrue(isAutoVacuumingDisabled(storage));
-
-    createFreePages(storage);
-    storage.vacuum();
-    assertEquals(0, getFreelistCount(storage));
-  }
-
-  public long getFreelistCount(final MockHealthReportDatabaseStorage storage) {
-    return storage.getIntFromQuery("PRAGMA freelist_count", null);
-  }
-
-  public boolean isAutoVacuumingDisabled(final MockHealthReportDatabaseStorage storage) {
-    return storage.getIntFromQuery("PRAGMA auto_vacuum", null) == 0;
-  }
-
-  private void createFreePages(final PrepopulatedMockHealthReportDatabaseStorage storage) throws Exception {
-    // Insert and delete until DB has free page fragmentation. The loop helps ensure that the
-    // fragmentation will occur with minimal disk usage. The upper loop limits are arbitrary.
-    final SQLiteDatabase db = storage.getDB();
-    for (int i = 10; i <= 1250; i *= 5) {
-      storage.insertTextualEvents(i);
-      db.delete("events_textual", "date < ?", new String[] {Integer.toString(i / 2)});
-      if (getFreelistCount(storage) > 0) {
-        return;
-      }
-    }
-    fail("Database free pages failed to fragment.");
-  }
-
-  public void testDisableAutoVacuuming() throws Exception {
-    final PrepopulatedMockHealthReportDatabaseStorage storage =
-        new PrepopulatedMockHealthReportDatabaseStorage(context, fakeProfileDirectory);
-    final SQLiteDatabase db = storage.getDB();
-    // The pragma changes only after a vacuum command.
-    db.execSQL("PRAGMA auto_vacuum=1");
-    db.execSQL("vacuum");
-    assertEquals(1, storage.getIntFromQuery("PRAGMA auto_vacuum", null));
-    storage.disableAutoVacuuming();
-    db.execSQL("vacuum");
-    assertTrue(isAutoVacuumingDisabled(storage));
-  }
-}
deleted file mode 100644
--- a/mobile/android/tests/background/junit3/src/org/mozilla/gecko/background/healthreport/TestHealthReportGenerator.java
+++ /dev/null
@@ -1,523 +0,0 @@
-/* Any copyright is dedicated to the Public Domain.
-   http://creativecommons.org/publicdomain/zero/1.0/ */
-
-package org.mozilla.gecko.background.healthreport;
-
-import java.io.File;
-import java.util.ArrayList;
-import java.util.Iterator;
-
-import org.json.JSONArray;
-import org.json.JSONException;
-import org.json.JSONObject;
-import org.mozilla.gecko.background.common.DateUtils;
-import org.mozilla.gecko.background.healthreport.HealthReportStorage.Field;
-import org.mozilla.gecko.background.healthreport.HealthReportStorage.MeasurementFields;
-import org.mozilla.gecko.background.helpers.FakeProfileTestCase;
-
-import android.database.Cursor;
-import android.database.sqlite.SQLiteDatabase;
-import android.util.SparseArray;
-
-public class TestHealthReportGenerator extends FakeProfileTestCase {
-  @SuppressWarnings("static-method")
-  public void testOptObject() throws JSONException {
-    JSONObject o = new JSONObject();
-    o.put("foo", JSONObject.NULL);
-    assertEquals(null, o.optJSONObject("foo"));
-  }
-
-  @SuppressWarnings("static-method")
-  public void testAppend() throws JSONException {
-    JSONObject o = new JSONObject();
-    HealthReportUtils.append(o, "yyy", 5);
-    assertNotNull(o.getJSONArray("yyy"));
-    assertEquals(5, o.getJSONArray("yyy").getInt(0));
-
-    o.put("foo", "noo");
-    HealthReportUtils.append(o, "foo", "bar");
-    assertNotNull(o.getJSONArray("foo"));
-    assertEquals("noo", o.getJSONArray("foo").getString(0));
-    assertEquals("bar", o.getJSONArray("foo").getString(1));
-  }
-
-  @SuppressWarnings("static-method")
-  public void testCount() throws JSONException {
-    JSONObject o = new JSONObject();
-    HealthReportUtils.count(o, "foo", "a");
-    HealthReportUtils.count(o, "foo", "b");
-    HealthReportUtils.count(o, "foo", "a");
-    HealthReportUtils.count(o, "foo", "c");
-    HealthReportUtils.count(o, "bar", "a");
-    HealthReportUtils.count(o, "bar", "d");
-    JSONObject foo = o.getJSONObject("foo");
-    JSONObject bar = o.getJSONObject("bar");
-    assertEquals(2, foo.getInt("a"));
-    assertEquals(1, foo.getInt("b"));
-    assertEquals(1, foo.getInt("c"));
-    assertFalse(foo.has("d"));
-    assertEquals(1, bar.getInt("a"));
-    assertEquals(1, bar.getInt("d"));
-    assertFalse(bar.has("b"));
-  }
-
-  // We don't initialize the env in testHashing, so these are just the default
-  // values for the Java types, in order.
-  private static final String EXPECTED_MOCK_BASE_HASH = "000nullnullnullnullnullnullnull"
-                                                        + "nullnullnullnullnullnull00000";
-
-  // v2 fields.
-  private static final String EXPECTED_MOCK_BASE_HASH_SUFFIX_V2 = "null" + "null" + 0 + "null";
-
-  // v3 fields.
-  private static final String EXPECTED_MOCK_BASE_HASH_SUFFIX_V3 = "" + 0 + "default" + 0 + 0 + 0 + 0;
-
-  public void testHashing() throws JSONException {
-    MockHealthReportDatabaseStorage storage = new MockHealthReportDatabaseStorage(context, fakeProfileDirectory);
-    MockDatabaseEnvironment env = new MockDatabaseEnvironment(storage, MockDatabaseEnvironment.MockEnvironmentAppender.class);
-    env.addons = new JSONObject();
-
-    String addonAHash = "{addonA}={appDisabled==falseforeignInstall==false"
-        + "hasBinaryComponents==falseinstallDay==15269scope==1"
-        + "type==extensionupdateDay==15602userDisabled==false"
-        + "version==1.10}";
-
-    JSONObject addonA1 = new JSONObject("{" +
-        "\"userDisabled\": false, " +