More movement from ActivityStack to Supervisor

- Move showAskCompatModeDialogLocked from ActivityStack to
ActivityManagerService.
- Keep track of userId in ActivityStackSupervisor and initialize
new ActivityStacks with it.
- Move realStartActivityLocked to ActivityStackSupervisor except
for the part that actually updates the ActivityStack.

Change-Id: I7467302f1249fee2ea3cf027a49e0894c9a2213d
diff --git a/services/java/com/android/server/am/ActivityStackSupervisor.java b/services/java/com/android/server/am/ActivityStackSupervisor.java
index e58bf06..175a29e 100644
--- a/services/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/java/com/android/server/am/ActivityStackSupervisor.java
@@ -16,11 +16,14 @@
 
 package com.android.server.am;
 
+import static com.android.server.am.ActivityManagerService.localLOGV;
 import static com.android.server.am.ActivityManagerService.DEBUG_CLEANUP;
 import static com.android.server.am.ActivityManagerService.DEBUG_CONFIGURATION;
 import static com.android.server.am.ActivityManagerService.DEBUG_PAUSE;
+import static com.android.server.am.ActivityManagerService.DEBUG_SWITCH;
 import static com.android.server.am.ActivityManagerService.TAG;
 
+import android.app.Activity;
 import android.app.ActivityManager;
 import android.app.ActivityOptions;
 import android.app.AppGlobals;
@@ -29,6 +32,7 @@
 import android.app.PendingIntent;
 import android.app.ActivityManager.RunningTaskInfo;
 import android.app.IActivityManager.WaitResult;
+import android.app.ResultInfo;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.IIntentSender;
@@ -43,12 +47,15 @@
 import android.os.Bundle;
 import android.os.IBinder;
 import android.os.Looper;
+import android.os.Message;
 import android.os.ParcelFileDescriptor;
 import android.os.RemoteException;
 import android.os.SystemClock;
+import android.util.EventLog;
 import android.util.Slog;
 
 import com.android.internal.app.HeavyWeightSwitcherActivity;
+import com.android.server.am.ActivityStack.ActivityState;
 
 import java.io.FileDescriptor;
 import java.io.IOException;
@@ -57,6 +64,11 @@
 import java.util.List;
 
 public class ActivityStackSupervisor {
+    static final boolean DEBUG_ADD_REMOVE = false;
+    static final boolean DEBUG_APP = false;
+    static final boolean DEBUG_SAVED_STATE = false;
+    static final boolean DEBUG_STATES = false;
+
     public static final int HOME_STACK_ID = 0;
 
     final ActivityManagerService mService;
@@ -73,6 +85,9 @@
      * new task is created. */
     private int mCurTaskId = 0;
 
+    /** The current user */
+    private int mCurrentUser;
+
     /** The stack containing the launcher app */
     private ActivityStack mHomeStack;
 
@@ -89,8 +104,8 @@
         mLooper = looper;
     }
 
-    void init() {
-        mHomeStack = new ActivityStack(mService, mContext, mLooper, HOME_STACK_ID, this);
+    void init(int userId) {
+        mHomeStack = new ActivityStack(mService, mContext, mLooper, HOME_STACK_ID, this, userId);
         setMainStack(mHomeStack);
         mStacks.add(mHomeStack);
     }
@@ -156,7 +171,7 @@
                         if (headless) {
                             Slog.e(TAG, "Starting activities not supported on headless device: "
                                     + hr);
-                        } else if (stack.realStartActivityLocked(hr, app, true, true)) {
+                        } else if (realStartActivityLocked(hr, app, true, true)) {
                             didSomething = true;
                         }
                     } catch (Exception e) {
@@ -488,6 +503,174 @@
         return ActivityManager.START_SUCCESS;
     }
 
