// Copyright 2015 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.os.Process; import org.chromium.base.ActivityState; import org.chromium.base.ApplicationStatus; import org.chromium.base.ContextUtils; import org.chromium.base.ThreadUtils; import org.chromium.base.annotations.SuppressFBWarnings; import org.chromium.chrome.browser.ApplicationLifetime; import org.chromium.chrome.browser.BrowserRestartActivity; import java.lang.ref.WeakReference; /** * Answers requests to kill and (potentially) restart Chrome's main browser process. * * This class fires an Intent to start the {@link BrowserRestartActivity}, which will utltimately * kill the main browser process from its own process. * * https://crbug.com/515919 details why another Activity is used instead of using the AlarmManager. * https://crbug.com/545453 details why the BrowserRestartActivity handles the process killing. */ class ChromeLifetimeController implements ApplicationLifetime.Observer, ApplicationStatus.ActivityStateListener { private static final String TAG = "LifetimeController"; private static ChromeLifetimeController sInstance; private boolean mRestartChromeOnDestroy; private int mRemainingActivitiesCount = 0; /** * Initialize the ChromeLifetimeController; */ @SuppressFBWarnings("LI_LAZY_INIT_UPDATE_STATIC") public static void initialize() { ThreadUtils.assertOnUiThread(); if (sInstance != null) return; sInstance = new ChromeLifetimeController(); ApplicationLifetime.addObserver(sInstance); } private ChromeLifetimeController() {} @Override public void onTerminate(boolean restart) { mRestartChromeOnDestroy = restart; for (WeakReference<Activity> weakActivity : ApplicationStatus.getRunningActivities()) { Activity activity = weakActivity.get(); if (activity != null) { ApplicationStatus.registerStateListenerForActivity(this, activity); mRemainingActivitiesCount++; activity.finish(); } } // Start the Activity that will ultimately kill this process. fireBrowserRestartActivityIntent(BrowserRestartActivity.ACTION_START_WATCHDOG); } @Override public void onActivityStateChange(Activity activity, int newState) { assert mRemainingActivitiesCount > 0; if (newState == ActivityState.DESTROYED) { mRemainingActivitiesCount--; if (mRemainingActivitiesCount == 0) { fireBrowserRestartActivityIntent(BrowserRestartActivity.ACTION_KILL_PROCESS); } } } private void fireBrowserRestartActivityIntent(String action) { Context context = ContextUtils.getApplicationContext(); Intent intent = new Intent(); intent.setAction(action); intent.setClassName( context.getPackageName(), BrowserRestartActivity.class.getName()); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); intent.putExtra(BrowserRestartActivity.EXTRA_MAIN_PID, Process.myPid()); intent.putExtra(BrowserRestartActivity.EXTRA_RESTART, mRestartChromeOnDestroy); context.startActivity(intent); } }