blob: b3fb992f071d01b590775be7da1c2905831e39c5 [file] [log] [blame]
// Copyright 2013 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;
import android.app.Application;
import android.content.Context;
import android.content.Intent;
import android.content.res.Configuration;
import android.os.Bundle;
import androidx.annotation.Nullable;
import org.chromium.base.ApplicationState;
import org.chromium.base.ApplicationStatus;
import org.chromium.base.BuildConfig;
import org.chromium.base.BuildInfo;
import org.chromium.base.CommandLineInitUtil;
import org.chromium.base.ContextUtils;
import org.chromium.base.JNIUtils;
import org.chromium.base.Log;
import org.chromium.base.PathUtils;
import org.chromium.base.TraceEvent;
import org.chromium.base.annotations.MainDex;
import org.chromium.base.memory.MemoryPressureMonitor;
import org.chromium.base.multidex.ChromiumMultiDexInstaller;
import org.chromium.base.task.AsyncTask;
import org.chromium.build.BuildHooks;
import org.chromium.build.BuildHooksAndroid;
import org.chromium.build.BuildHooksConfig;
import org.chromium.chrome.browser.background_task_scheduler.ChromeBackgroundTaskFactory;
import org.chromium.chrome.browser.crash.ApplicationStatusTracker;
import org.chromium.chrome.browser.crash.FirebaseConfig;
import org.chromium.chrome.browser.crash.PureJavaExceptionHandler;
import org.chromium.chrome.browser.crash.PureJavaExceptionReporter;
import org.chromium.chrome.browser.customtabs.CustomTabsConnection;
import org.chromium.chrome.browser.dependency_injection.ChromeAppComponent;
import org.chromium.chrome.browser.dependency_injection.ChromeAppModule;
import org.chromium.chrome.browser.dependency_injection.DaggerChromeAppComponent;
import org.chromium.chrome.browser.dependency_injection.ModuleFactoryOverrides;
import org.chromium.chrome.browser.metrics.UmaUtils;
import org.chromium.chrome.browser.night_mode.SystemNightModeMonitor;
import org.chromium.chrome.browser.preferences.ChromePreferenceManager;
import org.chromium.chrome.browser.vr.OnExitVrRequestListener;
import org.chromium.chrome.browser.vr.VrModuleProvider;
import org.chromium.components.embedder_support.application.FontPreloadingWorkaround;
import org.chromium.components.module_installer.util.ModuleUtil;
import org.chromium.ui.base.ResourceBundle;
/**
* Basic application functionality that should be shared among all browser applications that use
* chrome layer.
*/
public class ChromeApplication extends Application {
private static final String COMMAND_LINE_FILE = "chrome-command-line";
private static final String TAG = "ChromiumApplication";
// Public to allow use in ChromeBackupAgent
public static final String PRIVATE_DATA_DIRECTORY_SUFFIX = "chrome";
/** Lock on creation of sComponent. */
private static final Object sLock = new Object();
@Nullable
private static volatile ChromeAppComponent sComponent;
@Override
public void onCreate() {
super.onCreate();
// These can't go in attachBaseContext because Context.getApplicationContext() (which they
// use under-the-hood) does not work until after it returns.
FontPreloadingWorkaround.maybeInstallWorkaround(this);
MemoryPressureMonitor.INSTANCE.registerComponentCallbacks();
}
// Called by the framework for ALL processes. Runs before ContentProviders are created.
// Quirk: context.getApplicationContext() returns null during this method.
@Override
protected void attachBaseContext(Context context) {
boolean isBrowserProcess = isBrowserProcess();
if (isBrowserProcess) UmaUtils.recordMainEntryPointTime();
super.attachBaseContext(context);
ContextUtils.initApplicationContext(this);
if (isBrowserProcess) {
if (BuildConfig.IS_MULTIDEX_ENABLED) {
ChromiumMultiDexInstaller.install(this);
}
checkAppBeingReplaced();
PathUtils.setPrivateDataDirectorySuffix(PRIVATE_DATA_DIRECTORY_SUFFIX);
// Renderer and GPU processes have command line passed to them via IPC
// (see ChildProcessService.java).
CommandLineInitUtil.initCommandLine(
COMMAND_LINE_FILE, ChromeApplication::shouldUseDebugFlags);
// Requires command-line flags.
TraceEvent.maybeEnableEarlyTracing();
TraceEvent.begin("ChromeApplication.attachBaseContext");
// Register for activity lifecycle callbacks. Must be done before any activities are
// created and is needed only by processes that use the ApplicationStatus api (which for
// Chrome is just the browser process).
ApplicationStatus.initialize(this);
// Register and initialize application status listener for crashes, this needs to be
// done as early as possible so that this value is set before any crashes are reported.
ApplicationStatusTracker tracker = new ApplicationStatusTracker();
tracker.onApplicationStateChange(ApplicationStatus.getStateForApplication());
ApplicationStatus.registerApplicationStateListener(tracker);
// Only browser process requires custom resources.
BuildHooksAndroid.initCustomResources(this);
// Disable MemoryPressureMonitor polling when Chrome goes to the background.
ApplicationStatus.registerApplicationStateListener(
ChromeApplication::updateMemoryPressurePolling);
// Record via UMA all modules that have been requested and are currently installed. This
// will tell us the install penetration of each module over time.
ModuleUtil.recordModuleAvailability();
// Set Chrome factory for mapping BackgroundTask classes to TaskIds.
ChromeBackgroundTaskFactory.setAsDefault();
}
// Write installed modules to crash keys. This needs to be done as early as possible so that
// these values are set before any crashes are reported.
ModuleUtil.updateCrashKeys();
BuildInfo.setFirebaseAppId(FirebaseConfig.getFirebaseAppId());
if (!ContextUtils.isIsolatedProcess()) {
// Incremental install disables process isolation, so things in this block will actually
// be run for incremental apks, but not normal apks.
PureJavaExceptionHandler.installHandler();
if (BuildHooksConfig.REPORT_JAVA_ASSERT) {
BuildHooks.setReportAssertionCallback(
PureJavaExceptionReporter::reportJavaException);
}
}
AsyncTask.takeOverAndroidThreadPool();
JNIUtils.setClassLoader(getClassLoader());
ResourceBundle.setAvailablePakLocales(
LocaleConfig.COMPRESSED_LOCALES, LocaleConfig.UNCOMPRESSED_LOCALES);
if (isBrowserProcess) {
TraceEvent.end("ChromeApplication.attachBaseContext");
}
}
private static Boolean shouldUseDebugFlags() {
return ChromePreferenceManager.getInstance().readBoolean(
ChromePreferenceManager.COMMAND_LINE_ON_NON_ROOTED_ENABLED_KEY, false);
}
private static boolean isBrowserProcess() {
return !ContextUtils.getProcessName().contains(":");
}
private static void updateMemoryPressurePolling(@ApplicationState int newState) {
if (newState == ApplicationState.HAS_RUNNING_ACTIVITIES) {
MemoryPressureMonitor.INSTANCE.enablePolling();
} else if (newState == ApplicationState.HAS_STOPPED_ACTIVITIES) {
MemoryPressureMonitor.INSTANCE.disablePolling();
}
}
/** Ensure this application object is not out-of-date. */
private void checkAppBeingReplaced() {
// During app update the old apk can still be triggered by broadcasts and spin up an
// out-of-date application. Kill old applications in this bad state. See
// http://crbug.com/658130 for more context and http://b.android.com/56296 for the bug.
if (ContextUtils.getApplicationAssets() == null) {
Log.e(TAG, "getResources() null, closing app.");
System.exit(0);
}
}
@MainDex
@Override
public void onTrimMemory(int level) {
super.onTrimMemory(level);
if (isSevereMemorySignal(level)
&& GlobalDiscardableReferencePool.getReferencePool() != null) {
GlobalDiscardableReferencePool.getReferencePool().drain();
}
CustomTabsConnection.onTrimMemory(level);
}
/**
* Determines whether the given memory signal is considered severe.
* @param level The type of signal as defined in {@link android.content.ComponentCallbacks2}.
*/
public static boolean isSevereMemorySignal(int level) {
// The conditions are expressed using ranges to capture intermediate levels possibly added
// to the API in the future.
return (level >= TRIM_MEMORY_RUNNING_LOW && level < TRIM_MEMORY_UI_HIDDEN)
|| level >= TRIM_MEMORY_MODERATE;
}
@Override
public void startActivity(Intent intent) {
startActivity(intent, null);
}
@Override
public void startActivity(Intent intent, Bundle options) {
if (VrModuleProvider.getDelegate().canLaunch2DIntents()
|| VrModuleProvider.getIntentDelegate().isVrIntent(intent)) {
super.startActivity(intent, options);
return;
}
VrModuleProvider.getDelegate().requestToExitVr(new OnExitVrRequestListener() {
@Override
public void onSucceeded() {
if (!VrModuleProvider.getDelegate().canLaunch2DIntents()) {
throw new IllegalStateException("Still in VR after having exited VR.");
}
startActivity(intent, options);
}
@Override
public void onDenied() {}
});
}
@Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
// TODO(huayinz): Add observer pattern for application configuration changes.
if (isBrowserProcess()) {
SystemNightModeMonitor.getInstance().onApplicationConfigurationChanged();
}
}
/** Returns the application-scoped component. */
public static ChromeAppComponent getComponent() {
if (sComponent == null) {
synchronized (sLock) {
if (sComponent == null) {
sComponent = createComponent();
}
}
}
return sComponent;
}
private static ChromeAppComponent createComponent() {
ChromeAppModule.Factory overriddenFactory =
ModuleFactoryOverrides.getOverrideFor(ChromeAppModule.Factory.class);
ChromeAppModule module =
overriddenFactory == null ? new ChromeAppModule() : overriddenFactory.create();
AppHooksModule.Factory appHooksFactory =
ModuleFactoryOverrides.getOverrideFor(AppHooksModule.Factory.class);
AppHooksModule appHooksModule =
appHooksFactory == null ? new AppHooksModule() : appHooksFactory.create();
return DaggerChromeAppComponent.builder().chromeAppModule(module)
.appHooksModule(appHooksModule).build();
}
}