зеркало из https://github.com/mozilla/gecko-dev.git
Bug 875400 - Part 2: provide add-ons and pref changes to FHR. r=mfinkle
* * * Bug 875400 - Part 2a: review comments. * * * Bug 875400 - Part 2b: add flag to ignore add-ons.
This commit is contained in:
Родитель
203e8b3ef2
Коммит
8a4af7f535
|
@ -1860,7 +1860,7 @@ abstract public class GeckoApp
|
|||
|
||||
Tabs.unregisterOnTabsChangedListener(this);
|
||||
if (mHealthRecorder != null) {
|
||||
mHealthRecorder.close(GeckoAppShell.getEventDispatcher());
|
||||
mHealthRecorder.close();
|
||||
mHealthRecorder = null;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,6 +12,8 @@ import android.content.ContentProviderClient;
|
|||
import android.util.Log;
|
||||
|
||||
import org.mozilla.gecko.AppConstants;
|
||||
import org.mozilla.gecko.GeckoAppShell;
|
||||
import org.mozilla.gecko.GeckoEvent;
|
||||
import org.mozilla.gecko.PrefsHelper;
|
||||
import org.mozilla.gecko.PrefsHelper.PrefHandler;
|
||||
|
||||
|
@ -54,6 +56,9 @@ import java.util.Scanner;
|
|||
public class BrowserHealthRecorder implements GeckoEventListener {
|
||||
private static final String LOG_TAG = "GeckoHealthRec";
|
||||
private static final String PREF_BLOCKLIST_ENABLED = "extensions.blocklist.enabled";
|
||||
private static final String EVENT_ADDONS_ALL = "Addons:All";
|
||||
private static final String EVENT_ADDONS_CHANGE = "Addons:Change";
|
||||
private static final String EVENT_PREF_CHANGE = "Pref:Change";
|
||||
|
||||
public enum State {
|
||||
NOT_INITIALIZED,
|
||||
|
@ -69,6 +74,7 @@ public class BrowserHealthRecorder implements GeckoEventListener {
|
|||
private volatile HealthReportDatabaseStorage storage;
|
||||
private final ProfileInformationCache profileCache;
|
||||
private ContentProviderClient client;
|
||||
private final EventDispatcher dispatcher;
|
||||
|
||||
/**
|
||||
* Persist the opaque identifier for the current Firefox Health Report environment.
|
||||
|
@ -83,6 +89,7 @@ public class BrowserHealthRecorder implements GeckoEventListener {
|
|||
*/
|
||||
public BrowserHealthRecorder(final Context context, final String profilePath, final EventDispatcher dispatcher) {
|
||||
Log.d(LOG_TAG, "Initializing. Dispatcher is " + dispatcher);
|
||||
this.dispatcher = dispatcher;
|
||||
this.client = EnvironmentBuilder.getContentProviderClient(context);
|
||||
if (this.client == null) {
|
||||
throw new IllegalStateException("Could not fetch Health Report content provider.");
|
||||
|
@ -95,7 +102,7 @@ public class BrowserHealthRecorder implements GeckoEventListener {
|
|||
|
||||
this.profileCache = new ProfileInformationCache(profilePath);
|
||||
try {
|
||||
this.initialize(context, profilePath, dispatcher);
|
||||
this.initialize(context, profilePath);
|
||||
} catch (Exception e) {
|
||||
Log.e(LOG_TAG, "Exception initializing.", e);
|
||||
}
|
||||
|
@ -107,7 +114,7 @@ public class BrowserHealthRecorder implements GeckoEventListener {
|
|||
* Shut down database connections, unregister event listeners, and perform
|
||||
* provider-specific uninitialization.
|
||||
*/
|
||||
public synchronized void close(final EventDispatcher dispatcher) {
|
||||
public synchronized void close() {
|
||||
switch (this.state) {
|
||||
case CLOSED:
|
||||
Log.w(LOG_TAG, "Ignoring attempt to double-close closed BrowserHealthRecorder.");
|
||||
|
@ -120,7 +127,7 @@ public class BrowserHealthRecorder implements GeckoEventListener {
|
|||
}
|
||||
|
||||
this.state = State.CLOSED;
|
||||
this.unregisterEventListeners(dispatcher);
|
||||
this.unregisterEventListeners();
|
||||
|
||||
// Add any necessary provider uninitialization here.
|
||||
this.storage = null;
|
||||
|
@ -130,7 +137,10 @@ public class BrowserHealthRecorder implements GeckoEventListener {
|
|||
}
|
||||
}
|
||||
|
||||
private void unregisterEventListeners(EventDispatcher dispatcher) {
|
||||
private void unregisterEventListeners() {
|
||||
this.dispatcher.unregisterEventListener(EVENT_ADDONS_ALL, this);
|
||||
this.dispatcher.unregisterEventListener(EVENT_ADDONS_CHANGE, this);
|
||||
this.dispatcher.unregisterEventListener(EVENT_PREF_CHANGE, this);
|
||||
}
|
||||
|
||||
public void onBlocklistPrefChanged(boolean to) {
|
||||
|
@ -143,6 +153,15 @@ public class BrowserHealthRecorder implements GeckoEventListener {
|
|||
this.profileCache.setTelemetryEnabled(to);
|
||||
}
|
||||
|
||||
public void onAddonChanged(String id, JSONObject json) {
|
||||
this.profileCache.beginInitialization();
|
||||
try {
|
||||
this.profileCache.updateJSONForAddon(id, json);
|
||||
} catch (IllegalStateException e) {
|
||||
Log.w(LOG_TAG, "Attempted to update add-on cache prior to full init.", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Call this when a material change has occurred in the running environment,
|
||||
* such that a new environment should be computed and prepared for use in
|
||||
|
@ -162,6 +181,7 @@ public class BrowserHealthRecorder implements GeckoEventListener {
|
|||
this.state = State.INITIALIZATION_FAILED;
|
||||
return;
|
||||
}
|
||||
ensureEnvironment();
|
||||
}
|
||||
|
||||
protected synchronized int ensureEnvironment() {
|
||||
|
@ -247,7 +267,7 @@ public class BrowserHealthRecorder implements GeckoEventListener {
|
|||
// Let's look in the profile.
|
||||
long time = getProfileInitTimeFromFile(profilePath);
|
||||
if (time > 0) {
|
||||
Log.i(LOG_TAG, "Incorporating environment: times.json profile creation = " + time);
|
||||
Log.d(LOG_TAG, "Incorporating environment: times.json profile creation = " + time);
|
||||
return time;
|
||||
}
|
||||
|
||||
|
@ -270,23 +290,95 @@ public class BrowserHealthRecorder implements GeckoEventListener {
|
|||
}
|
||||
}
|
||||
|
||||
Log.i(LOG_TAG, "Incorporating environment: profile creation = " + time);
|
||||
Log.d(LOG_TAG, "Incorporating environment: profile creation = " + time);
|
||||
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);
|
||||
return;
|
||||
}
|
||||
if (PREF_BLOCKLIST_ENABLED.equals(pref)) {
|
||||
profileCache.setBlocklistEnabled(value);
|
||||
return;
|
||||
}
|
||||
Log.w(LOG_TAG, "Unexpected pref: " + pref);
|
||||
}
|
||||
|
||||
/**
|
||||
* Background init helper.
|
||||
*/
|
||||
private void initializeStorage() {
|
||||
Log.d(LOG_TAG, "Done initializing profile cache. Beginning storage init.");
|
||||
|
||||
final BrowserHealthRecorder self = this;
|
||||
ThreadUtils.postToBackgroundThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
synchronized (self) {
|
||||
if (state != State.INITIALIZING) {
|
||||
Log.w(LOG_TAG, "Unexpected state during init: " + state);
|
||||
return;
|
||||
}
|
||||
|
||||
// Belt and braces.
|
||||
if (storage == null) {
|
||||
Log.w(LOG_TAG, "Storage is null during init; shutting down?");
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
storage.beginInitialization();
|
||||
} catch (Exception e) {
|
||||
Log.e(LOG_TAG, "Failed to init storage.", e);
|
||||
state = State.INITIALIZATION_FAILED;
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
// Listen for add-ons and prefs changes.
|
||||
dispatcher.registerEventListener(EVENT_ADDONS_CHANGE, self);
|
||||
dispatcher.registerEventListener(EVENT_PREF_CHANGE, self);
|
||||
|
||||
// Initialize each provider here.
|
||||
|
||||
Log.d(LOG_TAG, "Ensuring environment.");
|
||||
ensureEnvironment();
|
||||
|
||||
Log.d(LOG_TAG, "Finishing init.");
|
||||
storage.finishInitialization();
|
||||
state = State.INITIALIZED;
|
||||
} catch (Exception e) {
|
||||
state = State.INITIALIZATION_FAILED;
|
||||
storage.abortInitialization();
|
||||
Log.e(LOG_TAG, "Initialization failed.", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Add provider-specific initialization in this method.
|
||||
*/
|
||||
private synchronized void initialize(final Context context,
|
||||
final String profilePath,
|
||||
final EventDispatcher dispatcher)
|
||||
final String profilePath)
|
||||
throws java.io.IOException {
|
||||
|
||||
Log.d(LOG_TAG, "Initializing profile cache.");
|
||||
this.state = State.INITIALIZING;
|
||||
this.profileCache.beginInitialization();
|
||||
|
||||
// If we can restore state from last time, great.
|
||||
if (this.profileCache.restoreUnlessInitialized()) {
|
||||
Log.i(LOG_TAG, "Successfully restored state. Initializing storage.");
|
||||
initializeStorage();
|
||||
return;
|
||||
}
|
||||
|
||||
// Otherwise, let's initialize it from scratch.
|
||||
this.profileCache.beginInitialization();
|
||||
this.profileCache.setProfileCreationTime(getAndPersistProfileInitTime(context, profilePath));
|
||||
|
||||
final BrowserHealthRecorder self = this;
|
||||
|
@ -294,69 +386,15 @@ public class BrowserHealthRecorder implements GeckoEventListener {
|
|||
PrefHandler handler = new PrefsHelper.PrefHandlerBase() {
|
||||
@Override
|
||||
public void prefValue(String pref, boolean value) {
|
||||
Log.i(LOG_TAG, "Incorporating environment: " + pref + " = " + value);
|
||||
if (AppConstants.TELEMETRY_PREF_NAME.equals(pref)) {
|
||||
profileCache.setTelemetryEnabled(value);
|
||||
return;
|
||||
}
|
||||
if (PREF_BLOCKLIST_ENABLED.equals(pref)) {
|
||||
profileCache.setBlocklistEnabled(value);
|
||||
return;
|
||||
}
|
||||
Log.w(LOG_TAG, "Unexpected pref: " + pref);
|
||||
handlePrefValue(pref, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void finish() {
|
||||
try {
|
||||
profileCache.completeInitialization();
|
||||
} catch (java.io.IOException e) {
|
||||
Log.e(LOG_TAG, "Error completing profile cache initialization.", e);
|
||||
return;
|
||||
}
|
||||
|
||||
Log.d(LOG_TAG, "Done initializing profile cache. Beginning storage init.");
|
||||
|
||||
ThreadUtils.postToBackgroundThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
synchronized (self) {
|
||||
if (state != State.INITIALIZING) {
|
||||
Log.w(LOG_TAG, "Unexpected state during init: " + state);
|
||||
return;
|
||||
}
|
||||
|
||||
// Belt and braces.
|
||||
if (storage == null) {
|
||||
Log.w(LOG_TAG, "Storage is null during init; shutting down?");
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
storage.beginInitialization();
|
||||
} catch (Exception e) {
|
||||
Log.e(LOG_TAG, "Failed to init storage.", e);
|
||||
state = State.INITIALIZATION_FAILED;
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
// Initialize each provider here.
|
||||
|
||||
Log.d(LOG_TAG, "Ensuring environment.");
|
||||
ensureEnvironment();
|
||||
|
||||
Log.d(LOG_TAG, "Finishing init.");
|
||||
storage.finishInitialization();
|
||||
state = State.INITIALIZED;
|
||||
} catch (Exception e) {
|
||||
state = State.INITIALIZATION_FAILED;
|
||||
storage.abortInitialization();
|
||||
Log.e(LOG_TAG, "Initialization failed.", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
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.
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -372,6 +410,40 @@ 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.");
|
||||
try {
|
||||
JSONObject addons = message.getJSONObject("json");
|
||||
Log.d(LOG_TAG, "Persisting " + addons.length() + " add-ons.");
|
||||
profileCache.setJSONForAddons(addons);
|
||||
profileCache.completeInitialization();
|
||||
} catch (java.io.IOException e) {
|
||||
Log.e(LOG_TAG, "Error completing profile cache initialization.", e);
|
||||
state = State.INITIALIZATION_FAILED;
|
||||
return;
|
||||
}
|
||||
|
||||
if (state == State.INITIALIZING) {
|
||||
initializeStorage();
|
||||
} else {
|
||||
this.onEnvironmentChanged();
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
if (EVENT_ADDONS_CHANGE.equals(event)) {
|
||||
Log.d(LOG_TAG, "Add-on changed: " + message.getString("id"));
|
||||
this.onAddonChanged(message.getString("id"), message.getJSONObject("json"));
|
||||
this.onEnvironmentChanged();
|
||||
return;
|
||||
}
|
||||
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.onEnvironmentChanged();
|
||||
return;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Log.e(LOG_TAG, "Exception handling message \"" + event + "\":", e);
|
||||
}
|
||||
|
|
|
@ -330,6 +330,7 @@ var BrowserApp = {
|
|||
Downloads.init();
|
||||
FormAssistant.init();
|
||||
IndexedDB.init();
|
||||
HealthReportStatusListener.init();
|
||||
XPInstallObserver.init();
|
||||
ClipboardHelper.init();
|
||||
CharacterEncoding.init();
|
||||
|
@ -616,6 +617,7 @@ var BrowserApp = {
|
|||
IndexedDB.uninit();
|
||||
ViewportHandler.uninit();
|
||||
XPInstallObserver.uninit();
|
||||
HealthReportStatusListener.uninit();
|
||||
CharacterEncoding.uninit();
|
||||
SearchEngines.uninit();
|
||||
WebappsUI.uninit();
|
||||
|
@ -5095,6 +5097,157 @@ var FormAssistant = {
|
|||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* An object to watch for Gecko status changes -- add-on installs, pref changes
|
||||
* -- and reflect them back to Java.
|
||||
*/
|
||||
let HealthReportStatusListener = {
|
||||
TELEMETRY_PREF:
|
||||
#ifdef MOZ_TELEMETRY_REPORTING
|
||||
// Telemetry pref differs based on build.
|
||||
#ifdef MOZ_TELEMETRY_ON_BY_DEFAULT
|
||||
"toolkit.telemetry.enabledPreRelease",
|
||||
#else
|
||||
"toolkit.telemetry.enabled",
|
||||
#endif
|
||||
#else
|
||||
null,
|
||||
#endif
|
||||
|
||||
init: function () {
|
||||
try {
|
||||
AddonManager.addAddonListener(this);
|
||||
} catch (ex) {
|
||||
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);
|
||||
}
|
||||
},
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
AddonManager.removeAddonListener(this);
|
||||
},
|
||||
|
||||
observe: function (aSubject, aTopic, aData) {
|
||||
switch (aTopic) {
|
||||
case "Addons:FetchAll":
|
||||
HealthReportStatusListener.sendAllAddonsToJava();
|
||||
break;
|
||||
case "nsPref:changed":
|
||||
sendMessageToJava({ type: "Pref:Change", pref: aData, value: Services.prefs.getBoolPref(aData) });
|
||||
break
|
||||
}
|
||||
},
|
||||
|
||||
MILLISECONDS_PER_DAY: 24 * 60 * 60 * 1000,
|
||||
|
||||
COPY_FIELDS: [
|
||||
"userDisabled",
|
||||
"appDisabled",
|
||||
"version",
|
||||
"type",
|
||||
"scope",
|
||||
"foreignInstall",
|
||||
"hasBinaryComponents",
|
||||
],
|
||||
|
||||
// Add-on types for which full details are recorded in FHR.
|
||||
// All other types are ignored.
|
||||
FULL_DETAIL_TYPES: [
|
||||
"plugin",
|
||||
"extension",
|
||||
"service",
|
||||
],
|
||||
|
||||
/**
|
||||
* Return true if either the add-on has opted out of AMO updates, and thus
|
||||
* we shouldn't provide details to FHR, or it's an add-on type that we
|
||||
* don't want to report details for.
|
||||
* These add-ons will still make it over to Java, but will be filtered out.
|
||||
*/
|
||||
_shouldIgnore: function (aAddon) {
|
||||
// TODO: check this pref. If it's false, the add-on has opted out of
|
||||
// AMO updates, and should not be reported.
|
||||
let optOutPref = "extensions." + aAddon.id + ".getAddons.cache.enabled";
|
||||
if (this.FULL_DETAIL_TYPES.indexOf(aAddon.type) == -1) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
|
||||
_dateToDays: function (aDate) {
|
||||
return Math.floor(aDate.getTime() / this.MILLISECONDS_PER_DAY);
|
||||
},
|
||||
|
||||
jsonForAddon: function (aAddon) {
|
||||
let o = {};
|
||||
if (aAddon.installDate) {
|
||||
o.installDay = this._dateToDays(aAddon.installDate);
|
||||
}
|
||||
if (aAddon.updateDate) {
|
||||
o.updateDay = this._dateToDays(aAddon.updateDate);
|
||||
}
|
||||
|
||||
for (let field of this.COPY_FIELDS) {
|
||||
o[field] = aAddon[field];
|
||||
}
|
||||
|
||||
return o;
|
||||
},
|
||||
|
||||
notifyJava: function (aAddon, aNeedsRestart) {
|
||||
let json = this.jsonForAddon(aAddon);
|
||||
if (this._shouldIgnore(aAddon)) {
|
||||
json.ignore = true;
|
||||
}
|
||||
sendMessageToJava({ type: "Addons:Change", id: aAddon.id, json: json });
|
||||
},
|
||||
|
||||
// Add-on listeners.
|
||||
onEnabling: function (aAddon, aNeedsRestart) {
|
||||
this.notifyJava(aAddon, aNeedsRestart);
|
||||
},
|
||||
onDisabling: function (aAddon, aNeedsRestart) {
|
||||
this.notifyJava(aAddon, aNeedsRestart);
|
||||
},
|
||||
onInstalling: function (aAddon, aNeedsRestart) {
|
||||
this.notifyJava(aAddon, aNeedsRestart);
|
||||
},
|
||||
onUninstalling: function (aAddon, aNeedsRestart) {
|
||||
this.notifyJava(aAddon, aNeedsRestart);
|
||||
},
|
||||
onPropertyChanged: function (aAddon, aProperties) {
|
||||
this.notifyJava(aAddon);
|
||||
},
|
||||
|
||||
sendAllAddonsToJava: function () {
|
||||
AddonManager.getAllAddons(function (aAddons) {
|
||||
let json = {};
|
||||
if (aAddons) {
|
||||
for (let i = 0; i < aAddons.length; ++i) {
|
||||
let addon = aAddons[i];
|
||||
let addonJSON = HealthReportStatusListener.jsonForAddon(addon);
|
||||
if (HealthReportStatusListener._shouldIgnore(addon)) {
|
||||
addonJSON.ignore = true;
|
||||
}
|
||||
json[addon.id] = addonJSON;
|
||||
}
|
||||
}
|
||||
sendMessageToJava({ type: "Addons:All", json: json });
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
var XPInstallObserver = {
|
||||
init: function xpi_init() {
|
||||
Services.obs.addObserver(XPInstallObserver, "addon-install-blocked", false);
|
||||
|
|
Загрузка…
Ссылка в новой задаче