// 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.app.AlarmManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.os.Handler;
import android.os.Looper;
import android.os.Process;
import android.util.Log;

import org.chromium.base.ActivityState;
import org.chromium.base.ApplicationStatus;

import java.lang.ref.WeakReference;

/**
 * Handles killing and potentially restarting Chrome's main Browser process.  Note that this class
 * relies on main Chrome activities to properly call {@link Activity#finish()} on themselves so that
 * it will be notified that all {@link Activity}s under this {@link Application} have been
 * destroyed.
 */
class ChromeLifetimeController implements ApplicationLifetime.Observer,
        ApplicationStatus.ActivityStateListener {
    private static final String TAG = "ChromeLifetimeController";

    private final Context mContext;
    private boolean mRestartChromeOnDestroy;
    private int mRemainingActivitiesCount = 0;

    /**
     * Creates a {@link ChromeLifetimeController} instance.
     * @param context A {@link Context} instance.  The application context will be saved from this
     *                one.
     */
    public ChromeLifetimeController(Context context) {
        mContext = context.getApplicationContext();
        ApplicationLifetime.addObserver(this);
    }

    @Override
    public void onTerminate(boolean restart) {
        mRestartChromeOnDestroy = restart;

        // We've called terminate twice, just wait for the first call to take effect.
        if (mRemainingActivitiesCount > 0) {
            Log.w(TAG, "onTerminate called twice");
            return;
        }

        for (WeakReference<Activity> weakActivity : ApplicationStatus.getRunningActivities()) {
            Activity activity = weakActivity.get();
            if (activity != null) {
                ApplicationStatus.registerStateListenerForActivity(this, activity);
                mRemainingActivitiesCount++;
                activity.finish();
            }
        }
    }


    @Override
    public void onActivityStateChange(Activity activity, int newState) {
        assert mRemainingActivitiesCount > 0;
        if (newState == ActivityState.DESTROYED) {
            mRemainingActivitiesCount--;
            if (mRemainingActivitiesCount == 0) {
                destroyProcess();
            }
        }

    }

    private void destroyProcess() {
        Handler handler = new Handler(Looper.getMainLooper());
        handler.post(new Runnable() {
            @Override
            public void run() {
                if (mRestartChromeOnDestroy) scheduleRestart(mContext);

                Log.w(TAG, "Forcefully killing process...");
                Process.killProcess(Process.myPid());

                mRestartChromeOnDestroy = false;
            }
        });
    }

    private static void scheduleRestart(Context context) {
        Intent intent = new Intent();
        intent.setPackage(context.getPackageName());
        intent.setAction(Intent.ACTION_MAIN);
        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, intent,
                PendingIntent.FLAG_ONE_SHOT);
        if (pendingIntent != null) {
            AlarmManager am = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
            am.set(AlarmManager.RTC, System.currentTimeMillis() + 1000, pendingIntent);
        }
    }
}

@@ -1086,16 +1086,146 @@
 ller%22;%0A%0A
+    // The amount of time to wait for Chrome to destroy all the activities.%0A    private static final long WATCHDOG_DELAY = 1000;%0A%0A
     priv
@@ -1252,16 +1252,16 @@
 ontext;%0A
-
     priv
@@ -1343,16 +1343,52 @@
 unt = 0;
+%0A    private final Handler mHandler;
 %0A%0A    /*
@@ -1737,16 +1737,72 @@
 (this);%0A
+        mHandler = new Handler(Looper.getMainLooper());%0A
     %7D%0A%0A 
@@ -2486,24 +2486,376 @@
  %7D%0A        %7D
+%0A%0A        // Post a watchdog -- if Android is taking a long time to call onDestroy, kill the process.%0A        mHandler.postDelayed(new Runnable() %7B%0A            @Override%0A            public void run() %7B%0A                if (mRemainingActivitiesCount %3E 0) %7B%0A                    destroyProcess();%0A                %7D%0A            %7D%0A        %7D, WATCHDOG_DELAY);
 %0A    %7D%0A%0A%0A   
@@ -3240,72 +3240,10 @@
     
+m
 H
-andler handler = new Handler(Looper.getMainLooper());%0A        h
 andl

Fix an issue where Chrome doesn't always restart from chrome://flags.