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