Bug 960709 - Part 1: Factor out HealthRecorder interface. r=rnewman
authorMichael Comella <michael.l.comella@gmail.com>
Tue, 04 Feb 2014 19:41:29 -0800
changeset 166821 e4a442b68a435d9976e2b06dc12f78a7f5bd3177
parent 166820 f1156ec42c25e1a4b1fd684f7ee5c5d2eeaca39e
child 166822 52ed9fc26da5827fad1f9242907f6962cf51fcda
push id4842
push usermichael.l.comella@gmail.com
push dateWed, 05 Feb 2014 03:41:25 +0000
treeherderfx-team@50c164585e25 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersrnewman
bugs960709
milestone30.0a1
Bug 960709 - Part 1: Factor out HealthRecorder interface. r=rnewman
mobile/android/base/GeckoApp.java
mobile/android/base/health/BrowserHealthRecorder.java
mobile/android/base/health/HealthRecorder.java
mobile/android/base/health/SessionInformation.java
mobile/android/base/health/StubbedHealthRecorder.java
mobile/android/base/moz.build
--- a/mobile/android/base/GeckoApp.java
+++ b/mobile/android/base/GeckoApp.java
@@ -12,17 +12,17 @@ import org.mozilla.gecko.gfx.BitmapUtils
 import org.mozilla.gecko.gfx.Layer;
 import org.mozilla.gecko.gfx.LayerView;
 import org.mozilla.gecko.gfx.PluginLayer;
 import org.mozilla.gecko.prompts.PromptService;
 import org.mozilla.gecko.menu.GeckoMenu;
 import org.mozilla.gecko.menu.GeckoMenuInflater;
 import org.mozilla.gecko.menu.MenuPanel;
 import org.mozilla.gecko.health.BrowserHealthRecorder;
-import org.mozilla.gecko.health.BrowserHealthRecorder.SessionInformation;
+import org.mozilla.gecko.health.SessionInformation;
 import org.mozilla.gecko.preferences.GeckoPreferences;
 import org.mozilla.gecko.updater.UpdateService;
 import org.mozilla.gecko.updater.UpdateServiceHelper;
 import org.mozilla.gecko.util.ActivityResultHandler;
 import org.mozilla.gecko.util.EventDispatcher;
 import org.mozilla.gecko.util.GeckoEventListener;
 import org.mozilla.gecko.util.GeckoEventResponder;
 import org.mozilla.gecko.util.HardwareUtils;
