michael@0: /* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*- michael@0: * This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: package org.mozilla.gecko.health; michael@0: michael@0: import android.content.SharedPreferences; michael@0: import android.util.Log; michael@0: michael@0: import org.mozilla.gecko.GeckoApp; michael@0: michael@0: import org.json.JSONException; michael@0: import org.json.JSONObject; michael@0: michael@0: public class SessionInformation { michael@0: private static final String LOG_TAG = "GeckoSessInfo"; michael@0: michael@0: public static final String PREFS_SESSION_START = "sessionStart"; michael@0: michael@0: public final long wallStartTime; // System wall clock. michael@0: public final long realStartTime; // Realtime clock. michael@0: michael@0: private final boolean wasOOM; michael@0: private final boolean wasStopped; michael@0: michael@0: private volatile long timedGeckoStartup = -1; michael@0: private volatile long timedJavaStartup = -1; michael@0: michael@0: // Current sessions don't (right now) care about wasOOM/wasStopped. michael@0: // Eventually we might want to lift that logic out of GeckoApp. michael@0: public SessionInformation(long wallTime, long realTime) { michael@0: this(wallTime, realTime, false, false); michael@0: } michael@0: michael@0: // Previous sessions do... michael@0: public SessionInformation(long wallTime, long realTime, boolean wasOOM, boolean wasStopped) { michael@0: this.wallStartTime = wallTime; michael@0: this.realStartTime = realTime; michael@0: this.wasOOM = wasOOM; michael@0: this.wasStopped = wasStopped; michael@0: } michael@0: michael@0: /** michael@0: * Initialize a new SessionInformation instance from the supplied prefs object. michael@0: * michael@0: * This includes retrieving OOM/crash data, as well as timings. michael@0: * michael@0: * If no wallStartTime was found, that implies that the previous michael@0: * session was correctly recorded, and an object with a zero michael@0: * wallStartTime is returned. michael@0: */ michael@0: public static SessionInformation fromSharedPrefs(SharedPreferences prefs) { michael@0: boolean wasOOM = prefs.getBoolean(GeckoApp.PREFS_OOM_EXCEPTION, false); michael@0: boolean wasStopped = prefs.getBoolean(GeckoApp.PREFS_WAS_STOPPED, true); michael@0: long wallStartTime = prefs.getLong(PREFS_SESSION_START, 0L); michael@0: long realStartTime = 0L; michael@0: Log.d(LOG_TAG, "Building SessionInformation from prefs: " + michael@0: wallStartTime + ", " + realStartTime + ", " + michael@0: wasStopped + ", " + wasOOM); michael@0: return new SessionInformation(wallStartTime, realStartTime, wasOOM, wasStopped); michael@0: } michael@0: michael@0: /** michael@0: * Initialize a new SessionInformation instance to 'split' the current michael@0: * session. michael@0: */ michael@0: public static SessionInformation forRuntimeTransition() { michael@0: final boolean wasOOM = false; michael@0: final boolean wasStopped = true; michael@0: final long wallStartTime = System.currentTimeMillis(); michael@0: final long realStartTime = android.os.SystemClock.elapsedRealtime(); michael@0: Log.v(LOG_TAG, "Recording runtime session transition: " + michael@0: wallStartTime + ", " + realStartTime); michael@0: return new SessionInformation(wallStartTime, realStartTime, wasOOM, wasStopped); michael@0: } michael@0: michael@0: public boolean wasKilled() { michael@0: return wasOOM || !wasStopped; michael@0: } michael@0: michael@0: /** michael@0: * Record the beginning of this session to SharedPreferences by michael@0: * recording our start time. If a session was already recorded, it is michael@0: * overwritten (there can only be one running session at a time). Does michael@0: * not commit the editor. michael@0: */ michael@0: public void recordBegin(SharedPreferences.Editor editor) { michael@0: Log.d(LOG_TAG, "Recording start of session: " + this.wallStartTime); michael@0: editor.putLong(PREFS_SESSION_START, this.wallStartTime); michael@0: } michael@0: michael@0: /** michael@0: * Record the completion of this session to SharedPreferences by michael@0: * deleting our start time. Does not commit the editor. michael@0: */ michael@0: public void recordCompletion(SharedPreferences.Editor editor) { michael@0: Log.d(LOG_TAG, "Recording session done: " + this.wallStartTime); michael@0: editor.remove(PREFS_SESSION_START); michael@0: } michael@0: michael@0: /** michael@0: * Return the JSON that we'll put in the DB for this session. michael@0: */ michael@0: public JSONObject getCompletionJSON(String reason, long realEndTime) throws JSONException { michael@0: long durationSecs = (realEndTime - this.realStartTime) / 1000; michael@0: JSONObject out = new JSONObject(); michael@0: out.put("r", reason); michael@0: out.put("d", durationSecs); michael@0: if (this.timedGeckoStartup > 0) { michael@0: out.put("sg", this.timedGeckoStartup); michael@0: } michael@0: if (this.timedJavaStartup > 0) { michael@0: out.put("sj", this.timedJavaStartup); michael@0: } michael@0: return out; michael@0: } michael@0: michael@0: public JSONObject getCrashedJSON() throws JSONException { michael@0: JSONObject out = new JSONObject(); michael@0: // We use ints here instead of booleans, because we're packing michael@0: // stuff into JSON, and saving bytes in the DB is a worthwhile michael@0: // goal. michael@0: out.put("oom", this.wasOOM ? 1 : 0); michael@0: out.put("stopped", this.wasStopped ? 1 : 0); michael@0: out.put("r", "A"); michael@0: return out; michael@0: } michael@0: michael@0: public void setTimedGeckoStartup(final long duration) { michael@0: timedGeckoStartup = duration; michael@0: } michael@0: michael@0: public void setTimedJavaStartup(final long duration) { michael@0: timedJavaStartup = duration; michael@0: } michael@0: }