mobile/android/base/health/BrowserHealthReporter.java

Wed, 31 Dec 2014 07:22:50 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 07:22:50 +0100
branch
TOR_BUG_3246
changeset 4
fc2d59ddac77
permissions
-rw-r--r--

Correct previous dual key logic pending first delivery installment.

michael@0 1 /* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
michael@0 2 * This Source Code Form is subject to the terms of the Mozilla Public
michael@0 3 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 5
michael@0 6 package org.mozilla.gecko.health;
michael@0 7
michael@0 8 import android.content.ContentProviderClient;
michael@0 9 import android.content.Context;
michael@0 10 import android.util.Log;
michael@0 11
michael@0 12 import org.mozilla.gecko.GeckoAppShell;
michael@0 13 import org.mozilla.gecko.GeckoEvent;
michael@0 14 import org.mozilla.gecko.GeckoProfile;
michael@0 15
michael@0 16 import org.mozilla.gecko.background.healthreport.EnvironmentBuilder;
michael@0 17 import org.mozilla.gecko.background.common.GlobalConstants;
michael@0 18 import org.mozilla.gecko.background.healthreport.HealthReportConstants;
michael@0 19 import org.mozilla.gecko.background.healthreport.HealthReportDatabaseStorage;
michael@0 20 import org.mozilla.gecko.background.healthreport.HealthReportGenerator;
michael@0 21
michael@0 22 import org.mozilla.gecko.util.GeckoEventListener;
michael@0 23 import org.mozilla.gecko.util.ThreadUtils;
michael@0 24
michael@0 25 import org.json.JSONException;
michael@0 26 import org.json.JSONObject;
michael@0 27
michael@0 28 /**
michael@0 29 * BrowserHealthReporter is the browser's interface to the Firefox Health
michael@0 30 * Report report generator.
michael@0 31 *
michael@0 32 * Each instance registers Gecko event listeners, so keep a single instance
michael@0 33 * around for the life of the browser. Java callers should use this globally
michael@0 34 * available singleton.
michael@0 35 */
michael@0 36 public class BrowserHealthReporter implements GeckoEventListener {
michael@0 37 private static final String LOGTAG = "GeckoHealthRep";
michael@0 38
michael@0 39 public static final String EVENT_REQUEST = "HealthReport:Request";
michael@0 40 public static final String EVENT_RESPONSE = "HealthReport:Response";
michael@0 41
michael@0 42 protected final Context context;
michael@0 43
michael@0 44 public BrowserHealthReporter() {
michael@0 45 GeckoAppShell.registerEventListener(EVENT_REQUEST, this);
michael@0 46
michael@0 47 context = GeckoAppShell.getContext();
michael@0 48 if (context == null) {
michael@0 49 throw new IllegalStateException("Null Gecko context");
michael@0 50 }
michael@0 51 }
michael@0 52
michael@0 53 public void uninit() {
michael@0 54 GeckoAppShell.unregisterEventListener(EVENT_REQUEST, this);
michael@0 55 }
michael@0 56
michael@0 57 /**
michael@0 58 * Generate a new Health Report.
michael@0 59 *
michael@0 60 * This method performs IO, so call it from a background thread.
michael@0 61 *
michael@0 62 * @param since timestamp of first day to report (milliseconds since epoch).
michael@0 63 * @param lastPingTime timestamp when last health report was uploaded
michael@0 64 * (milliseconds since epoch).
michael@0 65 * @param profilePath path of the profile to generate report for.
michael@0 66 * @throws JSONException if JSON generation fails.
michael@0 67 * @throws IllegalStateException if the environment does not allow to generate a report.
michael@0 68 * @return non-null report.
michael@0 69 */
michael@0 70 public JSONObject generateReport(long since, long lastPingTime, String profilePath) throws JSONException {
michael@0 71 // We abuse the life-cycle of an Android ContentProvider slightly by holding
michael@0 72 // onto a ContentProviderClient while we generate a payload. This keeps
michael@0 73 // our database storage alive, while also allowing us to share a database
michael@0 74 // connection with BrowserHealthRecorder and the uploader.
michael@0 75 // The ContentProvider owns all underlying Storage instances, so we don't
michael@0 76 // need to explicitly close them.
michael@0 77 ContentProviderClient client = EnvironmentBuilder.getContentProviderClient(context);
michael@0 78 if (client == null) {
michael@0 79 throw new IllegalStateException("Could not fetch Health Report content provider.");
michael@0 80 }
michael@0 81
michael@0 82 try {
michael@0 83 // Storage instance is owned by HealthReportProvider, so we don't need
michael@0 84 // to close it.
michael@0 85 HealthReportDatabaseStorage storage = EnvironmentBuilder.getStorage(client, profilePath);
michael@0 86 if (storage == null) {
michael@0 87 throw new IllegalStateException("No storage in Health Reporter.");
michael@0 88 }
michael@0 89
michael@0 90 HealthReportGenerator generator = new HealthReportGenerator(storage);
michael@0 91 JSONObject report = generator.generateDocument(since, lastPingTime, profilePath);
michael@0 92 if (report == null) {
michael@0 93 throw new IllegalStateException("Not enough profile information to generate report.");
michael@0 94 }
michael@0 95 return report;
michael@0 96 } finally {
michael@0 97 client.release();
michael@0 98 }
michael@0 99 }
michael@0 100
michael@0 101 /**
michael@0 102 * Get last time a health report was successfully uploaded.
michael@0 103 *
michael@0 104 * This is read from shared preferences, so call it from a background
michael@0 105 * thread. Bug 882182 tracks making this work with multiple profiles.
michael@0 106 *
michael@0 107 * @return milliseconds since the epoch, or 0 if never uploaded.
michael@0 108 */
michael@0 109 protected long getLastUploadLocalTime() {
michael@0 110 return context
michael@0 111 .getSharedPreferences(HealthReportConstants.PREFS_BRANCH, 0)
michael@0 112 .getLong(HealthReportConstants.PREF_LAST_UPLOAD_LOCAL_TIME, 0L);
michael@0 113 }
michael@0 114
michael@0 115 /**
michael@0 116 * Generate a new Health Report for the current Gecko profile.
michael@0 117 *
michael@0 118 * This method performs IO, so call it from a background thread.
michael@0 119 *
michael@0 120 * @throws JSONException if JSON generation fails.
michael@0 121 * @throws IllegalStateException if the environment does not allow to generate a report.
michael@0 122 * @return non-null Health Report.
michael@0 123 */
michael@0 124 public JSONObject generateReport() throws JSONException {
michael@0 125 GeckoProfile profile = GeckoAppShell.getGeckoInterface().getProfile();
michael@0 126 String profilePath = profile.getDir().getAbsolutePath();
michael@0 127
michael@0 128 long since = System.currentTimeMillis() - GlobalConstants.MILLISECONDS_PER_SIX_MONTHS;
michael@0 129 long lastPingTime = Math.max(getLastUploadLocalTime(), HealthReportConstants.EARLIEST_LAST_PING);
michael@0 130
michael@0 131 return generateReport(since, lastPingTime, profilePath);
michael@0 132 }
michael@0 133
michael@0 134 @Override
michael@0 135 public void handleMessage(String event, JSONObject message) {
michael@0 136 try {
michael@0 137 ThreadUtils.postToBackgroundThread(new Runnable() {
michael@0 138 @Override
michael@0 139 public void run() {
michael@0 140 JSONObject report = null;
michael@0 141 try {
michael@0 142 report = generateReport(); // non-null if it returns.
michael@0 143 } catch (Exception e) {
michael@0 144 Log.e(LOGTAG, "Generating report failed; responding with empty report.", e);
michael@0 145 report = new JSONObject();
michael@0 146 }
michael@0 147
michael@0 148 GeckoAppShell.sendEventToGecko(GeckoEvent.createBroadcastEvent(EVENT_RESPONSE, report.toString()));
michael@0 149 }
michael@0 150 });
michael@0 151 } catch (Exception e) {
michael@0 152 Log.e(LOGTAG, "Exception handling message \"" + event + "\":", e);
michael@0 153 }
michael@0 154 }
michael@0 155 }
michael@0 156

mercurial