Move idle handling from ActivityStack to Supervisor

Along with mFinishingActivities, mStartingUsers and
mCancelledThumbnails.

Change-Id: Ia354225d72e694b7ac68b0869fcab903a3d06436
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index 1bcc067..5ad5544 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -962,6 +962,7 @@
     static final int FIRST_ACTIVITY_STACK_MSG = 100;
     static final int FIRST_BROADCAST_QUEUE_MSG = 200;
     static final int FIRST_COMPAT_MODE_MSG = 300;
+    static final int FIRST_SUPERVISOR_STACK_MSG = 100;
 
     AlertDialog mUidAlert;
     CompatModeDialog mCompatModeDialog;
diff --git a/services/java/com/android/server/am/ActivityRecord.java b/services/java/com/android/server/am/ActivityRecord.java
index 031a1e72..88bcdab 100644
--- a/services/java/com/android/server/am/ActivityRecord.java
+++ b/services/java/com/android/server/am/ActivityRecord.java
@@ -845,9 +845,7 @@
                                     "Was waiting for visible: " + r);
                         }
                         mStackSupervisor.mWaitingVisibleActivities.clear();
-                        Message msg = Message.obtain();
-                        msg.what = ActivityStack.IDLE_NOW_MSG;
-                        stack.mHandler.sendMessage(msg);
+                        mStackSupervisor.scheduleIdleLocked();
                     }
                 }
                 service.scheduleAppGcsLocked();
@@ -960,7 +958,8 @@
     @Override
     public String toString() {
         if (stringName != null) {
-            return stringName + " t" + task.taskId + (finishing ? " f}" : "}");
+            return stringName + " t" + (task == null ? -1 : task.taskId) +
+                    (finishing ? " f}" : "}");
         }
         StringBuilder sb = new StringBuilder(128);
         sb.append("ActivityRecord{");
diff --git a/services/java/com/android/server/am/ActivityStack.java b/services/java/com/android/server/am/ActivityStack.java
index 5b2ac6d..9f31982 100644
--- a/services/java/com/android/server/am/ActivityStack.java
+++ b/services/java/com/android/server/am/ActivityStack.java
@@ -90,10 +90,6 @@
 
     static final boolean VALIDATE_TOKENS = ActivityManagerService.VALIDATE_TOKENS;
 
-    // How long we wait until giving up on the last activity telling us it
-    // is idle.
-    static final int IDLE_TIMEOUT = 10*1000;
-
     // Ticks during which we check progress while waiting for an app to launch.
     static final int LAUNCH_TICK = 500;
 
@@ -176,15 +172,6 @@
     final ArrayList<ActivityRecord> mNoAnimActivities = new ArrayList<ActivityRecord>();
 
     /**
-     * List of activities that are ready to be finished, but waiting
-     * for the previous activity to settle down before doing so.  It contains
-     * HistoryRecord objects.
-     */
-    final ArrayList<ActivityRecord> mFinishingActivities = new ArrayList<ActivityRecord>();
-
-    final ArrayList<UserStartedState> mStartingUsers = new ArrayList<UserStartedState>();
-
-    /**
      * Set when the system is going to sleep, until we have
      * successfully paused the current activity and released our wake lock.
      * At that point the system is allowed to actually sleep.
@@ -244,12 +231,6 @@
     private ActivityRecord mLastScreenshotActivity = null;
     private Bitmap mLastScreenshotBitmap = null;
 
-    /**
-     * List of ActivityRecord objects that have been finished and must
-     * still report back to a pending thumbnail receiver.
-     */
-    private final ArrayList<ActivityRecord> mCancelledThumbnails = new ArrayList<ActivityRecord>();
-
     int mThumbnailWidth = -1;
     int mThumbnailHeight = -1;
 
@@ -262,14 +243,12 @@
 
     static final int SLEEP_TIMEOUT_MSG = ActivityManagerService.FIRST_ACTIVITY_STACK_MSG;
     static final int PAUSE_TIMEOUT_MSG = ActivityManagerService.FIRST_ACTIVITY_STACK_MSG + 1;
-    static final int IDLE_TIMEOUT_MSG = ActivityManagerService.FIRST_ACTIVITY_STACK_MSG + 2;
-    static final int IDLE_NOW_MSG = ActivityManagerService.FIRST_ACTIVITY_STACK_MSG + 3;
-    static final int LAUNCH_TIMEOUT_MSG = ActivityManagerService.FIRST_ACTIVITY_STACK_MSG + 4;
-    static final int DESTROY_TIMEOUT_MSG = ActivityManagerService.FIRST_ACTIVITY_STACK_MSG + 5;
-    static final int RESUME_TOP_ACTIVITY_MSG = ActivityManagerService.FIRST_ACTIVITY_STACK_MSG + 6;
-    static final int LAUNCH_TICK_MSG = ActivityManagerService.FIRST_ACTIVITY_STACK_MSG + 7;
-    static final int STOP_TIMEOUT_MSG = ActivityManagerService.FIRST_ACTIVITY_STACK_MSG + 8;
-    static final int DESTROY_ACTIVITIES_MSG = ActivityManagerService.FIRST_ACTIVITY_STACK_MSG + 9;
+    static final int LAUNCH_TIMEOUT_MSG = ActivityManagerService.FIRST_ACTIVITY_STACK_MSG + 2;
+    static final int DESTROY_TIMEOUT_MSG = ActivityManagerService.FIRST_ACTIVITY_STACK_MSG + 3;
+    static final int RESUME_TOP_ACTIVITY_MSG = ActivityManagerService.FIRST_ACTIVITY_STACK_MSG + 4;
+    static final int LAUNCH_TICK_MSG = ActivityManagerService.FIRST_ACTIVITY_STACK_MSG + 5;
+    static final int STOP_TIMEOUT_MSG = ActivityManagerService.FIRST_ACTIVITY_STACK_MSG + 6;
+    static final int DESTROY_ACTIVITIES_MSG = ActivityManagerService.FIRST_ACTIVITY_STACK_MSG + 7;
 
     static class ScheduleDestroyArgs {
         final ProcessRecord mOwner;
@@ -318,22 +297,6 @@
                         activityPausedLocked(r != null ? r.appToken : null, true);
                     }
                 } break;