+    final boolean realStartActivityLocked(ActivityRecord r,
+            ProcessRecord app, boolean andResume, boolean checkConfig)
+            throws RemoteException {
+
+        r.startFreezingScreenLocked(app, 0);
+        mService.mWindowManager.setAppVisibility(r.appToken, true);
+
+        // schedule launch ticks to collect information about slow apps.
+        r.startLaunchTickingLocked();
+
+        // Have the window manager re-evaluate the orientation of
+        // the screen based on the new activity order.  Note that
+        // as a result of this, it can call back into the activity
+        // manager with a new orientation.  We don't care about that,
+        // because the activity is not currently running so we are
+        // just restarting it anyway.
+        if (checkConfig) {
+            Configuration config = mService.mWindowManager.updateOrientationFromAppTokens(
+                    mService.mConfiguration,
+                    r.mayFreezeScreenLocked(app) ? r.appToken : null);
+            mService.updateConfigurationLocked(config, r, false, false);
+        }
+
+        r.app = app;
+        app.waitingToKill = null;
+        r.launchCount++;
+        r.lastLaunchTime = SystemClock.uptimeMillis();
+
+        if (localLOGV) Slog.v(TAG, "Launching: " + r);
+
+        int idx = app.activities.indexOf(r);
+        if (idx < 0) {
+            app.activities.add(r);
+        }
+        mService.updateLruProcessLocked(app, true);
+
+        final ActivityStack stack = r.task.stack;
+        try {
+            if (app.thread == null) {
+                throw new RemoteException();
+            }
+            List<ResultInfo> results = null;
+            List<Intent> newIntents = null;
+            if (andResume) {
+                results = r.results;
+                newIntents = r.newIntents;
+            }
+            if (DEBUG_SWITCH) Slog.v(TAG, "Launching: " + r
+                    + " icicle=" + r.icicle
+                    + " with results=" + results + " newIntents=" + newIntents
+                    + " andResume=" + andResume);
+            if (andResume) {
+                EventLog.writeEvent(EventLogTags.AM_RESTART_ACTIVITY,
+                        r.userId, System.identityHashCode(r),
+                        r.task.taskId, r.shortComponentName);
+            }
+            if (r.isHomeActivity) {
+                mService.mHomeProcess = app;
+            }
+            mService.ensurePackageDexOpt(r.intent.getComponent().getPackageName());
+            r.sleeping = false;
+            r.forceNewConfig = false;
+            mService.showAskCompatModeDialogLocked(r);
+            r.compat = mService.compatibilityInfoForPackageLocked(r.info.applicationInfo);
+            String profileFile = null;
+            ParcelFileDescriptor profileFd = null;
+            boolean profileAutoStop = false;
+            if (mService.mProfileApp != null && mService.mProfileApp.equals(app.processName)) {
+                if (mService.mProfileProc == null || mService.mProfileProc == app) {
+                    mService.mProfileProc = app;
+                    profileFile = mService.mProfileFile;
+                    profileFd = mService.mProfileFd;
+                    profileAutoStop = mService.mAutoStopProfiler;
+                }
+            }
+            app.hasShownUi = true;
+            app.pendingUiClean = true;
+            if (profileFd != null) {
+                try {
+                    profileFd = profileFd.dup();
+                } catch (IOException e) {
+                    if (profileFd != null) {
+                        try {
+                            profileFd.close();
+                        } catch (IOException o) {
+                        }
+                        profileFd = null;
+                    }
+                }
+            }
+            app.thread.scheduleLaunchActivity(new Intent(r.intent), r.appToken,
+                    System.identityHashCode(r), r.info,
+                    new Configuration(mService.mConfiguration),
+                    r.compat, r.icicle, results, newIntents, !andResume,
+                    mService.isNextTransitionForward(), profileFile, profileFd,
+                    profileAutoStop);
+
+            if ((app.info.flags&ApplicationInfo.FLAG_CANT_SAVE_STATE) != 0) {
+                // This may be a heavy-weight process!  Note that the package
+                // manager will ensure that only activity can run in the main
+                // process of the .apk, which is the only thing that will be
+                // considered heavy-weight.
+                if (app.processName.equals(app.info.packageName)) {
+                    if (mService.mHeavyWeightProcess != null
+                            && mService.mHeavyWeightProcess != app) {
+                        Slog.w(TAG, "Starting new heavy weight process " + app
+                                + " when already running "
+                                + mService.mHeavyWeightProcess);
+                    }
+                    mService.mHeavyWeightProcess = app;
+                    Message msg = mService.mHandler.obtainMessage(
+                            ActivityManagerService.POST_HEAVY_NOTIFICATION_MSG);
+                    msg.obj = r;
+                    mService.mHandler.sendMessage(msg);
+                }
+            }
+
+        } catch (RemoteException e) {
+            if (r.launchFailed) {
+                // This is the second time we failed -- finish activity
+                // and give up.
+                Slog.e(TAG, "Second failure launching "
+                      + r.intent.getComponent().flattenToShortString()
+                      + ", giving up", e);
+                mService.appDiedLocked(app, app.pid, app.thread);
+                stack.requestFinishActivityLocked(r.appToken, Activity.RESULT_CANCELED, null,
+                        "2nd-crash", false);
+                return false;
+            }
+
+            // This is the first time we failed -- restart process and
+            // retry.
+            app.activities.remove(r);
+            throw e;
+        }
+
+        r.launchFailed = false;
+        if (stack.updateLRUListLocked(r)) {
+            Slog.w(TAG, "Activity " + r
+                  + " being launched, but already in LRU list");
+        }
+
+        if (andResume) {
+            // As part of the process of launching, ActivityThread also performs
+            // a resume.
+            stack.minimalResumeActivityLocked(r);
+        } else {
+            // This activity is not starting in the resumed state... which
+            // should look like we asked it to pause+stop (but remain visible),
+            // and it has done so and reported back the current icicle and
+            // other state.
+            if (DEBUG_STATES) Slog.v(TAG, "Moving to STOPPED: " + r
+                    + " (starting in stopped state)");
+            r.state = ActivityState.STOPPED;
+            r.stopped = true;
+        }
+
+        // Launch the new version setup screen if needed.  We do this -after-
+        // launching the initial activity (that is, home), so that it can have
+        // a chance to initialize itself while in the background, making the
+        // switch back to it faster and look better.
+        if (isMainStack(stack)) {
+            mService.startSetupActivityLocked();
+        }
+
+        return true;
+    }
+
     void handleAppDiedLocked(ProcessRecord app, boolean restarting) {
         // Just in case.
         final int numStacks = mStacks.size();
@@ -599,7 +782,8 @@
                     break;
                 }
             }
-            mStacks.add(new ActivityStack(mService, mContext, mLooper, mLastStackId, this));
+            mStacks.add(new ActivityStack(mService, mContext, mLooper, mLastStackId, this,
+                    mCurrentUser));
             return mLastStackId;
         }
     }
@@ -694,6 +878,7 @@
     }
 
     boolean switchUserLocked(int userId, UserStartedState uss) {
+        mCurrentUser = userId;
         boolean haveActivities = false;
         final int numStacks = mStacks.size();
         for (int stackNdx = 0; stackNdx < numStacks; ++stackNdx) {