Bug 922694 - Part 4: grab Accept-Locale pref in FHR. r=mcomella

This commit is contained in:
Richard Newman 2013-10-16 18:56:27 -07:00
parent 361e5f5a97
commit e1e1a77170
4 changed files with 170 additions and 72 deletions

View File

@ -117,6 +117,7 @@ import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.regex.Matcher;
@ -1291,7 +1292,16 @@ abstract public class GeckoApp
final String profilePath = getProfile().getDir().getAbsolutePath();
final EventDispatcher dispatcher = GeckoAppShell.getEventDispatcher();
Log.i(LOGTAG, "Creating BrowserHealthRecorder.");
mHealthRecorder = new BrowserHealthRecorder(GeckoApp.this, profilePath, dispatcher,
final String osLocale = Locale.getDefault().toString();
Log.d(LOGTAG, "Locale is " + osLocale);
// Replace the duplicate `osLocale` argument when we support switchable
// application locales.
mHealthRecorder = new BrowserHealthRecorder(GeckoApp.this,
profilePath,
dispatcher,
osLocale,
osLocale, // Placeholder.
previousSession);
}
});
@ -1555,8 +1565,15 @@ abstract public class GeckoApp
GeckoPreferences.broadcastHealthReportUploadPref(context);
/*
XXXX see bug 635342
We want to disable this code if possible. It is about 145ms in runtime
XXXX see Bug 635342.
We want to disable this code if possible. It is about 145ms in runtime.
If this code ever becomes live again, you'll need to chain the
new locale into BrowserHealthRecorder correctly. See
GeckoAppShell.setSelectedLocale.
We pass the OS locale into the BHR constructor: we need to grab
that *before* we modify the current locale!
SharedPreferences settings = getPreferences(Activity.MODE_PRIVATE);
String localeCode = settings.getString(getPackageName() + ".locale", "");
if (localeCode != null && localeCode.length() > 0)

View File

@ -1525,6 +1525,12 @@ public class GeckoAppShell
Gecko resets the locale to en-US by calling this function with an empty string.
This affects GeckoPreferences activity in multi-locale builds.
N.B., if this code ever becomes live again, you need to hook it up to locale
recording in BrowserHealthRecorder: we track the current app and OS locales
as part of the recorded environment.
See similar note in GeckoApp.java for the startup path.
//We're not using this, not need to save it (see bug 635342)
SharedPreferences settings =
getContext().getPreferences(Activity.MODE_PRIVATE);

View File

@ -16,8 +16,6 @@ import org.mozilla.gecko.AppConstants;
import org.mozilla.gecko.GeckoApp;
import org.mozilla.gecko.GeckoAppShell;
import org.mozilla.gecko.GeckoEvent;
import org.mozilla.gecko.PrefsHelper;
import org.mozilla.gecko.PrefsHelper.PrefHandler;
import org.mozilla.gecko.background.healthreport.EnvironmentBuilder;
import org.mozilla.gecko.background.healthreport.HealthReportDatabaseStorage;
@ -38,6 +36,7 @@ import java.io.FileOutputStream;
import java.io.OutputStreamWriter;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.Scanner;
import java.util.concurrent.atomic.AtomicBoolean;
@ -50,8 +49,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
* Keep an instance of this class around.
*
* Tell it when an environment attribute has changed: call {@link
* #onBlocklistPrefChanged(boolean)} or {@link
* #onTelemetryPrefChanged(boolean)}, followed by {@link
* #onAppLocaleChanged(String)} followed by {@link
* #onEnvironmentChanged()}.
*
* Use it to record events: {@link #recordSearch(String, String)}.
@ -60,8 +58,9 @@ import java.util.concurrent.atomic.AtomicBoolean;
*/
public class BrowserHealthRecorder implements 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_ADDONS_ALL = "Addons:All";
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";
@ -242,8 +241,15 @@ public class BrowserHealthRecorder implements GeckoEventListener {
/**
* This constructor does IO. Run it on a background thread.
*
* appLocale can be null, which indicates that it will be provided later.
*/
public BrowserHealthRecorder(final Context context, final String profilePath, final EventDispatcher dispatcher, SessionInformation previousSession) {
public BrowserHealthRecorder(final Context context,
final String profilePath,
final EventDispatcher dispatcher,
final String osLocale,
final String appLocale,
SessionInformation previousSession) {
Log.d(LOG_TAG, "Initializing. Dispatcher is " + dispatcher);
this.dispatcher = dispatcher;
this.previousSession = previousSession;
@ -263,9 +269,12 @@ public class BrowserHealthRecorder implements GeckoEventListener {
this.client = null;
}
// Note that the PIC is not necessarily fully initialized at this point:
// we haven't set the app locale. This must be done before an environment
// is recorded.
this.profileCache = new ProfileInformationCache(profilePath);
try {
this.initialize(context, profilePath);
this.initialize(context, profilePath, osLocale, appLocale);
} catch (Exception e) {
Log.e(LOG_TAG, "Exception initializing.", e);
}
@ -299,7 +308,7 @@ public class BrowserHealthRecorder implements GeckoEventListener {
}
private void unregisterEventListeners() {
this.dispatcher.unregisterEventListener(EVENT_ADDONS_ALL, this);
this.dispatcher.unregisterEventListener(EVENT_SNAPSHOT, this);
this.dispatcher.unregisterEventListener(EVENT_ADDONS_CHANGE, this);
this.dispatcher.unregisterEventListener(EVENT_ADDONS_UNINSTALLING, this);
this.dispatcher.unregisterEventListener(EVENT_PREF_CHANGE, this);
@ -307,14 +316,9 @@ public class BrowserHealthRecorder implements GeckoEventListener {
this.dispatcher.unregisterEventListener(EVENT_SEARCH, this);
}
public void onBlocklistPrefChanged(boolean to) {
public void onAppLocaleChanged(String to) {
this.profileCache.beginInitialization();
this.profileCache.setBlocklistEnabled(to);
}
public void onTelemetryPrefChanged(boolean to) {
this.profileCache.beginInitialization();
this.profileCache.setTelemetryEnabled(to);
this.profileCache.setAppLocale(to);
}
public void onAddonChanged(String id, JSONObject json) {
@ -340,8 +344,7 @@ public class BrowserHealthRecorder implements GeckoEventListener {
* environment, such that a new environment should be computed and prepared
* for use in future events.
*
* Invoke this method after calls that mutate the environment, such as
* {@link #onBlocklistPrefChanged(boolean)}.
* Invoke this method after calls that mutate the environment.
*
* If this change resulted in a transition between two environments, {@link
* #onEnvironmentTransition(int, int)} will be invoked on the background
@ -491,14 +494,36 @@ public class BrowserHealthRecorder implements GeckoEventListener {
return time;
}
private void handlePrefValue(final String pref, final boolean value) {
Log.d(LOG_TAG, "Incorporating environment: " + pref + " = " + value);
if (AppConstants.TELEMETRY_PREF_NAME.equals(pref)) {
profileCache.setTelemetryEnabled(value);
private void onPrefMessage(final String pref, final JSONObject message) {
Log.d(LOG_TAG, "Incorporating environment: " + pref);
if (PREF_ACCEPT_LANG.equals(pref)) {
// We only record whether this is user-set.
try {
this.profileCache.beginInitialization();
this.profileCache.setAcceptLangUserSet(message.getBoolean("isUserSet"));
} catch (JSONException ex) {
Log.w(LOG_TAG, "Unexpected JSONException fetching isUserSet for " + pref);
}
return;
}
if (PREF_BLOCKLIST_ENABLED.equals(pref)) {
profileCache.setBlocklistEnabled(value);
// (We only handle boolean prefs right now.)
try {
boolean value = message.getBoolean("value");
if (AppConstants.TELEMETRY_PREF_NAME.equals(pref)) {
this.profileCache.beginInitialization();
this.profileCache.setTelemetryEnabled(value);
return;
}
if (PREF_BLOCKLIST_ENABLED.equals(pref)) {
this.profileCache.beginInitialization();
this.profileCache.setBlocklistEnabled(value);
return;
}
} catch (JSONException ex) {
Log.w(LOG_TAG, "Unexpected JSONException fetching boolean value for " + pref);
return;
}
Log.w(LOG_TAG, "Unexpected pref: " + pref);
@ -571,7 +596,9 @@ public class BrowserHealthRecorder implements GeckoEventListener {
* Add provider-specific initialization in this method.
*/
private synchronized void initialize(final Context context,
final String profilePath)
final String profilePath,
final String osLocale,
final String appLocale)
throws java.io.IOException {
Log.d(LOG_TAG, "Initializing profile cache.");
@ -579,6 +606,9 @@ public class BrowserHealthRecorder implements GeckoEventListener {
// If we can restore state from last time, great.
if (this.profileCache.restoreUnlessInitialized()) {
this.profileCache.updateLocales(osLocale, appLocale);
this.profileCache.completeInitialization();
Log.d(LOG_TAG, "Successfully restored state. Initializing storage.");
initializeStorage();
return;
@ -587,31 +617,12 @@ public class BrowserHealthRecorder implements GeckoEventListener {
// Otherwise, let's initialize it from scratch.
this.profileCache.beginInitialization();
this.profileCache.setProfileCreationTime(getAndPersistProfileInitTime(context, profilePath));
this.profileCache.setOSLocale(osLocale);
this.profileCache.setAppLocale(appLocale);
final BrowserHealthRecorder self = this;
PrefHandler handler = new PrefsHelper.PrefHandlerBase() {
@Override
public void prefValue(String pref, boolean value) {
handlePrefValue(pref, value);
}
@Override
public void finish() {
Log.d(LOG_TAG, "Requesting all add-ons from Gecko.");
dispatcher.registerEventListener(EVENT_ADDONS_ALL, self);
GeckoAppShell.sendEventToGecko(GeckoEvent.createBroadcastEvent("Addons:FetchAll", null));
// Wait for the broadcast event which completes our initialization.
}
};
// Oh, singletons.
PrefsHelper.getPrefs(new String[] {
AppConstants.TELEMETRY_PREF_NAME,
PREF_BLOCKLIST_ENABLED
},
handler);
Log.d(LOG_TAG, "Requested prefs.");
Log.d(LOG_TAG, "Requesting all add-ons and FHR prefs from Gecko.");
dispatcher.registerEventListener(EVENT_SNAPSHOT, this);
GeckoAppShell.sendEventToGecko(GeckoEvent.createBroadcastEvent("HealthReport:RequestSnapshot", null));
}
/**
@ -638,12 +649,22 @@ public class BrowserHealthRecorder implements GeckoEventListener {
@Override
public void handleMessage(String event, JSONObject message) {
try {
if (EVENT_ADDONS_ALL.equals(event)) {
Log.d(LOG_TAG, "Got all add-ons.");
if (EVENT_SNAPSHOT.equals(event)) {
Log.d(LOG_TAG, "Got all add-ons and prefs.");
try {
JSONObject addons = message.getJSONObject("json");
JSONObject json = message.getJSONObject("json");
JSONObject addons = json.getJSONObject("addons");
Log.i(LOG_TAG, "Persisting " + addons.length() + " add-ons.");
profileCache.setJSONForAddons(addons);
JSONObject prefs = json.getJSONObject("prefs");
Log.i(LOG_TAG, "Persisting prefs.");
Iterator<?> keys = prefs.keys();
while (keys.hasNext()) {
String pref = (String) keys.next();
this.onPrefMessage(pref, prefs.getJSONObject(pref));
}
profileCache.completeInitialization();
} catch (java.io.IOException e) {
Log.e(LOG_TAG, "Error completing profile cache initialization.", e);
@ -675,7 +696,7 @@ public class BrowserHealthRecorder implements GeckoEventListener {
if (EVENT_PREF_CHANGE.equals(event)) {
final String pref = message.getString("pref");
Log.d(LOG_TAG, "Pref changed: " + pref);
handlePrefValue(pref, message.getBoolean("value"));
this.onPrefMessage(pref, message);
this.onEnvironmentChanged();
return;
}

View File

@ -5316,7 +5316,10 @@ var FormAssistant = {
* -- and reflect them back to Java.
*/
let HealthReportStatusListener = {
TELEMETRY_PREF:
PREF_ACCEPT_LANG: "intl.accept_languages",
PREF_BLOCKLIST_ENABLED: "extensions.blocklist.enabled",
PREF_TELEMETRY_ENABLED:
#ifdef MOZ_TELEMETRY_REPORTING
// Telemetry pref differs based on build.
#ifdef MOZ_TELEMETRY_ON_BY_DEFAULT
@ -5335,18 +5338,21 @@ let HealthReportStatusListener = {
console.log("Failed to initialize add-on status listener. FHR cannot report add-on state. " + ex);
}
Services.obs.addObserver(this, "Addons:FetchAll", false);
Services.prefs.addObserver("extensions.blocklist.enabled", this, false);
if (this.TELEMETRY_PREF) {
Services.prefs.addObserver(this.TELEMETRY_PREF, this, false);
console.log("Adding HealthReport:RequestSnapshot observer.");
Services.obs.addObserver(this, "HealthReport:RequestSnapshot", false);
Services.prefs.addObserver(this.PREF_ACCEPT_LANG, this, false);
Services.prefs.addObserver(this.PREF_BLOCKLIST_ENABLED, this, false);
if (this.PREF_TELEMETRY_ENABLED) {
Services.prefs.addObserver(this.PREF_TELEMETRY_ENABLED, this, false);
}
},
uninit: function () {
Services.obs.removeObserver(this, "Addons:FetchAll");
Services.prefs.removeObserver("extensions.blocklist.enabled", this);
if (this.TELEMETRY_PREF) {
Services.prefs.removeObserver(this.TELEMETRY_PREF, this);
Services.obs.removeObserver(this, "HealthReport:RequestSnapshot");
Services.prefs.removeObserver(this.PREF_ACCEPT_LANG, this);
Services.prefs.removeObserver(this.PREF_BLOCKLIST_ENABLED, this);
if (this.PREF_TELEMETRY_ENABLED) {
Services.prefs.removeObserver(this.PREF_TELEMETRY_ENABLED, this);
}
AddonManager.removeAddonListener(this);
@ -5354,11 +5360,30 @@ let HealthReportStatusListener = {
observe: function (aSubject, aTopic, aData) {
switch (aTopic) {
case "Addons:FetchAll":
HealthReportStatusListener.sendAllAddonsToJava();
case "HealthReport:RequestSnapshot":
HealthReportStatusListener.sendSnapshotToJava();
break;
case "nsPref:changed":
sendMessageToJava({ type: "Pref:Change", pref: aData, value: Services.prefs.getBoolPref(aData) });
let response = {
type: "Pref:Change",
pref: aData,
isUserSet: Services.prefs.prefHasUserValue(aData),
};
switch (aData) {
case this.PREF_ACCEPT_LANG:
response.value = Services.prefs.getCharPref(aData);
break;
case this.PREF_TELEMETRY_ENABLED:
case this.PREF_BLOCKLIST_ENABLED:
response.value = Services.prefs.getBoolPref(aData);
break;
default:
console.log("Unexpected pref in HealthReportStatusListener: " + aData);
return;
}
sendMessageToJava(response);
break;
}
},
@ -5440,9 +5465,9 @@ let HealthReportStatusListener = {
this.notifyJava(aAddon);
},
sendAllAddonsToJava: function () {
sendSnapshotToJava: function () {
AddonManager.getAllAddons(function (aAddons) {
let json = {};
let jsonA = {};
if (aAddons) {
for (let i = 0; i < aAddons.length; ++i) {
let addon = aAddons[i];
@ -5451,14 +5476,43 @@ let HealthReportStatusListener = {
if (HealthReportStatusListener._shouldIgnore(addon)) {
addonJSON.ignore = true;
}
json[addon.id] = addonJSON;
jsonA[addon.id] = addonJSON;
} catch (e) {
// Just skip this add-on.
}
}
}
sendMessageToJava({ type: "Addons:All", json: json });
});
// Now add prefs.
let jsonP = {};
for (let pref of [this.PREF_BLOCKLIST_ENABLED, this.PREF_TELEMETRY_ENABLED]) {
if (!pref) {
// This will be the case for PREF_TELEMETRY_ENABLED in developer builds.
continue;
}
jsonP[pref] = {
pref: pref,
value: Services.prefs.getBoolPref(pref),
isUserSet: Services.prefs.prefHasUserValue(pref),
};
}
for (let pref of [this.PREF_ACCEPT_LANG]) {
jsonP[pref] = {
pref: pref,
value: Services.prefs.getCharPref(pref),
isUserSet: Services.prefs.prefHasUserValue(pref),
};
}
console.log("Sending snapshot message.");
sendMessageToJava({
type: "HealthReport:Snapshot",
json: {
addons: jsonA,
prefs: jsonP,
},
});
}.bind(this));
},
};