// Copyright 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package org.chromium.chrome.browser.init;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.view.View;
import com.google.ipc.invalidation.external.client.android.service.AndroidLogger;
import org.chromium.base.ApiCompatibilityUtils;
import org.chromium.base.CommandLine;
import org.chromium.base.ContextUtils;
import org.chromium.base.Log;
import org.chromium.base.ThreadUtils;
import org.chromium.chrome.R;
import org.chromium.chrome.browser.ChromeActivitySessionTracker;
import org.chromium.chrome.browser.ChromeApplication;
import org.chromium.chrome.browser.DeferredStartupHandler;
import org.chromium.chrome.browser.DevToolsServer;
import org.chromium.chrome.browser.banners.AppBannerManager;
import org.chromium.chrome.browser.download.DownloadController;
import org.chromium.chrome.browser.download.DownloadManagerService;
import org.chromium.chrome.browser.firstrun.ForcedSigninProcessor;
import org.chromium.chrome.browser.identity.UniqueIdentificationGeneratorFactory;
import org.chromium.chrome.browser.identity.UuidBasedUniqueIdentificationGenerator;
import org.chromium.chrome.browser.invalidation.UniqueIdInvalidationClientNameGenerator;
import org.chromium.chrome.browser.multiwindow.MultiWindowUtils;
import org.chromium.chrome.browser.net.spdyproxy.DataReductionProxySettings;
import org.chromium.chrome.browser.preferences.PrefServiceBridge;
import org.chromium.chrome.browser.rlz.RevenueStats;
import org.chromium.chrome.browser.services.AccountsChangedReceiver;
import org.chromium.chrome.browser.services.GoogleServicesManager;
import org.chromium.chrome.browser.sync.SyncController;
import org.chromium.components.signin.AccountManagerHelper;
import org.chromium.content.common.ContentSwitches;
import org.chromium.printing.PrintDocumentAdapterWrapper;
import org.chromium.printing.PrintingControllerImpl;
import org.chromium.ui.UiUtils;
/**
* Handles the initialization dependences of the browser process. This is meant to handle the
* initialization that is not tied to any particular Activity, and the logic that should only be
* triggered a single time for the lifetime of the browser process.
*/
public class ProcessInitializationHandler {
private static final String SESSIONS_UUID_PREF_KEY = "chromium.sync.sessions.id";
private static final String DEV_TOOLS_SERVER_SOCKET_PREFIX = "chrome";
private static Class<? extends ProcessInitializationHandler> sHandlerClassOverride;
private static ProcessInitializationHandler sInstance;
private boolean mInitializedPreNative;
private boolean mInitializedPostNative;
private boolean mInitializedDeferredStartupTasks;
private DevToolsServer mDevToolsServer;
/**
* Overrides the type of ProcessInitializationHandler to be created.
* <p>
* This must be called before {@link #getInstance()} is triggered.
*
* @param classOverride The ProcessInitializationHandler class type to be created instead of
* the default.
*/
public static void setProcessInitializationHandlerType(
Class<? extends ProcessInitializationHandler> classOverride) {
if (sInstance != null) {
throw new IllegalStateException("Browser delegate override set after initialized");
}
sHandlerClassOverride = classOverride;
}
/**
* @return The ProcessInitializationHandler for use during the lifetime of the browser process.
*/
public static ProcessInitializationHandler getInstance() {
ThreadUtils.assertOnUiThread();
if (sInstance == null) {
if (sHandlerClassOverride != null) {
try {
sInstance = sHandlerClassOverride.newInstance();
} catch (InstantiationException | IllegalAccessException e) {
assert false : "Unable to instantiate ProcessInitializationHandler override.";
}
}
if (sInstance == null) sInstance = new ProcessInitializationHandler();
}
return sInstance;
}
/**
* Constructor exposed to allow overriding.
*/
protected ProcessInitializationHandler() {}
/**
* Initializes the any dependencies that must occur before native library has been loaded.
* <p>
* Adding anything expensive to this must be avoided as it would delay the Chrome startup path.
* <p>
* All entry points that do not rely on {@link ChromeBrowserInitializer} must call this on
* startup.
*/
public final void initializePreNative() {
ThreadUtils.assertOnUiThread();
if (mInitializedPreNative) return;
mInitializedPreNative = true;
handlePreNativeInitialization();
}
/**
* Performs the shared class initialization.
*/
protected void handlePreNativeInitialization() {
ChromeApplication application = (ChromeApplication) ContextUtils.getApplicationContext();
UiUtils.setKeyboardShowingDelegate(new UiUtils.KeyboardShowingDelegate() {
@Override
public boolean disableKeyboardCheck(Context context, View view) {
Activity activity = null;
if (context instanceof Activity) {
activity = (Activity) context;
} else if (view != null && view.getContext() instanceof Activity) {
activity = (Activity) view.getContext();
}
// For multiwindow mode we do not track keyboard visibility.
return activity != null
&& MultiWindowUtils.getInstance().isLegacyMultiWindow(activity);
}
});
// Initialize the AccountManagerHelper with the correct AccountManagerDelegate. Must be done
// only once and before AccountMangerHelper.get(...) is called to avoid using the
// default AccountManagerDelegate.
AccountManagerHelper.initializeAccountManagerHelper(
application, application.createAccountManagerDelegate());
// Set the unique identification generator for invalidations. The
// invalidations system can start and attempt to fetch the client ID
// very early. We need this generator to be ready before that happens.
UniqueIdInvalidationClientNameGenerator.doInitializeAndInstallGenerator(application);
// Set minimum Tango log level. This sets an in-memory static field, and needs to be
// set in the ApplicationContext instead of an activity, since Tango can be woken up
// by the system directly though messages from GCM.
AndroidLogger.setMinimumAndroidLogLevel(Log.WARN);
// Set up the identification generator for sync. The ID is actually generated
// in the SyncController constructor.
UniqueIdentificationGeneratorFactory.registerGenerator(SyncController.GENERATOR_ID,
new UuidBasedUniqueIdentificationGenerator(
application, SESSIONS_UUID_PREF_KEY), false);
}
/**
* Initializes any dependencies that must occur after the native library has been loaded.
*/
public final void initializePostNative() {
ThreadUtils.assertOnUiThread();
if (mInitializedPostNative) return;
mInitializedPostNative = true;
handlePostNativeInitialization();
}
/**
* Performs the post native initialization.
*/
protected void handlePostNativeInitialization() {
final ChromeApplication application =
(ChromeApplication) ContextUtils.getApplicationContext();
DataReductionProxySettings.reconcileDataReductionProxyEnabledState(application);
ChromeActivitySessionTracker.getInstance().initializeWithNative();
ChromeApplication.removeSessionCookies();
AppBannerManager.setAppDetailsDelegate(application.createAppDetailsDelegate());
ChromeLifetimeController.initialize();
PrefServiceBridge.getInstance().migratePreferences(application);
}
/**
* Initializes the deferred startup tasks that should only be triggered once per browser process
* lifetime.
*/
public final void initializeDeferredStartupTasks() {
ThreadUtils.assertOnUiThread();
if (mInitializedDeferredStartupTasks) return;
mInitializedDeferredStartupTasks = true;
handleDeferredStartupTasksInitialization();
}
/**
* Performs the deferred startup task initialization.
*/
protected void handleDeferredStartupTasksInitialization() {
final ChromeApplication application =
(ChromeApplication) ContextUtils.getApplicationContext();
DeferredStartupHandler.getInstance().addDeferredTask(new Runnable() {
@Override
public void run() {
ForcedSigninProcessor.start(application);
AccountsChangedReceiver.addObserver(
new AccountsChangedReceiver.AccountsChangedObserver() {
@Override
public void onAccountsChanged(Context context, Intent intent) {
ThreadUtils.runOnUiThread(new Runnable() {
@Override
public void run() {
ForcedSigninProcessor.start(application);
}
});
}
});
}
});
DeferredStartupHandler.getInstance().addDeferredTask(new Runnable() {
@Override
public void run() {
GoogleServicesManager.get(application).onMainActivityStart();
RevenueStats.getInstance();
}
});
DeferredStartupHandler.getInstance().addDeferredTask(new Runnable() {
@Override
public void run() {
mDevToolsServer = new DevToolsServer(DEV_TOOLS_SERVER_SOCKET_PREFIX);
mDevToolsServer.setRemoteDebuggingEnabled(
true, DevToolsServer.Security.ALLOW_DEBUG_PERMISSION);
}
});
DeferredStartupHandler.getInstance().addDeferredTask(new Runnable() {
@Override
public void run() {
// Add process check to diagnose http://crbug.com/606309. Remove this after the bug
// is fixed.
assert !CommandLine.getInstance().hasSwitch(ContentSwitches.SWITCH_PROCESS_TYPE);
if (!CommandLine.getInstance().hasSwitch(ContentSwitches.SWITCH_PROCESS_TYPE)) {
DownloadController.setDownloadNotificationService(
DownloadManagerService.getDownloadManagerService(application));
}
if (ApiCompatibilityUtils.isPrintingSupported()) {
String errorText = application.getResources().getString(
R.string.error_printing_failed);
PrintingControllerImpl.create(new PrintDocumentAdapterWrapper(), errorText);
}
}
});
}
}