// 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.Activity; import android.content.Intent; import android.content.SharedPreferences; import android.os.Handler; import android.os.Looper; import android.os.SystemClock; import android.util.Log; import org.chromium.base.ActivityState; import org.chromium.base.ApplicationStatus; import org.chromium.base.CommandLineInitUtil; import org.chromium.base.ContextUtils; import org.chromium.base.ResourceExtractor; import org.chromium.base.ThreadUtils; import org.chromium.base.TraceEvent; import org.chromium.base.annotations.CalledByNative; import org.chromium.base.annotations.SuppressFBWarnings; import org.chromium.base.library_loader.ProcessInitException; import org.chromium.chrome.R; import org.chromium.chrome.browser.banners.AppDetailsDelegate; import org.chromium.chrome.browser.customtabs.CustomTabsConnection; import org.chromium.chrome.browser.datausage.ExternalDataUseObserver; import org.chromium.chrome.browser.document.DocumentActivity; import org.chromium.chrome.browser.document.IncognitoDocumentActivity; import org.chromium.chrome.browser.externalauth.ExternalAuthUtils; import org.chromium.chrome.browser.feedback.EmptyFeedbackReporter; import org.chromium.chrome.browser.feedback.FeedbackReporter; import org.chromium.chrome.browser.gsa.GSAHelper; import org.chromium.chrome.browser.help.HelpAndFeedback; import org.chromium.chrome.browser.init.InvalidStartupDialog; import org.chromium.chrome.browser.instantapps.InstantAppsHandler; import org.chromium.chrome.browser.locale.LocaleManager; import org.chromium.chrome.browser.metrics.UmaUtils; import org.chromium.chrome.browser.metrics.VariationsSession; import org.chromium.chrome.browser.multiwindow.MultiWindowUtils; import org.chromium.chrome.browser.net.qualityprovider.ExternalEstimateProviderAndroid; import org.chromium.chrome.browser.omaha.RequestGenerator; import org.chromium.chrome.browser.partnercustomizations.PartnerBrowserCustomizations; import org.chromium.chrome.browser.physicalweb.PhysicalWebBleClient; import org.chromium.chrome.browser.physicalweb.PhysicalWebEnvironment; import org.chromium.chrome.browser.policy.PolicyAuditor; import org.chromium.chrome.browser.preferences.LocationSettings; import org.chromium.chrome.browser.preferences.PreferencesLauncher; import org.chromium.chrome.browser.preferences.autofill.AutofillPreferences; import org.chromium.chrome.browser.preferences.password.SavePasswordsPreferences; import org.chromium.chrome.browser.preferences.privacy.ClearBrowsingDataPreferences; import org.chromium.chrome.browser.rlz.RevenueStats; import org.chromium.chrome.browser.services.AndroidEduOwnerCheckCallback; import org.chromium.chrome.browser.signin.GoogleActivityController; import org.chromium.chrome.browser.sync.GmsCoreSyncListener; import org.chromium.chrome.browser.tab.AuthenticatorNavigationInterceptor; import org.chromium.chrome.browser.tab.Tab; import org.chromium.chrome.browser.tabmodel.document.ActivityDelegateImpl; import org.chromium.chrome.browser.tabmodel.document.DocumentTabModelSelector; import org.chromium.chrome.browser.tabmodel.document.StorageDelegate; import org.chromium.chrome.browser.tabmodel.document.TabDelegate; import org.chromium.components.signin.AccountManagerDelegate; import org.chromium.components.signin.SystemAccountManagerDelegate; import org.chromium.content.app.ContentApplication; import org.chromium.content.browser.ChildProcessCreationParams; import org.chromium.policy.AppRestrictionsProvider; import org.chromium.policy.CombinedPolicyProvider; 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 ContentApplication { public static final String COMMAND_LINE_FILE = "chrome-command-line"; private static final String TAG = "ChromiumApplication"; private static final String PREF_BOOT_TIMESTAMP = "com.google.android.apps.chrome.ChromeMobileApplication.BOOT_TIMESTAMP"; private static final long BOOT_TIMESTAMP_MARGIN_MS = 1000; private static DocumentTabModelSelector sDocumentTabModelSelector; public ChromeApplication() { super(); ContextUtils.initApplicationContext(this); } /** * This is called during early initialization in order to set up ChildProcessLauncher * for certain Chrome packaging configurations */ public ChildProcessCreationParams getChildProcessCreationParams() { return null; } /** * This is called once per ChromeApplication instance, which get created per process * (browser OR renderer). Don't stick anything in here that shouldn't be called multiple times * during Chrome's lifetime. */ @Override public void onCreate() { UmaUtils.recordMainEntryPointTime(); initCommandLine(); TraceEvent.maybeEnableEarlyTracing(); TraceEvent.begin("ChromeApplication.onCreate"); super.onCreate(); TraceEvent.end("ChromeApplication.onCreate"); } /** * Returns a new instance of VariationsSession. */ public VariationsSession createVariationsSession() { return new VariationsSession(); } /** * Return a {@link AuthenticatorNavigationInterceptor} for the given {@link Tab}. * This can be null if there are no applicable interceptor to be built. */ @SuppressWarnings("unused") public AuthenticatorNavigationInterceptor createAuthenticatorNavigationInterceptor(Tab tab) { return null; } /** * Initiate AndroidEdu device check. * @param callback Callback that should receive the results of the AndroidEdu device check. */ public void checkIsAndroidEduDevice(final AndroidEduOwnerCheckCallback callback) { new Handler(Looper.getMainLooper()).post(new Runnable() { @Override public void run() { callback.onSchoolCheckDone(false); } }); } @CalledByNative protected void showAutofillSettings() { PreferencesLauncher.launchSettingsPage(this, AutofillPreferences.class.getName()); } @CalledByNative protected void showPasswordSettings() { PreferencesLauncher.launchSettingsPage(this, SavePasswordsPreferences.class.getName()); } @Override protected void initializeLibraryDependencies() { // The ResourceExtractor is only needed by the browser process, but this will have no // impact on the renderer process construction. ResourceBundle.initializeLocalePaks(this, R.array.locale_paks); ResourceExtractor.setResourcesToExtract(ResourceBundle.getActiveLocaleResources()); } @Override public void initCommandLine() { CommandLineInitUtil.initCommandLine(this, COMMAND_LINE_FILE); } /** * Shows an error dialog following a startup error, and then exits the application. * @param e The exception reported by Chrome initialization. */ public static void reportStartupErrorAndExit(final ProcessInitException e) { Activity activity = ApplicationStatus.getLastTrackedFocusedActivity(); if (ApplicationStatus.getStateForActivity(activity) == ActivityState.DESTROYED) { return; } InvalidStartupDialog.show(activity, e.getErrorCode()); } /** * Returns an instance of LocationSettings to be installed as a singleton. */ public LocationSettings createLocationSettings() { // Using an anonymous subclass as the constructor is protected. // This is done to deter instantiation of LocationSettings elsewhere without using the // getInstance() helper method. return new LocationSettings(){}; } /** * Opens the UI to clear browsing data. * @param tab The tab that triggered the request. */ @CalledByNative protected void openClearBrowsingData(Tab tab) { Activity activity = tab.getWindowAndroid().getActivity().get(); if (activity == null) { Log.e(TAG, "Attempting to open clear browsing data for a tab without a valid activity"); return; } Intent intent = PreferencesLauncher.createIntentForSettingsPage(activity, ClearBrowsingDataPreferences.class.getName()); activity.startActivity(intent); } /** * @return Whether parental controls are enabled. Returning true will disable * incognito mode. */ @CalledByNative protected boolean areParentalControlsEnabled() { return PartnerBrowserCustomizations.isIncognitoDisabled(); } /** * @return A provider of external estimates. * @param nativePtr Pointer to the native ExternalEstimateProviderAndroid object. */ public ExternalEstimateProviderAndroid createExternalEstimateProviderAndroid(long nativePtr) { return new ExternalEstimateProviderAndroid(nativePtr) {}; } /** * @return An external observer of data use. * @param nativePtr Pointer to the native ExternalDataUseObserver object. */ public ExternalDataUseObserver createExternalDataUseObserver(long nativePtr) { return new ExternalDataUseObserver(nativePtr); } /** * @return The user agent string of Chrome. */ public static String getBrowserUserAgent() { return nativeGetBrowserUserAgent(); } /** * The host activity should call this during its onPause() handler to ensure * all state is saved when the app is suspended. Calling ChromiumApplication.onStop() does * this for you. */ public static void flushPersistentData() { try { TraceEvent.begin("ChromiumApplication.flushPersistentData"); nativeFlushPersistentData(); } finally { TraceEvent.end("ChromiumApplication.flushPersistentData"); } } /** * Removes all session cookies (cookies with no expiration date) after device reboots. * This function will incorrectly clear cookies when Daylight Savings Time changes the clock. * Without a way to get a monotonically increasing system clock, the boot timestamp will be off * by one hour. However, this should only happen at most once when the clock changes since the * updated timestamp is immediately saved. */ public static void removeSessionCookies() { long lastKnownBootTimestamp = ContextUtils.getAppSharedPreferences().getLong(PREF_BOOT_TIMESTAMP, 0); long bootTimestamp = System.currentTimeMillis() - SystemClock.uptimeMillis(); long difference = bootTimestamp - lastKnownBootTimestamp; // Allow some leeway to account for fractions of milliseconds. if (Math.abs(difference) > BOOT_TIMESTAMP_MARGIN_MS) { nativeRemoveSessionCookies(); SharedPreferences prefs = ContextUtils.getAppSharedPreferences(); SharedPreferences.Editor editor = prefs.edit(); editor.putLong(PREF_BOOT_TIMESTAMP, bootTimestamp); editor.apply(); } } private static native void nativeRemoveSessionCookies(); private static native String nativeGetBrowserUserAgent(); private static native void nativeFlushPersistentData(); /** * @return An instance of {@link FeedbackReporter} to report feedback. */ public FeedbackReporter createFeedbackReporter() { return new EmptyFeedbackReporter(); } /** * @return An instance of ExternalAuthUtils to be installed as a singleton. */ public ExternalAuthUtils createExternalAuthUtils() { return new ExternalAuthUtils(); } /** * Returns a new instance of HelpAndFeedback. */ public HelpAndFeedback createHelpAndFeedback() { return new HelpAndFeedback(); } /** * @return An instance of {@link CustomTabsConnection}. Should not be called * outside of {@link CustomTabsConnection#getInstance()}. */ public CustomTabsConnection createCustomTabsConnection() { return new CustomTabsConnection(this); } /** * @return A new {@link PhysicalWebBleClient} instance. */ public PhysicalWebBleClient createPhysicalWebBleClient() { return new PhysicalWebBleClient(); } /** * @return A new {@link PhysicalWebEnvironment} instance. */ public PhysicalWebEnvironment createPhysicalWebEnvironment() { return new PhysicalWebEnvironment(); } public InstantAppsHandler createInstantAppsHandler() { return new InstantAppsHandler(); } /** * @return An instance of {@link GSAHelper} that handles the start point of chrome's integration * with GSA. */ public GSAHelper createGsaHelper() { return new GSAHelper(); } /** * @return An instance of {@link LocaleManager} that handles customized locale related logic. */ public LocaleManager createLocaleManager() { return new LocaleManager(); } /** * Registers various policy providers with the policy manager. * Providers are registered in increasing order of precedence so overrides should call this * method in the end for this method to maintain the highest precedence. * @param combinedProvider The {@link CombinedPolicyProvider} to register the providers with. */ public void registerPolicyProviders(CombinedPolicyProvider combinedProvider) { combinedProvider.registerProvider(new AppRestrictionsProvider(getApplicationContext())); } /** * @return An instance of PolicyAuditor that notifies the policy system of the user's activity. * Only applicable when the user has a policy active, that is tracking the activity. */ public PolicyAuditor getPolicyAuditor() { // This class has a protected constructor to prevent accidental instantiation. return new PolicyAuditor() {}; } /** * @return An instance of MultiWindowUtils to be installed as a singleton. */ public MultiWindowUtils createMultiWindowUtils() { return new MultiWindowUtils(); } /** * @return An instance of RequestGenerator to be used for Omaha XML creation. Will be null if * a generator is unavailable. */ public RequestGenerator createOmahaRequestGenerator() { return null; } /** * @return An instance of GmsCoreSyncListener to notify GmsCore of sync encryption key changes. * Will be null if one is unavailable. */ public GmsCoreSyncListener createGmsCoreSyncListener() { return null; } /** * @return An instance of GoogleActivityController. */ public GoogleActivityController createGoogleActivityController() { return new GoogleActivityController(); } /** * @return An instance of AppDetailsDelegate that can be queried about app information for the * App Banner feature. Will be null if one is unavailable. */ public AppDetailsDelegate createAppDetailsDelegate() { return null; } /** * Returns the Singleton instance of the DocumentTabModelSelector. * TODO(dfalcantara): Find a better place for this once we differentiate between activity and * application-level TabModelSelectors. * @return The DocumentTabModelSelector for the application. */ @SuppressFBWarnings("LI_LAZY_INIT_STATIC") public static DocumentTabModelSelector getDocumentTabModelSelector() { ThreadUtils.assertOnUiThread(); if (sDocumentTabModelSelector == null) { ActivityDelegateImpl activityDelegate = new ActivityDelegateImpl( DocumentActivity.class, IncognitoDocumentActivity.class); sDocumentTabModelSelector = new DocumentTabModelSelector(activityDelegate, new StorageDelegate(), new TabDelegate(false), new TabDelegate(true)); } return sDocumentTabModelSelector; } /** * @return An instance of RevenueStats to be installed as a singleton. */ public RevenueStats createRevenueStatsInstance() { return new RevenueStats(); } /** * Creates a new {@link AccountManagerDelegate}. * @return the created {@link AccountManagerDelegate}. */ public AccountManagerDelegate createAccountManagerDelegate() { return new SystemAccountManagerDelegate(this); } }