| Index: chrome/android/java/src/org/chromium/chrome/browser/init/ProcessInitializationHandler.java
|
| diff --git a/chrome/android/java/src/org/chromium/chrome/browser/init/ProcessInitializationHandler.java b/chrome/android/java/src/org/chromium/chrome/browser/init/ProcessInitializationHandler.java
|
| index 49d3da01c80fe65ded8e912c072588b4a1ea4cb0..dddd8efabf100115a00bf3734dd948efebc2d17d 100644
|
| --- a/chrome/android/java/src/org/chromium/chrome/browser/init/ProcessInitializationHandler.java
|
| +++ b/chrome/android/java/src/org/chromium/chrome/browser/init/ProcessInitializationHandler.java
|
| @@ -6,6 +6,10 @@ package org.chromium.chrome.browser.init;
|
|
|
| import android.app.Activity;
|
| import android.content.Context;
|
| +import android.content.SharedPreferences;
|
| +import android.os.AsyncTask;
|
| +import android.os.SystemClock;
|
| +import android.support.annotation.WorkerThread;
|
| import android.view.View;
|
| import android.view.inputmethod.InputMethodInfo;
|
| import android.view.inputmethod.InputMethodManager;
|
| @@ -17,18 +21,26 @@ import org.chromium.base.ApiCompatibilityUtils;
|
| import org.chromium.base.CommandLine;
|
| import org.chromium.base.ContextUtils;
|
| import org.chromium.base.Log;
|
| +import org.chromium.base.PowerMonitor;
|
| import org.chromium.base.SysUtils;
|
| import org.chromium.base.ThreadUtils;
|
| +import org.chromium.base.TraceEvent;
|
| +import org.chromium.base.annotations.SuppressFBWarnings;
|
| import org.chromium.base.metrics.RecordHistogram;
|
| import org.chromium.chrome.R;
|
| +import org.chromium.chrome.browser.AfterStartupTaskUtils;
|
| import org.chromium.chrome.browser.AppHooks;
|
| import org.chromium.chrome.browser.ChromeActivitySessionTracker;
|
| import org.chromium.chrome.browser.ChromeApplication;
|
| import org.chromium.chrome.browser.ChromeBackupAgent;
|
| import org.chromium.chrome.browser.ChromeFeatureList;
|
| +import org.chromium.chrome.browser.DefaultBrowserInfo;
|
| import org.chromium.chrome.browser.DeferredStartupHandler;
|
| import org.chromium.chrome.browser.DevToolsServer;
|
| import org.chromium.chrome.browser.banners.AppBannerManager;
|
| +import org.chromium.chrome.browser.bookmarkswidget.BookmarkWidgetProvider;
|
| +import org.chromium.chrome.browser.crash.LogcatExtractionRunnable;
|
| +import org.chromium.chrome.browser.crash.MinidumpUploadService;
|
| import org.chromium.chrome.browser.download.DownloadController;
|
| import org.chromium.chrome.browser.download.DownloadManagerService;
|
| import org.chromium.chrome.browser.firstrun.ForcedSigninProcessor;
|
| @@ -37,16 +49,31 @@ import org.chromium.chrome.browser.identity.UuidBasedUniqueIdentificationGenerat
|
| import org.chromium.chrome.browser.invalidation.UniqueIdInvalidationClientNameGenerator;
|
| import org.chromium.chrome.browser.locale.LocaleManager;
|
| import org.chromium.chrome.browser.media.MediaCaptureNotificationService;
|
| +import org.chromium.chrome.browser.metrics.LaunchMetrics;
|
| +import org.chromium.chrome.browser.metrics.UmaUtils;
|
| import org.chromium.chrome.browser.multiwindow.MultiWindowUtils;
|
| import org.chromium.chrome.browser.net.spdyproxy.DataReductionProxySettings;
|
| +import org.chromium.chrome.browser.notifications.ChannelsUpdater;
|
| +import org.chromium.chrome.browser.ntp.NewTabPage;
|
| +import org.chromium.chrome.browser.offlinepages.OfflinePageUtils;
|
| +import org.chromium.chrome.browser.partnerbookmarks.PartnerBookmarksShim;
|
| +import org.chromium.chrome.browser.partnercustomizations.HomepageManager;
|
| +import org.chromium.chrome.browser.partnercustomizations.PartnerBrowserCustomizations;
|
| import org.chromium.chrome.browser.photo_picker.PhotoPickerDialog;
|
| import org.chromium.chrome.browser.physicalweb.PhysicalWeb;
|
| +import org.chromium.chrome.browser.precache.PrecacheLauncher;
|
| +import org.chromium.chrome.browser.preferences.ChromePreferenceManager;
|
| import org.chromium.chrome.browser.preferences.PrefServiceBridge;
|
| import org.chromium.chrome.browser.rlz.RevenueStats;
|
| import org.chromium.chrome.browser.searchwidget.SearchWidgetProvider;
|
| import org.chromium.chrome.browser.services.AccountsChangedReceiver;
|
| import org.chromium.chrome.browser.services.GoogleServicesManager;
|
| +import org.chromium.chrome.browser.share.ShareHelper;
|
| import org.chromium.chrome.browser.sync.SyncController;
|
| +import org.chromium.chrome.browser.webapps.ChromeWebApkHost;
|
| +import org.chromium.chrome.browser.webapps.WebApkVersionManager;
|
| +import org.chromium.chrome.browser.webapps.WebappRegistry;
|
| +import org.chromium.components.minidump_uploader.CrashFileManager;
|
| import org.chromium.components.signin.AccountManagerHelper;
|
| import org.chromium.content.browser.ChildProcessLauncher;
|
| import org.chromium.content.common.ContentSwitches;
|
| @@ -56,9 +83,13 @@ import org.chromium.printing.PrintingControllerImpl;
|
| import org.chromium.ui.PhotoPickerListener;
|
| import org.chromium.ui.UiUtils;
|
|
|
| +import java.io.File;
|
| import java.util.ArrayList;
|
| +import java.util.Arrays;
|
| +import java.util.Date;
|
| import java.util.List;
|
| import java.util.Locale;
|
| +import java.util.concurrent.TimeUnit;
|
|
|
| /**
|
| * Handles the initialization dependences of the browser process. This is meant to handle the
|
| @@ -66,10 +97,16 @@ import java.util.Locale;
|
| * triggered a single time for the lifetime of the browser process.
|
| */
|
| public class ProcessInitializationHandler {
|
| + private static final String TAG = "ProcessInitHandler";
|
|
|
| private static final String SESSIONS_UUID_PREF_KEY = "chromium.sync.sessions.id";
|
| private static final String DEV_TOOLS_SERVER_SOCKET_PREFIX = "chrome";
|
|
|
| + /** Prevents race conditions when deleting snapshot database. */
|
| + private static final Object SNAPSHOT_DATABASE_LOCK = new Object();
|
| + private static final String SNAPSHOT_DATABASE_REMOVED = "snapshot_database_removed";
|
| + private static final String SNAPSHOT_DATABASE_NAME = "snapshots.db";
|
| +
|
| private static ProcessInitializationHandler sInstance;
|
|
|
| private boolean mInitializedPreNative;
|
| @@ -80,6 +117,7 @@ public class ProcessInitializationHandler {
|
| /**
|
| * @return The ProcessInitializationHandler for use during the lifetime of the browser process.
|
| */
|
| + @SuppressFBWarnings("LI_LAZY_INIT_STATIC")
|
| public static ProcessInitializationHandler getInstance() {
|
| ThreadUtils.checkUiThread();
|
| if (sInstance == null) {
|
| @@ -199,14 +237,24 @@ public class ProcessInitializationHandler {
|
| }
|
|
|
| /**
|
| - * Initializes the deferred startup tasks that should only be triggered once per browser process
|
| - * lifetime.
|
| + * Handle application level deferred startup tasks that can be lazily done after all
|
| + * the necessary initialization has been completed. Should only be triggered once per browser
|
| + * process lifetime. Any calls requiring network access should probably go here.
|
| + *
|
| + * Keep these tasks short and break up long tasks into multiple smaller tasks, as they run on
|
| + * the UI thread and are blocking. Remember to follow RAIL guidelines, as much as possible, and
|
| + * that most devices are quite slow, so leave enough buffer.
|
| */
|
| public final void initializeDeferredStartupTasks() {
|
| ThreadUtils.checkUiThread();
|
| if (mInitializedDeferredStartupTasks) return;
|
| - handleDeferredStartupTasksInitialization();
|
| mInitializedDeferredStartupTasks = true;
|
| +
|
| + RecordHistogram.recordLongTimesHistogram("UMA.Debug.EnableCrashUpload.DeferredStartUptime2",
|
| + SystemClock.uptimeMillis() - UmaUtils.getForegroundStartTime(),
|
| + TimeUnit.MILLISECONDS);
|
| +
|
| + handleDeferredStartupTasksInitialization();
|
| }
|
|
|
| /**
|
| @@ -220,6 +268,40 @@ public class ProcessInitializationHandler {
|
| deferredStartupHandler.addDeferredTask(new Runnable() {
|
| @Override
|
| public void run() {
|
| + // Punt all tasks that may block on disk off onto a background thread.
|
| + initAsyncDiskTask(application);
|
| +
|
| + DefaultBrowserInfo.initBrowserFetcher();
|
| +
|
| + AfterStartupTaskUtils.setStartupComplete();
|
| +
|
| + PartnerBrowserCustomizations.setOnInitializeAsyncFinished(new Runnable() {
|
| + @Override
|
| + public void run() {
|
| + String homepageUrl = HomepageManager.getHomepageUri(application);
|
| + LaunchMetrics.recordHomePageLaunchMetrics(
|
| + HomepageManager.isHomepageEnabled(application),
|
| + NewTabPage.isNTPUrl(homepageUrl), homepageUrl);
|
| + }
|
| + });
|
| +
|
| + PartnerBookmarksShim.kickOffReading(application);
|
| +
|
| + PowerMonitor.create();
|
| +
|
| + ShareHelper.clearSharedImages();
|
| +
|
| + OfflinePageUtils.clearSharedOfflineFiles(application);
|
| +
|
| + if (ChannelsUpdater.getInstance().shouldUpdateChannels()) {
|
| + initChannelsAsync();
|
| + }
|
| + }
|
| + });
|
| +
|
| + deferredStartupHandler.addDeferredTask(new Runnable() {
|
| + @Override
|
| + public void run() {
|
| // Clear any media notifications that existed when Chrome was last killed.
|
| MediaCaptureNotificationService.clearMediaNotifications(application);
|
|
|
| @@ -316,6 +398,189 @@ public class ProcessInitializationHandler {
|
| });
|
| }
|
|
|
| + private void initChannelsAsync() {
|
| + new AsyncTask<Void, Void, Void>() {
|
| + @Override
|
| + protected Void doInBackground(Void... params) {
|
| + ChannelsUpdater.getInstance().updateChannels();
|
| + return null;
|
| + }
|
| + }
|
| + .executeOnExecutor(AsyncTask.SERIAL_EXECUTOR);
|
| + }
|
| +
|
| + private void initAsyncDiskTask(final Context context) {
|
| + new AsyncTask<Void, Void, Void>() {
|
| + /**
|
| + * The threshold after which it's no longer appropriate to try to attach logcat output
|
| + * to a minidump file.
|
| + * Note: This threshold of 12 hours was chosen fairly imprecisely, based on the
|
| + * following intuition: On the one hand, Chrome can only access its own logcat output,
|
| + * so the most recent lines should be relevant when available. On a typical device,
|
| + * multiple hours of logcat output are available. On the other hand, it's important to
|
| + * provide an escape hatch in case the logcat extraction code itself crashes, as
|
| + * described in the doesCrashMinidumpNeedLogcat() documentation. Since this is a fairly
|
| + * small and relatively frequently-executed piece of code, crashes are expected to be
|
| + * unlikely; so it's okay for the escape hatch to be hard to use -- it's intended as an
|
| + * extreme last resort.
|
| + */
|
| + private static final long LOGCAT_RELEVANCE_THRESHOLD_IN_HOURS = 12;
|
| +
|
| + private long mAsyncTaskStartTime;
|
| +
|
| + @Override
|
| + protected Void doInBackground(Void... params) {
|
| + try {
|
| + TraceEvent.begin("ChromeBrowserInitializer.onDeferredStartup.doInBackground");
|
| + mAsyncTaskStartTime = SystemClock.uptimeMillis();
|
| +
|
| + initCrashReporting();
|
| +
|
| + // Initialize the WebappRegistry if it's not already initialized. Must be in
|
| + // async task due to shared preferences disk access on N.
|
| + WebappRegistry.getInstance();
|
| +
|
| + // Force a widget refresh in order to wake up any possible zombie widgets.
|
| + // This is needed to ensure the right behavior when the process is suddenly
|
| + // killed.
|
| + BookmarkWidgetProvider.refreshAllWidgets(context);
|
| +
|
| + // Initialize whether or not precaching is enabled.
|
| + PrecacheLauncher.updatePrecachingEnabled(context);
|
| +
|
| + if (ChromeWebApkHost.isEnabled()) {
|
| + WebApkVersionManager.updateWebApksIfNeeded();
|
| + }
|
| +
|
| + removeSnapshotDatabase(context);
|
| +
|
| + // Warm up all web app shared prefs. This must be run after the WebappRegistry
|
| + // instance is initialized.
|
| + WebappRegistry.warmUpSharedPrefs();
|
| +
|
| + return null;
|
| + } finally {
|
| + TraceEvent.end("ChromeBrowserInitializer.onDeferredStartup.doInBackground");
|
| + }
|
| + }
|
| +
|
| + @Override
|
| + protected void onPostExecute(Void params) {
|
| + // Must be run on the UI thread after the WebappRegistry has been completely warmed.
|
| + WebappRegistry.getInstance().unregisterOldWebapps(System.currentTimeMillis());
|
| +
|
| + RecordHistogram.recordLongTimesHistogram(
|
| + "UMA.Debug.EnableCrashUpload.DeferredStartUpAsyncTaskDuration",
|
| + SystemClock.uptimeMillis() - mAsyncTaskStartTime, TimeUnit.MILLISECONDS);
|
| + }
|
| +
|
| + /**
|
| + * Initializes the crash reporting system. More specifically, enables the crash
|
| + * reporting system if it is user-permitted, and initiates uploading of any pending
|
| + * crash reports. Also updates some UMA metrics and performs cleanup in the local crash
|
| + * minidump storage directory.
|
| + */
|
| + private void initCrashReporting() {
|
| + RecordHistogram.recordLongTimesHistogram("UMA.Debug.EnableCrashUpload.Uptime3",
|
| + mAsyncTaskStartTime - UmaUtils.getForegroundStartTime(),
|
| + TimeUnit.MILLISECONDS);
|
| +
|
| + // Crash reports can be uploaded as part of a background service even while the main
|
| + // Chrome activity is not running, and hence regular metrics reporting is not
|
| + // possible. Instead, metrics are temporarily written to prefs; export those prefs
|
| + // to UMA metrics here.
|
| + MinidumpUploadService.storeBreakpadUploadStatsInUma(
|
| + ChromePreferenceManager.getInstance());
|
| +
|
| + // Likewise, this is a good time to process and clean up any pending or stale crash
|
| + // reports left behind by previous runs.
|
| + CrashFileManager crashFileManager =
|
| + new CrashFileManager(ContextUtils.getApplicationContext().getCacheDir());
|
| + crashFileManager.cleanOutAllNonFreshMinidumpFiles();
|
| +
|
| + // Finally, uploading any pending crash reports.
|
| + File[] minidumps = crashFileManager.getAllMinidumpFiles(
|
| + MinidumpUploadService.MAX_TRIES_ALLOWED);
|
| + int numMinidumpsSansLogcat = 0;
|
| + for (File minidump : minidumps) {
|
| + if (CrashFileManager.isMinidumpMIMEFirstTry(minidump.getName())) {
|
| + ++numMinidumpsSansLogcat;
|
| + }
|
| + }
|
| + // TODO(isherman): These two histograms are intended to be temporary, and can
|
| + // probably be removed around the M60 timeframe: http://crbug.com/699785
|
| + RecordHistogram.recordSparseSlowlyHistogram(
|
| + "Stability.Android.PendingMinidumpsOnStartup", minidumps.length);
|
| + RecordHistogram.recordSparseSlowlyHistogram(
|
| + "Stability.Android.PendingMinidumpsOnStartup.SansLogcat",
|
| + numMinidumpsSansLogcat);
|
| + if (minidumps.length == 0) return;
|
| +
|
| + Log.i(TAG, "Attempting to upload %d accumulated crash dumps.", minidumps.length);
|
| + File mostRecentMinidump = minidumps[0];
|
| + if (doesCrashMinidumpNeedLogcat(mostRecentMinidump)) {
|
| + AsyncTask.THREAD_POOL_EXECUTOR.execute(
|
| + new LogcatExtractionRunnable(mostRecentMinidump));
|
| +
|
| + // The JobScheduler will schedule uploads for all of the available minidumps
|
| + // once the logcat is attached. But if the JobScheduler API is not being used,
|
| + // then the logcat extraction process will only initiate an upload for the first
|
| + // minidump; it's required to manually initiate uploads for all of the remaining
|
| + // minidumps.
|
| + if (!MinidumpUploadService.shouldUseJobSchedulerForUploads()) {
|
| + List<File> remainingMinidumps =
|
| + Arrays.asList(minidumps).subList(1, minidumps.length);
|
| + for (File minidump : remainingMinidumps) {
|
| + MinidumpUploadService.tryUploadCrashDump(minidump);
|
| + }
|
| + }
|
| + } else if (MinidumpUploadService.shouldUseJobSchedulerForUploads()) {
|
| + MinidumpUploadService.scheduleUploadJob();
|
| + } else {
|
| + MinidumpUploadService.tryUploadAllCrashDumps();
|
| + }
|
| + }
|
| +
|
| + /**
|
| + * Returns whether or not it's appropriate to try to extract recent logcat output and
|
| + * include that logcat output alongside the given {@param minidump} in a crash report.
|
| + * Logcat output should only be extracted if (a) it hasn't already been extracted for
|
| + * this minidump file, and (b) the minidump is fairly fresh. The freshness check is
|
| + * important for two reasons: (1) First of all, it helps avoid including irrelevant
|
| + * logcat output for a crash report. (2) Secondly, it provides an escape hatch that can
|
| + * help circumvent a possible infinite crash loop, if the code responsible for
|
| + * extracting and appending the logcat content is itself crashing. That is, the user can
|
| + * wait 12 hours prior to relaunching Chrome, at which point this potential crash loop
|
| + * would be circumvented.
|
| + * @return Whether to try to include logcat output in the crash report corresponding to
|
| + * the given minidump.
|
| + */
|
| + private boolean doesCrashMinidumpNeedLogcat(File minidump) {
|
| + if (!CrashFileManager.isMinidumpMIMEFirstTry(minidump.getName())) return false;
|
| +
|
| + long ageInMillis = new Date().getTime() - minidump.lastModified();
|
| + long ageInHours = TimeUnit.HOURS.convert(ageInMillis, TimeUnit.MILLISECONDS);
|
| + return ageInHours < LOGCAT_RELEVANCE_THRESHOLD_IN_HOURS;
|
| + }
|
| + }
|
| + .executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
|
| + }
|
| +
|
| + /**
|
| + * Deletes the snapshot database which is no longer used because the feature has been removed
|
| + * in Chrome M41.
|
| + */
|
| + @WorkerThread
|
| + private void removeSnapshotDatabase(Context context) {
|
| + synchronized (SNAPSHOT_DATABASE_LOCK) {
|
| + SharedPreferences prefs = ContextUtils.getAppSharedPreferences();
|
| + if (!prefs.getBoolean(SNAPSHOT_DATABASE_REMOVED, false)) {
|
| + context.deleteDatabase(SNAPSHOT_DATABASE_NAME);
|
| + prefs.edit().putBoolean(SNAPSHOT_DATABASE_REMOVED, true).apply();
|
| + }
|
| + }
|
| + }
|
| +
|
| private void startModerateBindingManagementIfNeeded(Context context) {
|
| // Moderate binding doesn't apply to low end devices.
|
| if (SysUtils.isLowEndDevice()) return;
|
|
|