1 // Copyright 2013 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 package org.chromium.chrome.browser; 6 7 import android.content.Context; 8 import android.content.Intent; 9 import android.content.pm.ApplicationInfo; 10 import android.content.res.Configuration; 11 import android.os.Bundle; 12 13 import androidx.annotation.Nullable; 14 15 import org.chromium.base.ApplicationState; 16 import org.chromium.base.ApplicationStatus; 17 import org.chromium.base.BuildInfo; 18 import org.chromium.base.CommandLineInitUtil; 19 import org.chromium.base.ContextUtils; 20 import org.chromium.base.LocaleUtils; 21 import org.chromium.base.PathUtils; 22 import org.chromium.base.TraceEvent; 23 import org.chromium.base.annotations.MainDex; 24 import org.chromium.base.library_loader.LibraryLoader; 25 import org.chromium.base.memory.MemoryPressureMonitor; 26 import org.chromium.chrome.browser.background_task_scheduler.ChromeBackgroundTaskFactory; 27 import org.chromium.chrome.browser.base.MainDexApplicationImpl; 28 import org.chromium.chrome.browser.base.SplitCompatApplication; 29 import org.chromium.chrome.browser.crash.ApplicationStatusTracker; 30 import org.chromium.chrome.browser.crash.FirebaseConfig; 31 import org.chromium.chrome.browser.crash.PureJavaExceptionHandler; 32 import org.chromium.chrome.browser.crash.PureJavaExceptionReporter; 33 import org.chromium.chrome.browser.customtabs.CustomTabsConnection; 34 import org.chromium.chrome.browser.dependency_injection.ChromeAppComponent; 35 import org.chromium.chrome.browser.dependency_injection.ChromeAppModule; 36 import org.chromium.chrome.browser.dependency_injection.DaggerChromeAppComponent; 37 import org.chromium.chrome.browser.dependency_injection.ModuleFactoryOverrides; 38 import org.chromium.chrome.browser.flags.CachedFeatureFlags; 39 import org.chromium.chrome.browser.flags.ChromeFeatureList; 40 import org.chromium.chrome.browser.language.GlobalAppLocaleController; 41 import org.chromium.chrome.browser.metrics.UmaUtils; 42 import org.chromium.chrome.browser.night_mode.SystemNightModeMonitor; 43 import org.chromium.chrome.browser.vr.OnExitVrRequestListener; 44 import org.chromium.chrome.browser.vr.VrModuleProvider; 45 import org.chromium.components.browser_ui.util.GlobalDiscardableReferencePool; 46 import org.chromium.components.module_installer.util.ModuleUtil; 47 import org.chromium.components.version_info.Channel; 48 import org.chromium.components.version_info.VersionConstants; 49 import org.chromium.url.GURL; 50 51 /** 52 * Basic application functionality that should be shared among all browser applications that use 53 * chrome layer. 54 * 55 * Note: All application logic should be added to {@link ChromeApplicationImpl}, which will be 56 * called from the superclass. See {@link SplitCompatApplication} for more info. 57 */ 58 public class ChromeApplication extends SplitCompatApplication { 59 private static final String COMMAND_LINE_FILE = "chrome-command-line"; 60 // Public to allow use in ChromeBackupAgent 61 public static final String PRIVATE_DATA_DIRECTORY_SUFFIX = "chrome"; 62 63 /** Lock on creation of sComponent. */ 64 private static final Object sLock = new Object(); 65 @Nullable 66 private static volatile ChromeAppComponent sComponent; 67 68 /** Chrome application logic. */ 69 public static class ChromeApplicationImpl extends MainDexApplicationImpl { ChromeApplicationImpl()70 public ChromeApplicationImpl() {} 71 72 // Called by the framework for ALL processes. Runs before ContentProviders are created. 73 // Quirk: context.getApplicationContext() returns null during this method. 74 @Override attachBaseContext(Context context)75 public void attachBaseContext(Context context) { 76 boolean isBrowserProcess = SplitCompatApplication.isBrowserProcess(); 77 78 if (isBrowserProcess) { 79 UmaUtils.recordMainEntryPointTime(); 80 81 // If the app locale override preference is set, create a new override 82 // context to use as the base context for the application. 83 // Must be initialized early to override Application level localizations. 84 if (GlobalAppLocaleController.getInstance().init(context)) { 85 Configuration config = 86 GlobalAppLocaleController.getInstance().getOverrideConfig(context); 87 LocaleUtils.setDefaultLocalesFromConfiguration(config); 88 context = context.createConfigurationContext(config); 89 } 90 } 91 92 super.attachBaseContext(context); 93 if (isBrowserProcess) { 94 checkAppBeingReplaced(); 95 96 PathUtils.setPrivateDataDirectorySuffix(PRIVATE_DATA_DIRECTORY_SUFFIX); 97 // Renderer and GPU processes have command line passed to them via IPC 98 // (see ChildProcessService.java). 99 CommandLineInitUtil.initCommandLine( 100 COMMAND_LINE_FILE, ChromeApplicationImpl::shouldUseDebugFlags); 101 102 // Enable ATrace on debug OS or app builds. 103 int applicationFlags = context.getApplicationInfo().flags; 104 boolean isAppDebuggable = (applicationFlags & ApplicationInfo.FLAG_DEBUGGABLE) != 0; 105 boolean isOsDebuggable = BuildInfo.isDebugAndroid(); 106 // Requires command-line flags. 107 TraceEvent.maybeEnableEarlyTracing( 108 (isAppDebuggable || isOsDebuggable) ? TraceEvent.ATRACE_TAG_APP : 0, 109 /*readCommandLine=*/true); 110 TraceEvent.begin("ChromeApplication.attachBaseContext"); 111 112 // Register for activity lifecycle callbacks. Must be done before any activities are 113 // created and is needed only by processes that use the ApplicationStatus api (which 114 // for Chrome is just the browser process). 115 ApplicationStatus.initialize(getApplication()); 116 117 // Register and initialize application status listener for crashes, this needs to be 118 // done as early as possible so that this value is set before any crashes are 119 // reported. 120 ApplicationStatusTracker tracker = new ApplicationStatusTracker(); 121 tracker.onApplicationStateChange(ApplicationStatus.getStateForApplication()); 122 ApplicationStatus.registerApplicationStateListener(tracker); 123 124 // Disable MemoryPressureMonitor polling when Chrome goes to the background. 125 ApplicationStatus.registerApplicationStateListener( 126 ChromeApplicationImpl::updateMemoryPressurePolling); 127 128 // Initializes the support for dynamic feature modules (browser only). 129 ModuleUtil.initApplication(); 130 131 // Set Chrome factory for mapping BackgroundTask classes to TaskIds. 132 ChromeBackgroundTaskFactory.setAsDefault(); 133 134 AppHooks.get().getChimeDelegate().initialize(); 135 136 if (VersionConstants.CHANNEL == Channel.CANARY) { 137 GURL.setReportDebugThrowableCallback( 138 PureJavaExceptionReporter::reportJavaException); 139 } 140 } 141 142 BuildInfo.setFirebaseAppId(FirebaseConfig.getFirebaseAppId()); 143 144 if (!ContextUtils.isIsolatedProcess()) { 145 // Incremental install disables process isolation, so things in this block will 146 // actually be run for incremental apks, but not normal apks. 147 PureJavaExceptionHandler.installHandler(); 148 } 149 150 if (isBrowserProcess) { 151 TraceEvent.end("ChromeApplication.attachBaseContext"); 152 } 153 } 154 155 @Override onCreate()156 public void onCreate() { 157 super.onCreate(); 158 159 if (SplitCompatApplication.isBrowserProcess() 160 && CachedFeatureFlags.isEnabled(ChromeFeatureList.EARLY_LIBRARY_LOAD)) { 161 // Kick off library loading in a separate thread so it's ready when we need it. 162 new Thread(() -> LibraryLoader.getInstance().ensureMainDexInitialized()).start(); 163 } 164 } 165 shouldUseDebugFlags()166 private static Boolean shouldUseDebugFlags() { 167 return CachedFeatureFlags.isEnabled(ChromeFeatureList.COMMAND_LINE_ON_NON_ROOTED); 168 } 169 updateMemoryPressurePolling(@pplicationState int newState)170 private static void updateMemoryPressurePolling(@ApplicationState int newState) { 171 if (newState == ApplicationState.HAS_RUNNING_ACTIVITIES) { 172 MemoryPressureMonitor.INSTANCE.enablePolling(); 173 } else if (newState == ApplicationState.HAS_STOPPED_ACTIVITIES) { 174 MemoryPressureMonitor.INSTANCE.disablePolling(); 175 } 176 } 177 178 /** Ensure this application object is not out-of-date. */ checkAppBeingReplaced()179 private static void checkAppBeingReplaced() { 180 // During app update the old apk can still be triggered by broadcasts and spin up an 181 // out-of-date application. Kill old applications in this bad state. See 182 // http://crbug.com/658130 for more context and http://b.android.com/56296 for the bug. 183 if (ContextUtils.getApplicationAssets() == null) { 184 throw new RuntimeException("App out of date, getResources() null, closing app."); 185 } 186 } 187 188 @MainDex 189 @Override onTrimMemory(int level)190 public void onTrimMemory(int level) { 191 super.onTrimMemory(level); 192 if (isSevereMemorySignal(level) 193 && GlobalDiscardableReferencePool.getReferencePool() != null) { 194 GlobalDiscardableReferencePool.getReferencePool().drain(); 195 } 196 CustomTabsConnection.onTrimMemory(level); 197 } 198 199 @Override startActivity(Intent intent, Bundle options)200 public void startActivity(Intent intent, Bundle options) { 201 if (VrModuleProvider.getDelegate().canLaunch2DIntents() 202 || VrModuleProvider.getIntentDelegate().isVrIntent(intent)) { 203 super.startActivity(intent, options); 204 return; 205 } 206 207 VrModuleProvider.getDelegate().requestToExitVr(new OnExitVrRequestListener() { 208 @Override 209 public void onSucceeded() { 210 if (!VrModuleProvider.getDelegate().canLaunch2DIntents()) { 211 throw new IllegalStateException("Still in VR after having exited VR."); 212 } 213 startActivity(intent, options); 214 } 215 216 @Override 217 public void onDenied() {} 218 }); 219 } 220 221 @Override onConfigurationChanged(Configuration newConfig)222 public void onConfigurationChanged(Configuration newConfig) { 223 super.onConfigurationChanged(newConfig); 224 // TODO(huayinz): Add observer pattern for application configuration changes. 225 if (SplitCompatApplication.isBrowserProcess()) { 226 SystemNightModeMonitor.getInstance().onApplicationConfigurationChanged(); 227 } 228 } 229 } 230 ChromeApplication(Impl impl)231 public ChromeApplication(Impl impl) { 232 setImpl(impl); 233 } 234 ChromeApplication()235 public ChromeApplication() { 236 this(new ChromeApplicationImpl()); 237 } 238 239 /** 240 * Determines whether the given memory signal is considered severe. 241 * @param level The type of signal as defined in {@link android.content.ComponentCallbacks2}. 242 */ isSevereMemorySignal(int level)243 public static boolean isSevereMemorySignal(int level) { 244 // The conditions are expressed using ranges to capture intermediate levels possibly added 245 // to the API in the future. 246 return (level >= TRIM_MEMORY_RUNNING_LOW && level < TRIM_MEMORY_UI_HIDDEN) 247 || level >= TRIM_MEMORY_MODERATE; 248 } 249 250 /** Returns the application-scoped component. */ getComponent()251 public static ChromeAppComponent getComponent() { 252 if (sComponent == null) { 253 synchronized (sLock) { 254 if (sComponent == null) { 255 sComponent = createComponent(); 256 } 257 } 258 } 259 return sComponent; 260 } 261 createComponent()262 private static ChromeAppComponent createComponent() { 263 ChromeAppModule.Factory overriddenFactory = 264 ModuleFactoryOverrides.getOverrideFor(ChromeAppModule.Factory.class); 265 ChromeAppModule module = 266 overriddenFactory == null ? new ChromeAppModule() : overriddenFactory.create(); 267 268 AppHooksModule.Factory appHooksFactory = 269 ModuleFactoryOverrides.getOverrideFor(AppHooksModule.Factory.class); 270 AppHooksModule appHooksModule = 271 appHooksFactory == null ? new AppHooksModule() : appHooksFactory.create(); 272 273 return DaggerChromeAppComponent.builder().chromeAppModule(module) 274 .appHooksModule(appHooksModule).build(); 275 } 276 } 277