-                case IDLE_TIMEOUT_MSG: {
-                    if (mService.mDidDexOpt) {
-                        mService.mDidDexOpt = false;
-                        Message nmsg = mHandler.obtainMessage(IDLE_TIMEOUT_MSG);
-                        nmsg.obj = msg.obj;
-                        mHandler.sendMessageDelayed(nmsg, IDLE_TIMEOUT);
-                        return;
-                    }
-                    // We don't at this point know if the activity is fullscreen,
-                    // so we need to be conservative and assume it isn't.
-                    ActivityRecord r = (ActivityRecord)msg.obj;
-                    Slog.w(TAG, "Activity idle timeout for " + r);
-                    synchronized (mService) {
-                        activityIdleInternalLocked(r != null ? r.appToken : null, true, null);
-                    }
-                } break;
                 case LAUNCH_TICK_MSG: {
                     ActivityRecord r = (ActivityRecord)msg.obj;
                     synchronized (mService) {
@@ -352,12 +315,6 @@
                         activityDestroyedLocked(r != null ? r.appToken : null);
                     }
                 } break;
-                case IDLE_NOW_MSG: {
-                    ActivityRecord r = (ActivityRecord)msg.obj;
-                    synchronized (mService) {
-                        activityIdleInternalLocked(r != null ? r.appToken : null, false, null);
-                    }
-                } break;
                 case LAUNCH_TIMEOUT_MSG: {
                     if (mService.mDidDexOpt) {
                         mService.mDidDexOpt = false;
@@ -626,7 +583,6 @@
         if (VALIDATE_TOKENS) {
             validateAppTokensLocked();
         }
-        mStartingUsers.add(uss);
         if (mCurrentUser == userId) {
             return true;
         }
@@ -739,7 +695,7 @@
                 // Still need to tell some activities to stop; can't sleep yet.
                 if (DEBUG_PAUSE) Slog.v(TAG, "Sleep still need to stop "
                         + mStackSupervisor.mStoppingActivities.size() + " activities");
-                scheduleIdleLocked();
+                mStackSupervisor.scheduleIdleLocked();
                 return;
             }
 
@@ -995,7 +951,7 @@
                         // them out. Or if r is the last of activity of the last task the stack
                         // will be empty and must be cleared immediately.
                         if (DEBUG_PAUSE) Slog.v(TAG, "To many pending stops, forcing idle");
-                        scheduleIdleLocked();
+                        mStackSupervisor.scheduleIdleLocked();
                     } else {
                         checkReadyForSleepLocked();
                     }
