// 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; import android.app.Activity; import android.content.Context; import android.content.Intent; import android.os.Bundle; import android.os.Handler; import android.os.Looper; import android.os.Process; import android.text.TextUtils; import org.chromium.base.ContextUtils; import org.chromium.chrome.browser.util.IntentUtils; /** * Kills and (optionally) restarts the main Chrome process, then immediately kills itself. * * Starting this Activity should only be done by the * {@link org.chromium.chrome.browser.init.ChromeLifetimeController}, and requires * passing in the process ID (the Intent should have the value of Process#myPid() as an extra). * * This Activity runs on a separate process from the main Chrome browser and cannot see the main * process' Activities. It works around an Android framework issue for alarms set via the * AlarmManager, which requires a minimum alarm duration of 5 seconds: https://crbug.com/515919. */ public class BrowserRestartActivity extends Activity { public static final String ACTION_START_WATCHDOG = "org.chromium.chrome.browser.BrowserRestartActivity.start_watchdog"; public static final String ACTION_KILL_PROCESS = "org.chromium.chrome.browser.BrowserRestartActivity.kill_process"; public static final String EXTRA_MAIN_PID = "org.chromium.chrome.browser.BrowserRestartActivity.main_pid"; public static final String EXTRA_RESTART = "org.chromium.chrome.browser.BrowserRestartActivity.restart"; private static final String TAG = "BrowserRestartActivity"; // The amount of time to wait for Chrome to destroy all the activities of the main process // before this Activity forcefully kills it. private static final long WATCHDOG_DELAY_MS = 1000; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); handleIntent(getIntent()); } @Override public void onNewIntent(Intent intent) { handleIntent(intent); } private void handleIntent(final Intent intent) { if (TextUtils.equals(ACTION_START_WATCHDOG, intent.getAction())) { // Kick off a timer to kill the process after a delay. Handler handler = new Handler(Looper.getMainLooper()); handler.postDelayed(new Runnable() { @Override public void run() { destroyProcess(intent); } }, WATCHDOG_DELAY_MS); } else if (TextUtils.equals(ACTION_KILL_PROCESS, intent.getAction())) { destroyProcess(intent); } else { assert false; } } private void destroyProcess(Intent intent) { // Kill the main Chrome process. int mainBrowserPid = IntentUtils.safeGetIntExtra( intent, BrowserRestartActivity.EXTRA_MAIN_PID, -1); assert mainBrowserPid != -1; Process.killProcess(mainBrowserPid); // Fire an Intent to restart Chrome. boolean restart = IntentUtils.safeGetBooleanExtra( intent, BrowserRestartActivity.EXTRA_RESTART, false); if (restart) { Context context = ContextUtils.getApplicationContext(); Intent restartIntent = new Intent(Intent.ACTION_MAIN); restartIntent.setPackage(context.getPackageName()); restartIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); context.startActivity(restartIntent); } // Kill this process. finish(); Process.killProcess(Process.myPid()); } }