mobile/android/base/health/BrowserHealthReporter.java

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

mercurial