@@ -1061,19 +1017,7 @@
         next.newIntents = null;
 
         // schedule an idle timeout in case the app doesn't do it for us.
-        Message msg = mHandler.obtainMessage(IDLE_TIMEOUT_MSG);
-        msg.obj = next;
-        mHandler.sendMessageDelayed(msg, IDLE_TIMEOUT);
-
-        if (false) {
-            // The activity was never told to pause, so just keep
-            // things going as-is.  To maintain our own state,
-            // we need to emulate it coming back and saying it is
-            // idle.
-            msg = mHandler.obtainMessage(IDLE_NOW_MSG);
-            msg.obj = next;
-            mHandler.sendMessage(msg);
-        }
+        mStackSupervisor.scheduleIdleTimeoutLocked(next);
 
         mStackSupervisor.reportResumedActivityLocked(next);
 
@@ -2159,7 +2103,7 @@
         r.addResultLocked(null, resultWho, requestCode, resultCode, data);
     }
 
-    private final void stopActivityLocked(ActivityRecord r) {
+    final void stopActivityLocked(ActivityRecord r) {
         if (DEBUG_SWITCH) Slog.d(TAG, "Stopping: " + r);
         if ((r.intent.getFlags()&Intent.FLAG_ACTIVITY_NO_HISTORY) != 0
                 || (r.info.flags&ActivityInfo.FLAG_NO_HISTORY) != 0) {
@@ -2217,12 +2161,6 @@
         }
     }
 
-    final void scheduleIdleLocked() {
-        Message msg = Message.obtain();
-        msg.what = IDLE_NOW_MSG;
-        mHandler.sendMessage(msg);
-    }
-
     // Checked.
     final ActivityRecord activityIdleInternalLocked(final IBinder token, boolean fromTimeout,
             Configuration config) {
@@ -2240,145 +2178,18 @@
         boolean enableScreen = false;
         boolean activityRemoved = false;
 
-        ActivityRecord r = ActivityRecord.forToken(token);
-        if (r != null) {
-            mHandler.removeMessages(IDLE_TIMEOUT_MSG, r);
-            r.finishLaunchTickingLocked();
-        }
-
         // Get the activity record.
-        if (isInStackLocked(token) != null) {
-            res = r;
-
-            if (fromTimeout) {
-                mStackSupervisor.reportActivityLaunchedLocked(fromTimeout, r, -1, -1);
-            }
-
-            // This is a hack to semi-deal with a race condition
-            // in the client where it can be constructed with a
-            // newer configuration from when we asked it to launch.
-            // We'll update with whatever configuration it now says
-            // it used to launch.
-            if (config != null) {
-                r.configuration = config;
-            }
-
+        res = isInStackLocked(token);
+        if (res != null) {
             // No longer need to keep the device awake.
-            if (mResumedActivity == r && mLaunchingActivity.isHeld()) {
+            if (mResumedActivity == res && mLaunchingActivity.isHeld()) {
                 mHandler.removeMessages(LAUNCH_TIMEOUT_MSG);
                 mLaunchingActivity.release();
             }
 
-            // We are now idle.  If someone is waiting for a thumbnail from
-            // us, we can now deliver.
-            r.idle = true;
-            if (mStackSupervisor.allResumedActivitiesIdle()) {
-                mService.scheduleAppGcsLocked();
-            }
-            if (r.thumbnailNeeded && r.app != null && r.app.thread != null) {
-                sendThumbnail = r.app.thread;
-                r.thumbnailNeeded = false;
-            }
-
             // If this activity is fullscreen, set up to hide those under it.
-
-            if (DEBUG_VISBILITY) Slog.v(TAG, "Idle activity for " + r);
+            if (DEBUG_VISBILITY) Slog.v(TAG, "Idle activity for " + res);
             ensureActivitiesVisibleLocked(null, 0);
-
-            //Slog.i(TAG, "IDLE: mBooted=" + mBooted + ", fromTimeout=" + fromTimeout);
-            if (!mService.mBooted && mStackSupervisor.isFrontStack(this)) {
-                mService.mBooted = true;
-                enableScreen = true;
-            }
-        } else if (fromTimeout) {
-            mStackSupervisor.reportActivityLaunchedLocked(fromTimeout, null, -1, -1);
-        }
-
-        // Atomically retrieve all of the other things to do.
-        stops = mStackSupervisor.processStoppingActivitiesLocked(true);
-        NS = stops != null ? stops.size() : 0;
-        if ((NF=mFinishingActivities.size()) > 0) {
-            finishes = new ArrayList<ActivityRecord>(mFinishingActivities);
-            mFinishingActivities.clear();
-        }
-
-        final ArrayList<ActivityRecord> thumbnails;
-        final int NT = mCancelledThumbnails.size();
-        if (NT > 0) {
-            thumbnails = new ArrayList<ActivityRecord>(mCancelledThumbnails);
-            mCancelledThumbnails.clear();
-        } else {
-            thumbnails = null;
-        }
-
-        if (mStackSupervisor.isFrontStack(this)) {
-            booting = mService.mBooting;
-            mService.mBooting = false;
-        }
-
-        if (mStartingUsers.size() > 0) {
-            startingUsers = new ArrayList<UserStartedState>(mStartingUsers);
-            mStartingUsers.clear();
-        }
-
-        // Perform the following actions from unsynchronized state.
-        final IApplicationThread thumbnailThread = sendThumbnail;
-        mHandler.post(new Runnable() {
-            @Override
-            public void run() {
-                if (thumbnailThread != null) {
-                    try {
-                        thumbnailThread.requestThumbnail(token);
-                    } catch (Exception e) {
-                        Slog.w(TAG, "Exception thrown when requesting thumbnail", e);
-                        mService.sendPendingThumbnail(null, token, null, null, true);
-                    }
-                }
-
-                // Report back to any thumbnail receivers.
-                for (int i = 0; i < NT; i++) {
-                    ActivityRecord r = thumbnails.get(i);
-                    mService.sendPendingThumbnail(r, null, null, null, true);
-                }
-            }
-        });
-
-        // Stop any activities that are scheduled to do so but have been
-        // waiting for the next one to start.
-        for (int i = 0; i < NS; i++) {
-            r = stops.get(i);
-            if (r.finishing) {
-                finishCurrentActivityLocked(r, FINISH_IMMEDIATELY, false);
-            } else {
-                r.task.stack.stopActivityLocked(r);
-            }
-        }
-
-        // Finish any activities that are scheduled to do so but have been
-        // waiting for the next one to start.
-        for (int i = 0; i < NF; i++) {
-            r = finishes.get(i);
-            activityRemoved |= r.task.stack.destroyActivityLocked(r, true, false, "finish-idle");
-        }
-
-        if (booting) {
-            mService.finishBooting();
-        } else if (startingUsers != null) {
-            for (int i = 0; i < startingUsers.size(); i++) {
-                mService.finishUserSwitch(startingUsers.get(i));
-            }
-        }
-
-        mService.trimApplications();
-        //dump();
-        //mWindowManager.dump();
-
-        if (enableScreen) {
-            mService.enableScreenAfterBoot();
-        }
-
-        if (activityRemoved) {
-            mStackSupervisor.getFocusedStack().resumeTopActivityLocked(null);
         }
 
         return res;
@@ -2501,8 +2312,8 @@
      * @return Returns true if this activity has been removed from the history
      * list, or false if it is still in the list and will be removed later.
      */
-    final boolean finishActivityLocked(ActivityRecord r, int resultCode,
-            Intent resultData, String reason, boolean oomAdj) {
+    final boolean finishActivityLocked(ActivityRecord r, int resultCode, Intent resultData,
+            String reason, boolean oomAdj) {
         if (r.finishing) {
             Slog.w(TAG, "Duplicate finish request for " + r);
             return false;
@@ -2541,7 +2352,7 @@
             // There are clients waiting to receive thumbnails so, in case
             // this is an activity that someone is waiting for, add it
             // to the pending list so we can correctly update the clients.
-            mCancelledThumbnails.add(r);
+            mStackSupervisor.mCancelledThumbnails.add(r);
         }
 
         if (mResumedActivity == r) {
@@ -2573,12 +2384,11 @@
         return false;
     }
 
-    private static final int FINISH_IMMEDIATELY = 0;
-    private static final int FINISH_AFTER_PAUSE = 1;
-    private static final int FINISH_AFTER_VISIBLE = 2;
+    static final int FINISH_IMMEDIATELY = 0;
+    static final int FINISH_AFTER_PAUSE = 1;
+    static final int FINISH_AFTER_VISIBLE = 2;
 
-    private final ActivityRecord finishCurrentActivityLocked(ActivityRecord r,
-            int mode, boolean oomAdj) {
+    final ActivityRecord finishCurrentActivityLocked(ActivityRecord r, int mode, boolean oomAdj) {
         // First things first: if this activity is currently visible,
         // and the resumed activity is not yet visible, then hold off on
         // finishing until the resumed one becomes visible.
@@ -2591,7 +2401,7 @@
                     // then give up on things going idle and start clearing
                     // them out. Or if r is the last of activity of the last task the stack
                     // will be empty and must be cleared immediately.
-                    scheduleIdleLocked();
+                    mStackSupervisor.scheduleIdleLocked();
                 } else {
                     checkReadyForSleepLocked();
                 }
@@ -2632,7 +2442,7 @@
         // Need to go through the full pause cycle to get this
         // activity into the stopped state and then finish it.
         if (localLOGV) Slog.v(TAG, "Enqueueing pending finish: " + r);
-        mFinishingActivities.add(r);
+        mStackSupervisor.mFinishingActivities.add(r);
         mStackSupervisor.getFocusedStack().resumeTopActivityLocked(null);
         return r;
     }
@@ -2745,7 +2555,7 @@
         // Make sure this record is no longer in the pending finishes list.
         // This could happen, for example, if we are trimming activities
         // down to the max limit while they are still waiting to finish.
-        mFinishingActivities.remove(r);
+        mStackSupervisor.mFinishingActivities.remove(r);
         mStackSupervisor.mWaitingVisibleActivities.remove(r);
 
         // Remove any pending results.
@@ -2767,7 +2577,7 @@
             // There are clients waiting to receive thumbnails so, in case
             // this is an activity that someone is waiting for, add it
             // to the pending list so we can correctly update the clients.
-            mCancelledThumbnails.add(r);
+            mStackSupervisor.mCancelledThumbnails.add(r);
         }
 
         // Get rid of any pending idle timeouts.
@@ -2775,9 +2585,9 @@
     }
 
     private void removeTimeoutsForActivityLocked(ActivityRecord r) {
+        mStackSupervisor.removeTimeoutsForActivityLocked(r);
         mHandler.removeMessages(PAUSE_TIMEOUT_MSG, r);
         mHandler.removeMessages(STOP_TIMEOUT_MSG, r);
-        mHandler.removeMessages(IDLE_TIMEOUT_MSG, r);
         mHandler.removeMessages(DESTROY_TIMEOUT_MSG, r);
         r.finishLaunchTickingLocked();
     }
@@ -3014,7 +2824,8 @@
         removeHistoryRecordsForAppLocked(mGoingToSleepActivities, app, "mGoingToSleepActivities");
         removeHistoryRecordsForAppLocked(mStackSupervisor.mWaitingVisibleActivities, app,
                 "mWaitingVisibleActivities");
-        removeHistoryRecordsForAppLocked(mFinishingActivities, app, "mFinishingActivities");
+        removeHistoryRecordsForAppLocked(mStackSupervisor.mFinishingActivities, app,
+                "mFinishingActivities");
 
         boolean hasVisibleActivities = false;
 
diff --git a/services/java/com/android/server/am/ActivityStackSupervisor.java b/services/java/com/android/server/am/ActivityStackSupervisor.java
index be3e260..af358c2 100644
--- a/services/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/java/com/android/server/am/ActivityStackSupervisor.java
@@ -85,6 +85,12 @@
 
     public static final int HOME_STACK_ID = 0;
 
+    /** How long we wait until giving up on the last activity telling us it is idle. */
+    static final int IDLE_TIMEOUT = 10*1000;
+
+    static final int IDLE_TIMEOUT_MSG = ActivityManagerService.FIRST_SUPERVISOR_STACK_MSG; 
+    static final int IDLE_NOW_MSG = ActivityManagerService.FIRST_SUPERVISOR_STACK_MSG + 1;
+
     final ActivityManagerService mService;
     final Context mContext;
     final Looper mLooper;
@@ -139,6 +145,17 @@
      * settle down before doing so. */
     final ArrayList<ActivityRecord> mStoppingActivities = new ArrayList<ActivityRecord>();
 
+    /** List of activities that are ready to be finished, but waiting for the previous activity to
+     * settle down before doing so.  It contains ActivityRecord objects. */
+    final ArrayList<ActivityRecord> mFinishingActivities = new ArrayList<ActivityRecord>();
+
+    /** List of ActivityRecord objects that have been finished and must still report back to a
+     * pending thumbnail receiver. */
+    final ArrayList<ActivityRecord> mCancelledThumbnails = new ArrayList<ActivityRecord>();
+
+    /** Used on user changes */
+    final ArrayList<UserStartedState> mStartingUsers = new ArrayList<UserStartedState>();
+
     /** Set to indicate whether to issue an onUserLeaving callback when a newly launched activity
      * is being brought in front of us. */
     boolean mUserLeaving = false;
@@ -1572,6 +1589,154 @@
         return ActivityManager.START_SUCCESS;
     }
 
+    // Checked.
+    final ActivityRecord activityIdleInternalLocked(final IBinder token, boolean fromTimeout,
+            Configuration config) {
+        if (localLOGV) Slog.v(TAG, "Activity idle: " + token);
+
+        ActivityRecord res = null;
+
+        ArrayList<ActivityRecord> stops = null;
+        ArrayList<ActivityRecord> finishes = null;
+        ArrayList<UserStartedState> startingUsers = null;
+        int NS = 0;
+        int NF = 0;
+        IApplicationThread sendThumbnail = null;
+        boolean booting = false;
+        boolean enableScreen = false;
+        boolean activityRemoved = false;
+
+        ActivityRecord r = ActivityRecord.forToken(token);
+        if (r != null) {
+            mHandler.removeMessages(IDLE_TIMEOUT_MSG, r);
+            r.finishLaunchTickingLocked();
+            res = r.task.stack.activityIdleInternalLocked(token, fromTimeout, config);
+            if (res != null) {
+                if (fromTimeout) {
+                    reportActivityLaunchedLocked(fromTimeout, r, -1, -1);
+                }
+
+                // This is a hack to semi-deal with a race condition
+                // in the client where it can be constructed with a
+                // newer configuration from when we asked it to launch.
+                // We'll update with whatever configuration it now says
+                // it used to launch.
+                if (config != null) {
+                    r.configuration = config;
+                }
+
+                // We are now idle.  If someone is waiting for a thumbnail from
+                // us, we can now deliver.
+                r.idle = true;
+                if (allResumedActivitiesIdle()) {
+                    mService.scheduleAppGcsLocked();
+                }
+                if (r.thumbnailNeeded && r.app != null && r.app.thread != null) {
+                    sendThumbnail = r.app.thread;
+                    r.thumbnailNeeded = false;
+                }
+    
+                //Slog.i(TAG, "IDLE: mBooted=" + mBooted + ", fromTimeout=" + fromTimeout);
+                if (!mService.mBooted && isFrontStack(r.task.stack)) {
+                    mService.mBooted = true;
+                    enableScreen = true;
+                }
+            } else if (fromTimeout) {
+                reportActivityLaunchedLocked(fromTimeout, null, -1, -1);
+            }
+        }
+
+        // Atomically retrieve all of the other things to do.
+        stops = processStoppingActivitiesLocked(true);
+        NS = stops != null ? stops.size() : 0;
+        if ((NF=mFinishingActivities.size()) > 0) {
+            finishes = new ArrayList<ActivityRecord>(mFinishingActivities);
+            mFinishingActivities.clear();
+        }
+
+        final ArrayList<ActivityRecord> thumbnails;
+        final int NT = mCancelledThumbnails.size();
+        if (NT > 0) {
+            thumbnails = new ArrayList<ActivityRecord>(mCancelledThumbnails);
+            mCancelledThumbnails.clear();
+        } else {
+            thumbnails = null;
+        }
+
+        if (isFrontStack(mHomeStack)) {
+            booting = mService.mBooting;
+            mService.mBooting = false;
+        }
+
+        if (mStartingUsers.size() > 0) {
+            startingUsers = new ArrayList<UserStartedState>(mStartingUsers);
+            mStartingUsers.clear();
+        }
+
+        // Perform the following actions from unsynchronized state.
+        final IApplicationThread thumbnailThread = sendThumbnail;
+        mHandler.post(new Runnable() {
+            @Override
+            public void run() {
+                if (thumbnailThread != null) {
+                    try {
+                        thumbnailThread.requestThumbnail(token);
+                    } catch (Exception e) {
+                        Slog.w(TAG, "Exception thrown when requesting thumbnail", e);
+                        mService.sendPendingThumbnail(null, token, null, null, true);
+                    }
+                }
+
+                // Report back to any thumbnail receivers.
+                for (int i = 0; i < NT; i++) {
+                    ActivityRecord r = thumbnails.get(i);
+                    mService.sendPendingThumbnail(r, null, null, null, true);
+                }
+            }
+        });
+
+        // Stop any activities that are scheduled to do so but have been
+        // waiting for the next one to start.
+        for (int i = 0; i < NS; i++) {
+            r = stops.get(i);
+            final ActivityStack stack = r.task.stack;
+            if (r.finishing) {
+                stack.finishCurrentActivityLocked(r, ActivityStack.FINISH_IMMEDIATELY, false);
+            } else {
+                stack.stopActivityLocked(r);
+            }
+        }
+
+        // Finish any activities that are scheduled to do so but have been
+        // waiting for the next one to start.
+        for (int i = 0; i < NF; i++) {
+            r = finishes.get(i);
+            activityRemoved |= r.task.stack.destroyActivityLocked(r, true, false, "finish-idle");
+        }
+
+        if (booting) {
+            mService.finishBooting();
+        } else if (startingUsers != null) {
+            for (int i = 0; i < startingUsers.size(); i++) {
+                mService.finishUserSwitch(startingUsers.get(i));
+            }
+        }
+
+        mService.trimApplications();
+        //dump();
+        //mWindowManager.dump();
+
+        if (enableScreen) {
+            mService.enableScreenAfterBoot();
+        }
+
+        if (activityRemoved) {
+            getFocusedStack().resumeTopActivityLocked(null);
+        }
+
+        return res;
+    }
+
     void handleAppDiedLocked(ProcessRecord app, boolean restarting) {
         // Just in case.
         final int numStacks = mStacks.size();
@@ -1620,12 +1785,6 @@
         }
     }
 
-    void scheduleIdleLocked() {
-        for (int stackNdx = mStacks.size() - 1; stackNdx >= 0; --stackNdx) {
-            mStacks.get(stackNdx).scheduleIdleLocked();
-        }
-    }
-
     void findTaskToMoveToFrontLocked(int taskId, int flags, Bundle options) {
         for (int stackNdx = mStacks.size() - 1; stackNdx >= 0; --stackNdx) {
             if (mStacks.get(stackNdx).findTaskToMoveToFrontLocked(taskId, flags, options)) {
@@ -1772,12 +1931,12 @@
 
     boolean switchUserLocked(int userId, UserStartedState uss) {
         mCurrentUser = userId;
-        boolean homeInBack = !homeIsInFront();
+        mStartingUsers.add(uss);
         boolean haveActivities = false;
         final int numStacks = mStacks.size();
         for (int stackNdx = 0; stackNdx < numStacks; ++stackNdx) {
             final ActivityStack stack = mStacks.get(stackNdx);
-            if (stack.isHomeStack() ^ homeInBack) {
+            if (isFrontStack(stack)) {
                 haveActivities |= stack.switchUserLocked(userId, uss);
             }
         }
@@ -1850,12 +2009,6 @@
                 dumpHistoryList(fd, pw, stack.mGoingToSleepActivities, "  ", "Sleep", false,
                         !dumpAll, false, dumpPackage);
             }
-            if (stack.mFinishingActivities.size() > 0) {
-                pw.println(" ");
-                pw.println("  Activities waiting to finish:");
-                dumpHistoryList(fd, pw, stack.mFinishingActivities, "  ", "Fin", false,
-                        !dumpAll, false, dumpPackage);
-            }
 
             pw.print("  Stack #"); pw.println(mStacks.indexOf(stack));
             if (stack.mPausingActivity != null) {
@@ -1868,6 +2021,13 @@
             }
         }
 
+        if (mFinishingActivities.size() > 0) {
+            pw.println(" ");
+            pw.println("  Activities waiting to finish:");
+            dumpHistoryList(fd, pw, mFinishingActivities, "  ", "Fin", false, !dumpAll, false,
+                    dumpPackage);
+        }
+
         if (mStoppingActivities.size() > 0) {
             pw.println(" ");
             pw.println("  Activities waiting to stop:");
@@ -1958,15 +2118,48 @@
         }
     }
 
+    void scheduleIdleTimeoutLocked(ActivityRecord next) {
+        mHandler.obtainMessage(IDLE_TIMEOUT_MSG, next).sendToTarget();
+    }
+
+    final void scheduleIdleLocked() {
+        mHandler.obtainMessage(IDLE_NOW_MSG).sendToTarget();
+    }
+
+    void removeTimeoutsForActivityLocked(ActivityRecord r) {
+        mHandler.removeMessages(IDLE_TIMEOUT_MSG, r);
+    }
+
     private final class ActivityStackSupervisorHandler extends Handler {
+
         public ActivityStackSupervisorHandler(Looper looper) {
             super(looper);
         }
 
+        void activityIdleInternal(ActivityRecord r) {
+            synchronized (mService) {
+                activityIdleInternalLocked(r != null ? r.appToken : null, true, null);
+            }
+        }    
+            
         @Override
         public void handleMessage(Message msg) {
             switch (msg.what) {
-                
+                case IDLE_TIMEOUT_MSG: {
+                    if (mService.mDidDexOpt) {
+                        mService.mDidDexOpt = false;
+                        Message nmsg = mHandler.obtainMessage(IDLE_TIMEOUT_MSG);
+                        nmsg.obj = msg.obj;
+                        mHandler.sendMessageDelayed(nmsg, IDLE_TIMEOUT);
+                        return;
+                    }
+                    // We don't at this point know if the activity is fullscreen,
+                    // so we need to be conservative and assume it isn't.
+                    activityIdleInternal((ActivityRecord)msg.obj);
+                } break;
+                case IDLE_NOW_MSG: {
+                    activityIdleInternal((ActivityRecord)msg.obj);
+                } break;
             }
         }
     }