Index: chrome/android/java/src/org/chromium/chrome/browser/ChromeBackupAgent.java |
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ChromeBackupAgent.java b/chrome/android/java/src/org/chromium/chrome/browser/ChromeBackupAgent.java |
index 6e400876ab6b4c6d49b82dce9a742e47fae24a6a..e873fc6d18decfe0121f31614b2201661dcf61f5 100644 |
--- a/chrome/android/java/src/org/chromium/chrome/browser/ChromeBackupAgent.java |
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ChromeBackupAgent.java |
@@ -10,6 +10,7 @@ import android.app.backup.BackupDataOutput; |
import android.content.Context; |
import android.content.SharedPreferences; |
import android.os.ParcelFileDescriptor; |
+import android.support.annotation.IntDef; |
import org.chromium.base.ContextUtils; |
import org.chromium.base.Log; |
@@ -18,6 +19,7 @@ import org.chromium.base.ThreadUtils; |
import org.chromium.base.VisibleForTesting; |
import org.chromium.base.annotations.SuppressFBWarnings; |
import org.chromium.base.library_loader.ProcessInitException; |
+import org.chromium.base.metrics.RecordHistogram; |
import org.chromium.chrome.browser.firstrun.FirstRunGlueImpl; |
import org.chromium.chrome.browser.firstrun.FirstRunSignInProcessor; |
import org.chromium.chrome.browser.firstrun.FirstRunStatus; |
@@ -32,6 +34,8 @@ import java.io.FileOutputStream; |
import java.io.IOException; |
import java.io.ObjectInputStream; |
import java.io.ObjectOutputStream; |
+import java.lang.annotation.Retention; |
+import java.lang.annotation.RetentionPolicy; |
import java.util.ArrayList; |
import java.util.Arrays; |
import java.util.concurrent.Callable; |
@@ -48,8 +52,31 @@ public class ChromeBackupAgent extends BackupAgent { |
private static final String TAG = "ChromeBackupAgent"; |
- // Lists of preferences that should be restored unchanged. |
- |
+ @VisibleForTesting |
+ static final String HISTOGRAM_ANDROID_RESTORE_RESULT = "Android.RestoreResult"; |
+ |
+ // Restore status is used to pass the result of any restore to Chrome's first run, so that |
+ // it can be recorded as a histogram. |
+ @Retention(RetentionPolicy.SOURCE) |
+ @IntDef({NO_RESTORE, RESTORE_COMPLETED, RESTORE_AFTER_FIRST_RUN, BROWSER_STARTUP_FAILED, |
+ NOT_SIGNED_IN, RESTORE_STATUS_RECORDED}) |
+ public @interface RestoreStatus {} |
+ |
+ // Values must match those in histogram.xml AndroidRestoreResult. |
+ static final int NO_RESTORE = 0; |
+ static final int RESTORE_COMPLETED = 1; |
+ static final int RESTORE_AFTER_FIRST_RUN = 2; |
+ static final int BROWSER_STARTUP_FAILED = 3; |
+ static final int NOT_SIGNED_IN = 4; |
+ static final int RESTORE_HISTOGRAM_BOUNDARY = 5; |
+ |
+ // Set RESTORE_STATUS_RECORDED when the histogram has been recorded; so that it is only recorded |
+ // once. |
+ public static final int RESTORE_STATUS_RECORDED = 5; |
+ |
+ private static final String RESTORE_STATUS = "android_restore_status"; |
+ |
+ // List of preferences that should be restored unchanged. |
static final String[] BACKUP_ANDROID_BOOL_PREFS = { |
FirstRunGlueImpl.CACHED_TOS_ACCEPTED_PREF, |
FirstRunStatus.FIRST_RUN_FLOW_COMPLETE, |
@@ -146,6 +173,7 @@ public class ChromeBackupAgent extends BackupAgent { |
ThreadUtils.runOnUiThreadBlockingNoException(new Callable<Boolean>() { |
@Override |
public Boolean call() { |
+ |
// Start the browser if necessary, so that Chrome can access the native |
// preferences. Although Chrome requests the backup, it doesn't happen |
// immediately, so by the time it does Chrome may not be running. |
@@ -168,6 +196,7 @@ public class ChromeBackupAgent extends BackupAgent { |
// Something went wrong reading the native preferences, skip the backup. |
return; |
} |
+ |
// Add the Android boolean prefs. |
SharedPreferences sharedPrefs = ContextUtils.getAppSharedPreferences(); |
for (String prefName : BACKUP_ANDROID_BOOL_PREFS) { |
@@ -197,11 +226,13 @@ public class ChromeBackupAgent extends BackupAgent { |
// corrupt. Create a new backup in either case. |
Log.i(TAG, "Can't read backup status file"); |
} |
+ |
// Write the backup data |
for (int i = 0; i < backupNames.size(); i++) { |
data.writeEntityHeader(backupNames.get(i), backupValues.get(i).length); |
data.writeEntityData(backupValues.get(i), backupValues.get(i).length); |
} |
+ |
// Remember the backup state. |
newBackupState.save(newState); |
@@ -217,9 +248,9 @@ public class ChromeBackupAgent extends BackupAgent { |
// Check that the user hasn't already seen FRE (not sure if this can ever happen, but if it |
// does then restoring the backup will overwrite the user's choices). |
SharedPreferences sharedPrefs = ContextUtils.getAppSharedPreferences(); |
- if (sharedPrefs.getBoolean(FirstRunStatus.FIRST_RUN_FLOW_COMPLETE, false) |
- || sharedPrefs.getBoolean( |
- FirstRunStatus.LIGHTWEIGHT_FIRST_RUN_FLOW_COMPLETE, false)) { |
+ if (FirstRunStatus.getFirstRunFlowComplete() |
+ || FirstRunStatus.getLightweightFirstRunFlowComplete()) { |
+ setRestoreStatus(RESTORE_AFTER_FIRST_RUN); |
Log.w(TAG, "Restore attempted after first run"); |
return; |
} |
@@ -240,6 +271,7 @@ public class ChromeBackupAgent extends BackupAgent { |
backupValues.add(buffer); |
} |
} |
+ |
// Start and wait for the Async init tasks. This loads the library, and attempts to load the |
// first run variations seed. Since these are both slow it makes sense to run them in |
// parallel as Android AsyncTasks, reusing some of Chrome's async startup logic. |
@@ -267,6 +299,7 @@ public class ChromeBackupAgent extends BackupAgent { |
} catch (InterruptedException e) { |
// Should never happen, but can be ignored (as explained above) anyway. |
} |
+ |
// Chrome has to be running before it can check if the account exists. Because the native |
// library is already loaded Chrome startup should be fast. |
final ChromeBackupAgent backupAgent = this; |
@@ -280,13 +313,17 @@ public class ChromeBackupAgent extends BackupAgent { |
}); |
if (!browserStarted) { |
// Something went wrong starting Chrome, skip the restore. |
+ setRestoreStatus(BROWSER_STARTUP_FAILED); |
return; |
} |
+ |
// If the user hasn't signed in, or can't sign in, then don't restore anything. |
if (restoredUserName == null || !accountExistsOnDevice(restoredUserName)) { |
+ setRestoreStatus(NOT_SIGNED_IN); |
Log.i(TAG, "Chrome was not signed in with a known account name, not restoring"); |
return; |
} |
+ |
// Restore the native preferences on the UI thread |
ThreadUtils.runOnUiThreadBlocking(new Runnable() { |
@Override |
@@ -310,6 +347,7 @@ public class ChromeBackupAgent extends BackupAgent { |
// Now that everything looks good so restore the Android preferences. |
SharedPreferences.Editor editor = sharedPrefs.edit(); |
+ |
// Only restore preferences that we know about. |
int prefixLength = ANDROID_DEFAULT_PREFIX.length(); |
for (int i = 0; i < backupNames.size(); i++) { |
@@ -331,6 +369,7 @@ public class ChromeBackupAgent extends BackupAgent { |
// The silent first run will change things, so there is no point in trying to prevent |
// additional backups at this stage. Don't write anything to |newState|. |
+ setRestoreStatus(RESTORE_COMPLETED); |
Log.i(TAG, "Restore complete"); |
} |
@@ -352,6 +391,40 @@ public class ChromeBackupAgent extends BackupAgent { |
}; |
} |
+ /** |
+ * Get the saved result of any restore that may have happened. |
+ * |
+ * @return the restore status, a RestoreStatus value. |
+ */ |
+ @VisibleForTesting |
+ @RestoreStatus |
+ static int getRestoreStatus() { |
+ return ContextUtils.getAppSharedPreferences().getInt(RESTORE_STATUS, NO_RESTORE); |
+ } |
+ |
+ /** |
+ * Save the restore status for later transfer to a histogram. |
+ * |
+ * @param status the status. |
+ */ |
+ @VisibleForTesting |
+ static void setRestoreStatus(@RestoreStatus int status) { |
+ ContextUtils.getAppSharedPreferences().edit().putInt(RESTORE_STATUS, status).apply(); |
+ } |
+ |
+ /** |
+ * Record the restore histogram. To be called from Chrome itself once it is running. |
+ */ |
+ static void recordRestoreHistogram() { |
+ int restoreStatus = getRestoreStatus(); |
+ // Ensure restore status is only recorded once |
+ if (restoreStatus != RESTORE_STATUS_RECORDED) { |
+ RecordHistogram.recordEnumeratedHistogram( |
+ HISTOGRAM_ANDROID_RESTORE_RESULT, restoreStatus, RESTORE_HISTOGRAM_BOUNDARY); |
+ setRestoreStatus(RESTORE_STATUS_RECORDED); |
+ } |
+ } |
+ |
@VisibleForTesting |
protected native String[] nativeGetBoolBackupNames(); |