--- a/mobile/android/base/health/BrowserHealthRecorder.java
+++ b/mobile/android/base/health/BrowserHealthRecorder.java
@@ -57,17 +57,17 @@ import java.util.concurrent.atomic.Atomi
  * Tell it when an environment attribute has changed: call {@link
  * #onAppLocaleChanged(String)} followed by {@link
  * #onEnvironmentChanged()}.
  *
  * Use it to record events: {@link #recordSearch(String, String)}.
  *
  * Shut it down when you're done being a browser: {@link #close()}.
  */
-public class BrowserHealthRecorder implements GeckoEventListener {
+public class BrowserHealthRecorder implements HealthRecorder, GeckoEventListener {
     private static final String LOG_TAG = "GeckoHealthRec";
     private static final String PREF_ACCEPT_LANG = "intl.accept_languages";
     private static final String PREF_BLOCKLIST_ENABLED = "extensions.blocklist.enabled";
     private static final String EVENT_SNAPSHOT = "HealthReport:Snapshot";
     private static final String EVENT_ADDONS_CHANGE = "Addons:Change";
     private static final String EVENT_ADDONS_UNINSTALLING = "Addons:Uninstalling";
     private static final String EVENT_PREF_CHANGE = "Pref:Change";
 
@@ -92,154 +92,39 @@ public class BrowserHealthRecorder imple
     private final AtomicBoolean orphanChecked = new AtomicBoolean(false);
     private volatile int env = -1;
 
     private ContentProviderClient client;
     private volatile HealthReportDatabaseStorage storage;
     private final ProfileInformationCache profileCache;
     private final EventDispatcher dispatcher;
 
-    public static class SessionInformation {
-        private static final String LOG_TAG = "GeckoSessInfo";
-
-        public static final String PREFS_SESSION_START = "sessionStart";
-
-        public final long wallStartTime;    // System wall clock.
-        public final long realStartTime;    // Realtime clock.
-
-        private final boolean wasOOM;
-        private final boolean wasStopped;
-
-        private volatile long timedGeckoStartup = -1;
-        private volatile long timedJavaStartup = -1;
-
-        // Current sessions don't (right now) care about wasOOM/wasStopped.
-        // Eventually we might want to lift that logic out of GeckoApp.
-        public SessionInformation(long wallTime, long realTime) {
-            this(wallTime, realTime, false, false);
-        }
-
-        // Previous sessions do...
-        public SessionInformation(long wallTime, long realTime, boolean wasOOM, boolean wasStopped) {
-            this.wallStartTime = wallTime;
-            this.realStartTime = realTime;
-            this.wasOOM = wasOOM;
-            this.wasStopped = wasStopped;
-        }
-
-        /**
-         * Initialize a new SessionInformation instance from the supplied prefs object.
-         *
-         * This includes retrieving OOM/crash data, as well as timings.
-         *
-         * If no wallStartTime was found, that implies that the previous
-         * session was correctly recorded, and an object with a zero
-         * wallStartTime is returned.
-         */
-        public static SessionInformation fromSharedPrefs(SharedPreferences prefs) {
-            boolean wasOOM = prefs.getBoolean(GeckoApp.PREFS_OOM_EXCEPTION, false);
-            boolean wasStopped = prefs.getBoolean(GeckoApp.PREFS_WAS_STOPPED, true);
-            long wallStartTime = prefs.getLong(PREFS_SESSION_START, 0L);
-            long realStartTime = 0L;
-            Log.d(LOG_TAG, "Building SessionInformation from prefs: " +
-                           wallStartTime + ", " + realStartTime + ", " +
-                           wasStopped + ", " + wasOOM);
-            return new SessionInformation(wallStartTime, realStartTime, wasOOM, wasStopped);
-        }
-
-        /**
-         * Initialize a new SessionInformation instance to 'split' the current
-         * session.
-         */
-        public static SessionInformation forRuntimeTransition() {
-            final boolean wasOOM = false;
-            final boolean wasStopped = true;
-            final long wallStartTime = System.currentTimeMillis();
-            final long realStartTime = android.os.SystemClock.elapsedRealtime();
-            Log.v(LOG_TAG, "Recording runtime session transition: " +
-                           wallStartTime + ", " + realStartTime);
-            return new SessionInformation(wallStartTime, realStartTime, wasOOM, wasStopped);
-        }
-
-        public boolean wasKilled() {
-            return wasOOM || !wasStopped;
-        }
-
-        /**
-         * Record the beginning of this session to SharedPreferences by
-         * recording our start time. If a session was already recorded, it is
-         * overwritten (there can only be one running session at a time). Does
-         * not commit the editor.
-         */
-        public void recordBegin(SharedPreferences.Editor editor) {
-            Log.d(LOG_TAG, "Recording start of session: " + this.wallStartTime);
-            editor.putLong(PREFS_SESSION_START, this.wallStartTime);
-        }
-
-        /**
-         * Record the completion of this session to SharedPreferences by
-         * deleting our start time. Does not commit the editor.
-         */
-        public void recordCompletion(SharedPreferences.Editor editor) {
-            Log.d(LOG_TAG, "Recording session done: " + this.wallStartTime);
-            editor.remove(PREFS_SESSION_START);
-        }
-
-        /**
-         * Return the JSON that we'll put in the DB for this session.
-         */
-        public JSONObject getCompletionJSON(String reason, long realEndTime) throws JSONException {
-            long durationSecs = (realEndTime - this.realStartTime) / 1000;
-            JSONObject out = new JSONObject();
-            out.put("r", reason);
-            out.put("d", durationSecs);
-            if (this.timedGeckoStartup > 0) {
-                out.put("sg", this.timedGeckoStartup);
-            }
-            if (this.timedJavaStartup > 0) {
-                out.put("sj", this.timedJavaStartup);
-            }
-            return out;
-        }
-
-        public JSONObject getCrashedJSON() throws JSONException {
-            JSONObject out = new JSONObject();
-            // We use ints here instead of booleans, because we're packing
-            // stuff into JSON, and saving bytes in the DB is a worthwhile
-            // goal.
-            out.put("oom", this.wasOOM ? 1 : 0);
-            out.put("stopped", this.wasStopped ? 1 : 0);
-            out.put("r", "A");
-            return out;
-        }
-    }
-
     // We track previousSession to avoid order-of-initialization confusion. We
     // accept it in the constructor, and process it after init.
     private final SessionInformation previousSession;
     private volatile SessionInformation session = null;
     public SessionInformation getCurrentSession() {
         return this.session;
     }
 
     public void setCurrentSession(SessionInformation session) {
         this.session = session;
     }
 
     public void recordGeckoStartupTime(long duration) {
         if (this.session == null) {
             return;
         }
-        this.session.timedGeckoStartup = duration;
+        this.session.setTimedGeckoStartup(duration);
     }
     public void recordJavaStartupTime(long duration) {
         if (this.session == null) {
             return;
         }
-        this.session.timedJavaStartup = duration;
+        this.session.setTimedJavaStartup(duration);
     }
 
     /**
      * Persist the opaque identifier for the current Firefox Health Report environment.
      * This changes in certain circumstances; be sure to use the current value when recording data.
      */
     private void setHealthEnvironment(final int env) {
         this.env = env;
@@ -1012,9 +897,8 @@ public class BrowserHealthRecorder imple
             // Future: record this exception in FHR's own error submitter.
         }
 
         // Track the end of this session in shared prefs, so it doesn't get
         // double-counted on next run.
         session.recordCompletion(editor);
     }
 }
-
new file mode 100644
--- /dev/null
+++ b/mobile/android/base/health/HealthRecorder.java
@@ -0,0 +1,24 @@
+/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
+ * 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.health;
+
+import android.content.SharedPreferences;
+
+/**
+ * HealthRecorder is an interface into the Firefox Health Report storage system.
+ */
+public interface HealthRecorder {
+    public void setCurrentSession(SessionInformation session);
+    public void recordSessionEnd(String reason, SharedPreferences.Editor editor);
+
+    public void recordGeckoStartupTime(long duration);
+    public void recordJavaStartupTime(long duration);
+
+    public void onAppLocaleChanged(String to);
+    public void onEnvironmentChanged(final boolean startNewSession, final String sessionEndReason);
+
+    public void close();
+}
new file mode 100644
--- /dev/null
+++ b/mobile/android/base/health/SessionInformation.java
@@ -0,0 +1,137 @@
+/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
+ * 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.health;
+
+import android.content.SharedPreferences;
+import android.util.Log;
+
+import org.mozilla.gecko.GeckoApp;
+
+import org.json.JSONException;
+import org.json.JSONObject;
+
+public class SessionInformation {
+    private static final String LOG_TAG = "GeckoSessInfo";
+
+    public static final String PREFS_SESSION_START = "sessionStart";
+
+    public final long wallStartTime;    // System wall clock.
+    public final long realStartTime;    // Realtime clock.
+
+    private final boolean wasOOM;
+    private final boolean wasStopped;
+
+    private volatile long timedGeckoStartup = -1;
+    private volatile long timedJavaStartup = -1;
+
+    // Current sessions don't (right now) care about wasOOM/wasStopped.
+    // Eventually we might want to lift that logic out of GeckoApp.
+    public SessionInformation(long wallTime, long realTime) {
+        this(wallTime, realTime, false, false);
+    }
+
+    // Previous sessions do...
+    public SessionInformation(long wallTime, long realTime, boolean wasOOM, boolean wasStopped) {
+        this.wallStartTime = wallTime;
+        this.realStartTime = realTime;
+        this.wasOOM = wasOOM;
+        this.wasStopped = wasStopped;
+    }
+
+    /**
+     * Initialize a new SessionInformation instance from the supplied prefs object.
+     *
+     * This includes retrieving OOM/crash data, as well as timings.
+     *
+     * If no wallStartTime was found, that implies that the previous
+     * session was correctly recorded, and an object with a zero
+     * wallStartTime is returned.
+     */
+    public static SessionInformation fromSharedPrefs(SharedPreferences prefs) {
+        boolean wasOOM = prefs.getBoolean(GeckoApp.PREFS_OOM_EXCEPTION, false);
+        boolean wasStopped = prefs.getBoolean(GeckoApp.PREFS_WAS_STOPPED, true);
+        long wallStartTime = prefs.getLong(PREFS_SESSION_START, 0L);
+        long realStartTime = 0L;
+        Log.d(LOG_TAG, "Building SessionInformation from prefs: " +
+                       wallStartTime + ", " + realStartTime + ", " +
+                       wasStopped + ", " + wasOOM);
+        return new SessionInformation(wallStartTime, realStartTime, wasOOM, wasStopped);
+    }
+
+    /**
+     * Initialize a new SessionInformation instance to 'split' the current
+     * session.
+     */
+    public static SessionInformation forRuntimeTransition() {
+        final boolean wasOOM = false;
+        final boolean wasStopped = true;
+        final long wallStartTime = System.currentTimeMillis();
+        final long realStartTime = android.os.SystemClock.elapsedRealtime();
+        Log.v(LOG_TAG, "Recording runtime session transition: " +
+                       wallStartTime + ", " + realStartTime);
+        return new SessionInformation(wallStartTime, realStartTime, wasOOM, wasStopped);
+    }
+
+    public boolean wasKilled() {
+        return wasOOM || !wasStopped;
+    }
+
+    /**
+     * Record the beginning of this session to SharedPreferences by
+     * recording our start time. If a session was already recorded, it is
+     * overwritten (there can only be one running session at a time). Does
+     * not commit the editor.
+     */
+    public void recordBegin(SharedPreferences.Editor editor) {
+        Log.d(LOG_TAG, "Recording start of session: " + this.wallStartTime);
+        editor.putLong(PREFS_SESSION_START, this.wallStartTime);
+    }
+
+    /**
+     * Record the completion of this session to SharedPreferences by
+     * deleting our start time. Does not commit the editor.
+     */
+    public void recordCompletion(SharedPreferences.Editor editor) {
+        Log.d(LOG_TAG, "Recording session done: " + this.wallStartTime);
+        editor.remove(PREFS_SESSION_START);
+    }
+
+    /**
+     * Return the JSON that we'll put in the DB for this session.
+     */
+    public JSONObject getCompletionJSON(String reason, long realEndTime) throws JSONException {
+        long durationSecs = (realEndTime - this.realStartTime) / 1000;
+        JSONObject out = new JSONObject();
+        out.put("r", reason);
+        out.put("d", durationSecs);
+        if (this.timedGeckoStartup > 0) {
+            out.put("sg", this.timedGeckoStartup);
+        }
+        if (this.timedJavaStartup > 0) {
+            out.put("sj", this.timedJavaStartup);
+        }
+        return out;
+    }
+
+    public JSONObject getCrashedJSON() throws JSONException {
+        JSONObject out = new JSONObject();
+        // We use ints here instead of booleans, because we're packing
+        // stuff into JSON, and saving bytes in the DB is a worthwhile
+        // goal.
+        out.put("oom", this.wasOOM ? 1 : 0);
+        out.put("stopped", this.wasStopped ? 1 : 0);
+        out.put("r", "A");
+        return out;
+    }
+
+    public void setTimedGeckoStartup(final long duration) {
+        timedGeckoStartup = duration;
+    }
+
+    public void setTimedJavaStartup(final long duration) {
+        timedJavaStartup = duration;
+    }
+}
new file mode 100644
--- /dev/null
+++ b/mobile/android/base/health/StubbedHealthRecorder.java
@@ -0,0 +1,27 @@
+/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
+ * 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.health;
+
+import android.content.SharedPreferences;
+
+/**
+ * StubbedHealthRecorder is an implementation of HealthRecorder that does (you guessed it!)
+ * nothing.
+ */
+public class StubbedHealthRecorder implements HealthRecorder {
+    public StubbedHealthRecorder() { }
+
+    public void setCurrentSession(SessionInformation session) { }
+    public void recordSessionEnd(String reason, SharedPreferences.Editor editor) { }
+
+    public void recordGeckoStartupTime(long duration) { }
+    public void recordJavaStartupTime(long duration) { }
+
+    public void onAppLocaleChanged(String to) { }
+    public void onEnvironmentChanged(final boolean startNewSession, final String sessionEndReason) { }
+
+    public void close() { }
+}
--- a/mobile/android/base/moz.build
+++ b/mobile/android/base/moz.build
@@ -202,16 +202,19 @@ gbjar.sources += [
     'gfx/TextureReaper.java',
     'gfx/TileLayer.java',
     'gfx/TouchEventHandler.java',
     'gfx/ViewTransform.java',
     'gfx/VirtualLayer.java',
     'GlobalHistory.java',
     'health/BrowserHealthRecorder.java',
     'health/BrowserHealthReporter.java',
+    'health/HealthRecorder.java',
+    'health/SessionInformation.java',
+    'health/StubbedHealthRecorder.java',
     'home/BookmarkFolderView.java',
     'home/BookmarksListAdapter.java',
     'home/BookmarksListView.java',
     'home/BookmarksPanel.java',
     'home/BrowserSearch.java',
     'home/DynamicPanel.java',
     'home/FadedTextView.java',
     'home/FramePanelLayout.java',