author | Richard Newman <rnewman@mozilla.com> |
Tue, 04 Jun 2013 17:16:56 -0700 | |
changeset 134130 | 79088e422daf4d1af974ba219fc01e13ffa245d2 |
parent 134129 | 30fe681b27bea80b484fc0b8e70928246eed4f2f |
child 134131 | 530fbc32771b6c5420fa5adfcbdada6c322cc67f |
push id | 29067 |
push user | ryanvm@gmail.com |
push date | Wed, 05 Jun 2013 20:37:20 +0000 |
treeherder | mozilla-inbound@72fbfb2f8e51 [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | nalexander |
bugs | 868445 |
milestone | 24.0a1 |
first release with | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
--- a/mobile/android/base/background/healthreport/Environment.java +++ b/mobile/android/base/background/healthreport/Environment.java @@ -236,16 +236,20 @@ public abstract class Environment { } } 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; } /**
--- a/mobile/android/base/background/healthreport/HealthReportDatabaseStorage.java +++ b/mobile/android/base/background/healthreport/HealthReportDatabaseStorage.java @@ -6,16 +6,17 @@ package org.mozilla.gecko.background.hea 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.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; @@ -210,17 +211,17 @@ public class HealthReportDatabaseStorage // *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.info(LOG_TAG, "Opening database through absolute path " + path.getAbsolutePath()); + 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; } @@ -228,17 +229,17 @@ public class HealthReportDatabaseStorage public HealthReportSQLiteOpenHelper(Context context, File profileDirectory, String name) { super( (CAN_USE_ABSOLUTE_DB_PATH ? context : new AbsolutePathContext(context, profileDirectory)), (CAN_USE_ABSOLUTE_DB_PATH ? getAbsolutePath(profileDirectory, name) : name), null, CURRENT_VERSION); if (CAN_USE_ABSOLUTE_DB_PATH) { - Logger.info(LOG_TAG, "Opening: " + getAbsolutePath(profileDirectory, name)); + Logger.pii(LOG_TAG, "Opening: " + getAbsolutePath(profileDirectory, name)); } } @Override public void onCreate(SQLiteDatabase db) { db.beginTransaction(); try { db.execSQL("CREATE TABLE addons (id INTEGER PRIMARY KEY AUTOINCREMENT, " + @@ -1030,16 +1031,21 @@ public class HealthReportDatabaseStorage v.put("env", env); v.put("field", field); v.put("date", day); db.insert(table, null, v); } } @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, Integer.valueOf(value), EVENTS_INTEGER); } @@ -1056,16 +1062,21 @@ public class HealthReportDatabaseStorage v.put("date", day); final SQLiteDatabase db = this.helper.getWritableDatabase(); putValue(v, value); db.insert(table, null, v); } @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); }
--- a/mobile/android/base/background/healthreport/HealthReportGenerator.java +++ b/mobile/android/base/background/healthreport/HealthReportGenerator.java @@ -107,17 +107,17 @@ public class HealthReportGenerator { 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.moveToNext()) { + 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 @@ -181,16 +181,32 @@ public class HealthReportGenerator { } days.put(HealthReportUtils.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()) { @@ -200,28 +216,36 @@ public class HealthReportGenerator { 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.
--- a/mobile/android/base/background/healthreport/HealthReportStorage.java +++ b/mobile/android/base/background/healthreport/HealthReportStorage.java @@ -1,14 +1,16 @@ /* 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. @@ -24,30 +26,34 @@ public interface HealthReportStorage { 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; @@ -68,16 +74,24 @@ public interface HealthReportStorage { 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. @@ -149,18 +163,20 @@ public interface HealthReportStorage { 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); /** * Obtain a cursor over events that were recorded since <code>time</code>. * This cursor exposes 'raw' events, with integer identifiers for values.