Merge "Add implementations for saveLayerAlpha() and textured rects."
diff --git a/api/current.xml b/api/current.xml
index 3cf3df6..8d89bdc 100644
--- a/api/current.xml
+++ b/api/current.xml
@@ -23711,6 +23711,18 @@
 <parameter name="context" type="android.content.Context">
 </parameter>
 </constructor>
+<constructor name="AlertDialog.Builder"
+ type="android.app.AlertDialog.Builder"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="context" type="android.content.Context">
+</parameter>
+<parameter name="theme" type="int">
+</parameter>
+</constructor>
 <method name="create"
  return="android.app.AlertDialog"
  abstract="false"
diff --git a/core/java/android/app/AlertDialog.java b/core/java/android/app/AlertDialog.java
index 2714de5..61a8fc3 100644
--- a/core/java/android/app/AlertDialog.java
+++ b/core/java/android/app/AlertDialog.java
@@ -265,12 +265,22 @@
     
     public static class Builder {
         private final AlertController.AlertParams P;
+        private int mTheme;
         
         /**
          * Constructor using a context for this builder and the {@link AlertDialog} it creates.
          */
         public Builder(Context context) {
+            this(context, com.android.internal.R.style.Theme_Dialog_Alert);
+        }
+
+        /**
+         * Constructor using a context and theme for this builder and
+         * the {@link AlertDialog} it creates.
+         */
+        public Builder(Context context, int theme) {
             P = new AlertController.AlertParams(context);
+            mTheme = theme;
         }
         
         /**
@@ -783,7 +793,7 @@
          * to do and want this to be created and displayed.
          */
         public AlertDialog create() {
-            final AlertDialog dialog = new AlertDialog(P.mContext);
+            final AlertDialog dialog = new AlertDialog(P.mContext, mTheme);
             P.apply(dialog.mAlert);
             dialog.setCancelable(P.mCancelable);
             dialog.setOnCancelListener(P.mOnCancelListener);
diff --git a/core/java/android/database/AbstractCursor.java b/core/java/android/database/AbstractCursor.java
index b1f33ec..80eb18b 100644
--- a/core/java/android/database/AbstractCursor.java
+++ b/core/java/android/database/AbstractCursor.java
@@ -187,7 +187,7 @@
      * @param window
      */
     public void fillWindow(int position, CursorWindow window) {
-        if (position < 0 || position > getCount()) {
+        if (position < 0 || position >= getCount()) {
             return;
         }
         window.acquireReference();
diff --git a/core/java/android/text/format/DateUtils.java b/core/java/android/text/format/DateUtils.java
index dde0889..89b3cba 100644
--- a/core/java/android/text/format/DateUtils.java
+++ b/core/java/android/text/format/DateUtils.java
@@ -496,7 +496,7 @@
                 }
             }
         } else if (duration < WEEK_IN_MILLIS && minResolution < WEEK_IN_MILLIS) {
-            count = duration / DAY_IN_MILLIS;
+            count = getNumberOfDaysPassed(time, now);
             if (past) {
                 if (abbrevRelative) {
                     resId = com.android.internal.R.plurals.abbrev_num_days_ago;
@@ -521,6 +521,24 @@
     }
     
     /**
+     * Returns the number of days passed between two dates.
+     *
+     * @param date1 first date
+     * @param date2 second date
+     * @return number of days passed between to dates.
+     */
+    private synchronized static long getNumberOfDaysPassed(long date1, long date2) {
+        if (sThenTime == null) {
+            sThenTime = new Time();
+        }
+        sThenTime.set(date1);
+        int day1 = Time.getJulianDay(date1, sThenTime.gmtoff);
+        sThenTime.set(date2);
+        int day2 = Time.getJulianDay(date2, sThenTime.gmtoff);
+        return Math.abs(day2 - day1);
+    }
+
+    /**
      * Return string describing the elapsed time since startTime formatted like
      * "[relative time/date], [time]".
      * <p>
@@ -1550,40 +1568,45 @@
     public static CharSequence getRelativeTimeSpanString(Context c, long millis,
             boolean withPreposition) {
 
+        String result;
         long now = System.currentTimeMillis();
         long span = now - millis;
 
-        if (sNowTime == null) {
-            sNowTime = new Time();
-            sThenTime = new Time();
-        }
+        synchronized (DateUtils.class) {
+            if (sNowTime == null) {
+                sNowTime = new Time();
+            }
 
-        sNowTime.set(now);
-        sThenTime.set(millis);
+            if (sThenTime == null) {
+                sThenTime = new Time();
+            }
 
-        String result;
-        int prepositionId;
-        if (span < DAY_IN_MILLIS && sNowTime.weekDay == sThenTime.weekDay) {
-            // Same day
-            int flags = FORMAT_SHOW_TIME;
-            result = formatDateRange(c, millis, millis, flags);
-            prepositionId = R.string.preposition_for_time;
-        } else if (sNowTime.year != sThenTime.year) {
-            // Different years
-            int flags = FORMAT_SHOW_DATE | FORMAT_SHOW_YEAR | FORMAT_NUMERIC_DATE;
-            result = formatDateRange(c, millis, millis, flags);
+            sNowTime.set(now);
+            sThenTime.set(millis);
 
-            // This is a date (like "10/31/2008" so use the date preposition)
-            prepositionId = R.string.preposition_for_date;
-        } else {
-            // Default
-            int flags = FORMAT_SHOW_DATE | FORMAT_ABBREV_MONTH;
-            result = formatDateRange(c, millis, millis, flags);
-            prepositionId = R.string.preposition_for_date;
-        }
-        if (withPreposition) {
-            Resources res = c.getResources();
-            result = res.getString(prepositionId, result);
+            int prepositionId;
+            if (span < DAY_IN_MILLIS && sNowTime.weekDay == sThenTime.weekDay) {
+                // Same day
+                int flags = FORMAT_SHOW_TIME;
+                result = formatDateRange(c, millis, millis, flags);
+                prepositionId = R.string.preposition_for_time;
+            } else if (sNowTime.year != sThenTime.year) {
+                // Different years
+                int flags = FORMAT_SHOW_DATE | FORMAT_SHOW_YEAR | FORMAT_NUMERIC_DATE;
+                result = formatDateRange(c, millis, millis, flags);
+
+                // This is a date (like "10/31/2008" so use the date preposition)
+                prepositionId = R.string.preposition_for_date;
+            } else {
+                // Default
+                int flags = FORMAT_SHOW_DATE | FORMAT_ABBREV_MONTH;
+                result = formatDateRange(c, millis, millis, flags);
+                prepositionId = R.string.preposition_for_date;
+            }
+            if (withPreposition) {
+                Resources res = c.getResources();
+                result = res.getString(prepositionId, result);
+            }
         }
         return result;
     }
diff --git a/core/res/res/drawable-hdpi/stat_sys_phone_call_ringing.png b/core/res/res/drawable-hdpi/stat_sys_phone_call_ringing.png
new file mode 100644
index 0000000..950713b
--- /dev/null
+++ b/core/res/res/drawable-hdpi/stat_sys_phone_call_ringing.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/stat_sys_phone_call_ringing.png b/core/res/res/drawable-mdpi/stat_sys_phone_call_ringing.png
new file mode 100644
index 0000000..c44d062
--- /dev/null
+++ b/core/res/res/drawable-mdpi/stat_sys_phone_call_ringing.png
Binary files differ
diff --git a/include/media/AudioCommon.h b/include/media/AudioCommon.h
deleted file mode 100644
index 245d760..0000000
--- a/include/media/AudioCommon.h
+++ /dev/null
@@ -1,80 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ANDROID_AUDIOCOMMON_H_
-#define ANDROID_AUDIOCOMMON_H_
-
-#if __cplusplus
-extern "C" {
-#endif
-
-/////////////////////////////////////////////////
-//      Common definitions for PCM audio
-/////////////////////////////////////////////////
-
-
-// PCM Sample format
-enum audio_format_e {
-    PCM_FORMAT_S15 = 1,     // PCM signed 16 bits, must be 1 for backward compatibility
-    PCM_FORMAT_U8 = 2,      // PCM unsigned 8 bits, must be 2 for backward compatibility
-    PCM_FORMAT_S7_24        // signed 7.24 fixed point representation
-};
-
-// Channel mask definitions
-enum audio_channels_e {
-    CHANNEL_FRONT_LEFT = 0x4,                   // front left channel
-    CHANNEL_FRONT_RIGHT = 0x8,                  // front right channel
-    CHANNEL_FRONT_CENTER = 0x10,                // front center channel
-    CHANNEL_LOW_FREQUENCY = 0x20,               // low frequency channel
-    CHANNEL_BACK_LEFT = 0x40,                   // back left channel
-    CHANNEL_BACK_RIGHT = 0x80,                  // back right channel
-    CHANNEL_FRONT_LEFT_OF_CENTER = 0x100,       // front left of center channel
-    CHANNEL_FRONT_RIGHT_OF_CENTER = 0x200,      // front right of center channel
-    CHANNEL_BACK_CENTER = 0x400,                // back center channel
-    CHANNEL_MONO = CHANNEL_FRONT_LEFT,
-    CHANNEL_STEREO = (CHANNEL_FRONT_LEFT | CHANNEL_FRONT_RIGHT),
-    CHANNEL_QUAD = (CHANNEL_FRONT_LEFT | CHANNEL_FRONT_RIGHT |
-            CHANNEL_BACK_LEFT | CHANNEL_BACK_RIGHT),
-    CHANNEL_SURROUND = (CHANNEL_FRONT_LEFT | CHANNEL_FRONT_RIGHT |
-            CHANNEL_FRONT_CENTER | CHANNEL_BACK_CENTER),
-    CHANNEL_5POINT1 = (CHANNEL_FRONT_LEFT | CHANNEL_FRONT_RIGHT |
-            CHANNEL_FRONT_CENTER | CHANNEL_LOW_FREQUENCY | CHANNEL_BACK_LEFT | CHANNEL_BACK_RIGHT),
-    CHANNEL_7POINT1 = (CHANNEL_FRONT_LEFT | CHANNEL_FRONT_RIGHT |
-            CHANNEL_FRONT_CENTER | CHANNEL_LOW_FREQUENCY | CHANNEL_BACK_LEFT | CHANNEL_BACK_RIGHT |
-            CHANNEL_FRONT_LEFT_OF_CENTER | CHANNEL_FRONT_RIGHT_OF_CENTER),
-};
-
-// Render device definitions
-enum audio_device_e {
-    DEVICE_EARPIECE = 0x1,                      // earpiece
-    DEVICE_SPEAKER = 0x2,                       // speaker
-    DEVICE_WIRED_HEADSET = 0x4,                 // wired headset, with microphone
-    DEVICE_WIRED_HEADPHONE = 0x8,               // wired headphone, without microphone
-    DEVICE_BLUETOOTH_SCO = 0x10,                // generic bluetooth SCO
-    DEVICE_BLUETOOTH_SCO_HEADSET = 0x20,        // bluetooth SCO headset
-    DEVICE_BLUETOOTH_SCO_CARKIT = 0x40,         // bluetooth SCO car kit
-    DEVICE_BLUETOOTH_A2DP = 0x80,               // generic bluetooth A2DP
-    DEVICE_BLUETOOTH_A2DP_HEADPHONES = 0x100,   // bluetooth A2DP headphones
-    DEVICE_BLUETOOTH_A2DP_SPEAKER = 0x200,      // bluetooth A2DP speakers
-    DEVICE_AUX_DIGITAL = 0x400                  // digital output
-};
-
-#if __cplusplus
-}  // extern "C"
-#endif
-
-
-#endif /*ANDROID_AUDIOCOMMON_H_*/
diff --git a/include/media/AudioEffect.h b/include/media/AudioEffect.h
index 2bdba2d..66670f3 100644
--- a/include/media/AudioEffect.h
+++ b/include/media/AudioEffect.h
@@ -93,14 +93,14 @@
 
     /*
      * Returns the number of effects available. This method together
-     * with EffectQueryNext() is used to enumerate all effects:
+     * with queryEffect() is used to enumerate all effects:
      * The enumeration sequence is:
-     *      QueryNumberEffects(&num_effects);
-     *      while (num_effects--)
-     *          QueryNextEffect();
+     *      queryNumberEffects(&num_effects);
+     *      for (i = 0; i < num_effects; i++)
+     *          queryEffect(i,...);
      *
      * Parameters:
-     *      pNumEffects:    address where the number of effects should be returned.
+     *      numEffects:    address where the number of effects should be returned.
      *
      * Returned status (from utils/Errors.h) can be:
      *      NO_ERROR   successful operation.
@@ -114,24 +114,24 @@
     static status_t queryNumberEffects(uint32_t *numEffects);
 
     /*
-     * Returns number effect descriptor during effect
+     * Returns an effect descriptor during effect
      * enumeration.
      *
      * Parameters:
-     *      pDescriptor:    address where the effect descriptor should be returned.
+     *      index:      index of the queried effect.
+     *      descriptor: address where the effect descriptor should be returned.
      *
      * Returned status (from utils/Errors.h) can be:
      *      NO_ERROR        successful operation.
-     *      NAME_NOT_FOUND  no more effect available
      *      PERMISSION_DENIED could not get AudioFlinger interface
      *      NO_INIT         effect library failed to initialize
-     *      BAD_VALUE       invalid descriptor pointer
+     *      BAD_VALUE       invalid descriptor pointer or index
      *      INVALID_OPERATION  effect list has changed since last execution of queryNumberEffects()
      *
      * Returned value
      *   *descriptor:     updated with effect descriptor
      */
-    static status_t queryNextEffect(effect_descriptor_t *descriptor);
+    static status_t queryEffect(uint32_t index, effect_descriptor_t *descriptor);
 
 
     /*
diff --git a/include/media/AudioSystem.h b/include/media/AudioSystem.h
index f21e83d..194f23a 100644
--- a/include/media/AudioSystem.h
+++ b/include/media/AudioSystem.h
@@ -20,7 +20,6 @@
 #include <utils/RefBase.h>
 #include <utils/threads.h>
 #include <media/IAudioFlinger.h>
-#include <media/AudioCommon.h>
 
 namespace android {
 
@@ -51,8 +50,8 @@
 
     // Audio sub formats (see AudioSystem::audio_format).
     enum pcm_sub_format {
-        PCM_SUB_16_BIT          = PCM_FORMAT_S15, // must be 1 for backward compatibility
-        PCM_SUB_8_BIT           = PCM_FORMAT_U8, // must be 2 for backward compatibility
+        PCM_SUB_16_BIT          = 0x1, // must be 1 for backward compatibility
+        PCM_SUB_8_BIT           = 0x2, // must be 2 for backward compatibility
     };
 
     // MP3 sub format field definition : can use 11 LSBs in the same way as MP3 frame header to specify
@@ -104,21 +103,26 @@
     // Channel mask definitions must be kept in sync with JAVA values in /media/java/android/media/AudioFormat.java
     enum audio_channels {
         // output channels
-        CHANNEL_OUT_FRONT_LEFT = CHANNEL_FRONT_LEFT,
-        CHANNEL_OUT_FRONT_RIGHT = CHANNEL_FRONT_RIGHT,
-        CHANNEL_OUT_FRONT_CENTER = CHANNEL_FRONT_CENTER,
-        CHANNEL_OUT_LOW_FREQUENCY = CHANNEL_LOW_FREQUENCY,
-        CHANNEL_OUT_BACK_LEFT = CHANNEL_BACK_LEFT,
-        CHANNEL_OUT_BACK_RIGHT = CHANNEL_BACK_RIGHT,
-        CHANNEL_OUT_FRONT_LEFT_OF_CENTER = CHANNEL_FRONT_LEFT_OF_CENTER,
-        CHANNEL_OUT_FRONT_RIGHT_OF_CENTER = CHANNEL_FRONT_RIGHT_OF_CENTER,
-        CHANNEL_OUT_BACK_CENTER = CHANNEL_BACK_CENTER,
-        CHANNEL_OUT_MONO = CHANNEL_MONO,
-        CHANNEL_OUT_STEREO = CHANNEL_STEREO,
-        CHANNEL_OUT_QUAD = CHANNEL_QUAD,
-        CHANNEL_OUT_SURROUND = CHANNEL_SURROUND,
-        CHANNEL_OUT_5POINT1 = CHANNEL_5POINT1,
-        CHANNEL_OUT_7POINT1 = CHANNEL_7POINT1,
+        CHANNEL_OUT_FRONT_LEFT = 0x4,
+        CHANNEL_OUT_FRONT_RIGHT = 0x8,
+        CHANNEL_OUT_FRONT_CENTER = 0x10,
+        CHANNEL_OUT_LOW_FREQUENCY = 0x20,
+        CHANNEL_OUT_BACK_LEFT = 0x40,
+        CHANNEL_OUT_BACK_RIGHT = 0x80,
+        CHANNEL_OUT_FRONT_LEFT_OF_CENTER = 0x100,
+        CHANNEL_OUT_FRONT_RIGHT_OF_CENTER = 0x200,
+        CHANNEL_OUT_BACK_CENTER = 0x400,
+        CHANNEL_OUT_MONO = CHANNEL_OUT_FRONT_LEFT,
+        CHANNEL_OUT_STEREO = (CHANNEL_OUT_FRONT_LEFT | CHANNEL_OUT_FRONT_RIGHT),
+        CHANNEL_OUT_QUAD = (CHANNEL_OUT_FRONT_LEFT | CHANNEL_OUT_FRONT_RIGHT |
+                CHANNEL_OUT_BACK_LEFT | CHANNEL_OUT_BACK_RIGHT),
+        CHANNEL_OUT_SURROUND = (CHANNEL_OUT_FRONT_LEFT | CHANNEL_OUT_FRONT_RIGHT |
+                CHANNEL_OUT_FRONT_CENTER | CHANNEL_OUT_BACK_CENTER),
+        CHANNEL_OUT_5POINT1 = (CHANNEL_OUT_FRONT_LEFT | CHANNEL_OUT_FRONT_RIGHT |
+                CHANNEL_OUT_FRONT_CENTER | CHANNEL_OUT_LOW_FREQUENCY | CHANNEL_OUT_BACK_LEFT | CHANNEL_OUT_BACK_RIGHT),
+        CHANNEL_OUT_7POINT1 = (CHANNEL_OUT_FRONT_LEFT | CHANNEL_OUT_FRONT_RIGHT |
+                CHANNEL_OUT_FRONT_CENTER | CHANNEL_OUT_LOW_FREQUENCY | CHANNEL_OUT_BACK_LEFT | CHANNEL_OUT_BACK_RIGHT |
+                CHANNEL_OUT_FRONT_LEFT_OF_CENTER | CHANNEL_OUT_FRONT_RIGHT_OF_CENTER),
         CHANNEL_OUT_ALL = (CHANNEL_OUT_FRONT_LEFT | CHANNEL_OUT_FRONT_RIGHT |
                 CHANNEL_OUT_FRONT_CENTER | CHANNEL_OUT_LOW_FREQUENCY | CHANNEL_OUT_BACK_LEFT | CHANNEL_OUT_BACK_RIGHT |
                 CHANNEL_OUT_FRONT_LEFT_OF_CENTER | CHANNEL_OUT_FRONT_RIGHT_OF_CENTER | CHANNEL_OUT_BACK_CENTER),
@@ -238,17 +242,17 @@
 
     enum audio_devices {
         // output devices
-        DEVICE_OUT_EARPIECE = DEVICE_EARPIECE,
-        DEVICE_OUT_SPEAKER = DEVICE_SPEAKER,
-        DEVICE_OUT_WIRED_HEADSET = DEVICE_WIRED_HEADSET,
-        DEVICE_OUT_WIRED_HEADPHONE = DEVICE_WIRED_HEADPHONE,
-        DEVICE_OUT_BLUETOOTH_SCO = DEVICE_BLUETOOTH_SCO,
-        DEVICE_OUT_BLUETOOTH_SCO_HEADSET = DEVICE_BLUETOOTH_SCO_HEADSET,
-        DEVICE_OUT_BLUETOOTH_SCO_CARKIT = DEVICE_BLUETOOTH_SCO_CARKIT,
-        DEVICE_OUT_BLUETOOTH_A2DP = DEVICE_BLUETOOTH_A2DP,
-        DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES = DEVICE_BLUETOOTH_A2DP_HEADPHONES,
-        DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER = DEVICE_BLUETOOTH_A2DP_SPEAKER,
-        DEVICE_OUT_AUX_DIGITAL = DEVICE_AUX_DIGITAL,
+        DEVICE_OUT_EARPIECE = 0x1,
+        DEVICE_OUT_SPEAKER = 0x2,
+        DEVICE_OUT_WIRED_HEADSET = 0x4,
+        DEVICE_OUT_WIRED_HEADPHONE = 0x8,
+        DEVICE_OUT_BLUETOOTH_SCO = 0x10,
+        DEVICE_OUT_BLUETOOTH_SCO_HEADSET = 0x20,
+        DEVICE_OUT_BLUETOOTH_SCO_CARKIT = 0x40,
+        DEVICE_OUT_BLUETOOTH_A2DP = 0x80,
+        DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES = 0x100,
+        DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER = 0x200,
+        DEVICE_OUT_AUX_DIGITAL = 0x400,
         DEVICE_OUT_DEFAULT = 0x8000,
         DEVICE_OUT_ALL = (DEVICE_OUT_EARPIECE | DEVICE_OUT_SPEAKER | DEVICE_OUT_WIRED_HEADSET |
                 DEVICE_OUT_WIRED_HEADPHONE | DEVICE_OUT_BLUETOOTH_SCO | DEVICE_OUT_BLUETOOTH_SCO_HEADSET |
diff --git a/include/media/EffectApi.h b/include/media/EffectApi.h
index 97874f7..b4d738c 100644
--- a/include/media/EffectApi.h
+++ b/include/media/EffectApi.h
@@ -20,7 +20,6 @@
 #include <errno.h>
 #include <stdint.h>
 #include <sys/types.h>
-#include <media/AudioCommon.h>
 
 #if __cplusplus
 extern "C" {
@@ -65,10 +64,11 @@
 //--- Effect descriptor structure effect_descriptor_t
 //
 
-// Unique effect ID (can be generated from the following site: http://www.itu.int/ITU-T/asn1/uuid.html)
+// Unique effect ID (can be generated from the following site:
+//  http://www.itu.int/ITU-T/asn1/uuid.html)
 // This format is used for both "type" and "uuid" fields of the effect descriptor structure.
-// - When used for effect type and the engine is implementing and effect corresponding to a standard OpenSL ES
-// interface, this ID must be the one defined in OpenSLES_IID.h for that interface.
+// - When used for effect type and the engine is implementing and effect corresponding to a standard
+// OpenSL ES interface, this ID must be the one defined in OpenSLES_IID.h for that interface.
 // - When used as uuid, it should be a unique UUID for this particular implementation.
 typedef struct effect_uuid_s {
     uint32_t timeLow;
@@ -79,23 +79,32 @@
 } effect_uuid_t;
 
 // NULL UUID definition (matches SL_IID_NULL_)
-#define EFFECT_UUID_INITIALIZER { 0xec7178ec, 0xe5e1, 0x4432, 0xa3f4, { 0x46, 0x57, 0xe6, 0x79, 0x52, 0x10 } }
+#define EFFECT_UUID_INITIALIZER { 0xec7178ec, 0xe5e1, 0x4432, 0xa3f4, \
+                                  { 0x46, 0x57, 0xe6, 0x79, 0x52, 0x10 } }
 static const effect_uuid_t EFFECT_UUID_NULL_ = EFFECT_UUID_INITIALIZER;
 const effect_uuid_t * const EFFECT_UUID_NULL = &EFFECT_UUID_NULL_;
 const char * const EFFECT_UUID_NULL_STR = "ec7178ec-e5e1-4432-a3f4-4657e6795210";
 
-// the effect descriptor contains necessary information to facilitate the enumeration of the effect
+// The effect descriptor contains necessary information to facilitate the enumeration of the effect
 // engines present in a library.
 typedef struct effect_descriptor_s {
-    effect_uuid_t type;     // UUID corresponding to the OpenSL ES interface implemented by this effect
+    effect_uuid_t type;     // UUID of to the OpenSL ES interface implemented by this effect
     effect_uuid_t uuid;     // UUID for this particular implementation
-    uint16_t apiVersion;    // Version of the effect API implemented: must match current EFFECT_API_VERSION
+    uint16_t apiVersion;    // Version of the effect API implemented: matches EFFECT_API_VERSION
     uint32_t flags;         // effect engine capabilities/requirements flags (see below)
-    char    name[EFFECT_STRING_LEN_MAX] ;   // human readable effect name
-    char    implementor[EFFECT_STRING_LEN_MAX] ;    // human readable effect implementor name
+    uint16_t cpuLoad;       // CPU load indication (see below)
+    uint16_t memoryUsage;   // Data Memory usage (see below)
+    char    name[EFFECT_STRING_LEN_MAX];   // human readable effect name
+    char    implementor[EFFECT_STRING_LEN_MAX];    // human readable effect implementor name
 } effect_descriptor_t;
 
-// definitions for flags field of effect descriptor.
+// CPU load and memory usage indication: each effect implementation must provide an indication of
+// its CPU and memory usage for the audio effect framework to limit the number of effects
+// instantiated at a given time on a given platform.
+// The CPU load is expressed in 0.1 MIPS units as estimated on an ARM9E core (ARMv5TE) with 0 WS.
+// The memory usage is expressed in KB and includes only dynamically allocated memory
+
+// Definitions for flags field of effect descriptor.
 //  +---------------------------+-----------+-----------------------------------
 //  | description               | bits      | values
 //  +---------------------------+-----------+-----------------------------------
@@ -117,7 +126,7 @@
 //  |                           |           | 2 requires volume indication
 //  |                           |           | 3 reserved
 //  +---------------------------+-----------+-----------------------------------
-//  | Device management         | 7..8      | 0 none
+//  | Device indication         | 7..8      | 0 none
 //  |                           |           | 1 requires device updates
 //  |                           |           | 2..3 reserved
 //  +---------------------------+-----------+-----------------------------------
@@ -125,7 +134,8 @@
 //  |                           |           |   command must specify a buffer descriptor
 //  |                           |           | 1 provider: process() function uses the
 //  |                           |           |   bufferProvider indicated by the
-//  |                           |           |   EFFECT_CMD_CONFIGURE command to request input buffers.
+//  |                           |           |   EFFECT_CMD_CONFIGURE command to request input.
+//  |                           |           |   buffers.
 //  |                           |           | 2 both: both input modes are supported
 //  |                           |           | 3 reserved
 //  +---------------------------+-----------+-----------------------------------
@@ -133,18 +143,34 @@
 //  |                           |           |   command must specify a buffer descriptor
 //  |                           |           | 1 provider: process() function uses the
 //  |                           |           |   bufferProvider indicated by the
-//  |                           |           |   EFFECT_CMD_CONFIGURE command to request output buffers.
+//  |                           |           |   EFFECT_CMD_CONFIGURE command to request output
+//  |                           |           |   buffers.
 //  |                           |           | 2 both: both output modes are supported
 //  |                           |           | 3 reserved
 //  +---------------------------+-----------+-----------------------------------
+//  | Hardware acceleration     | 13..15    | 0 No hardware acceleration
+//  |                           |           | 1 non tunneled hw acceleration: the process() function
+//  |                           |           |   reads the samples, send them to HW accelerated
+//  |                           |           |   effect processor, reads back the processed samples
+//  |                           |           |   and returns them to the output buffer.
+//  |                           |           | 2 tunneled hw acceleration: the process() function is
+//  |                           |           |   transparent. The effect interface is only used to
+//  |                           |           |   control the effect engine. This mode is relevant for
+//  |                           |           |   global effects actually applied by the audio
+//  |                           |           |   hardware on the output stream.
+//  +---------------------------+-----------+-----------------------------------
+//  | Audio Mode indication     | 16..17    | 0 none
+//  |                           |           | 1 requires audio mode updates
+//  |                           |           | 2..3 reserved
+//  +---------------------------+-----------+-----------------------------------
 
-// insert mode
+// Insert mode
 #define EFFECT_FLAG_TYPE_MASK           0x00000003
 #define EFFECT_FLAG_TYPE_INSERT         0x00000000
 #define EFFECT_FLAG_TYPE_AUXILIARY      0x00000001
 #define EFFECT_FLAG_TYPE_REPLACE        0x00000002
 
-// insert preference
+// Insert preference
 #define EFFECT_FLAG_INSERT_MASK         0x0000001C
 #define EFFECT_FLAG_INSERT_ANY          0x00000000
 #define EFFECT_FLAG_INSERT_FIRST        0x00000004
@@ -152,30 +178,40 @@
 #define EFFECT_FLAG_INSERT_EXCLUSIVE    0x0000000C
 
 
-// volume control
+// Volume control
 #define EFFECT_FLAG_VOLUME_MASK         0x00000060
 #define EFFECT_FLAG_VOLUME_CTRL         0x00000020
 #define EFFECT_FLAG_VOLUME_IND          0x00000040
 #define EFFECT_FLAG_VOLUME_NONE         0x00000000
 
-// device control
+// Device indication
 #define EFFECT_FLAG_DEVICE_MASK         0x00000180
 #define EFFECT_FLAG_DEVICE_IND          0x00000080
 #define EFFECT_FLAG_DEVICE_NONE         0x00000000
 
-// sample input modes
+// Sample input modes
 #define EFFECT_FLAG_INPUT_MASK          0x00000600
 #define EFFECT_FLAG_INPUT_DIRECT        0x00000000
 #define EFFECT_FLAG_INPUT_PROVIDER      0x00000200
 #define EFFECT_FLAG_INPUT_BOTH          0x00000400
 
-// sample output modes
+// Sample output modes
 #define EFFECT_FLAG_OUTPUT_MASK          0x00001800
 #define EFFECT_FLAG_OUTPUT_DIRECT        0x00000000
 #define EFFECT_FLAG_OUTPUT_PROVIDER      0x00000800
 #define EFFECT_FLAG_OUTPUT_BOTH          0x00001000
 
-// forward definition of type audio_buffer_t
+// Hardware acceleration mode
+#define EFFECT_FLAG_HW_ACC_MASK          0x00006000
+#define EFFECT_FLAG_HW_ACC_SIMPLE        0x00002000
+#define EFFECT_FLAG_HW_ACC_TUNNEL        0x00004000
+
+// Audio mode indication
+#define EFFECT_FLAG_AUDIO_MODE_MASK      0x00018000
+#define EFFECT_FLAG_AUDIO_MODE_IND       0x00008000
+#define EFFECT_FLAG_AUDIO_MODE_NONE      0x00000000
+
+// Forward definition of type audio_buffer_t
 typedef struct audio_buffer_s audio_buffer_t;
 
 ////////////////////////////////////////////////////////////////////////////////
@@ -206,7 +242,9 @@
 //                          -EINVAL invalid interface handle or
 //                                  invalid input/output buffer description
 ////////////////////////////////////////////////////////////////////////////////
-typedef int32_t (*effect_process_t)(effect_interface_t self, audio_buffer_t *inBuffer, audio_buffer_t *outBuffer);
+typedef int32_t (*effect_process_t)(effect_interface_t self,
+                                    audio_buffer_t *inBuffer,
+                                    audio_buffer_t *outBuffer);
 
 ////////////////////////////////////////////////////////////////////////////////
 //
@@ -238,7 +276,12 @@
 //          *pReplyData updated with command response
 //
 ////////////////////////////////////////////////////////////////////////////////
-typedef int32_t (*effect_command_t)(effect_interface_t self, int32_t cmdCode, int32_t cmdSize, void *pCmdData, int32_t *replySize, void *pReplyData);
+typedef int32_t (*effect_command_t)(effect_interface_t self,
+                                    int32_t cmdCode,
+                                    int32_t cmdSize,
+                                    void *pCmdData,
+                                    int32_t *replySize,
+                                    void *pReplyData);
 
 
 // Effect control interface definition
@@ -248,75 +291,9 @@
 };
 
 
-//--- Standardized command codes for command function
-//  +--------------------------------+-------------------------------+-------------------------------+--------------------------
-//  | description                    | command code                  | command format                | reply format
-//  +--------------------------------+-------------------------------+-------------------------------+--------------------------
-//  | Initialize effect engine:      | EFFECT_CMD_INIT               | size: 0                       | size: sizeof(int)
-//  |  All configurations return to  |                               | data: N/A                     | data: status
-//  |  default                       |                               |                               |
-//  +--------------------------------+-------------------------------+-------------------------------+--------------------------
-//  | Apply new audio parameters     | EFFECT_CMD_CONFIGURE          | size: sizeof(effect_config_t) | size: sizeof(int)
-//  | configurations for input and   |                               | data: effect_config_t         | data: status
-//  | output buffers                 |                               |                               |
-//  +--------------------------------+-------------------------------+-------------------------------+--------------------------
-//  | Reset effect engine: keep      | EFFECT_CMD_RESET              | size: 0                       | size: 0
-//  | configuration but reset state  |                               | data: N/A                     | data: N/A
-//  | and buffer content.            |                               |                               |
-//  | Called by the framework before |                               |                               |
-//  | enabling the effect            |                               |                               |
-//  +--------------------------------+-------------------------------+-------------------------------+--------------------------
-//  | Enable the process             | EFFECT_CMD_ENABLE             | size: 0                       | size: sizeof(int)
-//  | Called by the framework before |                               | data: N/A                     | data: status
-//  | the first call to process()    |                               |                               |
-//  +--------------------------------+-------------------------------+-------------------------------+--------------------------
-//  | Disable the process            | EFFECT_CMD_DISABLE            | size: 0                       | size: sizeof(int)
-//  | Called by the framework after  |                               | data: N/A                     | data: status
-//  | the last call to process()     |                               |                               |
-//  +--------------------------------+-------------------------------+-------------------------------+--------------------------
-//  | Set a parameter and apply it   | EFFECT_CMD_SET_PARAM          | size: sizeof(effect_param_t)  | size: sizeof(int)
-//  | immediately                    |                               |       + size of param + value | data: status
-//  |                                |                               | data: effect_param_t          |
-//  +--------------------------------+-------------------------------+-------------------------------+--------------------------
-//  | Set a parameter but apply it   | EFFECT_CMD_SET_PARAM_DEFERRED | size: sizeof(effect_param_t)  | size: 0
-//  | only when receiving command    |                               |       + size of param + value | data: N/A
-//  | EFFECT_CMD_SET_PARAM_COMMIT    |                               | data: effect_param_t          |
-//  +--------------------------------+-------------------------------+-------------------------------+--------------------------
-//  | Apply all previously received  | EFFECT_CMD_SET_PARAM_COMMIT   | size: 0                       | size: sizeof(int)
-//  | EFFECT_CMD_SET_PARAM_DEFERRED  |                               | data: N/A                     | data: status
-//  | commands                       |                               |                               |
-//  +--------------------------------+-------------------------------+-------------------------------+--------------------------
-//  | Get a parameter value          | EFFECT_CMD_GET_PARAM          | size: sizeof(effect_param_t)  | size: sizeof(effect_param_t)
-//  |                                |                               |       + size of param         |       + size of param + value
-//  |                                |                               | data: effect_param_t          | data: effect_param_t
-//  +--------------------------------+-------------------------------+-------------------------------+--------------------------
-//  | Set the rendering device the   | EFFECT_CMD_SET_DEVICE         | size: sizeof(uint32_t)        | size: 0
-//  | audio output path is connected |                               | data: audio_device_e          | data: N/A
-//  | to. See audio_device_e in      |                               |                               |
-//  | AudioCommon.h for device values|                               |                               |
-//  +--------------------------------+-------------------------------+-------------------------------+--------------------------
-//  | Set and get volume. Used by    | EFFECT_CMD_SET_VOLUME         | size: n * sizeof(uint32_t)    | size: n * sizeof(uint32_t)
-//  | audio framework to delegate    |                               | data: volume for each channel | data: volume for each channel
-//  | volume control to effect engine|                               | defined in effect_config_t in | defined in effect_config_t in
-//  | If volume control flag is set  |                               | 8.24 fixed point format       | 8.24 fixed point format
-//  | in the effect descriptor, the  |                               |                               | It is legal to receive a null
-//  | effect engine must return the  |                               |                               | pointer as pReplyData in which
-//  | volume that should be applied  |                               |                               | case the effect framework has
-//  | before the effect is processed |                               |                               | delegated volume control to
-//  | The overall volume (the volume |                               |                               | another effect.
-//  | actually applied by the effect |                               |                               |
-//  | multiplied by the returned     |                               |                               |
-//  | value) should match the        |                               |                               |
-//  | requested value                |                               |                               |
-//  +--------------------------------+-------------------------------+-------------------------------+--------------------------
-//  | All proprietary effect commands| EFFECT_CMD_FIRST_PROPRIETARY  |                               |
-//  | must use command codes above   |                               |                               |
-//  | this value. The size and format|                               |                               |
-//  | of command and response fields |                               |                               |
-//  | is free in this case.          |                               |                               |
-//  +--------------------------------+-------------------------------+-------------------------------+--------------------------
-
-
+//
+//--- Standardized command codes for command() function
+//
 enum effect_command_e {
    EFFECT_CMD_INIT,                 // initialize effect engine
    EFFECT_CMD_CONFIGURE,            // configure effect engine (see effect_config_t)
@@ -327,18 +304,202 @@
    EFFECT_CMD_SET_PARAM_DEFERRED,   // set parameter deferred
    EFFECT_CMD_SET_PARAM_COMMIT,     // commit previous set parameter deferred
    EFFECT_CMD_GET_PARAM,            // get parameter
-   EFFECT_CMD_SET_DEVICE,           // set audio device (see audio_device_e in AudioCommon.h)
+   EFFECT_CMD_SET_DEVICE,           // set audio device (see audio_device_e)
    EFFECT_CMD_SET_VOLUME,           // set volume
+   EFFECT_CMD_SET_AUDIO_MODE,       // set the audio mode (normal, ring, ...)
    EFFECT_CMD_FIRST_PROPRIETARY = 0x10000 // first proprietary command code
 };
 
-// Audio buffer descriptor used by process(), bufferProvider() functions and buffer_config_t structure
-// Multi-channel audio is always interleaved. The channel order is from LSB to MSB with regard to the
-// channel mask definition in audio_channels_e (AudioCommon.h) e.g :
+//==================================================================================================
+// command: EFFECT_CMD_INIT
+//--------------------------------------------------------------------------------------------------
+// description:
+//  Initialize effect engine: All configurations return to default
+//--------------------------------------------------------------------------------------------------
+// command format:
+//  size: 0
+//  data: N/A
+//--------------------------------------------------------------------------------------------------
+// reply format:
+//  size: sizeof(int)
+//  data: status
+//==================================================================================================
+// command: EFFECT_CMD_CONFIGURE
+//--------------------------------------------------------------------------------------------------
+// description:
+//  Apply new audio parameters configurations for input and output buffers
+//--------------------------------------------------------------------------------------------------
+// command format:
+//  size: sizeof(effect_config_t)
+//  data: effect_config_t
+//--------------------------------------------------------------------------------------------------
+// reply format:
+//  size: sizeof(int)
+//  data: status
+//==================================================================================================
+// command: EFFECT_CMD_RESET
+//--------------------------------------------------------------------------------------------------
+// description:
+//  Reset the effect engine. Keep configuration but resets state and buffer content
+//--------------------------------------------------------------------------------------------------
+// command format:
+//  size: 0
+//  data: N/A
+//--------------------------------------------------------------------------------------------------
+// reply format:
+//  size: 0
+//  data: N/A
+//==================================================================================================
+// command: EFFECT_CMD_ENABLE
+//--------------------------------------------------------------------------------------------------
+// description:
+//  Enable the process. Called by the framework before the first call to process()
+//--------------------------------------------------------------------------------------------------
+// command format:
+//  size: 0
+//  data: N/A
+//--------------------------------------------------------------------------------------------------
+// reply format:
+//  size: sizeof(int)
+//  data: status
+//==================================================================================================
+// command: EFFECT_CMD_DISABLE
+//--------------------------------------------------------------------------------------------------
+// description:
+//  Disable the process. Called by the framework after the last call to process()
+//--------------------------------------------------------------------------------------------------
+// command format:
+//  size: 0
+//  data: N/A
+//--------------------------------------------------------------------------------------------------
+// reply format:
+//  size: sizeof(int)
+//  data: status
+//==================================================================================================
+// command: EFFECT_CMD_SET_PARAM
+//--------------------------------------------------------------------------------------------------
+// description:
+//  Set a parameter and apply it immediately
+//--------------------------------------------------------------------------------------------------
+// command format:
+//  size: sizeof(effect_param_t) + size of param and value
+//  data: effect_param_t + param + value. See effect_param_t definition below for value offset
+//--------------------------------------------------------------------------------------------------
+// reply format:
+//  size: sizeof(int)
+//  data: status
+//==================================================================================================
+// command: EFFECT_CMD_SET_PARAM_DEFERRED
+//--------------------------------------------------------------------------------------------------
+// description:
+//  Set a parameter but apply it only when receiving EFFECT_CMD_SET_PARAM_COMMIT command
+//--------------------------------------------------------------------------------------------------
+// command format:
+//  size: sizeof(effect_param_t) + size of param and value
+//  data: effect_param_t + param + value. See effect_param_t definition below for value offset
+//--------------------------------------------------------------------------------------------------
+// reply format:
+//  size: 0
+//  data: N/A
+//==================================================================================================
+// command: EFFECT_CMD_SET_PARAM_COMMIT
+//--------------------------------------------------------------------------------------------------
+// description:
+//  Apply all previously received EFFECT_CMD_SET_PARAM_DEFERRED commands
+//--------------------------------------------------------------------------------------------------
+// command format:
+//  size: 0
+//  data: N/A
+//--------------------------------------------------------------------------------------------------
+// reply format:
+//  size: sizeof(int)
+//  data: status
+//==================================================================================================
+// command: EFFECT_CMD_GET_PARAM
+//--------------------------------------------------------------------------------------------------
+// description:
+//  Get a parameter value
+//--------------------------------------------------------------------------------------------------
+// command format:
+//  size: sizeof(effect_param_t) + size of param
+//  data: effect_param_t + param
+//--------------------------------------------------------------------------------------------------
+// reply format:
+//  size: sizeof(effect_param_t) + size of param and value
+//  data: effect_param_t + param + value. See effect_param_t definition below for value offset
+//==================================================================================================
+// command: EFFECT_CMD_SET_DEVICE
+//--------------------------------------------------------------------------------------------------
+// description:
+//  Set the rendering device the audio output path is connected to. See audio_device_e for device
+//  values.
+//  The effect implementation must set EFFECT_FLAG_DEVICE_IND flag in its descriptor to receive this
+//  command when the device changes
+//--------------------------------------------------------------------------------------------------
+// command format:
+//  size: sizeof(uint32_t)
+//  data: audio_device_e
+//--------------------------------------------------------------------------------------------------
+// reply format:
+//  size: 0
+//  data: N/A
+//==================================================================================================
+// command: EFFECT_CMD_SET_VOLUME
+//--------------------------------------------------------------------------------------------------
+// description:
+//  Set and get volume. Used by audio framework to delegate volume control to effect engine.
+//  The effect implementation must set EFFECT_FLAG_VOLUME_IND and/or EFFECT_FLAG_VOLUME_CTRL flag in
+//  its descriptor to receive this command before every call to process() function
+//  If EFFECT_FLAG_VOLUME_CTRL flag is set in the effect descriptor, the effect engine must return
+//  the volume that should be applied before the effect is processed. The overall volume (the volume
+//  actually applied by the effect engine multiplied by the returned value) should match the value
+//  indicated in the command.
+//--------------------------------------------------------------------------------------------------
+// command format:
+//  size: n * sizeof(uint32_t)
+//  data: volume for each channel defined in effect_config_t for output buffer expressed in
+//      8.24 fixed point format
+//--------------------------------------------------------------------------------------------------
+// reply format:
+//  size: n * sizeof(uint32_t) / 0
+//  data: - if EFFECT_FLAG_VOLUME_CTRL is set in effect descriptor:
+//              volume for each channel defined in effect_config_t for output buffer expressed in
+//              8.24 fixed point format
+//        - if EFFECT_FLAG_VOLUME_CTRL is not set in effect descriptor:
+//              N/A
+//  It is legal to receive a null pointer as pReplyData in which case the effect framework has
+//  delegated volume control to another effect
+//==================================================================================================
+// command: EFFECT_CMD_SET_AUDIO_MODE
+//--------------------------------------------------------------------------------------------------
+// description:
+//  Set the audio mode. The effect implementation must set EFFECT_FLAG_AUDIO_MODE_IND flag in its
+//  descriptor to receive this command when the audio mode changes.
+//--------------------------------------------------------------------------------------------------
+// command format:
+//  size: sizeof(uint32_t)
+//  data: audio_mode_e
+//--------------------------------------------------------------------------------------------------
+// reply format:
+//  size: 0
+//  data: N/A
+//==================================================================================================
+// command: EFFECT_CMD_FIRST_PROPRIETARY
+//--------------------------------------------------------------------------------------------------
+// description:
+//  All proprietary effect commands must use command codes above this value. The size and format of
+//  command and response fields is free in this case
+//==================================================================================================
+
+
+// Audio buffer descriptor used by process(), bufferProvider() functions and buffer_config_t
+// structure. Multi-channel audio is always interleaved. The channel order is from LSB to MSB with
+// regard to the channel mask definition in audio_channels_e e.g :
 // Stereo: left, right
 // 5 point 1: front left, front right, front center, low frequency, back left, back right
 // The buffer size is expressed in frame count, a frame being composed of samples for all
-// channels at a given time
+// channels at a given time. Frame size for unspecified format (AUDIO_FORMAT_OTHER) is 8 bit by
+// definition
 struct audio_buffer_s {
     size_t   frameCount;        // number of frames in buffer
     union {
@@ -349,7 +510,7 @@
     };
 };
 
-// the buffer_provider_s structure contains functions that can be used
+// The buffer_provider_s structure contains functions that can be used
 // by the effect engine process() function to query and release input
 // or output audio buffer.
 // The getBuffer() function is called to retrieve a buffer where data
@@ -369,6 +530,7 @@
     void       *cookie;                // for use by client of buffer provider functions
 } buffer_provider_t;
 
+
 // The buffer_config_s structure specifies the input or output audio format
 // to be used by the effect engine. It is part of the effect_config_t
 // structure that defines both input and output buffer configurations and is
@@ -376,14 +538,69 @@
 typedef struct buffer_config_s {
     audio_buffer_t  buffer;     // buffer for use by process() function if not passed explicitly
     uint32_t   samplingRate;    // sampling rate
-    uint32_t   channels;        // channel mask (see audio_channels_e in AudioCommon.h)
+    uint32_t   channels;        // channel mask (see audio_channels_e)
     buffer_provider_t bufferProvider;   // buffer provider
-    uint8_t    format;          // PCM format  (see audio_format_e in AudioCommon.h)
+    uint8_t    format;          // Audio format  (see audio_format_e)
     uint8_t    accessMode;      // read/write or accumulate in buffer (effect_buffer_access_e)
     uint16_t   mask;            // indicates which of the above fields is valid
 } buffer_config_t;
 
-// values for "accessMode" field of buffer_config_t:
+// Sample format
+enum audio_format_e {
+    SAMPLE_FORMAT_PCM_S15,   // PCM signed 16 bits
+    SAMPLE_FORMAT_PCM_U8,    // PCM unsigned 8 bits
+    SAMPLE_FORMAT_PCM_S7_24, // PCM signed 7.24 fixed point representation
+    SAMPLE_FORMAT_OTHER      // other format (e.g. compressed)
+};
+
+// Channel mask
+enum audio_channels_e {
+    CHANNEL_FRONT_LEFT = 0x1,                   // front left channel
+    CHANNEL_FRONT_RIGHT = 0x2,                  // front right channel
+    CHANNEL_FRONT_CENTER = 0x4,                // front center channel
+    CHANNEL_LOW_FREQUENCY = 0x8,               // low frequency channel
+    CHANNEL_BACK_LEFT = 0x10,                   // back left channel
+    CHANNEL_BACK_RIGHT = 0x20,                  // back right channel
+    CHANNEL_FRONT_LEFT_OF_CENTER = 0x40,       // front left of center channel
+    CHANNEL_FRONT_RIGHT_OF_CENTER = 0x80,      // front right of center channel
+    CHANNEL_BACK_CENTER = 0x100,                // back center channel
+    CHANNEL_MONO = CHANNEL_FRONT_LEFT,
+    CHANNEL_STEREO = (CHANNEL_FRONT_LEFT | CHANNEL_FRONT_RIGHT),
+    CHANNEL_QUAD = (CHANNEL_FRONT_LEFT | CHANNEL_FRONT_RIGHT |
+            CHANNEL_BACK_LEFT | CHANNEL_BACK_RIGHT),
+    CHANNEL_SURROUND = (CHANNEL_FRONT_LEFT | CHANNEL_FRONT_RIGHT |
+            CHANNEL_FRONT_CENTER | CHANNEL_BACK_CENTER),
+    CHANNEL_5POINT1 = (CHANNEL_FRONT_LEFT | CHANNEL_FRONT_RIGHT |
+            CHANNEL_FRONT_CENTER | CHANNEL_LOW_FREQUENCY | CHANNEL_BACK_LEFT | CHANNEL_BACK_RIGHT),
+    CHANNEL_7POINT1 = (CHANNEL_FRONT_LEFT | CHANNEL_FRONT_RIGHT |
+            CHANNEL_FRONT_CENTER | CHANNEL_LOW_FREQUENCY | CHANNEL_BACK_LEFT | CHANNEL_BACK_RIGHT |
+            CHANNEL_FRONT_LEFT_OF_CENTER | CHANNEL_FRONT_RIGHT_OF_CENTER),
+};
+
+// Render device
+enum audio_device_e {
+    DEVICE_EARPIECE = 0x1,                      // earpiece
+    DEVICE_SPEAKER = 0x2,                       // speaker
+    DEVICE_WIRED_HEADSET = 0x4,                 // wired headset, with microphone
+    DEVICE_WIRED_HEADPHONE = 0x8,               // wired headphone, without microphone
+    DEVICE_BLUETOOTH_SCO = 0x10,                // generic bluetooth SCO
+    DEVICE_BLUETOOTH_SCO_HEADSET = 0x20,        // bluetooth SCO headset
+    DEVICE_BLUETOOTH_SCO_CARKIT = 0x40,         // bluetooth SCO car kit
+    DEVICE_BLUETOOTH_A2DP = 0x80,               // generic bluetooth A2DP
+    DEVICE_BLUETOOTH_A2DP_HEADPHONES = 0x100,   // bluetooth A2DP headphones
+    DEVICE_BLUETOOTH_A2DP_SPEAKER = 0x200,      // bluetooth A2DP speakers
+    DEVICE_AUX_DIGITAL = 0x400,                 // digital output
+    DEVICE_EXTERNAL_SPEAKER = 0x800             // external speaker (stereo and High quality)
+};
+
+// Audio mode
+enum audio_mode_e {
+    AUDIO_MODE_NORMAL,      // phone idle
+    AUDIO_MODE_RINGTONE,    // phone ringing
+    AUDIO_MODE_IN_CALL      // phone call connected
+};
+
+// Values for "accessMode" field of buffer_config_t:
 //   overwrite, read only, accumulate (read/modify/write)
 enum effect_buffer_access_e {
     EFFECT_BUFFER_ACCESS_WRITE,
@@ -392,7 +609,7 @@
 
 };
 
-// values for bit field "mask" in buffer_config_t. If a bit is set, the corresponding field
+// Values for bit field "mask" in buffer_config_t. If a bit is set, the corresponding field
 // in buffer_config_t must be taken into account when executing the EFFECT_CMD_CONFIGURE command
 #define EFFECT_CONFIG_BUFFER    0x0001  // buffer field must be taken into account
 #define EFFECT_CONFIG_SMP_RATE  0x0002  // samplingRate field must be taken into account
@@ -404,13 +621,15 @@
                            EFFECT_CONFIG_CHANNELS | EFFECT_CONFIG_FORMAT | \
                            EFFECT_CONFIG_ACC_MODE | EFFECT_CONFIG_PROVIDER)
 
-// effect_config_s structure describes the format of the pCmdData argument of EFFECT_CMD_CONFIGURE command
-// to configure audio parameters and buffers for effect engine input and output.
+
+// effect_config_s structure describes the format of the pCmdData argument of EFFECT_CMD_CONFIGURE
+// command to configure audio parameters and buffers for effect engine input and output.
 typedef struct effect_config_s {
     buffer_config_t   inputCfg;
     buffer_config_t   outputCfg;;
 } effect_config_t;
 
+
 // effect_param_s structure describes the format of the pCmdData argument of EFFECT_CMD_SET_PARAM
 // command and pCmdData and pReplyData of EFFECT_CMD_GET_PARAM command.
 // psize and vsize represent the actual size of parameter and value.
@@ -452,7 +671,7 @@
 // specified here as the effect framework will get the function address with dlsym():
 //
 // - effect_QueryNumberEffects_t EffectQueryNumberEffects;
-// - effect_QueryNextEffect_t EffectQueryNext;
+// - effect_QueryEffect_t EffectQueryEffect;
 // - effect_CreateEffect_t EffectCreate;
 // - effect_ReleaseEffect_t EffectRelease;
 
@@ -463,11 +682,8 @@
 //
 //    Description:    Returns the number of different effects exposed by the
 //          library. Each effect must have a unique effect uuid (see
-//          effect_descriptor_t). This function together with EffectQueryNext()
+//          effect_descriptor_t). This function together with EffectQueryEffect()
 //          is used to enumerate all effects present in the library.
-//          Each time EffectQueryNumberEffects() is called, the library must
-//          reset the index of the effect descriptor returned by next call to
-//          EffectQueryNext() to restart enumeration from the beginning.
 //
 //    Input/Output:
 //          pNumEffects:    address where the number of effects should be returned.
@@ -483,28 +699,33 @@
 
 ////////////////////////////////////////////////////////////////////////////////
 //
-//    Function:       EffectQueryNext
+//    Function:       EffectQueryEffect
 //
-//    Description:    Returns a descriptor of the next available effect.
+//    Description:    Returns the descriptor of the effect engine which index is
+//          given as first argument.
 //          See effect_descriptor_t for details on effect descriptors.
-//          This function together with EffectQueryNext() is used to enumerate all
+//          This function together with EffectQueryNumberEffects() is used to enumerate all
 //          effects present in the library. The enumeration sequence is:
 //              EffectQueryNumberEffects(&num_effects);
-//              while (num_effects--)
-//                  EffectQueryNext();
+//              for (i = 0; i < num_effects; i++)
+//                  EffectQueryEffect(i,...);
 //
 //    Input/Output:
+//          index:          index of the effect
 //          pDescriptor:    address where to return the effect descriptor.
 //
 //    Output:
 //        returned value:    0          successful operation.
 //                          -ENODEV     library failed to initialize
-//                          -EINVAL     invalid pDescriptor
+//                          -EINVAL     invalid pDescriptor or index
+//                          -ENOSYS     effect list has changed since last execution of
+//                                      EffectQueryNumberEffects()
 //                          -ENOENT     no more effect available
 //        *pDescriptor:     updated with the effect descriptor.
 //
 ////////////////////////////////////////////////////////////////////////////////
-typedef int32_t (*effect_QueryNextEffect_t)(effect_descriptor_t *pDescriptor);
+typedef int32_t (*effect_QueryEffect_t)(uint32_t index,
+                                        effect_descriptor_t *pDescriptor);
 
 ////////////////////////////////////////////////////////////////////////////////
 //
@@ -516,7 +737,13 @@
 //          a handle on the effect control interface.
 //
 //    Input:
-//          pEffectUuid:    pointer to the effect uuid.
+//          uuid:    pointer to the effect uuid.
+//          sessionId:  audio session to which this effect instance will be attached. All effects
+//              created with the same session ID are connected in series and process the same signal
+//              stream. Knowing that two effects are part of the same effect chain can help the
+//              library implement some kind of optimizations.
+//          ioId:   identifies the output or input stream this effect is directed to at audio HAL.
+//              For future use especially with tunneled HW accelerated effects
 //
 //    Input/Output:
 //          pInterface:    address where to return the effect interface.
@@ -529,7 +756,10 @@
 //        *pInterface:     updated with the effect interface handle.
 //
 ////////////////////////////////////////////////////////////////////////////////
-typedef int32_t (*effect_CreateEffect_t)(effect_uuid_t *uuid, effect_interface_t *pInterface);
+typedef int32_t (*effect_CreateEffect_t)(effect_uuid_t *uuid,
+                                         int32_t sessionId,
+                                         int32_t ioId,
+                                         effect_interface_t *pInterface);
 
 ////////////////////////////////////////////////////////////////////////////////
 //
diff --git a/include/media/EffectFactoryApi.h b/include/media/EffectsFactoryApi.h
similarity index 86%
rename from include/media/EffectFactoryApi.h
rename to include/media/EffectsFactoryApi.h
index 6cc9932..0ed1a14 100644
--- a/include/media/EffectFactoryApi.h
+++ b/include/media/EffectsFactoryApi.h
@@ -14,8 +14,8 @@
  * limitations under the License.
  */
 
-#ifndef ANDROID_EFFECTFACTORYAPI_H_
-#define ANDROID_EFFECTFACTORYAPI_H_
+#ifndef ANDROID_EFFECTSFACTORYAPI_H_
+#define ANDROID_EFFECTSFACTORYAPI_H_
 
 #include <errno.h>
 #include <stdint.h>
@@ -36,11 +36,11 @@
 //
 //    Description:    Returns the number of different effects in all loaded libraries.
 //          Each effect must have a different effect uuid (see
-//          effect_descriptor_t). This function together with EffectQueryNext()
+//          effect_descriptor_t). This function together with EffectQueryEffect()
 //          is used to enumerate all effects present in all loaded libraries.
 //          Each time EffectQueryNumberEffects() is called, the factory must
 //          reset the index of the effect descriptor returned by next call to
-//          EffectQueryNext() to restart enumeration from the beginning.
+//          EffectQueryEffect() to restart enumeration from the beginning.
 //
 //    Input/Output:
 //          pNumEffects:    address where the number of effects should be returned.
@@ -56,28 +56,29 @@
 
 ////////////////////////////////////////////////////////////////////////////////
 //
-//    Function:       EffectQueryNext
+//    Function:       EffectQueryEffect
 //
 //    Description:    Returns a descriptor of the next available effect.
 //          See effect_descriptor_t for a details on effect descriptor.
-//          This function together with EffectQueryNext() is used to enumerate all
+//          This function together with EffectQueryNumberEffects() is used to enumerate all
 //          effects present in all loaded libraries. The enumeration sequence is:
 //              EffectQueryNumberEffects(&num_effects);
-//              while (num_effects--)
-//                  EffectQueryNext();
+//              for (i = 0; i < num_effects; i++)
+//                  EffectQueryEffect(i,...);
 //
 //    Input/Output:
 //          pDescriptor:    address where to return the effect descriptor.
 //
 //    Output:
 //        returned value:    0          successful operation.
+//                          -ENOENT     no more effect available
 //                          -ENODEV     factory failed to initialize
 //                          -EINVAL     invalid pDescriptor
-//                          -ENOENT     no more effect available
+//                          -ENOSYS     effect list has changed since last execution of EffectQueryNumberEffects()
 //        *pDescriptor:     updated with the effect descriptor.
 //
 ////////////////////////////////////////////////////////////////////////////////
-int EffectQueryNext(effect_descriptor_t *pDescriptor);
+int EffectQueryEffect(uint32_t index, effect_descriptor_t *pDescriptor);
 
 ////////////////////////////////////////////////////////////////////////////////
 //
@@ -90,6 +91,12 @@
 //
 //    Input:
 //          pEffectUuid:    pointer to the effect uuid.
+//          sessionId:  audio session to which this effect instance will be attached. All effects created
+//              with the same session ID are connected in series and process the same signal stream.
+//              Knowing that two effects are part of the same effect chain can help the library implement
+//              some kind of optimizations.
+//          ioId:   identifies the output or input stream this effect is directed to at audio HAL. For future
+//              use especially with tunneled HW accelerated effects
 //
 //    Input/Output:
 //          pInterface:    address where to return the effect interface.
@@ -102,7 +109,7 @@
 //        *pInterface:     updated with the effect interface.
 //
 ////////////////////////////////////////////////////////////////////////////////
-int EffectCreate(effect_uuid_t *pEffectUuid, effect_interface_t *pInterface);
+int EffectCreate(effect_uuid_t *pEffectUuid, int32_t sessionId, int32_t ioId, effect_interface_t *pInterface);
 
 ////////////////////////////////////////////////////////////////////////////////
 //
@@ -211,4 +218,4 @@
 #endif
 
 
-#endif /*ANDROID_EFFECTFACTORYAPI_H_*/
+#endif /*ANDROID_EFFECTSFACTORYAPI_H_*/
diff --git a/include/media/IAudioFlinger.h b/include/media/IAudioFlinger.h
index ccfa530..5814fd6 100644
--- a/include/media/IAudioFlinger.h
+++ b/include/media/IAudioFlinger.h
@@ -148,7 +148,7 @@
 
     virtual status_t queryNumberEffects(uint32_t *numEffects) = 0;
 
-    virtual status_t queryNextEffect(effect_descriptor_t *pDescriptor) = 0;
+    virtual status_t queryEffect(uint32_t index, effect_descriptor_t *pDescriptor) = 0;
 
     virtual status_t getEffectDescriptor(effect_uuid_t *pEffectUUID, effect_descriptor_t *pDescriptor) = 0;
 
diff --git a/include/media/mediarecorder.h b/include/media/mediarecorder.h
index eead166..4f4ec43 100644
--- a/include/media/mediarecorder.h
+++ b/include/media/mediarecorder.h
@@ -136,7 +136,9 @@
     MEDIA_RECORDER_INFO_UNKNOWN                   = 1,
     MEDIA_RECORDER_INFO_MAX_DURATION_REACHED      = 800,
     MEDIA_RECORDER_INFO_MAX_FILESIZE_REACHED      = 801,
-    MEDIA_RECORDER_INFO_STOP_PREMATURELY          = 802
+    MEDIA_RECORDER_INFO_COMPLETION_STATUS         = 802,
+    MEDIA_RECORDER_INFO_PROGRESS_FRAME_STATUS     = 803,
+    MEDIA_RECORDER_INFO_PROGRESS_TIME_STATUS      = 804,
 };
 
 // ----------------------------------------------------------------------------
diff --git a/include/media/stagefright/AMRWriter.h b/include/media/stagefright/AMRWriter.h
index b0eaba4..813dd43 100644
--- a/include/media/stagefright/AMRWriter.h
+++ b/include/media/stagefright/AMRWriter.h
@@ -26,6 +26,7 @@
 namespace android {
 
 struct MediaSource;
+struct MetaData;
 
 struct AMRWriter : public MediaWriter {
     AMRWriter(const char *filename);
@@ -35,7 +36,7 @@
 
     virtual status_t addSource(const sp<MediaSource> &source);
     virtual bool reachedEOS();
-    virtual status_t start();
+    virtual status_t start(MetaData *params = NULL);
     virtual void stop();
     virtual void pause();
 
diff --git a/include/media/stagefright/AudioSource.h b/include/media/stagefright/AudioSource.h
index f2001e1..628200d 100644
--- a/include/media/stagefright/AudioSource.h
+++ b/include/media/stagefright/AudioSource.h
@@ -39,6 +39,9 @@
     virtual status_t stop();
     virtual sp<MetaData> getFormat();
 
+    // Returns the maximum amplitude since last call.
+    int16_t getMaxAmplitude();
+
     virtual status_t read(
             MediaBuffer **buffer, const ReadOptions *options = NULL);
 
@@ -53,13 +56,17 @@
     bool mStarted;
 
     bool mCollectStats;
+    bool mTrackMaxAmplitude;
     int64_t mTotalReadTimeUs;
     int64_t mTotalReadBytes;
     int64_t mTotalReads;
     int64_t mStartTimeUs;
+    int16_t mMaxAmplitude;
 
     MediaBufferGroup *mGroup;
 
+    void trackMaxAmplitude(int16_t *data, int nSamples);
+
     AudioSource(const AudioSource &);
     AudioSource &operator=(const AudioSource &);
 };
diff --git a/include/media/stagefright/MPEG4Writer.h b/include/media/stagefright/MPEG4Writer.h
index 39d0ea1..962b38b 100644
--- a/include/media/stagefright/MPEG4Writer.h
+++ b/include/media/stagefright/MPEG4Writer.h
@@ -36,7 +36,7 @@
     MPEG4Writer(int fd);
 
     virtual status_t addSource(const sp<MediaSource> &source);
-    virtual status_t start();
+    virtual status_t start(MetaData *param = NULL);
     virtual bool reachedEOS();
     virtual void stop();
     virtual void pause();
@@ -81,8 +81,9 @@
 
     void setStartTimestampUs(int64_t timeUs);
     int64_t getStartTimestampUs();  // Not const
-    status_t startTracks();
+    status_t startTracks(MetaData *params);
     size_t numTracks();
+    int64_t estimateMoovBoxSize(int32_t bitRate);
 
     void lock();
     void unlock();
diff --git a/include/media/stagefright/MediaWriter.h b/include/media/stagefright/MediaWriter.h
index 8528203..46aaf7c 100644
--- a/include/media/stagefright/MediaWriter.h
+++ b/include/media/stagefright/MediaWriter.h
@@ -24,13 +24,14 @@
 namespace android {
 
 struct MediaSource;
+struct MetaData;
 
 struct MediaWriter : public RefBase {
     MediaWriter() {}
 
     virtual status_t addSource(const sp<MediaSource> &source) = 0;
     virtual bool reachedEOS() = 0;
-    virtual status_t start() = 0;
+    virtual status_t start(MetaData *params = NULL) = 0;
     virtual void stop() = 0;
     virtual void pause() = 0;
     virtual void setMaxFileSize(int64_t bytes) { mMaxFileSizeLimitBytes = bytes; }
diff --git a/include/media/stagefright/MetaData.h b/include/media/stagefright/MetaData.h
index 6a20602..95fe6f6 100644
--- a/include/media/stagefright/MetaData.h
+++ b/include/media/stagefright/MetaData.h
@@ -36,7 +36,7 @@
     kKeyStride            = 'strd',  // int32_t
     kKeySliceHeight       = 'slht',  // int32_t
     kKeyChannelCount      = '#chn',  // int32_t
-    kKeySampleRate        = 'srte',  // int32_t
+    kKeySampleRate        = 'srte',  // int32_t (also video frame rate)
     kKeyBitRate           = 'brte',  // int32_t (bps)
     kKeyESDS              = 'esds',  // raw data
     kKeyAVCC              = 'avcc',  // raw data
@@ -68,6 +68,21 @@
     kKeyDiscNumber        = 'dnum',  // cstring
     kKeyDate              = 'date',  // cstring
     kKeyWriter            = 'writ',  // cstring
+
+    // Set this key to enable authoring files in 64-bit offset
+    kKey64BitFileOffset   = 'fobt',  // int32_t (bool)
+
+    // Identify the file output format for authoring
+    // Please see <media/mediarecorder.h> for the supported
+    // file output formats.
+    kKeyFileType          = 'ftyp',  // int32_t
+
+    // Track authoring progress status
+    // kKeyTrackTimeStatus is used to track progress in elapsed time
+    // kKeyTrackFrameStatus is used to track progress in authored frames
+    kKeyTrackFrameStatus  = 'tkfm',  // int32_t
+    kKeyTrackTimeStatus   = 'tktm',  // int64_t
+
 };
 
 enum {
diff --git a/include/utils/RefBase.h b/include/utils/RefBase.h
index bd7f28c..9c64ac0 100644
--- a/include/utils/RefBase.h
+++ b/include/utils/RefBase.h
@@ -333,9 +333,10 @@
 
 template<typename T>
 sp<T>& sp<T>::operator = (const sp<T>& other) {
-    if (other.m_ptr) other.m_ptr->incStrong(this);
+    T* otherPtr(other.m_ptr);
+    if (otherPtr) otherPtr->incStrong(this);
     if (m_ptr) m_ptr->decStrong(this);
-    m_ptr = other.m_ptr;
+    m_ptr = otherPtr;
     return *this;
 }
 
@@ -351,9 +352,10 @@
 template<typename T> template<typename U>
 sp<T>& sp<T>::operator = (const sp<U>& other)
 {
-    if (other.m_ptr) other.m_ptr->incStrong(this);
+    U* otherPtr(other.m_ptr);
+    if (otherPtr) otherPtr->incStrong(this);
     if (m_ptr) m_ptr->decStrong(this);
-    m_ptr = other.m_ptr;
+    m_ptr = otherPtr;
     return *this;
 }
 
@@ -466,10 +468,12 @@
 template<typename T>
 wp<T>& wp<T>::operator = (const wp<T>& other)
 {
-    if (other.m_ptr) other.m_refs->incWeak(this);
+    weakref_type* otherRefs(other.m_refs);
+    T* otherPtr(other.m_ptr);
+    if (otherPtr) otherRefs->incWeak(this);
     if (m_ptr) m_refs->decWeak(this);
-    m_ptr = other.m_ptr;
-    m_refs = other.m_refs;
+    m_ptr = otherPtr;
+    m_refs = otherRefs;
     return *this;
 }
 
@@ -478,8 +482,9 @@
 {
     weakref_type* newRefs =
         other != NULL ? other->createWeak(this) : 0;
+    T* otherPtr(other.m_ptr);
     if (m_ptr) m_refs->decWeak(this);
-    m_ptr = other.get();
+    m_ptr = otherPtr;
     m_refs = newRefs;
     return *this;
 }
@@ -498,10 +503,12 @@
 template<typename T> template<typename U>
 wp<T>& wp<T>::operator = (const wp<U>& other)
 {
-    if (other.m_ptr) other.m_refs->incWeak(this);
+    weakref_type* otherRefs(other.m_refs);
+    U* otherPtr(other.m_ptr);
+    if (otherPtr) otherRefs->incWeak(this);
     if (m_ptr) m_refs->decWeak(this);
-    m_ptr = other.m_ptr;
-    m_refs = other.m_refs;
+    m_ptr = otherPtr;
+    m_refs = otherRefs;
     return *this;
 }
 
@@ -510,8 +517,9 @@
 {
     weakref_type* newRefs =
         other != NULL ? other->createWeak(this) : 0;
+    U* otherPtr(other.m_ptr);
     if (m_ptr) m_refs->decWeak(this);
-    m_ptr = other.get();
+    m_ptr = otherPtr;
     m_refs = newRefs;
     return *this;
 }
diff --git a/libs/audioflinger/AudioFlinger.cpp b/libs/audioflinger/AudioFlinger.cpp
index 1860793..48c04a6 100644
--- a/libs/audioflinger/AudioFlinger.cpp
+++ b/libs/audioflinger/AudioFlinger.cpp
@@ -51,7 +51,7 @@
 #include "lifevibes.h"
 #endif
 
-#include <media/EffectFactoryApi.h>
+#include <media/EffectsFactoryApi.h>
 
 // ----------------------------------------------------------------------------
 // the sim build doesn't have gettid
@@ -126,7 +126,8 @@
 
 AudioFlinger::AudioFlinger()
     : BnAudioFlinger(),
-        mAudioHardware(0), mMasterVolume(1.0f), mMasterMute(false), mNextUniqueId(1)
+        mAudioHardware(0), mMasterVolume(1.0f), mMasterMute(false), mNextUniqueId(1),
+        mTotalEffectsCpuLoad(0), mTotalEffectsMemory(0)
 {
     mHardwareStatus = AUDIO_HW_IDLE;
 
@@ -135,8 +136,8 @@
     mHardwareStatus = AUDIO_HW_INIT;
     if (mAudioHardware->initCheck() == NO_ERROR) {
         // open 16-bit output stream for s/w mixer
-
-        setMode(AudioSystem::MODE_NORMAL);
+        mMode = AudioSystem::MODE_NORMAL;
+        setMode(mMode);
 
         setMasterVolume(1.0f);
         setMasterMute(false);
@@ -431,6 +432,8 @@
 
 status_t AudioFlinger::setMode(int mode)
 {
+    status_t ret;
+
     // check calling permissions
     if (!settingsAllowed()) {
         return PERMISSION_DENIED;
@@ -440,15 +443,23 @@
         return BAD_VALUE;
     }
 
-    AutoMutex lock(mHardwareLock);
-    mHardwareStatus = AUDIO_HW_SET_MODE;
-    status_t ret = mAudioHardware->setMode(mode);
-#ifdef LVMX
-    if (NO_ERROR == ret) {
-        LifeVibes::setMode(mode);
+    { // scope for the lock
+        AutoMutex lock(mHardwareLock);
+        mHardwareStatus = AUDIO_HW_SET_MODE;
+        ret = mAudioHardware->setMode(mode);
+        mHardwareStatus = AUDIO_HW_IDLE;
     }
+
+    if (NO_ERROR == ret) {
+        Mutex::Autolock _l(mLock);
+        mMode = mode;
+        for (uint32_t i = 0; i < mPlaybackThreads.size(); i++)
+           mPlaybackThreads.valueAt(i)->setMode(mode);
+#ifdef LVMX
+        LifeVibes::setMode(mode);
 #endif
-    mHardwareStatus = AUDIO_HW_IDLE;
+    }
+
     return ret;
 }
 
@@ -1385,6 +1396,15 @@
     return chain;
 }
 
+void AudioFlinger::PlaybackThread::setMode(uint32_t mode)
+{
+    Mutex::Autolock _l(mLock);
+    size_t size = mEffectChains.size();
+    for (size_t i = 0; i < size; i++) {
+        mEffectChains[i]->setMode(mode);
+    }
+}
+
 // ----------------------------------------------------------------------------
 
 AudioFlinger::MixerThread::MixerThread(const sp<AudioFlinger>& audioFlinger, AudioStreamOut* output, int id, uint32_t device)
@@ -1803,9 +1823,9 @@
             t->mCblk->flags |= CBLK_INVALID_ON;
             t->mCblk->cv.signal();
             t->mCblk->lock.unlock();
-                }
-            }
         }
+    }
+}
 
 
 // getTrackName_l() must be called with ThreadBase::mLock held
@@ -4466,10 +4486,10 @@
     return EffectQueryNumberEffects(numEffects);
 }
 
-status_t AudioFlinger::queryNextEffect(effect_descriptor_t *descriptor)
+status_t AudioFlinger::queryEffect(uint32_t index, effect_descriptor_t *descriptor)
 {
     Mutex::Autolock _l(mLock);
-    return EffectQueryNext(descriptor);
+    return EffectQueryEffect(index, descriptor);
 }
 
 status_t AudioFlinger::getEffectDescriptor(effect_uuid_t *pUuid, effect_descriptor_t *descriptor)
@@ -4529,10 +4549,10 @@
                 LOGW("createEffect() error %d from EffectQueryNumberEffects", lStatus);
                 goto Exit;
             }
-            for (; numEffects > 0; numEffects--) {
-                lStatus = EffectQueryNext(&desc);
+            for (uint32_t i = 0; i < numEffects; i++) {
+                lStatus = EffectQueryEffect(i, &desc);
                 if (lStatus < 0) {
-                    LOGW("createEffect() error %d from EffectQueryNext", lStatus);
+                    LOGW("createEffect() error %d from EffectQueryEffect", lStatus);
                     continue;
                 }
                 if (memcmp(&desc.type, &pDesc->type, sizeof(effect_uuid_t)) == 0) {
@@ -4567,6 +4587,13 @@
             goto Exit;
         }
 
+        // Session -1 is reserved for output stage effects that can only be created
+        // by audio policy manager (running in same process)
+        if (sessionId == -1 && getpid() != IPCThreadState::self()->getCallingPid()) {
+            lStatus = INVALID_OPERATION;
+            goto Exit;
+        }
+
         // return effect descriptor
         memcpy(pDesc, &desc, sizeof(effect_descriptor_t));
 
@@ -4574,7 +4601,7 @@
         // output threads.
         // TODO: allow attachment of effect to inputs
         if (output == 0) {
-            if (sessionId == 0) {
+            if (sessionId <= 0) {
                 // default to first output
                 // TODO: define criteria to choose output when not specified. Or
                 // receive output from audio policy manager
@@ -4621,6 +4648,33 @@
     return handle;
 }
 
+status_t AudioFlinger::registerEffectResource_l(effect_descriptor_t *desc) {
+    if (mTotalEffectsCpuLoad + desc->cpuLoad > MAX_EFFECTS_CPU_LOAD) {
+        LOGW("registerEffectResource() CPU Load limit exceeded for Fx %s, CPU %f MIPS",
+                desc->name, (float)desc->cpuLoad/10);
+        return INVALID_OPERATION;
+    }
+    if (mTotalEffectsMemory + desc->memoryUsage > MAX_EFFECTS_MEMORY) {
+        LOGW("registerEffectResource() memory limit exceeded for Fx %s, Memory %d KB",
+                desc->name, desc->memoryUsage);
+        return INVALID_OPERATION;
+    }
+    mTotalEffectsCpuLoad += desc->cpuLoad;
+    mTotalEffectsMemory += desc->memoryUsage;
+    LOGV("registerEffectResource_l() effect %s, CPU %d, memory %d",
+            desc->name, desc->cpuLoad, desc->memoryUsage);
+    LOGV("  total CPU %d, total memory %d", mTotalEffectsCpuLoad, mTotalEffectsMemory);
+    return NO_ERROR;
+}
+
+void AudioFlinger::unregisterEffectResource_l(effect_descriptor_t *desc) {
+    mTotalEffectsCpuLoad -= desc->cpuLoad;
+    mTotalEffectsMemory -= desc->memoryUsage;
+    LOGV("unregisterEffectResource_l() effect %s, CPU %d, memory %d",
+            desc->name, desc->cpuLoad, desc->memoryUsage);
+    LOGV("  total CPU %d, total memory %d", mTotalEffectsCpuLoad, mTotalEffectsMemory);
+}
+
 // PlaybackThread::createEffect_l() must be called with AudioFlinger::mLock held
 sp<AudioFlinger::EffectHandle> AudioFlinger::PlaybackThread::createEffect_l(
         const sp<AudioFlinger::Client>& client,
@@ -4638,6 +4692,7 @@
     sp<Track> track;
     sp<EffectChain> chain;
     bool effectCreated = false;
+    bool effectRegistered = false;
 
     if (mOutput == 0) {
         LOGW("createEffect_l() Audio driver not initialized.");
@@ -4680,20 +4735,26 @@
         LOGV("createEffect_l() got effect %p on chain %p", effect == 0 ? 0 : effect.get(), chain.get());
 
         if (effect == 0) {
+            // Check CPU and memory usage
+            lStatus = mAudioFlinger->registerEffectResource_l(desc);
+            if (lStatus != NO_ERROR) {
+                goto Exit;
+            }
+            effectRegistered = true;
             // create a new effect module if none present in the chain
-            effectCreated = true;
             effect = new EffectModule(this, chain, desc, mAudioFlinger->nextUniqueId(), sessionId);
             lStatus = effect->status();
             if (lStatus != NO_ERROR) {
                 goto Exit;
             }
-            //TODO: handle CPU load and memory usage here
             lStatus = chain->addEffect(effect);
             if (lStatus != NO_ERROR) {
                 goto Exit;
             }
+            effectCreated = true;
 
             effect->setDevice(mDevice);
+            effect->setMode(mAudioFlinger->getMode());
         }
         // create effect handle and connect it to effect module
         handle = new EffectHandle(effect, client, effectClient, priority);
@@ -4705,11 +4766,14 @@
 
 Exit:
     if (lStatus != NO_ERROR && lStatus != ALREADY_EXISTS) {
-        if (chain != 0 && effectCreated) {
+        if (effectCreated) {
             if (chain->removeEffect(effect) == 0) {
                 removeEffectChain_l(chain);
             }
         }
+        if (effectRegistered) {
+            mAudioFlinger->unregisterEffectResource_l(desc);
+        }
         handle.clear();
     }
 
@@ -4719,21 +4783,37 @@
     return handle;
 }
 
+void AudioFlinger::PlaybackThread::disconnectEffect(const sp< EffectModule>& effect,
+                                                    const wp<EffectHandle>& handle) {
+    effect_descriptor_t desc = effect->desc();
+    Mutex::Autolock _l(mLock);
+    // delete the effect module if removing last handle on it
+    if (effect->removeHandle(handle) == 0) {
+        if ((desc.flags & EFFECT_FLAG_TYPE_MASK) == EFFECT_FLAG_TYPE_AUXILIARY) {
+            detachAuxEffect_l(effect->id());
+        }
+        sp<EffectChain> chain = effect->chain().promote();
+        if (chain != 0) {
+            // remove effect chain if remove last effect
+            if (chain->removeEffect(effect) == 0) {
+                removeEffectChain_l(chain);
+            }
+        }
+        mLock.unlock();
+        mAudioFlinger->mLock.lock();
+        mAudioFlinger->unregisterEffectResource_l(&desc);
+        mAudioFlinger->mLock.unlock();
+    }
+}
+
 status_t AudioFlinger::PlaybackThread::addEffectChain_l(const sp<EffectChain>& chain)
 {
     int session = chain->sessionId();
     int16_t *buffer = mMixBuffer;
+    bool ownsBuffer = false;
 
     LOGV("addEffectChain_l() %p on thread %p for session %d", chain.get(), this, session);
-    if (session == 0) {
-        chain->setInBuffer(buffer, false);
-        chain->setOutBuffer(buffer);
-        // Effect chain for session 0 is inserted at end of effect chains list
-        // to be processed last as it contains output mix effects to apply after
-        // all track specific effects
-        mEffectChains.add(chain);
-    } else {
-        bool ownsBuffer = false;
+    if (session > 0) {
         // Only one effect chain can be present in direct output thread and it uses
         // the mix buffer as input
         if (mType != DIRECT) {
@@ -4743,32 +4823,42 @@
             LOGV("addEffectChain_l() creating new input buffer %p session %d", buffer, session);
             ownsBuffer = true;
         }
-        chain->setInBuffer(buffer, ownsBuffer);
-        chain->setOutBuffer(mMixBuffer);
-        // Effect chain for session other than 0 is inserted at beginning of effect
-        // chains list to be processed before output mix effects. Relative order between
-        // sessions other than 0 is not important
-        mEffectChains.insertAt(chain, 0);
-    }
 
-    // Attach all tracks with same session ID to this chain.
-    for (size_t i = 0; i < mTracks.size(); ++i) {
-        sp<Track> track = mTracks[i];
-        if (session == track->sessionId()) {
-            LOGV("addEffectChain_l() track->setMainBuffer track %p buffer %p", track.get(), buffer);
-            track->setMainBuffer(buffer);
+        // Attach all tracks with same session ID to this chain.
+        for (size_t i = 0; i < mTracks.size(); ++i) {
+            sp<Track> track = mTracks[i];
+            if (session == track->sessionId()) {
+                LOGV("addEffectChain_l() track->setMainBuffer track %p buffer %p", track.get(), buffer);
+                track->setMainBuffer(buffer);
+            }
+        }
+
+        // indicate all active tracks in the chain
+        for (size_t i = 0 ; i < mActiveTracks.size() ; ++i) {
+            sp<Track> track = mActiveTracks[i].promote();
+            if (track == 0) continue;
+            if (session == track->sessionId()) {
+                LOGV("addEffectChain_l() activating track %p on session %d", track.get(), session);
+                chain->startTrack();
+            }
         }
     }
 
-    // indicate all active tracks in the chain
-    for (size_t i = 0 ; i < mActiveTracks.size() ; ++i) {
-        sp<Track> track = mActiveTracks[i].promote();
-        if (track == 0) continue;
-        if (session == track->sessionId()) {
-            LOGV("addEffectChain_l() activating track %p on session %d", track.get(), session);
-            chain->startTrack();
-        }
+    chain->setInBuffer(buffer, ownsBuffer);
+    chain->setOutBuffer(mMixBuffer);
+    // Effect chain for session -1 is inserted at end of effect chains list
+    // in order to be processed last as it contains output stage effects
+    // Effect chain for session 0 is inserted before session -1 to be processed
+    // after track specific effects and before output stage
+    // Effect chain for session other than 0 is inserted at beginning of effect
+    // chains list to be processed before output mix effects. Relative order between
+    // sessions other than 0 is not important
+    size_t size = mEffectChains.size();
+    size_t i = 0;
+    for (i = 0; i < size; i++) {
+        if (mEffectChains[i]->sessionId() < session) break;
     }
+    mEffectChains.insertAt(chain, i);
 
     return NO_ERROR;
 }
@@ -4884,7 +4974,8 @@
     memcpy(&mDescriptor, desc, sizeof(effect_descriptor_t));
 
     // create effect engine from effect factory
-    mStatus = EffectCreate(&desc->uuid, &mEffectInterface);
+    mStatus = EffectCreate(&desc->uuid, sessionId, p->id(), &mEffectInterface);
+
     if (mStatus != NO_ERROR) {
         return;
     }
@@ -4969,22 +5060,11 @@
     // keep a strong reference on this EffectModule to avoid calling the
     // destructor before we exit
     sp<EffectModule> keep(this);
-    sp<ThreadBase> thread = mThread.promote();
-    if (thread != 0) {
-        Mutex::Autolock _l(thread->mLock);
-        // delete the effect module if removing last handle on it
-        if (removeHandle(handle) == 0) {
+    {
+        sp<ThreadBase> thread = mThread.promote();
+        if (thread != 0) {
             PlaybackThread *playbackThread = (PlaybackThread *)thread.get();
-            if ((mDescriptor.flags & EFFECT_FLAG_TYPE_MASK) == EFFECT_FLAG_TYPE_AUXILIARY) {
-                playbackThread->detachAuxEffect_l(mId);
-            }
-            sp<EffectChain> chain = mChain.promote();
-            if (chain != 0) {
-                // remove effect chain if remove last effect
-                if (chain->removeEffect(keep) == 0) {
-                    playbackThread->removeEffectChain_l(chain);
-                }
-            }
+            playbackThread->disconnectEffect(keep, handle);
         }
     }
 }
@@ -5007,88 +5087,25 @@
 
         // TODO: handle effects with buffer provider
         if (mState != ACTIVE) {
-            uint32_t count = mConfig.inputCfg.buffer.frameCount;
-            int32_t amp = 32767L << 16;
-            int32_t step = amp / count;
-            int16_t *pIn = mConfig.inputCfg.buffer.s16;
-            int16_t *pOut = mConfig.outputCfg.buffer.s16;
-            int inChannels;
-            int outChannels;
-
-            if (mConfig.inputCfg.channels == CHANNEL_MONO) {
-                inChannels = 1;
-            } else {
-                inChannels = 2;
-            }
-            if (mConfig.outputCfg.channels == CHANNEL_MONO) {
-                outChannels = 1;
-            } else {
-                outChannels = 2;
-            }
-
             switch (mState) {
             case RESET:
                 reset();
+                mState = STARTING;
                 // clear auxiliary effect input buffer for next accumulation
                 if ((mDescriptor.flags & EFFECT_FLAG_TYPE_MASK) == EFFECT_FLAG_TYPE_AUXILIARY) {
                     memset(mConfig.inputCfg.buffer.raw, 0, mConfig.inputCfg.buffer.frameCount*sizeof(int32_t));
                 }
-                step = -step;
-                mState = STARTING;
-                break;
+                return;
             case STARTING:
                 start();
-                amp = 0;
-                pOut = mConfig.inputCfg.buffer.s16;
-                outChannels = inChannels;
                 mState = ACTIVE;
                 break;
             case STOPPING:
-                step = -step;
-                pOut = mConfig.inputCfg.buffer.s16;
-                outChannels = inChannels;
                 mState = STOPPED;
                 break;
             case STOPPED:
                 stop();
-                amp = 0;
                 mState = IDLE;
-                break;
-            }
-
-            // ramp volume down or up before activating or deactivating the effect
-            if (inChannels == 1) {
-                if (outChannels == 1) {
-                    while (count--) {
-                        *pOut++ = (int16_t)(((int32_t)*pIn++ * (amp >> 16)) >> 15);
-                        amp += step;
-                    }
-                } else {
-                    while (count--) {
-                        int32_t smp = (int16_t)(((int32_t)*pIn++ * (amp >> 16)) >> 15);
-                        *pOut++ = smp;
-                        *pOut++ = smp;
-                        amp += step;
-                    }
-                }
-            } else {
-                if (outChannels == 1) {
-                    while (count--) {
-                        int32_t smp = (((int32_t)*pIn * (amp >> 16)) >> 16) +
-                                      (((int32_t)*(pIn + 1) * (amp >> 16)) >> 16);
-                        pIn += 2;
-                        *pOut++ = (int16_t)smp;
-                        amp += step;
-                    }
-                } else {
-                    while (count--) {
-                        *pOut++ = (int16_t)((int32_t)*pIn++ * (amp >> 16)) >> 15;
-                        *pOut++ = (int16_t)((int32_t)*pIn++ * (amp >> 16)) >> 15;
-                         amp += step;
-                    }
-                }
-            }
-            if (mState == STARTING || mState == IDLE) {
                 return;
             }
         }
@@ -5148,8 +5165,8 @@
         mConfig.inputCfg.channels = channels;
     }
     mConfig.outputCfg.channels = channels;
-    mConfig.inputCfg.format = PCM_FORMAT_S15;
-    mConfig.outputCfg.format = PCM_FORMAT_S15;
+    mConfig.inputCfg.format = SAMPLE_FORMAT_PCM_S15;
+    mConfig.outputCfg.format = SAMPLE_FORMAT_PCM_S15;
     mConfig.inputCfg.samplingRate = thread->sampleRate();
     mConfig.outputCfg.samplingRate = mConfig.inputCfg.samplingRate;
     mConfig.inputCfg.bufferProvider.cookie = NULL;
@@ -5160,7 +5177,7 @@
     mConfig.outputCfg.bufferProvider.releaseBuffer = NULL;
     mConfig.inputCfg.accessMode = EFFECT_BUFFER_ACCESS_READ;
     // Insert effect:
-    // - in session 0, always overwrites output buffer: input buffer == output buffer
+    // - in session 0 or -1, always overwrites output buffer: input buffer == output buffer
     // - in other sessions:
     //      last effect in the chain accumulates in output buffer: input buffer != output buffer
     //      other effect: overwrites output buffer: input buffer == output buffer
@@ -5331,7 +5348,12 @@
 status_t AudioFlinger::EffectModule::setDevice(uint32_t device)
 {
     status_t status = NO_ERROR;
-    if ((mDescriptor.flags & EFFECT_FLAG_DEVICE_MASK) == EFFECT_FLAG_DEVICE_MASK) {
+    if ((mDescriptor.flags & EFFECT_FLAG_DEVICE_MASK) == EFFECT_FLAG_DEVICE_IND) {
+        // convert device bit field from AudioSystem to EffectApi format.
+        device = deviceAudioSystemToEffectApi(device);
+        if (device == 0) {
+            return BAD_VALUE;
+        }
         status_t cmdStatus;
         int size = sizeof(status_t);
         status = (*mEffectInterface)->command(mEffectInterface, EFFECT_CMD_SET_DEVICE, sizeof(uint32_t), &device, &size, &cmdStatus);
@@ -5342,6 +5364,70 @@
     return status;
 }
 
+status_t AudioFlinger::EffectModule::setMode(uint32_t mode)
+{
+    status_t status = NO_ERROR;
+    if ((mDescriptor.flags & EFFECT_FLAG_AUDIO_MODE_MASK) == EFFECT_FLAG_AUDIO_MODE_IND) {
+        // convert audio mode from AudioSystem to EffectApi format.
+        int effectMode = modeAudioSystemToEffectApi(mode);
+        if (effectMode < 0) {
+            return BAD_VALUE;
+        }
+        status_t cmdStatus;
+        int size = sizeof(status_t);
+        status = (*mEffectInterface)->command(mEffectInterface, EFFECT_CMD_SET_AUDIO_MODE, sizeof(int), &effectMode, &size, &cmdStatus);
+        if (status == NO_ERROR) {
+            status = cmdStatus;
+        }
+    }
+    return status;
+}
+
+// update this table when AudioSystem::audio_devices or audio_device_e (in EffectApi.h) are modified
+const uint32_t AudioFlinger::EffectModule::sDeviceConvTable[] = {
+    DEVICE_EARPIECE, // AudioSystem::DEVICE_OUT_EARPIECE
+    DEVICE_SPEAKER, // AudioSystem::DEVICE_OUT_SPEAKER
+    DEVICE_WIRED_HEADSET, // case AudioSystem::DEVICE_OUT_WIRED_HEADSET
+    DEVICE_WIRED_HEADPHONE, // AudioSystem::DEVICE_OUT_WIRED_HEADPHONE
+    DEVICE_BLUETOOTH_SCO, // AudioSystem::DEVICE_OUT_BLUETOOTH_SCO
+    DEVICE_BLUETOOTH_SCO_HEADSET, // AudioSystem::DEVICE_OUT_BLUETOOTH_SCO_HEADSET
+    DEVICE_BLUETOOTH_SCO_CARKIT, //  AudioSystem::DEVICE_OUT_BLUETOOTH_SCO_CARKIT
+    DEVICE_BLUETOOTH_A2DP, //  AudioSystem::DEVICE_OUT_BLUETOOTH_A2DP
+    DEVICE_BLUETOOTH_A2DP_HEADPHONES, // AudioSystem::DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES
+    DEVICE_BLUETOOTH_A2DP_SPEAKER, // AudioSystem::DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER
+    DEVICE_AUX_DIGITAL // AudioSystem::DEVICE_OUT_AUX_DIGITAL
+};
+
+uint32_t AudioFlinger::EffectModule::deviceAudioSystemToEffectApi(uint32_t device)
+{
+    uint32_t deviceOut = 0;
+    while (device) {
+        const uint32_t i = 31 - __builtin_clz(device);
+        device &= ~(1 << i);
+        if (i >= sizeof(sDeviceConvTable)/sizeof(uint32_t)) {
+            LOGE("device convertion error for AudioSystem device 0x%08x", device);
+            return 0;
+        }
+        deviceOut |= (uint32_t)sDeviceConvTable[i];
+    }
+    return deviceOut;
+}
+
+// update this table when AudioSystem::audio_mode or audio_mode_e (in EffectApi.h) are modified
+const uint32_t AudioFlinger::EffectModule::sModeConvTable[] = {
+    AUDIO_MODE_NORMAL,   // AudioSystem::MODE_NORMAL
+    AUDIO_MODE_RINGTONE, // AudioSystem::MODE_RINGTONE
+    AUDIO_MODE_IN_CALL   // AudioSystem::MODE_IN_CALL
+};
+
+int AudioFlinger::EffectModule::modeAudioSystemToEffectApi(uint32_t mode)
+{
+    int modeOut = -1;
+    if (mode < sizeof(sModeConvTable) / sizeof(uint32_t)) {
+        modeOut = (int)sModeConvTable[mode];
+    }
+    return modeOut;
+}
 
 status_t AudioFlinger::EffectModule::dump(int fd, const Vector<String16>& args)
 {
@@ -5525,7 +5611,16 @@
             int rsize = sizeof(int);
             int *p = (int *)(mBuffer + mCblk->serverIndex);
             int size = *p++;
+            if (((uint8_t *)p + size) > mBuffer + mCblk->clientIndex) {
+                LOGW("command(): invalid parameter block size");
+                break;
+            }
             effect_param_t *param = (effect_param_t *)p;
+            if (param->psize == 0 || param->vsize == 0) {
+                LOGW("command(): null parameter or value size");
+                mCblk->serverIndex += size;
+                continue;
+            }
             int psize = sizeof(effect_param_t) + ((param->psize - 1) / sizeof(int) + 1) * sizeof(int) + param->vsize;
             status_t ret = mEffect->command(EFFECT_CMD_SET_PARAM, psize, p, &rsize, &reply);
             if (ret == NO_ERROR) {
@@ -5659,7 +5754,7 @@
     }
     // if no track is active, input buffer must be cleared here as the mixer process
     // will not do it
-    if (mSessionId != 0 && activeTracks() == 0) {
+    if (mSessionId > 0 && activeTracks() == 0) {
         sp<ThreadBase> thread = mThread.promote();
         if (thread != 0) {
             size_t numSamples = thread->frameCount() * thread->channelCount();
@@ -5697,15 +5792,16 @@
     } else {
         // Insert effects are inserted at the end of mEffects vector as they are processed
         //  after track and auxiliary effects.
-        // Insert effect order:
-        //  if EFFECT_FLAG_INSERT_FIRST or EFFECT_FLAG_INSERT_EXCLUSIVE insert as first insert effect
+        // Insert effect order as a function of indicated preference:
+        //  if EFFECT_FLAG_INSERT_EXCLUSIVE, insert in first position or reject if
+        //  another effect is present
+        //  else if EFFECT_FLAG_INSERT_FIRST, insert in first position or after the
+        //  last effect claiming first position
+        //  else if EFFECT_FLAG_INSERT_LAST, insert in last position or before the
+        //  first effect claiming last position
         //  else if EFFECT_FLAG_INSERT_ANY insert after first or before last
-        //  else insert as last insert effect
-        // Reject insertion if:
-        //  - EFFECT_FLAG_INSERT_EXCLUSIVE and another effect is present
-        //  - an effect with EFFECT_FLAG_INSERT_EXCLUSIVE is present
-        //  - EFFECT_FLAG_INSERT_FIRST or EFFECT_FLAG_INSERT_LAST and an effect with same
-        //  preference is present
+        // Reject insertion if an effect with EFFECT_FLAG_INSERT_EXCLUSIVE is
+        // already present
 
         int size = (int)mEffects.size();
         int idx_insert = size;
@@ -5719,35 +5815,40 @@
             if (iMode == EFFECT_FLAG_TYPE_INSERT) {
                 // check invalid effect chaining combinations
                 if (insertPref == EFFECT_FLAG_INSERT_EXCLUSIVE ||
-                    iPref == EFFECT_FLAG_INSERT_EXCLUSIVE ||
-                    (insertPref != EFFECT_FLAG_INSERT_ANY
-                                && insertPref == iPref)) {
+                    iPref == EFFECT_FLAG_INSERT_EXCLUSIVE) {
+                    LOGW("addEffect() could not insert effect %s: exclusive conflict with %s", desc.name, d.name);
                     return INVALID_OPERATION;
                 }
-                // remember position of first insert effect
+                // remember position of first insert effect and by default
+                // select this as insert position for new effect
                 if (idx_insert == size) {
                     idx_insert = i;
                 }
-                // remember position of insert effect claiming
-                // first place
+                // remember position of last insert effect claiming
+                // first position
                 if (iPref == EFFECT_FLAG_INSERT_FIRST) {
                     idx_insert_first = i;
                 }
-                // remember position of insert effect claiming
-                // last place
-                if (iPref == EFFECT_FLAG_INSERT_LAST) {
+                // remember position of first insert effect claiming
+                // last position
+                if (iPref == EFFECT_FLAG_INSERT_LAST &&
+                    idx_insert_last == -1) {
                     idx_insert_last = i;
                 }
             }
         }
 
-        // modify idx_insert from first place if needed
-        if (idx_insert_first != -1) {
-            idx_insert = idx_insert_first + 1;
-        } else if (idx_insert_last != -1) {
-            idx_insert = idx_insert_last;
-        } else if (insertPref == EFFECT_FLAG_INSERT_LAST) {
-            idx_insert = size;
+        // modify idx_insert from first position if needed
+        if (insertPref == EFFECT_FLAG_INSERT_LAST) {
+            if (idx_insert_last != -1) {
+                idx_insert = idx_insert_last;
+            } else {
+                idx_insert = size;
+            }
+        } else {
+            if (idx_insert_first != -1) {
+                idx_insert = idx_insert_first + 1;
+            }
         }
 
         // always read samples from chain input buffer
@@ -5764,14 +5865,14 @@
         } else {
             effect->setOutBuffer(mInBuffer);
         }
-        status_t status = mEffects.insertAt(effect, idx_insert);
+        mEffects.insertAt(effect, idx_insert);
         // Always give volume control to last effect in chain with volume control capability
         if (((desc.flags & EFFECT_FLAG_VOLUME_MASK) & EFFECT_FLAG_VOLUME_CTRL) &&
                 mVolumeCtrlIdx < idx_insert) {
             mVolumeCtrlIdx = idx_insert;
         }
 
-        LOGV("addEffect() effect %p, added in chain %p at rank %d status %d", effect.get(), this, idx_insert, status);
+        LOGV("addEffect() effect %p, added in chain %p at rank %d", effect.get(), this, idx_insert);
     }
     effect->configure();
     return NO_ERROR;
@@ -5823,6 +5924,14 @@
     }
 }
 
+void AudioFlinger::EffectChain::setMode(uint32_t mode)
+{
+    size_t size = mEffects.size();
+    for (size_t i = 0; i < size; i++) {
+        mEffects[i]->setMode(mode);
+    }
+}
+
 bool AudioFlinger::EffectChain::setVolume(uint32_t *left, uint32_t *right)
 {
     uint32_t newLeft = *left;
diff --git a/libs/audioflinger/AudioFlinger.h b/libs/audioflinger/AudioFlinger.h
index e543334..42dca4c 100644
--- a/libs/audioflinger/AudioFlinger.h
+++ b/libs/audioflinger/AudioFlinger.h
@@ -149,7 +149,7 @@
 
     virtual status_t queryNumberEffects(uint32_t *numEffects);
 
-    virtual status_t queryNextEffect(effect_descriptor_t *descriptor);
+    virtual status_t queryEffect(uint32_t index, effect_descriptor_t *descriptor);
 
     virtual status_t getEffectDescriptor(effect_uuid_t *pUuid, effect_descriptor_t *descriptor);
 
@@ -163,6 +163,9 @@
                         int *id,
                         int *enabled);
 
+            status_t registerEffectResource_l(effect_descriptor_t *desc);
+            void     unregisterEffectResource_l(effect_descriptor_t *desc);
+
     enum hardware_call_state {
         AUDIO_HW_IDLE = 0,
         AUDIO_HW_INIT,
@@ -200,6 +203,8 @@
                                 Parcel* reply,
                                 uint32_t flags);
 
+                uint32_t    getMode() { return mMode; }
+
 private:
                             AudioFlinger();
     virtual                 ~AudioFlinger();
@@ -601,6 +606,8 @@
                                         effect_descriptor_t *desc,
                                         int *enabled,
                                         status_t *status);
+                    void disconnectEffect(const sp< EffectModule>& effect,
+                                          const wp<EffectHandle>& handle);
 
                     bool hasAudioSession(int sessionId);
                     sp<EffectChain> getEffectChain(int sessionId);
@@ -614,6 +621,7 @@
                     void detachAuxEffect_l(int effectId);
                     status_t attachAuxEffect(const sp<AudioFlinger::PlaybackThread::Track> track, int EffectId);
                     status_t attachAuxEffect_l(const sp<AudioFlinger::PlaybackThread::Track> track, int EffectId);
+                    void setMode(uint32_t mode);
 
         struct  stream_type_t {
             stream_type_t()
@@ -930,9 +938,11 @@
         size_t removeHandle (const wp<EffectHandle>& handle);
 
         effect_descriptor_t& desc() { return mDescriptor; }
+        wp<EffectChain>&     chain() { return mChain; }
 
         status_t         setDevice(uint32_t device);
         status_t         setVolume(uint32_t *left, uint32_t *right, bool controller);
+        status_t         setMode(uint32_t mode);
 
         status_t         dump(int fd, const Vector<String16>& args);
 
@@ -944,6 +954,14 @@
         status_t start();
         status_t stop();
 
+        // update this table when AudioSystem::audio_devices or audio_device_e (in EffectApi.h) are modified
+        static const uint32_t sDeviceConvTable[];
+        static uint32_t deviceAudioSystemToEffectApi(uint32_t device);
+
+        // update this table when AudioSystem::audio_mode or audio_mode_e (in EffectApi.h) are modified
+        static const uint32_t sModeConvTable[];
+        static int modeAudioSystemToEffectApi(uint32_t mode);
+
         Mutex               mLock;      // mutex for process, commands and handles list protection
         wp<ThreadBase>      mThread;    // parent thread
         wp<EffectChain>     mChain;     // parent effect chain
@@ -1042,6 +1060,8 @@
         sp<EffectModule> getVolumeController();
         bool setVolume(uint32_t *left, uint32_t *right);
         void setDevice(uint32_t device);
+        void setMode(uint32_t mode);
+
 
         void setInBuffer(int16_t *buffer, bool ownsBuffer = false) {
             mInBuffer = buffer;
@@ -1104,6 +1124,14 @@
 #ifdef LVMX
                 int mLifeVibesClientPid;
 #endif
+                uint32_t mMode;
+
+                // Maximum CPU load allocated to audio effects in 0.1 MIPS (ARMv5TE, 0 WS memory) units
+                static const uint32_t MAX_EFFECTS_CPU_LOAD = 1000;
+                // Maximum memory allocated to audio effects in KB
+                static const uint32_t MAX_EFFECTS_MEMORY = 512;
+                uint32_t mTotalEffectsCpuLoad; // current CPU load used by effects
+                uint32_t mTotalEffectsMemory;  // current memory used by effects
 };
 
 // ----------------------------------------------------------------------------
diff --git a/libs/rs/java/Fountain/res/raw/fountain.rs b/libs/rs/java/Fountain/res/raw/fountain.rs
index 6ff4c5bb..ef12d9a 100644
--- a/libs/rs/java/Fountain/res/raw/fountain.rs
+++ b/libs/rs/java/Fountain/res/raw/fountain.rs
@@ -40,7 +40,7 @@
     return 1;
 }
 
-void addParticles(int rate, float x, float y, int newColor)
+void addParticles(int rate, float x, float y, bool newColor)
 {
     if (newColor) {
         partColor.x = rsRand(0.5f, 1.0f);
diff --git a/libs/rs/java/Fountain/res/raw/fountain_bc.bc b/libs/rs/java/Fountain/res/raw/fountain_bc.bc
index b200687..ac6b7d4 100644
--- a/libs/rs/java/Fountain/res/raw/fountain_bc.bc
+++ b/libs/rs/java/Fountain/res/raw/fountain_bc.bc
Binary files differ
diff --git a/libs/rs/java/Fountain/src/com/android/fountain/FountainRS.java b/libs/rs/java/Fountain/src/com/android/fountain/FountainRS.java
index 6f4737f..ffe2fad 100644
--- a/libs/rs/java/Fountain/src/com/android/fountain/FountainRS.java
+++ b/libs/rs/java/Fountain/src/com/android/fountain/FountainRS.java
@@ -51,7 +51,7 @@
     boolean holdingColor = false;
     public void newTouchPosition(int x, int y, int rate) {
         if (rate > 0) {
-            mScript.invoke_addParticles(rate, x, y, !holdingColor ? 1 : 0);
+            mScript.invoke_addParticles(rate, x, y, !holdingColor);
             holdingColor = true;
         } else {
             holdingColor = false;
diff --git a/libs/rs/java/Fountain/src/com/android/fountain/ScriptC_Fountain.java b/libs/rs/java/Fountain/src/com/android/fountain/ScriptC_Fountain.java
index 2653f6b..a4c598b 100644
--- a/libs/rs/java/Fountain/src/com/android/fountain/ScriptC_Fountain.java
+++ b/libs/rs/java/Fountain/src/com/android/fountain/ScriptC_Fountain.java
@@ -50,12 +50,13 @@
     }
 
     private final static int mExportFuncIdx_addParticles = 0;
-    public void invoke_addParticles(int rate, float x, float y, int newColor) {
+    public void invoke_addParticles(int rate, float x, float y, boolean newColor) {
         FieldPacker addParticles_fp = new FieldPacker(16);
         addParticles_fp.addI32(rate);
         addParticles_fp.addF32(x);
         addParticles_fp.addF32(y);
-        addParticles_fp.addI32(newColor);
+        addParticles_fp.addBoolean(newColor);
+        addParticles_fp.skip(3);
         invoke(mExportFuncIdx_addParticles, addParticles_fp);
     }
 
diff --git a/libs/rs/java/Fountain/src/com/android/fountain/ScriptField_Point.java b/libs/rs/java/Fountain/src/com/android/fountain/ScriptField_Point.java
index dee4e96..d88f47b 100644
--- a/libs/rs/java/Fountain/src/com/android/fountain/ScriptField_Point.java
+++ b/libs/rs/java/Fountain/src/com/android/fountain/ScriptField_Point.java
@@ -38,17 +38,18 @@
 
     private Item mItemArray[];
     private FieldPacker mIOBuffer;
+    public static Element createElement(RenderScript rs) {
+        Element.Builder eb = new Element.Builder(rs);
+        eb.add(Element.createVector(rs, Element.DataType.FLOAT_32, 2), "delta");
+        eb.add(Element.createVector(rs, Element.DataType.FLOAT_32, 2), "position");
+        eb.add(Element.createVector(rs, Element.DataType.UNSIGNED_8, 4), "color");
+        return eb.create();
+    }
+
     public  ScriptField_Point(RenderScript rs, int count) {
         mItemArray = null;
         mIOBuffer = null;
-        {
-            Element.Builder eb = new Element.Builder(rs);
-            eb.add(Element.createVector(rs, Element.DataType.FLOAT_32, 2), "delta");
-            eb.add(Element.createVector(rs, Element.DataType.FLOAT_32, 2), "position");
-            eb.add(Element.createVector(rs, Element.DataType.UNSIGNED_8, 4), "color");
-            mElement = eb.create();
-        }
-
+        mElement = createElement(rs);
         init(rs, count);
     }
 
@@ -65,7 +66,7 @@
         mItemArray[index] = i;
         if (copyNow)  {
             copyToArray(i, index);
-            mAllocation.subData1D(index * Item.sizeof, Item.sizeof, mIOBuffer.getData());
+            mAllocation.subData1D(index, 1, mIOBuffer.getData());
         }
 
     }
diff --git a/media/java/android/media/MediaScanner.java b/media/java/android/media/MediaScanner.java
index 013f8fc..3333268 100644
--- a/media/java/android/media/MediaScanner.java
+++ b/media/java/android/media/MediaScanner.java
@@ -1215,8 +1215,12 @@
             prescan(path);
 
             File file = new File(path);
+
+            // lastModified is in milliseconds on Files.
+            long lastModifiedSeconds = file.lastModified() / 1000;
+
             // always scan the file, so we can return the content://media Uri for existing files
-            return mClient.doScanFile(path, mimeType, file.lastModified(), file.length(), true);
+            return mClient.doScanFile(path, mimeType, lastModifiedSeconds, file.length(), true);
         } catch (RemoteException e) {
             Log.e(TAG, "RemoteException in MediaScanner.scanFile()", e);
             return null;
diff --git a/media/jni/audioeffect/android_media_AudioEffect.cpp b/media/jni/audioeffect/android_media_AudioEffect.cpp
index de01dd3..17f2d8f 100644
--- a/media/jni/audioeffect/android_media_AudioEffect.cpp
+++ b/media/jni/audioeffect/android_media_AudioEffect.cpp
@@ -758,7 +758,7 @@
     LOGV("queryEffects() numEffects: %d", numEffects);
 
     for (i = 0; i < numEffects; i++) {
-        if (AudioEffect::queryNextEffect(&desc) != NO_ERROR) {
+        if (AudioEffect::queryEffect(i, &desc) != NO_ERROR) {
             goto queryEffects_failure;
         }
 
diff --git a/media/libeffects/Android.mk b/media/libeffects/Android.mk
index ff21454..b5f1d42 100644
--- a/media/libeffects/Android.mk
+++ b/media/libeffects/Android.mk
@@ -1,5 +1,8 @@
 LOCAL_PATH:= $(call my-dir)
 
+#
+TEST_EFFECT_LIBRARIES := true
+
 # Effect factory library
 include $(CLEAR_VARS)
 
@@ -25,7 +28,8 @@
 include $(BUILD_SHARED_LIBRARY)
 
 
-# Default Reverb library
+ifeq ($(TEST_EFFECT_LIBRARIES),true)
+# Test Reverb library
 include $(CLEAR_VARS)
 
 LOCAL_SRC_FILES:= \
@@ -54,7 +58,7 @@
 
 include $(BUILD_SHARED_LIBRARY)
 
-# Default Equalizer library
+# Test Equalizer library
 include $(CLEAR_VARS)
 
 LOCAL_SRC_FILES:= \
@@ -87,4 +91,6 @@
 
 LOCAL_PRELINK_MODULE := false
 
-include $(BUILD_SHARED_LIBRARY)
\ No newline at end of file
+include $(BUILD_SHARED_LIBRARY)
+
+endif
diff --git a/media/libeffects/AudioCoefInterpolator.cpp b/media/libeffects/AudioCoefInterpolator.cpp
index 05898c9..039ab9f 100644
--- a/media/libeffects/AudioCoefInterpolator.cpp
+++ b/media/libeffects/AudioCoefInterpolator.cpp
@@ -46,7 +46,7 @@
     while (dim-- > 0) {
         if (UNLIKELY(intCoord[dim] < 0)) {
             fracCoord[dim] = 0;
-        } else if (UNLIKELY(intCoord[dim] >= mInDims[dim] - 1)) {
+        } else if (UNLIKELY(intCoord[dim] >= (int)mInDims[dim] - 1)) {
             fracCoord[dim] = 0;
             index += mInDimOffsets[dim] * (mInDims[dim] - 1);
         } else {
diff --git a/media/libeffects/AudioCommon.h b/media/libeffects/AudioCommon.h
index 12d2193e..444f93a 100644
--- a/media/libeffects/AudioCommon.h
+++ b/media/libeffects/AudioCommon.h
@@ -1,4 +1,4 @@
-/* //device/include/server/AudioFlinger/AudioCommon.h
+/*
 **
 ** Copyright 2009, The Android Open Source Project
 **
diff --git a/media/libeffects/AudioFormatAdapter.h b/media/libeffects/AudioFormatAdapter.h
index 8aa5e65..d93ebe9 100644
--- a/media/libeffects/AudioFormatAdapter.h
+++ b/media/libeffects/AudioFormatAdapter.h
@@ -75,7 +75,7 @@
         while (numSamples > 0) {
             uint32_t numSamplesIter = min(numSamples, mMaxSamplesPerCall);
             uint32_t nSamplesChannels = numSamplesIter * mNumChannels;
-            if (mPcmFormat == PCM_FORMAT_S7_24) {
+            if (mPcmFormat == SAMPLE_FORMAT_PCM_S7_24) {
                 if (mBehavior == EFFECT_BUFFER_ACCESS_WRITE) {
                     mpProcessor->process(
                         reinterpret_cast<const audio_sample_t *> (pIn),
@@ -125,7 +125,7 @@
     //              sample.
     // numSamples   The number of single-channel samples to process.
     void ConvertInput(const void *& pIn, uint32_t numSamples) {
-        if (mPcmFormat == PCM_FORMAT_S15) {
+        if (mPcmFormat == SAMPLE_FORMAT_PCM_S15) {
             const int16_t * pIn16 = reinterpret_cast<const int16_t *>(pIn);
             audio_sample_t * pOut = mBuffer;
             while (numSamples-- > 0) {
@@ -143,7 +143,7 @@
     //              When function exist will point to the next output sample.
     // numSamples   The number of single-channel samples to process.
     void ConvertOutput(void *& pOut, uint32_t numSamples) {
-        if (mPcmFormat == PCM_FORMAT_S15) {
+        if (mPcmFormat == SAMPLE_FORMAT_PCM_S15) {
             const audio_sample_t * pIn = mBuffer;
             int16_t * pOut16 = reinterpret_cast<int16_t *>(pOut);
             if (mBehavior == EFFECT_BUFFER_ACCESS_WRITE) {
diff --git a/media/libeffects/AudioShelvingFilter.cpp b/media/libeffects/AudioShelvingFilter.cpp
index d8abbd2..b8650ba 100644
--- a/media/libeffects/AudioShelvingFilter.cpp
+++ b/media/libeffects/AudioShelvingFilter.cpp
@@ -50,8 +50,8 @@
 
 AudioShelvingFilter::AudioShelvingFilter(ShelfType type, int nChannels,
                                          int sampleRate)
-        : mBiquad(nChannels, sampleRate)
-        , mType(type) {
+        : mType(type),
+          mBiquad(nChannels, sampleRate)  {
     configure(nChannels, sampleRate);
 }
 
diff --git a/media/libeffects/EffectEqualizer.cpp b/media/libeffects/EffectEqualizer.cpp
index e39e595..d19c6b9 100644
--- a/media/libeffects/EffectEqualizer.cpp
+++ b/media/libeffects/EffectEqualizer.cpp
@@ -39,10 +39,11 @@
         {0xe25aa840, 0x543b, 0x11df, 0x98a5, {0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b}}, // uuid
         EFFECT_API_VERSION,
         (EFFECT_FLAG_TYPE_INSERT | EFFECT_FLAG_INSERT_LAST),
+        0, // TODO
+        1,
         "Graphic Equalizer",
         "Google Inc.",
 };
-static int gEffectIndex;
 
 /////////////////// BEGIN EQ PRESETS ///////////////////////////////////////////
 const int kNumBands = 5;
@@ -101,7 +102,6 @@
     AudioEqualizer * pEqualizer;
 };
 
-
 //--- local function prototypes
 
 int Equalizer_init(EqualizerContext *pContext);
@@ -116,22 +116,23 @@
 
 extern "C" int EffectQueryNumberEffects(uint32_t *pNumEffects) {
     *pNumEffects = 1;
-    gEffectIndex = 0;
     return 0;
 } /* end EffectQueryNumberEffects */
 
-extern "C" int EffectQueryNext(effect_descriptor_t *pDescriptor) {
+extern "C" int EffectQueryEffect(uint32_t index, effect_descriptor_t *pDescriptor) {
     if (pDescriptor == NULL) {
         return -EINVAL;
     }
-    if (gEffectIndex++ > 0) {
-        return -ENOENT;
+    if (index > 0) {
+        return -EINVAL;
     }
     memcpy(pDescriptor, &gEqualizerDescriptor, sizeof(effect_descriptor_t));
     return 0;
 } /* end EffectQueryNext */
 
 extern "C" int EffectCreate(effect_uuid_t *uuid,
+        int32_t sessionId,
+        int32_t ioId,
         effect_interface_t *pInterface) {
     int ret;
     int i;
@@ -160,7 +161,7 @@
 
     *pInterface = (effect_interface_t)pContext;
 
-    LOGV("EffectLibCreateEffect %p", pContext);
+    LOGV("EffectLibCreateEffect %p, size %d", pContext, AudioEqualizer::GetInstanceSize(kNumBands)+sizeof(EqualizerContext));
 
     return 0;
 
@@ -219,8 +220,8 @@
     CHECK_ARG((pConfig->inputCfg.channels == CHANNEL_MONO) || (pConfig->inputCfg.channels == CHANNEL_STEREO));
     CHECK_ARG(pConfig->outputCfg.accessMode == EFFECT_BUFFER_ACCESS_WRITE
               || pConfig->outputCfg.accessMode == EFFECT_BUFFER_ACCESS_ACCUMULATE);
-    CHECK_ARG(pConfig->inputCfg.format == PCM_FORMAT_S7_24
-              || pConfig->inputCfg.format == PCM_FORMAT_S15);
+    CHECK_ARG(pConfig->inputCfg.format == SAMPLE_FORMAT_PCM_S7_24
+              || pConfig->inputCfg.format == SAMPLE_FORMAT_PCM_S15);
 
     int channelCount;
     if (pConfig->inputCfg.channels == CHANNEL_MONO) {
@@ -230,6 +231,8 @@
     }
     CHECK_ARG(channelCount <= AudioBiquadFilter::MAX_CHANNELS);
 
+    memcpy(&pContext->config, pConfig, sizeof(effect_config_t));
+
     pContext->pEqualizer->configure(channelCount,
                           pConfig->inputCfg.samplingRate);
 
@@ -268,7 +271,7 @@
 
     pContext->config.inputCfg.accessMode = EFFECT_BUFFER_ACCESS_READ;
     pContext->config.inputCfg.channels = CHANNEL_STEREO;
-    pContext->config.inputCfg.format = PCM_FORMAT_S15;
+    pContext->config.inputCfg.format = SAMPLE_FORMAT_PCM_S15;
     pContext->config.inputCfg.samplingRate = 44100;
     pContext->config.inputCfg.bufferProvider.getBuffer = NULL;
     pContext->config.inputCfg.bufferProvider.releaseBuffer = NULL;
@@ -276,7 +279,7 @@
     pContext->config.inputCfg.mask = EFFECT_CONFIG_ALL;
     pContext->config.outputCfg.accessMode = EFFECT_BUFFER_ACCESS_ACCUMULATE;
     pContext->config.outputCfg.channels = CHANNEL_STEREO;
-    pContext->config.outputCfg.format = PCM_FORMAT_S15;
+    pContext->config.outputCfg.format = SAMPLE_FORMAT_PCM_S15;
     pContext->config.outputCfg.samplingRate = 44100;
     pContext->config.outputCfg.bufferProvider.getBuffer = NULL;
     pContext->config.outputCfg.bufferProvider.releaseBuffer = NULL;
@@ -526,6 +529,7 @@
     }
 
     pContext->adapter.process(inBuffer->raw, outBuffer->raw, outBuffer->frameCount);
+
     return 0;
 }   // end Equalizer_process
 
@@ -589,6 +593,17 @@
         *(int *)pReplyData = android::Equalizer_setParameter(pEqualizer, (int32_t *)p->data,
                 p->data + p->psize);
         } break;
+    case EFFECT_CMD_ENABLE:
+    case EFFECT_CMD_DISABLE:
+        if (pReplyData == NULL || *replySize != sizeof(int)) {
+            return -EINVAL;
+        }
+        *(int *)pReplyData = 0;
+        break;
+    case EFFECT_CMD_SET_DEVICE:
+    case EFFECT_CMD_SET_VOLUME:
+    case EFFECT_CMD_SET_AUDIO_MODE:
+        break;
     default:
         LOGW("Equalizer_command invalid command %d",cmdCode);
         return -EINVAL;
diff --git a/media/libeffects/EffectReverb.c b/media/libeffects/EffectReverb.c
index 202f50b..ada252c 100644
--- a/media/libeffects/EffectReverb.c
+++ b/media/libeffects/EffectReverb.c
@@ -24,8 +24,6 @@
 #include "EffectReverb.h"
 #include "EffectsMath.h"
 
-static int gEffectIndex;
-
 // effect_interface_t interface implementation for reverb effect
 const struct effect_interface_s gReverbInterface = {
         Reverb_Process,
@@ -37,7 +35,10 @@
         {0xc2e5d5f0, 0x94bd, 0x4763, 0x9cac, {0x4e, 0x23, 0x4d, 0x06, 0x83, 0x9e}},
         {0x1f0ae2e0, 0x4ef7, 0x11df, 0xbc09, {0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b}},
         EFFECT_API_VERSION,
-        EFFECT_FLAG_TYPE_AUXILIARY,
+        // flags other than EFFECT_FLAG_TYPE_AUXILIARY set for test purpose
+        EFFECT_FLAG_TYPE_AUXILIARY | EFFECT_FLAG_DEVICE_IND | EFFECT_FLAG_AUDIO_MODE_IND,
+        0, // TODO
+        33,
         "Aux Environmental Reverb",
         "Google Inc."
 };
@@ -48,6 +49,8 @@
         {0xaa476040, 0x6342, 0x11df, 0x91a4, {0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b}},
         EFFECT_API_VERSION,
         EFFECT_FLAG_TYPE_INSERT | EFFECT_FLAG_INSERT_FIRST,
+        0, // TODO
+        33,
         "Insert Environmental reverb",
         "Google Inc."
 };
@@ -58,6 +61,8 @@
         {0x63909320, 0x53a6, 0x11df, 0xbdbd, {0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b}},
         EFFECT_API_VERSION,
         EFFECT_FLAG_TYPE_AUXILIARY,
+        0, // TODO
+        33,
         "Aux Preset Reverb",
         "Google Inc."
 };
@@ -68,6 +73,8 @@
         {0xd93dc6a0, 0x6342, 0x11df, 0xb128, {0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b}},
         EFFECT_API_VERSION,
         EFFECT_FLAG_TYPE_INSERT | EFFECT_FLAG_INSERT_FIRST,
+        0, // TODO
+        33,
         "Insert Preset Reverb",
         "Google Inc."
 };
@@ -77,8 +84,7 @@
         &gAuxEnvReverbDescriptor,
         &gInsertEnvReverbDescriptor,
         &gAuxPresetReverbDescriptor,
-        &gInsertPresetReverbDescriptor,
-        NULL
+        &gInsertPresetReverbDescriptor
 };
 
 /*----------------------------------------------------------------------------
@@ -88,25 +94,25 @@
 /*--- Effect Library Interface Implementation ---*/
 
 int EffectQueryNumberEffects(uint32_t *pNumEffects) {
-    *pNumEffects = sizeof(gDescriptors) / sizeof(const effect_descriptor_t *)
-            - 1;
-    gEffectIndex = 0;
+    *pNumEffects = sizeof(gDescriptors) / sizeof(const effect_descriptor_t *);
     return 0;
 }
 
-int EffectQueryNext(effect_descriptor_t *pDescriptor) {
+int EffectQueryEffect(uint32_t index, effect_descriptor_t *pDescriptor) {
     if (pDescriptor == NULL) {
         return -EINVAL;
     }
-    if (gDescriptors[gEffectIndex] == NULL) {
-        return -ENOENT;
+    if (index >= sizeof(gDescriptors) / sizeof(const effect_descriptor_t *)) {
+        return -EINVAL;
     }
-    memcpy(pDescriptor, gDescriptors[gEffectIndex++],
+    memcpy(pDescriptor, gDescriptors[index],
             sizeof(effect_descriptor_t));
     return 0;
 }
 
 int EffectCreate(effect_uuid_t *uuid,
+        int32_t sessionId,
+        int32_t ioId,
         effect_interface_t *pInterface) {
     int ret;
     int i;
@@ -152,7 +158,7 @@
 
     *pInterface = (effect_interface_t) module;
 
-    LOGV("EffectLibCreateEffect %p", module);
+    LOGV("EffectLibCreateEffect %p ,size %d", module, sizeof(reverb_module_t));
 
     return 0;
 }
@@ -191,8 +197,23 @@
 
     //if bypassed or the preset forces the signal to be completely dry
     if (pReverb->m_bBypass) {
-        if (inBuffer->raw != outBuffer->raw && !pReverb->m_Aux) {
-            memcpy(outBuffer->raw, inBuffer->raw, outBuffer->frameCount * NUM_OUTPUT_CHANNELS * sizeof(int16_t));
+        if (inBuffer->raw != outBuffer->raw) {
+            int16_t smp;
+            pSrc = inBuffer->s16;
+            pDst = outBuffer->s16;
+            size_t count = inBuffer->frameCount;
+            if (pRvbModule->config.inputCfg.channels == pRvbModule->config.outputCfg.channels) {
+                count *= 2;
+                while (count--) {
+                    *pDst++ = *pSrc++;
+                }
+            } else {
+                while (count--) {
+                    smp = *pSrc++;
+                    *pDst++ = smp;
+                    *pDst++ = smp;
+                }
+            }
         }
         return 0;
     }
@@ -226,10 +247,11 @@
 
         numSamples -= processedSamples;
         if (pReverb->m_Aux) {
-            pDst += processedSamples;
+            pSrc += processedSamples;
         } else {
             pSrc += processedSamples * NUM_OUTPUT_CHANNELS;
         }
+        pDst += processedSamples * NUM_OUTPUT_CHANNELS;
     }
 
     return 0;
@@ -292,6 +314,35 @@
         *(int *)pReplyData = Reverb_setParameter(pReverb, *(int32_t *)cmd->data,
                 cmd->vsize, cmd->data + sizeof(int32_t));
         break;
+    case EFFECT_CMD_ENABLE:
+    case EFFECT_CMD_DISABLE:
+        if (pReplyData == NULL || *replySize != sizeof(int)) {
+            return -EINVAL;
+        }
+        *(int *)pReplyData = 0;
+        break;
+    case EFFECT_CMD_SET_DEVICE:
+        if (pCmdData == NULL || cmdSize != (int)sizeof(uint32_t)) {
+            return -EINVAL;
+        }
+        LOGV("Reverb_Command EFFECT_CMD_SET_DEVICE: 0x%08x", *(uint32_t *)pCmdData);
+        break;
+    case EFFECT_CMD_SET_VOLUME: {
+        // audio output is always stereo => 2 channel volumes
+        if (pCmdData == NULL || cmdSize != (int)sizeof(uint32_t) * 2) {
+            return -EINVAL;
+        }
+        float left = (float)(*(uint32_t *)pCmdData) / (1 << 24);
+        float right = (float)(*((uint32_t *)pCmdData + 1)) / (1 << 24);
+        LOGV("Reverb_Command EFFECT_CMD_SET_VOLUME: left %f, right %f ", left, right);
+        break;
+        }
+    case EFFECT_CMD_SET_AUDIO_MODE:
+        if (pCmdData == NULL || cmdSize != (int)sizeof(uint32_t)) {
+            return -EINVAL;
+        }
+        LOGV("Reverb_Command EFFECT_CMD_SET_AUDIO_MODE: %d", *(uint32_t *)pCmdData);
+        break;
     default:
         LOGW("Reverb_Command invalid command %d",cmdCode);
         return -EINVAL;
@@ -339,7 +390,7 @@
     } else {
         pRvbModule->config.inputCfg.channels = CHANNEL_STEREO;
     }
-    pRvbModule->config.inputCfg.format = PCM_FORMAT_S15;
+    pRvbModule->config.inputCfg.format = SAMPLE_FORMAT_PCM_S15;
     pRvbModule->config.inputCfg.bufferProvider.getBuffer = NULL;
     pRvbModule->config.inputCfg.bufferProvider.releaseBuffer = NULL;
     pRvbModule->config.inputCfg.bufferProvider.cookie = NULL;
@@ -347,7 +398,7 @@
     pRvbModule->config.inputCfg.mask = EFFECT_CONFIG_ALL;
     pRvbModule->config.outputCfg.samplingRate = 44100;
     pRvbModule->config.outputCfg.channels = CHANNEL_STEREO;
-    pRvbModule->config.outputCfg.format = PCM_FORMAT_S15;
+    pRvbModule->config.outputCfg.format = SAMPLE_FORMAT_PCM_S15;
     pRvbModule->config.outputCfg.bufferProvider.getBuffer = NULL;
     pRvbModule->config.outputCfg.bufferProvider.releaseBuffer = NULL;
     pRvbModule->config.outputCfg.bufferProvider.cookie = NULL;
@@ -391,8 +442,8 @@
     if (pConfig->inputCfg.samplingRate
         != pConfig->outputCfg.samplingRate
         || pConfig->outputCfg.channels != OUTPUT_CHANNELS
-        || pConfig->inputCfg.format != PCM_FORMAT_S15
-        || pConfig->outputCfg.format != PCM_FORMAT_S15) {
+        || pConfig->inputCfg.format != SAMPLE_FORMAT_PCM_S15
+        || pConfig->outputCfg.format != SAMPLE_FORMAT_PCM_S15) {
         LOGV("Reverb_Configure invalid config");
         return -EINVAL;
     }
@@ -1033,6 +1084,7 @@
         // Convert milliseconds to => m_nRvbLpfFwd (function of m_nRvbLpfFbk)
         // convert ms to samples
         value32 = (value32 * pReverb->m_nSamplingRate) / 1000;
+
         // calculate valid decay time range as a function of current reverb delay and
         // max feed back gain. Min value <=> -40dB in one pass, Max value <=> feedback gain = -1 dB
         // Calculate attenuation for each round in late reverb given a total attenuation of -6000 millibels.
@@ -1834,7 +1886,6 @@
     //gsReverbObject.m_nXfadeInterval = pPreset->m_nXfadeInterval;
     pReverb->m_nXfadeCounter = pReverb->m_nXfadeInterval + 1; // force update on first iteration
 
-
     pReverb->m_nCurrentRoom = pReverb->m_nNextRoom;
 
     return 0;
diff --git a/media/libeffects/EffectReverb.h b/media/libeffects/EffectReverb.h
index 578e09e..f5aadfa 100644
--- a/media/libeffects/EffectReverb.h
+++ b/media/libeffects/EffectReverb.h
@@ -293,8 +293,8 @@
  *------------------------------------
 */
 int EffectQueryNumberEffects(uint32_t *pNumEffects);
-int EffectQueryNext(effect_descriptor_t *pDescriptor);
-int EffectCreate(effect_uuid_t *effectUID, effect_interface_t *pInterface);
+int EffectQueryEffect(uint32_t index, effect_descriptor_t *pDescriptor);
+int EffectCreate(effect_uuid_t *effectUID, int32_t sessionId, int32_t ioId, effect_interface_t *pInterface);
 int EffectRelease(effect_interface_t interface);
 
 static int Reverb_Process(effect_interface_t self, audio_buffer_t *inBuffer, audio_buffer_t *outBuffer);
diff --git a/media/libeffects/EffectsFactory.c b/media/libeffects/EffectsFactory.c
index 6800765..edd6184 100644
--- a/media/libeffects/EffectsFactory.c
+++ b/media/libeffects/EffectsFactory.c
@@ -26,11 +26,16 @@
 static list_elem_t *gEffectList; // list of effect_entry_t: all currently created effects
 static list_elem_t *gLibraryList; // list of lib_entry_t: all currently loaded libraries
 static pthread_mutex_t gLibLock = PTHREAD_MUTEX_INITIALIZER; // controls access to gLibraryList
+static uint32_t gNumEffects;         // total number number of effects
 static list_elem_t *gCurLib;    // current library in enumeration process
 static list_elem_t *gCurEffect; // current effect in enumeration process
+static uint32_t gCurEffectIdx;       // current effect index in enumeration process
 
 static const char * const gEffectLibPath = "/system/lib/soundfx"; // path to built-in effect libraries
 static int gInitDone; // true is global initialization has been preformed
+static int gNextLibId; // used by loadLibrary() to allocate unique library handles
+static int gCanQueryEffect; // indicates that call to EffectQueryEffect() is valid, i.e. that the list of effects
+                          // was not modified since last call to EffectQueryNumberEffects()
 
 /////////////////////////////////////////////////
 //      Local functions prototypes
@@ -39,7 +44,8 @@
 static int init();
 static int loadLibrary(const char *libPath, int *handle);
 static int unloadLibrary(int handle);
-static uint32_t numEffectModules();
+static void resetEffectEnumeration();
+static uint32_t updateNumEffects();
 static int findEffect(effect_uuid_t *uuid, lib_entry_t **lib, effect_descriptor_t **desc);
 static void dumpEffectDescriptor(effect_descriptor_t *desc, char *str, size_t len);
 
@@ -107,38 +113,53 @@
     }
 
     pthread_mutex_lock(&gLibLock);
-    *pNumEffects = numEffectModules();
+    *pNumEffects = gNumEffects;
+    gCanQueryEffect = 1;
     pthread_mutex_unlock(&gLibLock);
     LOGV("EffectQueryNumberEffects(): %d", *pNumEffects);
     return ret;
 }
 
-int EffectQueryNext(effect_descriptor_t *pDescriptor)
+int EffectQueryEffect(uint32_t index, effect_descriptor_t *pDescriptor)
 {
     int ret = init();
     if (ret < 0) {
         return ret;
     }
-    if (pDescriptor == NULL) {
+    if (pDescriptor == NULL ||
+        index >= gNumEffects) {
         return -EINVAL;
     }
+    if (gCanQueryEffect == 0) {
+        return -ENOSYS;
+    }
 
     pthread_mutex_lock(&gLibLock);
     ret = -ENOENT;
+    if (index < gCurEffectIdx) {
+        resetEffectEnumeration();
+    }
     while (gCurLib) {
         if (gCurEffect) {
-            memcpy(pDescriptor, gCurEffect->object, sizeof(effect_descriptor_t));
-            gCurEffect = gCurEffect->next;
-            ret = 0;
-            break;
+            if (index == gCurEffectIdx) {
+                memcpy(pDescriptor, gCurEffect->object, sizeof(effect_descriptor_t));
+                ret = 0;
+                break;
+            } else {
+                gCurEffect = gCurEffect->next;
+                gCurEffectIdx++;
+            }
         } else {
             gCurLib = gCurLib->next;
             gCurEffect = ((lib_entry_t *)gCurLib->object)->effects;
         }
     }
+
+#if (LOG_NDEBUG == 0)
     char str[256];
     dumpEffectDescriptor(pDescriptor, str, 256);
-    LOGV("EffectQueryNext() desc:%s", str);
+    LOGV("EffectQueryEffect() desc:%s", str);
+#endif
     pthread_mutex_unlock(&gLibLock);
     return ret;
 }
@@ -164,7 +185,7 @@
     return ret;
 }
 
-int EffectCreate(effect_uuid_t *uuid, effect_interface_t *pInterface)
+int EffectCreate(effect_uuid_t *uuid, int32_t sessionId, int32_t ioId, effect_interface_t *pInterface)
 {
     list_elem_t *e = gLibraryList;
     lib_entry_t *l = NULL;
@@ -198,9 +219,9 @@
     }
 
     // create effect in library
-    ret = l->createFx(uuid, &itfe);
-    if (ret < 0) {
-        LOGW("EffectCreate() library %s: could not create fx %s", l->path, d->name);
+    ret = l->createFx(uuid, sessionId, ioId, &itfe);
+    if (ret != 0) {
+        LOGW("EffectCreate() library %s: could not create fx %s, error %d", l->path, d->name, ret);
         goto exit;
     }
 
@@ -282,7 +303,10 @@
     if (libPath == NULL) {
         return -EINVAL;
     }
-    return loadLibrary(libPath, handle);
+
+    ret = loadLibrary(libPath, handle);
+    updateNumEffects();
+    return ret;
 }
 
 int EffectUnloadLibrary(int handle)
@@ -292,7 +316,9 @@
         return ret;
     }
 
-    return unloadLibrary(handle);
+    ret = unloadLibrary(handle);
+    updateNumEffects();
+    return ret;
 }
 
 int EffectIsNullUuid(effect_uuid_t *uuid)
@@ -339,7 +365,7 @@
         }
     }
     closedir(dir);
-
+    updateNumEffects();
     gInitDone = 1;
     LOGV("init() done");
     return 0;
@@ -350,7 +376,7 @@
 {
     void *hdl;
     effect_QueryNumberEffects_t queryNumFx;
-    effect_QueryNextEffect_t queryFx;
+    effect_QueryEffect_t queryFx;
     effect_CreateEffect_t createFx;
     effect_ReleaseEffect_t releaseFx;
     uint32_t numFx;
@@ -378,9 +404,9 @@
         ret = -ENODEV;
         goto error;
     }
-    queryFx = (effect_QueryNextEffect_t)dlsym(hdl, "EffectQueryNext");
+    queryFx = (effect_QueryEffect_t)dlsym(hdl, "EffectQueryEffect");
     if (queryFx == NULL) {
-        LOGW("could not get EffectQueryNext from lib %s", libPath);
+        LOGW("could not get EffectQueryEffect from lib %s", libPath);
         ret = -ENODEV;
         goto error;
     }
@@ -409,7 +435,7 @@
             ret = -ENOMEM;
             goto error;
         }
-        ret = queryFx(d);
+        ret = queryFx(fx, d);
         if (ret == 0) {
 #if (LOG_NDEBUG==0)
             char s[256];
@@ -434,8 +460,12 @@
             LOGW("Error querying effect # %d on lib %s", fx, libPath);
         }
     }
+
+    pthread_mutex_lock(&gLibLock);
+
     // add entry for library in gLibraryList
     l = malloc(sizeof(lib_entry_t));
+    l->id = ++gNextLibId;
     l->handle = hdl;
     strncpy(l->path, libPath, PATH_MAX);
     l->createFx = createFx;
@@ -444,14 +474,13 @@
     pthread_mutex_init(&l->lock, NULL);
 
     e = malloc(sizeof(list_elem_t));
-    pthread_mutex_lock(&gLibLock);
     e->next = gLibraryList;
     e->object = l;
     gLibraryList = e;
     pthread_mutex_unlock(&gLibLock);
     LOGV("loadLibrary() linked library %p", l);
 
-    *handle = (int)hdl;
+    *handle = l->id;
 
     return 0;
 
@@ -480,7 +509,7 @@
     el2 = NULL;
     while (el1) {
         l = (lib_entry_t *)el1->object;
-        if (handle == (int)l->handle) {
+        if (handle == l->id) {
             if (el2) {
                 el2->next = el1->next;
             } else {
@@ -508,6 +537,7 @@
 
     // disable all effects from this library
     pthread_mutex_lock(&l->lock);
+
     el1 = gEffectList;
     while (el1) {
         fx = (effect_entry_t *)el1->object;
@@ -523,17 +553,23 @@
     return 0;
 }
 
+void resetEffectEnumeration()
+{
+    gCurLib = gLibraryList;
+    gCurEffect = NULL;
+    if (gCurLib) {
+        gCurEffect = ((lib_entry_t *)gCurLib->object)->effects;
+    }
+    gCurEffectIdx = 0;
+}
 
-
-uint32_t numEffectModules() {
-    list_elem_t *e = gLibraryList;
+uint32_t updateNumEffects() {
+    list_elem_t *e;
     uint32_t cnt = 0;
 
-    // Reset pointers for EffectQueryNext()
-    gCurLib = e;
-    if (e) {
-        gCurEffect = ((lib_entry_t *)e->object)->effects;
-    }
+    resetEffectEnumeration();
+
+    e = gLibraryList;
     while (e) {
         lib_entry_t *l = (lib_entry_t *)e->object;
         list_elem_t *efx = l->effects;
@@ -543,6 +579,8 @@
         }
         e = e->next;
     }
+    gNumEffects = cnt;
+    gCanQueryEffect = 0;
     return cnt;
 }
 
diff --git a/media/libeffects/EffectsFactory.h b/media/libeffects/EffectsFactory.h
index 17ad3f0..8f543ca 100644
--- a/media/libeffects/EffectsFactory.h
+++ b/media/libeffects/EffectsFactory.h
@@ -20,7 +20,7 @@
 #include <cutils/log.h>
 #include <pthread.h>
 #include <dirent.h>
-#include <media/EffectFactoryApi.h>
+#include <media/EffectsFactoryApi.h>
 
 
 #if __cplusplus
@@ -35,6 +35,7 @@
 typedef struct lib_entry_s {
     char path[PATH_MAX];
     void *handle;
+    int id;
     effect_CreateEffect_t createFx;
     effect_ReleaseEffect_t releaseFx;
     list_elem_t *effects; //list of effect_descriptor_t
diff --git a/media/libmedia/AudioEffect.cpp b/media/libmedia/AudioEffect.cpp
index 8648211..4afa2dc 100644
--- a/media/libmedia/AudioEffect.cpp
+++ b/media/libmedia/AudioEffect.cpp
@@ -394,11 +394,11 @@
     return af->queryNumberEffects(numEffects);
 }
 
-status_t AudioEffect::queryNextEffect(effect_descriptor_t *descriptor)
+status_t AudioEffect::queryEffect(uint32_t index, effect_descriptor_t *descriptor)
 {
     const sp<IAudioFlinger>& af = AudioSystem::get_audio_flinger();
     if (af == 0) return PERMISSION_DENIED;
-    return af->queryNextEffect(descriptor);
+    return af->queryEffect(index, descriptor);
 }
 
 status_t AudioEffect::getEffectDescriptor(effect_uuid_t *uuid, effect_descriptor_t *descriptor)
diff --git a/media/libmedia/IAudioFlinger.cpp b/media/libmedia/IAudioFlinger.cpp
index f2a8db3..7d6a5d3 100644
--- a/media/libmedia/IAudioFlinger.cpp
+++ b/media/libmedia/IAudioFlinger.cpp
@@ -67,7 +67,7 @@
     LOAD_EFFECT_LIBRARY,
     UNLOAD_EFFECT_LIBRARY,
     QUERY_NUM_EFFECTS,
-    QUERY_NEXT_EFFECT,
+    QUERY_EFFECT,
     GET_EFFECT_DESCRIPTOR,
     CREATE_EFFECT
 };
@@ -586,14 +586,15 @@
         return NO_ERROR;
     }
 
-    virtual status_t queryNextEffect(effect_descriptor_t *pDescriptor)
+    virtual status_t queryEffect(uint32_t index, effect_descriptor_t *pDescriptor)
     {
         if (pDescriptor == NULL) {
             return BAD_VALUE;
         }
         Parcel data, reply;
         data.writeInterfaceToken(IAudioFlinger::getInterfaceDescriptor());
-        status_t status = remote()->transact(QUERY_NEXT_EFFECT, data, &reply);
+        data.writeInt32(index);
+        status_t status = remote()->transact(QUERY_EFFECT, data, &reply);
         if (status != NO_ERROR) {
             return status;
         }
@@ -980,10 +981,10 @@
             }
             return NO_ERROR;
         }
-        case QUERY_NEXT_EFFECT: {
+        case QUERY_EFFECT: {
             CHECK_INTERFACE(IAudioFlinger, data, reply);
             effect_descriptor_t desc;
-            status_t status = queryNextEffect(&desc);
+            status_t status = queryEffect(data.readInt32(), &desc);
             reply->writeInt32(status);
             if (status == NO_ERROR) {
                 reply->write(&desc, sizeof(effect_descriptor_t));
diff --git a/media/libmediaplayerservice/StagefrightRecorder.cpp b/media/libmediaplayerservice/StagefrightRecorder.cpp
index d49c4e0..1e20f7e 100644
--- a/media/libmediaplayerservice/StagefrightRecorder.cpp
+++ b/media/libmediaplayerservice/StagefrightRecorder.cpp
@@ -310,12 +310,46 @@
 // If interval <  0, only the first frame is I frame, and rest are all P frames
 // If interval == 0, all frames are encoded as I frames. No P frames
 // If interval >  0, it is the time spacing between 2 neighboring I frames
-status_t StagefrightRecorder::setParamIFramesInterval(int32_t interval) {
-    LOGV("setParamIFramesInterval: %d seconds", interval);
+status_t StagefrightRecorder::setParamVideoIFramesInterval(int32_t interval) {
+    LOGV("setParamVideoIFramesInterval: %d seconds", interval);
     mIFramesInterval = interval;
     return OK;
 }
 
+status_t StagefrightRecorder::setParam64BitFileOffset(bool use64Bit) {
+    LOGV("setParam64BitFileOffset: %s",
+        use64Bit? "use 64 bit file offset": "use 32 bit file offset");
+    mUse64BitFileOffset = use64Bit;
+    return OK;
+}
+
+status_t StagefrightRecorder::setParamVideoCameraId(int32_t cameraId) {
+    LOGV("setParamVideoCameraId: %d", cameraId);
+    if (cameraId < 0) {
+        return BAD_VALUE;
+    }
+    mCameraId = cameraId;
+    return OK;
+}
+
+status_t StagefrightRecorder::setParamTrackFrameStatus(int32_t nFrames) {
+    LOGV("setParamTrackFrameStatus: %d", nFrames);
+    if (nFrames <= 0) {
+        return BAD_VALUE;
+    }
+    mTrackEveryNumberOfFrames = nFrames;
+    return OK;
+}
+
+status_t StagefrightRecorder::setParamTrackTimeStatus(int64_t timeDurationUs) {
+    LOGV("setParamTrackTimeStatus: %lld", timeDurationUs);
+    if (timeDurationUs < 20000) {  // Infeasible if shorter than 20 ms?
+        return BAD_VALUE;
+    }
+    mTrackEveryTimeDurationUs = timeDurationUs;
+    return OK;
+}
+
 status_t StagefrightRecorder::setParameter(
         const String8 &key, const String8 &value) {
     LOGV("setParameter: key (%s) => value (%s)", key.string(), value.string());
@@ -331,6 +365,26 @@
             return setParamMaxDurationOrFileSize(
                     max_filesize_bytes, false /* limit is filesize */);
         }
+    } else if (key == "interleave-duration-us") {
+        int32_t durationUs;
+        if (safe_strtoi32(value.string(), &durationUs)) {
+            return setParamInterleaveDuration(durationUs);
+        }
+    } else if (key == "param-use-64bit-offset") {
+        int32_t use64BitOffset;
+        if (safe_strtoi32(value.string(), &use64BitOffset)) {
+            return setParam64BitFileOffset(use64BitOffset != 0);
+        }
+    } else if (key == "param-track-frame-status") {
+        int32_t nFrames;
+        if (safe_strtoi32(value.string(), &nFrames)) {
+            return setParamTrackFrameStatus(nFrames);
+        }
+    } else if (key == "param-track-time-status") {
+        int64_t timeDurationUs;
+        if (safe_strtoi64(value.string(), &timeDurationUs)) {
+            return setParamTrackTimeStatus(timeDurationUs);
+        }
     } else if (key == "audio-param-sampling-rate") {
         int32_t sampling_rate;
         if (safe_strtoi32(value.string(), &sampling_rate)) {
@@ -351,15 +405,15 @@
         if (safe_strtoi32(value.string(), &video_bitrate)) {
             return setParamVideoEncodingBitRate(video_bitrate);
         }
-    } else if (key == "param-interleave-duration-us") {
-        int32_t durationUs;
-        if (safe_strtoi32(value.string(), &durationUs)) {
-            return setParamInterleaveDuration(durationUs);
-        }
-    } else if (key == "param-i-frames-interval") {
+    } else if (key == "video-param-i-frames-interval") {
         int32_t interval;
         if (safe_strtoi32(value.string(), &interval)) {
-            return setParamIFramesInterval(interval);
+            return setParamVideoIFramesInterval(interval);
+        }
+    } else if (key == "video-param-camera-id") {
+        int32_t cameraId;
+        if (safe_strtoi32(value.string(), &cameraId)) {
+            return setParamVideoCameraId(cameraId);
         }
     } else {
         LOGE("setParameter: failed to find key %s", key.string());
@@ -484,6 +538,7 @@
     sp<MediaSource> audioEncoder =
         OMXCodec::Create(client.interface(), encMeta,
                          true /* createEncoder */, audioSource);
+    mAudioSourceNode = audioSource;
 
     return audioEncoder;
 }
@@ -632,6 +687,7 @@
 
 status_t StagefrightRecorder::startMPEG4Recording() {
     mWriter = new MPEG4Writer(dup(mOutputFd));
+    int32_t totalBitRate = 0;
 
     // Add audio source first if it exists
     if (mAudioSource != AUDIO_SOURCE_LIST_END) {
@@ -650,7 +706,7 @@
         if (audioEncoder == NULL) {
             return UNKNOWN_ERROR;
         }
-
+        totalBitRate += mAudioBitRate;
         mWriter->addSource(audioEncoder);
     }
     if (mVideoSource == VIDEO_SOURCE_DEFAULT
@@ -663,7 +719,7 @@
 
         int64_t token = IPCThreadState::self()->clearCallingIdentity();
         if (mCamera == 0) {
-            mCamera = Camera::connect(0);
+            mCamera = Camera::connect(mCameraId);
             mCamera->lock();
         }
 
@@ -703,7 +759,7 @@
 
         sp<MetaData> enc_meta = new MetaData;
         enc_meta->setInt32(kKeyBitRate, mVideoBitRate);
-        enc_meta->setInt32(kKeySampleRate, mFrameRate);  // XXX: kKeySampleRate?
+        enc_meta->setInt32(kKeySampleRate, mFrameRate);
 
         switch (mVideoEncoder) {
             case VIDEO_ENCODER_H263:
@@ -746,12 +802,13 @@
                     true /* createEncoder */, cameraSource);
 
         CHECK(mOutputFd >= 0);
+        totalBitRate += mVideoBitRate;
         mWriter->addSource(encoder);
     }
 
     {
         // MPEGWriter specific handling
-        MPEG4Writer *writer = ((MPEG4Writer *) mWriter.get());  // mWriter is an MPEGWriter
+        MPEG4Writer *writer = ((MPEG4Writer *) mWriter.get());
         writer->setInterleaveDuration(mInterleaveDurationUs);
     }
 
@@ -762,7 +819,18 @@
         mWriter->setMaxFileSize(mMaxFileSizeBytes);
     }
     mWriter->setListener(mListener);
-    mWriter->start();
+    sp<MetaData> meta = new MetaData;
+    meta->setInt64(kKeyTime, systemTime() / 1000);
+    meta->setInt32(kKeyFileType, mOutputFormat);
+    meta->setInt32(kKeyBitRate, totalBitRate);
+    meta->setInt32(kKey64BitFileOffset, mUse64BitFileOffset);
+    if (mTrackEveryNumberOfFrames > 0) {
+        meta->setInt32(kKeyTrackFrameStatus, mTrackEveryNumberOfFrames);
+    }
+    if (mTrackEveryTimeDurationUs > 0) {
+        meta->setInt64(kKeyTrackTimeStatus, mTrackEveryTimeDurationUs);
+    }
+    mWriter->start(meta.get());
     return OK;
 }
 
@@ -822,6 +890,11 @@
     mAudioBitRate  = 12200;
     mInterleaveDurationUs = 0;
     mIFramesInterval = 1;
+    mAudioSourceNode = 0;
+    mUse64BitFileOffset = false;
+    mCameraId        = 0;
+    mTrackEveryNumberOfFrames = 0;
+    mTrackEveryTimeDurationUs = 0;
     mEncoderProfiles = MediaProfiles::getInstance();
 
     mOutputFd = -1;
@@ -831,7 +904,11 @@
 }
 
 status_t StagefrightRecorder::getMaxAmplitude(int *max) {
-    *max = 0;
+    if (mAudioSourceNode != 0) {
+        *max = mAudioSourceNode->getMaxAmplitude();
+    } else {
+        *max = 0;
+    }
 
     return OK;
 }
diff --git a/media/libmediaplayerservice/StagefrightRecorder.h b/media/libmediaplayerservice/StagefrightRecorder.h
index 7de96f6..9fb7e8f 100644
--- a/media/libmediaplayerservice/StagefrightRecorder.h
+++ b/media/libmediaplayerservice/StagefrightRecorder.h
@@ -26,6 +26,7 @@
 class Camera;
 struct MediaSource;
 struct MediaWriter;
+struct AudioSource;
 class MediaProfiles;
 
 struct StagefrightRecorder : public MediaRecorderBase {
@@ -64,12 +65,14 @@
     sp<ISurface> mPreviewSurface;
     sp<IMediaPlayerClient> mListener;
     sp<MediaWriter> mWriter;
+    sp<AudioSource> mAudioSourceNode;
 
     audio_source mAudioSource;
     video_source mVideoSource;
     output_format mOutputFormat;
     audio_encoder mAudioEncoder;
     video_encoder mVideoEncoder;
+    bool mUse64BitFileOffset;
     int32_t mVideoWidth, mVideoHeight;
     int32_t mFrameRate;
     int32_t mVideoBitRate;
@@ -78,8 +81,11 @@
     int32_t mSampleRate;
     int32_t mInterleaveDurationUs;
     int32_t mIFramesInterval;
+    int32_t mCameraId;
     int64_t mMaxFileSizeBytes;
     int64_t mMaxFileDurationUs;
+    int32_t mTrackEveryNumberOfFrames;
+    int64_t mTrackEveryTimeDurationUs;
 
     String8 mParams;
     int mOutputFd;
@@ -92,12 +98,16 @@
     status_t startAACRecording();
     sp<MediaSource> createAudioSource();
     status_t setParameter(const String8 &key, const String8 &value);
-    status_t setParamVideoEncodingBitRate(int32_t bitRate);
     status_t setParamAudioEncodingBitRate(int32_t bitRate);
     status_t setParamAudioNumberOfChannels(int32_t channles);
     status_t setParamAudioSamplingRate(int32_t sampleRate);
+    status_t setParamVideoEncodingBitRate(int32_t bitRate);
+    status_t setParamVideoIFramesInterval(int32_t interval);
+    status_t setParamVideoCameraId(int32_t cameraId);
+    status_t setParamTrackTimeStatus(int64_t timeDurationUs);
+    status_t setParamTrackFrameStatus(int32_t nFrames);
     status_t setParamInterleaveDuration(int32_t durationUs);
-    status_t setParamIFramesInterval(int32_t interval);
+    status_t setParam64BitFileOffset(bool use64BitFileOffset);
     status_t setParamMaxDurationOrFileSize(int64_t limit, bool limit_is_duration);
     void clipVideoBitRate();
     void clipVideoFrameRate();
diff --git a/media/libstagefright/AMRWriter.cpp b/media/libstagefright/AMRWriter.cpp
index 8951f5b..c71743e 100644
--- a/media/libstagefright/AMRWriter.cpp
+++ b/media/libstagefright/AMRWriter.cpp
@@ -97,7 +97,7 @@
     return OK;
 }
 
-status_t AMRWriter::start() {
+status_t AMRWriter::start(MetaData *params) {
     if (mInitCheck != OK) {
         return mInitCheck;
     }
@@ -253,7 +253,7 @@
     }
 
     if (stoppedPrematurely) {
-        notify(MEDIA_RECORDER_EVENT_INFO, MEDIA_RECORDER_INFO_STOP_PREMATURELY, 0);
+        notify(MEDIA_RECORDER_EVENT_INFO, MEDIA_RECORDER_INFO_COMPLETION_STATUS, UNKNOWN_ERROR);
     }
 
     fflush(mFile);
diff --git a/media/libstagefright/AudioSource.cpp b/media/libstagefright/AudioSource.cpp
index d203dbf..6031797 100644
--- a/media/libstagefright/AudioSource.cpp
+++ b/media/libstagefright/AudioSource.cpp
@@ -78,6 +78,8 @@
         mCollectStats = true;
     }
 
+    mTrackMaxAmplitude = false;
+    mMaxAmplitude = 0;
     mStartTimeUs = 0;
     int64_t startTimeUs;
     if (params && params->findInt64(kKeyTime, &startTimeUs)) {
@@ -168,6 +170,10 @@
         return (status_t)n;
     }
 
+    if (mTrackMaxAmplitude) {
+        trackMaxAmplitude((int16_t *) buffer->data(), n >> 1);
+    }
+
     uint32_t sampleRate = mRecord->getSampleRate();
     int64_t timestampUs = (1000000LL * numFramesRecorded) / sampleRate + mStartTimeUs;
     buffer->meta_data()->setInt64(kKeyTime, timestampUs);
@@ -181,4 +187,27 @@
     return OK;
 }
 
+void AudioSource::trackMaxAmplitude(int16_t *data, int nSamples) {
+    for (int i = nSamples; i > 0; --i) {
+        int16_t value = *data++;
+        if (value < 0) {
+            value = -value;
+        }
+        if (mMaxAmplitude < value) {
+            mMaxAmplitude = value;
+        }
+    }
+}
+
+int16_t AudioSource::getMaxAmplitude() {
+    // First call activates the tracking.
+    if (!mTrackMaxAmplitude) {
+        mTrackMaxAmplitude = true;
+    }
+    int16_t value = mMaxAmplitude;
+    mMaxAmplitude = 0;
+    LOGV("max amplitude since last call: %d", value);
+    return value;
+}
+
 }  // namespace android
diff --git a/media/libstagefright/MPEG4Writer.cpp b/media/libstagefright/MPEG4Writer.cpp
index f16b225..b3e1a01 100644
--- a/media/libstagefright/MPEG4Writer.cpp
+++ b/media/libstagefright/MPEG4Writer.cpp
@@ -41,7 +41,7 @@
     Track(MPEG4Writer *owner, const sp<MediaSource> &source);
     ~Track();
 
-    status_t start(int64_t startTimeUs);
+    status_t start(MetaData *params);
     void stop();
     void pause();
     bool reachedEOS();
@@ -101,9 +101,13 @@
     void *mCodecSpecificData;
     size_t mCodecSpecificDataSize;
     bool mGotAllCodecSpecificData;
+    bool mTrackingProgressStatus;
 
     bool mReachedEOS;
     int64_t mStartTimestampUs;
+    int64_t mPreviousTrackTimeUs;
+    int64_t mTrackEveryTimeDurationUs;
+    int32_t mTrackEveryNumberOfFrames;
 
     static void *ThreadWrapper(void *me);
     void threadEntry();
@@ -114,6 +118,8 @@
     void logStatisticalData(bool isAudio);
     void findMinMaxFrameRates(float *minFps, float *maxFps);
     void findMinMaxChunkDurations(int64_t *min, int64_t *max);
+    void trackProgressStatus(int32_t nFrames, int64_t timeUs);
+    void initTrackingProgressStatus(MetaData *params);
 
     Track(const Track &);
     Track &operator=(const Track &);
@@ -162,11 +168,10 @@
     return OK;
 }
 
-status_t MPEG4Writer::startTracks() {
-    int64_t startTimeUs = systemTime() / 1000;
+status_t MPEG4Writer::startTracks(MetaData *params) {
     for (List<Track *>::iterator it = mTracks.begin();
          it != mTracks.end(); ++it) {
-        status_t err = (*it)->start(startTimeUs);
+        status_t err = (*it)->start(params);
 
         if (err != OK) {
             for (List<Track *>::iterator it2 = mTracks.begin();
@@ -180,16 +185,78 @@
     return OK;
 }
 
-status_t MPEG4Writer::start() {
+int64_t MPEG4Writer::estimateMoovBoxSize(int32_t bitRate) {
+    // This implementation is highly experimental/heurisitic.
+    //
+    // Statistical analysis shows that metadata usually accounts
+    // for a small portion of the total file size, usually < 0.6%.
+    // Currently, lets set to 0.4% for now.
+
+    // The default MIN_MOOV_BOX_SIZE is set to 0.4% x 1MB,
+    // where 1MB is the common file size limit for MMS application.
+    // The default MAX _MOOV_BOX_SIZE value is based on about 4
+    // minute video recording with a bit rate about 3 Mbps, because
+    // statistics also show that most of the video captured are going
+    // to be less than 3 minutes.
+
+    // If the estimation is wrong, we will pay the price of wasting
+    // some reserved space. This should not happen so often statistically.
+    static const int32_t factor = mUse32BitOffset? 1: 2;
+    static const int64_t MIN_MOOV_BOX_SIZE = 4 * 1024;  // 4 KB
+    static const int64_t MAX_MOOV_BOX_SIZE = (180 * 3000000 * 6LL / 8000);
+    int64_t size = MIN_MOOV_BOX_SIZE;
+
+    if (mMaxFileSizeLimitBytes != 0) {
+        size = mMaxFileSizeLimitBytes * 4 / 1000;
+    } else if (mMaxFileDurationLimitUs != 0) {
+        if (bitRate <= 0) {
+            // We could not estimate the file size since bitRate is not set.
+            size = MIN_MOOV_BOX_SIZE;
+        } else {
+            size = ((mMaxFileDurationLimitUs * bitRate * 4) / 1000 / 8000000);
+        }
+    }
+    if (size < MIN_MOOV_BOX_SIZE) {
+        size = MIN_MOOV_BOX_SIZE;
+    }
+
+    // Any long duration recording will be probably end up with
+    // non-streamable mp4 file.
+    if (size > MAX_MOOV_BOX_SIZE) {
+        size = MAX_MOOV_BOX_SIZE;
+    }
+
+    LOGI("limits: %lld/%lld bytes/us, bit rate: %d bps and the estimated"
+         " moov size %lld bytes",
+         mMaxFileSizeLimitBytes, mMaxFileDurationLimitUs, bitRate, size);
+    return factor * size;
+}
+
+status_t MPEG4Writer::start(MetaData *param) {
     if (mFile == NULL) {
         return UNKNOWN_ERROR;
     }
 
+    int32_t use64BitOffset;
+    if (param &&
+        param->findInt32(kKey64BitFileOffset, &use64BitOffset) &&
+        use64BitOffset) {
+        mUse32BitOffset = false;
+    }
+
+    // System property can overwrite the file offset bits parameter
+    char value[PROPERTY_VALUE_MAX];
+    if (property_get("media.stagefright.record-64bits", value, NULL)
+        && (!strcmp(value, "1") || !strcasecmp(value, "true"))) {
+        mUse32BitOffset = false;
+    }
+
     mStartTimestampUs = -1;
+
     if (mStarted) {
         if (mPaused) {
             mPaused = false;
-            return startTracks();
+            return startTracks(param);
         }
         return OK;
     }
@@ -200,17 +267,28 @@
     mMoovBoxBufferOffset = 0;
 
     beginBox("ftyp");
-      writeFourcc("isom");
+      {
+        int32_t fileType;
+        if (param && param->findInt32(kKeyFileType, &fileType) &&
+            fileType != OUTPUT_FORMAT_MPEG_4) {
+            writeFourcc("3gp4");
+        } else {
+            writeFourcc("isom");
+        }
+      }
       writeInt32(0);
       writeFourcc("isom");
+      writeFourcc("3gp4");
     endBox();
 
     mFreeBoxOffset = mOffset;
 
     if (mEstimatedMoovBoxSize == 0) {
-        // XXX: Estimate the moov box size
-        //      based on max file size or duration limit
-        mEstimatedMoovBoxSize = 0x0F00;
+        int32_t bitRate = -1;
+        if (param) {
+            param->findInt32(kKeyBitRate, &bitRate);
+        }
+        mEstimatedMoovBoxSize = estimateMoovBoxSize(bitRate);
     }
     CHECK(mEstimatedMoovBoxSize >= 8);
     fseeko(mFile, mFreeBoxOffset, SEEK_SET);
@@ -225,8 +303,7 @@
     } else {
         write("\x00\x00\x00\x01mdat????????", 16);
     }
-
-    status_t err = startTracks();
+    status_t err = startTracks(param);
     if (err != OK) {
         return err;
     }
@@ -332,8 +409,7 @@
         write(mMoovBoxBuffer, 1, mMoovBoxBufferOffset, mFile);
 
         // Free box
-        mFreeBoxOffset = mStreamableFile? mOffset: mFreeBoxOffset;
-        fseeko(mFile, mFreeBoxOffset, SEEK_SET);
+        fseeko(mFile, mOffset, SEEK_SET);
         writeInt32(mEstimatedMoovBoxSize - mMoovBoxBufferOffset);
         write("free", 4);
 
@@ -341,6 +417,8 @@
         free(mMoovBoxBuffer);
         mMoovBoxBuffer = NULL;
         mMoovBoxBufferOffset = 0;
+    } else {
+        LOGI("The mp4 file will not be streamable.");
     }
 
     CHECK(mBoxes.empty());
@@ -606,13 +684,41 @@
     }
 }
 
-status_t MPEG4Writer::Track::start(int64_t startTimeUs) {
+void MPEG4Writer::Track::initTrackingProgressStatus(MetaData *params) {
+    LOGV("initTrackingProgressStatus");
+    mPreviousTrackTimeUs = -1;
+    mTrackingProgressStatus = false;
+    mTrackEveryTimeDurationUs = 0;
+    mTrackEveryNumberOfFrames = 0;
+    {
+        int64_t timeUs;
+        if (params && params->findInt64(kKeyTrackTimeStatus, &timeUs)) {
+            LOGV("Receive request to track progress status for every %lld us", timeUs);
+            mTrackEveryTimeDurationUs = timeUs;
+            mTrackingProgressStatus = true;
+        }
+    }
+    {
+        int32_t nFrames;
+        if (params && params->findInt32(kKeyTrackFrameStatus, &nFrames)) {
+            LOGV("Receive request to track progress status for every %d frames", nFrames);
+            mTrackEveryNumberOfFrames = nFrames;
+            mTrackingProgressStatus = true;
+        }
+    }
+}
+
+status_t MPEG4Writer::Track::start(MetaData *params) {
     if (!mDone && mPaused) {
         mPaused = false;
         mResumed = true;
         return OK;
     }
 
+    int64_t startTimeUs;
+    CHECK(params && params->findInt64(kKeyTime, &startTimeUs));
+    initTrackingProgressStatus(params);
+
     sp<MetaData> meta = new MetaData;
     meta->setInt64(kKeyTime, startTimeUs);
     status_t err = mSource->start(meta.get());
@@ -784,8 +890,9 @@
     int64_t previousPausedDurationUs = 0;
     sp<MetaData> meta_data;
 
+    status_t err = OK;
     MediaBuffer *buffer;
-    while (!mDone && mSource->read(&buffer) == OK) {
+    while (!mDone && (err = mSource->read(&buffer)) == OK) {
         if (buffer->range_length() == 0) {
             buffer->release();
             buffer = NULL;
@@ -998,6 +1105,12 @@
             mStssTableEntries.push_back(mSampleInfos.size());
         }
 
+        if (mTrackingProgressStatus) {
+            if (mPreviousTrackTimeUs <= 0) {
+                mPreviousTrackTimeUs = mStartTimestampUs;
+            }
+            trackProgressStatus(mSampleInfos.size(), timestampUs);
+        }
         if (mOwner->numTracks() == 1) {
             off_t offset = is_avc? mOwner->addLengthPrefixedSample_l(copy)
                                  : mOwner->addSample_l(copy);
@@ -1037,8 +1150,9 @@
     }
 
     if (mSampleInfos.empty()) {
-        mOwner->notify(MEDIA_RECORDER_EVENT_INFO, MEDIA_RECORDER_INFO_STOP_PREMATURELY, 0);
+        err = UNKNOWN_ERROR;
     }
+    mOwner->notify(MEDIA_RECORDER_EVENT_INFO, MEDIA_RECORDER_INFO_COMPLETION_STATUS, err);
 
     // Last chunk
     if (mOwner->numTracks() == 1) {
@@ -1068,6 +1182,24 @@
     logStatisticalData(is_audio);
 }
 
+void MPEG4Writer::Track::trackProgressStatus(int32_t nFrames, int64_t timeUs) {
+    LOGV("trackProgressStatus: %d frames and %lld us", nFrames, timeUs);
+    if (nFrames % mTrackEveryNumberOfFrames == 0) {
+        LOGV("Fire frame tracking progress status at frame %d", nFrames);
+        mOwner->notify(MEDIA_RECORDER_EVENT_INFO,
+                       MEDIA_RECORDER_INFO_PROGRESS_FRAME_STATUS,
+                       nFrames);
+    }
+
+    if (timeUs - mPreviousTrackTimeUs >= mTrackEveryTimeDurationUs) {
+        LOGV("Fire time tracking progress status at %lld us", timeUs);
+        mOwner->notify(MEDIA_RECORDER_EVENT_INFO,
+                       MEDIA_RECORDER_INFO_PROGRESS_TIME_STATUS,
+                       timeUs / 1000);
+        mPreviousTrackTimeUs = timeUs;
+    }
+}
+
 void MPEG4Writer::Track::findMinMaxFrameRates(float *minFps, float *maxFps) {
     int32_t minSampleDuration = 0x7FFFFFFF;
     int32_t maxSampleDuration = 0;
diff --git a/media/libstagefright/include/NuCachedSource2.h b/media/libstagefright/include/NuCachedSource2.h
index f73e837..3a20c16 100644
--- a/media/libstagefright/include/NuCachedSource2.h
+++ b/media/libstagefright/include/NuCachedSource2.h
@@ -49,8 +49,8 @@
     friend struct AHandlerReflector<NuCachedSource2>;
 
     enum {
-        kPageSize            = 16384,
-        kHighWaterThreshold  = 3 * 1024 * 1024,
+        kPageSize            = 65536,
+        kHighWaterThreshold  = 5 * 1024 * 1024,
         kLowWaterThreshold   = 512 * 1024,
 
         // Read data after a 15 sec timeout whether we're actively
diff --git a/services/java/com/android/server/WifiService.java b/services/java/com/android/server/WifiService.java
index 6d9888b..35baaa7 100644
--- a/services/java/com/android/server/WifiService.java
+++ b/services/java/com/android/server/WifiService.java
@@ -619,6 +619,7 @@
     }
 
     public WifiConfiguration getWifiApConfiguration() {
+        enforceAccessPermission();
         final ContentResolver cr = mContext.getContentResolver();
         WifiConfiguration wifiConfig = new WifiConfiguration();
         int authType;
@@ -636,7 +637,8 @@
         }
     }
 
-    private void persistApConfiguration(WifiConfiguration wifiConfig) {
+    public void setWifiApConfiguration(WifiConfiguration wifiConfig) {
+        enforceChangePermission();
         final ContentResolver cr = mContext.getContentResolver();
         boolean isWpa;
         if (wifiConfig == null)
@@ -669,7 +671,7 @@
                 try {
                     nwService.setAccessPoint(wifiConfig, mWifiStateTracker.getInterfaceName(),
                                              SOFTAP_IFACE);
-                    persistApConfiguration(wifiConfig);
+                    setWifiApConfiguration(wifiConfig);
                     return true;
                 } catch(Exception e) {
                     Slog.e(TAG, "Exception in nwService during AP restart");
@@ -721,7 +723,7 @@
                 return false;
             }
 
-            persistApConfiguration(wifiConfig);
+            setWifiApConfiguration(wifiConfig);
 
         } else {
 
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index 252392b..2c6806b 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -26,6 +26,7 @@
 import com.android.server.SystemServer;
 import com.android.server.Watchdog;
 import com.android.server.WindowManagerService;
+import com.android.server.am.ActivityStack.ActivityState;
 
 import dalvik.system.Zygote;
 
@@ -183,47 +184,21 @@
 
     // Maximum number of recent tasks that we can remember.
     static final int MAX_RECENT_TASKS = 20;
-
+    
     // Amount of time after a call to stopAppSwitches() during which we will
     // prevent further untrusted switches from happening.
     static final long APP_SWITCH_DELAY_TIME = 5*1000;
-    
-    // How long until we reset a task when the user returns to it.  Currently
-    // 30 minutes.
-    static final long ACTIVITY_INACTIVE_RESET_TIME = 1000*60*30;
-    
-    // Set to true to disable the icon that is shown while a new activity
-    // is being started.
-    static final boolean SHOW_APP_STARTING_ICON = true;
-
-    // How long we wait until giving up on the last activity to pause.  This
-    // is short because it directly impacts the responsiveness of starting the
-    // next activity.
-    static final int PAUSE_TIMEOUT = 500;
-
-    /**
-     * How long we can hold the launch wake lock before giving up.
-     */
-    static final int LAUNCH_TIMEOUT = 10*1000;
 
     // How long we wait for a launched process to attach to the activity manager
     // before we decide it's never going to come up for real.
     static final int PROC_START_TIMEOUT = 10*1000;
 
-    // How long we wait until giving up on the last activity telling us it
-    // is idle.
-    static final int IDLE_TIMEOUT = 10*1000;
-
     // How long to wait after going idle before forcing apps to GC.
     static final int GC_TIMEOUT = 5*1000;
 
     // The minimum amount of time between successive GC requests for a process.
     static final int GC_MIN_INTERVAL = 60*1000;
 
-    // How long we wait until giving up on an activity telling us it has
-    // finished destroying itself.
-    static final int DESTROY_TIMEOUT = 10*1000;
-    
     // How long we allow a receiver to run before giving up on it.
     static final int BROADCAST_TIMEOUT = 10*1000;
 
@@ -388,29 +363,13 @@
     
     static final String[] EMPTY_STRING_ARRAY = new String[0];
 
-    enum ActivityState {
-        INITIALIZING,
-        RESUMED,
-        PAUSING,
-        PAUSED,
-        STOPPING,
-        STOPPED,
-        FINISHING,
-        DESTROYING,
-        DESTROYED
-    }
-
-    /**
-     * The back history of all previous (and possibly still
-     * running) activities.  It contains HistoryRecord objects.
-     */
-    final ArrayList mHistory = new ArrayList();
-
+    public ActivityStack mMainStack;
+    
     /**
      * Description of a request to start a new activity, which has been held
      * due to app switches being disabled.
      */
-    class PendingActivityLaunch {
+    static class PendingActivityLaunch {
         ActivityRecord r;
         ActivityRecord sourceRecord;
         Uri[] grantedUriPermissions;
@@ -422,18 +381,6 @@
             = new ArrayList<PendingActivityLaunch>();
     
     /**
-     * List of people waiting to find out about the next launched activity.
-     */
-    final ArrayList<IActivityManager.WaitResult> mWaitingActivityLaunched
-            = new ArrayList<IActivityManager.WaitResult>();
-    
-    /**
-     * List of people waiting to find out about the next visible activity.
-     */
-    final ArrayList<IActivityManager.WaitResult> mWaitingActivityVisible
-            = new ArrayList<IActivityManager.WaitResult>();
-    
-    /**
      * List of all active broadcasts that are to be executed immediately
      * (without waiting for another broadcast to finish).  Currently this only
      * contains broadcasts to registered receivers, to avoid spinning up
@@ -463,57 +410,9 @@
     boolean mBroadcastsScheduled = false;
 
     /**
-     * Set to indicate whether to issue an onUserLeaving callback when a
-     * newly launched activity is being brought in front of us.
-     */
-    boolean mUserLeaving = false;
-
-    /**
-     * When we are in the process of pausing an activity, before starting the
-     * next one, this variable holds the activity that is currently being paused.
-     */
-    ActivityRecord mPausingActivity = null;
-
-    /**
-     * Current activity that is resumed, or null if there is none.
-     */
-    ActivityRecord mResumedActivity = null;
-
-    /**
      * Activity we have told the window manager to have key focus.
      */
     ActivityRecord mFocusedActivity = null;
-
-    /**
-     * This is the last activity that we put into the paused state.  This is
-     * used to determine if we need to do an activity transition while sleeping,
-     * when we normally hold the top activity paused.
-     */
-    ActivityRecord mLastPausedActivity = null;
-
-    /**
-     * List of activities that are waiting for a new activity
-     * to become visible before completing whatever operation they are
-     * supposed to do.
-     */
-    final ArrayList<ActivityRecord> mWaitingVisibleActivities
-            = new ArrayList<ActivityRecord>();
-
-    /**
-     * List of activities that are ready to be stopped, but waiting
-     * for the next activity to settle down before doing so.  It contains
-     * HistoryRecord objects.
-     */
-    final ArrayList<ActivityRecord> mStoppingActivities
-            = new ArrayList<ActivityRecord>();
-
-    /**
-     * Animations that for the current transition have requested not to
-     * be considered for the transition animation.
-     */
-    final ArrayList<ActivityRecord> mNoAnimActivities
-            = new ArrayList<ActivityRecord>();
-    
     /**
      * List of intents that were used to start the most recent tasks.
      */
@@ -521,14 +420,6 @@
             = new ArrayList<TaskRecord>();
 
     /**
-     * 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>();
-
-    /**
      * All of the applications we currently have running organized by name.
      * The keys are strings of the application package name (as
      * returned by the package manager), and the keys are ApplicationRecord
@@ -628,16 +519,9 @@
      * This is the process holding what we currently consider to be
      * the "home" activity.
      */
-    private ProcessRecord mHomeProcess;
+    ProcessRecord mHomeProcess;
     
     /**
-     * List of running activities, sorted by recent usage.
-     * The first entry in the list is the least recently used.
-     * It contains HistoryRecord objects.
-     */
-    private final ArrayList mLRUActivities = new ArrayList();
-
-    /**
      * Set of PendingResultRecord objects that are currently active.
      */
     final HashSet mPendingResultRecords = new HashSet();
@@ -831,12 +715,6 @@
     int mConfigurationSeq = 0;
     
     /**
-     * Set when we know we are going to be calling updateConfiguration()
-     * soon, so want to skip intermediate config checks.
-     */
-    boolean mConfigWillChange;
-    
-    /**
      * Hardware-reported OpenGLES version.
      */
     final int GL_ES_VERSION;
@@ -892,21 +770,6 @@
      * Set if we are shutting down the system, similar to sleeping.
      */
     boolean mShuttingDown = false;
-    
-    /**
-     * 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.
-     */
-    PowerManager.WakeLock mGoingToSleep;
-
-    /**
-     * We don't want to allow the device to go to sleep while in the process
-     * of launching an activity.  This is primarily to allow alarm intent
-     * receivers to launch an activity and get that to run before the device
-     * goes back to sleep.
-     */
-    PowerManager.WakeLock mLaunchingActivity;
 
     /**
      * Task identifier that activities are currently being started
@@ -986,8 +849,6 @@
 
     long mLastWriteTime = 0;
 
-    long mInitialStartTime = 0;
-    
     /**
      * Set to true after the system has finished booting.
      */
@@ -1034,16 +895,10 @@
     static final int WAIT_FOR_DEBUGGER_MSG = 6;
     static final int BROADCAST_INTENT_MSG = 7;
     static final int BROADCAST_TIMEOUT_MSG = 8;
-    static final int PAUSE_TIMEOUT_MSG = 9;
-    static final int IDLE_TIMEOUT_MSG = 10;
-    static final int IDLE_NOW_MSG = 11;
     static final int SERVICE_TIMEOUT_MSG = 12;
     static final int UPDATE_TIME_ZONE = 13;
     static final int SHOW_UID_ERROR_MSG = 14;
     static final int IM_FEELING_LUCKY_MSG = 15;
-    static final int LAUNCH_TIMEOUT_MSG = 16;
-    static final int DESTROY_TIMEOUT_MSG = 17;
-    static final int RESUME_TOP_ACTIVITY_MSG = 19;
     static final int PROC_START_TIMEOUT_MSG = 20;
     static final int DO_PENDING_ACTIVITY_LAUNCHES_MSG = 21;
     static final int KILL_APPLICATION_MSG = 22;
@@ -1183,38 +1038,6 @@
                     broadcastTimeout();
                 }
             } break;
-            case PAUSE_TIMEOUT_MSG: {
-                IBinder token = (IBinder)msg.obj;
-                // We don't at this point know if the activity is fullscreen,
-                // so we need to be conservative and assume it isn't.
-                Slog.w(TAG, "Activity pause timeout for " + token);
-                activityPaused(token, null, true);
-            } break;
-            case IDLE_TIMEOUT_MSG: {
-                if (mDidDexOpt) {
-                    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.
-                IBinder token = (IBinder)msg.obj;
-                Slog.w(TAG, "Activity idle timeout for " + token);
-                activityIdleInternal(token, true, null);
-            } break;
-            case DESTROY_TIMEOUT_MSG: {
-                IBinder token = (IBinder)msg.obj;
-                // We don't at this point know if the activity is fullscreen,
-                // so we need to be conservative and assume it isn't.
-                Slog.w(TAG, "Activity destroy timeout for " + token);
-                activityDestroyed(token);
-            } break;
-            case IDLE_NOW_MSG: {
-                IBinder token = (IBinder)msg.obj;
-                activityIdle(token, null);
-            } break;
             case SERVICE_TIMEOUT_MSG: {
                 if (mDidDexOpt) {
                     mDidDexOpt = false;
@@ -1257,25 +1080,6 @@
                     mUidAlert = null;
                 }
             } break;
-            case LAUNCH_TIMEOUT_MSG: {
-                if (mDidDexOpt) {
-                    mDidDexOpt = false;
-                    Message nmsg = mHandler.obtainMessage(LAUNCH_TIMEOUT_MSG);
-                    mHandler.sendMessageDelayed(nmsg, LAUNCH_TIMEOUT);
-                    return;
-                }
-                synchronized (ActivityManagerService.this) {
-                    if (mLaunchingActivity.isHeld()) {
-                        Slog.w(TAG, "Launch timeout has expired, giving up wake lock!");
-                        mLaunchingActivity.release();
-                    }
-                }
-            } break;
-            case RESUME_TOP_ACTIVITY_MSG: {
-                synchronized (ActivityManagerService.this) {
-                    resumeTopActivityLocked(null);
-                }
-            } break;
             case PROC_START_TIMEOUT_MSG: {
                 if (mDidDexOpt) {
                     mDidDexOpt = false;
@@ -1424,11 +1228,7 @@
         Context context = at.getSystemContext();
         m.mContext = context;
         m.mFactoryTest = factoryTest;
-        PowerManager pm =
-            (PowerManager)context.getSystemService(Context.POWER_SERVICE);
-        m.mGoingToSleep = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "ActivityManager-Sleep");
-        m.mLaunchingActivity = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "ActivityManager-Launch");
-        m.mLaunchingActivity.setReferenceCounted(false);
+        m.mMainStack = new ActivityStack(m, context, true);
         
         m.mBatteryStatsService.publish(context);
         m.mUsageStatsService.publish(context);
@@ -1717,7 +1517,7 @@
         return mAppBindArgs;
     }
 
-    private final void setFocusedActivityLocked(ActivityRecord r) {
+    final void setFocusedActivityLocked(ActivityRecord r) {
         if (mFocusedActivity != r) {
             mFocusedActivity = r;
             mWindowManager.setFocusedApp(r, true);
@@ -1802,65 +1602,13 @@
         }
     }
 
-    private final void updateLruProcessLocked(ProcessRecord app,
+    final void updateLruProcessLocked(ProcessRecord app,
             boolean oomAdj, boolean updateActivityTime) {
         mLruSeq++;
         updateLruProcessInternalLocked(app, oomAdj, updateActivityTime, 0);
     }
     
-    private final boolean updateLRUListLocked(ActivityRecord r) {
-        final boolean hadit = mLRUActivities.remove(r);
-        mLRUActivities.add(r);
-        return hadit;
-    }
-
-    private final ActivityRecord topRunningActivityLocked(ActivityRecord notTop) {
-        int i = mHistory.size()-1;
-        while (i >= 0) {
-            ActivityRecord r = (ActivityRecord)mHistory.get(i);
-            if (!r.finishing && r != notTop) {
-                return r;
-            }
-            i--;
-        }
-        return null;
-    }
-
-    private final ActivityRecord topRunningNonDelayedActivityLocked(ActivityRecord notTop) {
-        int i = mHistory.size()-1;
-        while (i >= 0) {
-            ActivityRecord r = (ActivityRecord)mHistory.get(i);
-            if (!r.finishing && !r.delayedResume && r != notTop) {
-                return r;
-            }
-            i--;
-        }
-        return null;
-    }
-
-    /**
-     * This is a simplified version of topRunningActivityLocked that provides a number of
-     * optional skip-over modes.  It is intended for use with the ActivityController hook only.
-     * 
-     * @param token If non-null, any history records matching this token will be skipped.
-     * @param taskId If non-zero, we'll attempt to skip over records with the same task ID.
-     * 
-     * @return Returns the HistoryRecord of the next activity on the stack.
-     */
-    private final ActivityRecord topRunningActivityLocked(IBinder token, int taskId) {
-        int i = mHistory.size()-1;
-        while (i >= 0) {
-            ActivityRecord r = (ActivityRecord)mHistory.get(i);
-            // Note: the taskId check depends on real taskId fields being non-zero
-            if (!r.finishing && (token != r) && (taskId != r.task.taskId)) {
-                return r;
-            }
-            i--;
-        }
-        return null;
-    }
-
-    private final ProcessRecord getProcessRecordLocked(
+    final ProcessRecord getProcessRecordLocked(
             String processName, int uid) {
         if (uid == Process.SYSTEM_UID) {
             // The system gets to run in any process.  If there are multiple
@@ -1874,7 +1622,7 @@
         return proc;
     }
 
-    private void ensurePackageDexOpt(String packageName) {
+    void ensurePackageDexOpt(String packageName) {
         IPackageManager pm = AppGlobals.getPackageManager();
         try {
             if (pm.performDexOpt(packageName)) {
@@ -1884,175 +1632,14 @@
         }
     }
     
-    private boolean isNextTransitionForward() {
+    boolean isNextTransitionForward() {
         int transit = mWindowManager.getPendingAppTransition();
         return transit == WindowManagerPolicy.TRANSIT_ACTIVITY_OPEN
                 || transit == WindowManagerPolicy.TRANSIT_TASK_OPEN
                 || transit == WindowManagerPolicy.TRANSIT_TASK_TO_FRONT;
     }
     
-    private final boolean realStartActivityLocked(ActivityRecord r,
-            ProcessRecord app, boolean andResume, boolean checkConfig)
-            throws RemoteException {
-
-        r.startFreezingScreenLocked(app, 0);
-        mWindowManager.setAppVisibility(r, true);
-
-        // 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 = mWindowManager.updateOrientationFromAppTokens(
-                    mConfiguration,
-                    r.mayFreezeScreenLocked(app) ? r : null);
-            updateConfigurationLocked(config, r);
-        }
-
-        r.app = app;
-
-        if (localLOGV) Slog.v(TAG, "Launching: " + r);
-
-        int idx = app.activities.indexOf(r);
-        if (idx < 0) {
-            app.activities.add(r);
-        }
-        updateLruProcessLocked(app, true, true);
-
-        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,
-                        System.identityHashCode(r),
-                        r.task.taskId, r.shortComponentName);
-            }
-            if (r.isHomeActivity) {
-                mHomeProcess = app;
-            }
-            ensurePackageDexOpt(r.intent.getComponent().getPackageName());
-            app.thread.scheduleLaunchActivity(new Intent(r.intent), r,
-                    System.identityHashCode(r),
-                    r.info, r.icicle, results, newIntents, !andResume,
-                    isNextTransitionForward());
-            
-            if ((app.info.flags&ApplicationInfo.FLAG_HEAVY_WEIGHT) != 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 (mHeavyWeightProcess != null && mHeavyWeightProcess != app) {
-                        Log.w(TAG, "Starting new heavy weight process " + app
-                                + " when already running " + mHeavyWeightProcess);
-                    }
-                    mHeavyWeightProcess = app;
-                    Message msg = mHandler.obtainMessage(POST_HEAVY_NOTIFICATION_MSG);
-                    msg.obj = r;
-                    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);
-                appDiedLocked(app, app.pid, app.thread);
-                requestFinishActivityLocked(r, Activity.RESULT_CANCELED, null,
-                        "2nd-crash");
-                return false;
-            }
-
-            // This is the first time we failed -- restart process and
-            // retry.
-            app.activities.remove(r);
-            throw e;
-        }
-
-        r.launchFailed = false;
-        if (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.
-            r.state = ActivityState.RESUMED;
-            r.icicle = null;
-            r.haveState = false;
-            r.stopped = false;
-            mResumedActivity = r;
-            r.task.touchActiveTime();
-            completeResumeLocked(r);
-            pauseIfSleepingLocked();                
-        } 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.
-            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.
-        startSetupActivityLocked();
-        
-        return true;
-    }
-
-    private final void startSpecificActivityLocked(ActivityRecord r,
-            boolean andResume, boolean checkConfig) {
-        // Is this activity's application already running?
-        ProcessRecord app = getProcessRecordLocked(r.processName,
-                r.info.applicationInfo.uid);
-        
-        if (r.startTime == 0) {
-            r.startTime = SystemClock.uptimeMillis();
-            if (mInitialStartTime == 0) {
-                mInitialStartTime = r.startTime;
-            }
-        } else if (mInitialStartTime == 0) {
-            mInitialStartTime = SystemClock.uptimeMillis();
-        }
-        
-        if (app != null && app.thread != null) {
-            try {
-                realStartActivityLocked(r, app, andResume, checkConfig);
-                return;
-            } catch (RemoteException e) {
-                Slog.w(TAG, "Exception when starting activity "
-                        + r.intent.getComponent().flattenToShortString(), e);
-            }
-
-            // If a dead object exception was thrown -- fall through to
-            // restart the application.
-        }
-
-        startProcessLocked(r.processName, r.info.applicationInfo, true, 0,
-                "activity", r.intent.getComponent(), false);
-    }
-
-    private final ProcessRecord startProcessLocked(String processName,
+    final ProcessRecord startProcessLocked(String processName,
             ApplicationInfo info, boolean knownToBeDead, int intentFlags,
             String hostingType, ComponentName hostingName, boolean allowWhileBooting) {
         ProcessRecord app = getProcessRecordLocked(processName, info.uid);
@@ -2253,365 +1840,7 @@
         }
     }
 
-    private final void startPausingLocked(boolean userLeaving, boolean uiSleeping) {
-        if (mPausingActivity != null) {
-            RuntimeException e = new RuntimeException();
-            Slog.e(TAG, "Trying to pause when pause is already pending for "
-                  + mPausingActivity, e);
-        }
-        ActivityRecord prev = mResumedActivity;
-        if (prev == null) {
-            RuntimeException e = new RuntimeException();
-            Slog.e(TAG, "Trying to pause when nothing is resumed", e);
-            resumeTopActivityLocked(null);
-            return;
-        }
-        if (DEBUG_PAUSE) Slog.v(TAG, "Start pausing: " + prev);
-        mResumedActivity = null;
-        mPausingActivity = prev;
-        mLastPausedActivity = prev;
-        prev.state = ActivityState.PAUSING;
-        prev.task.touchActiveTime();
-
-        updateCpuStats();
-        
-        if (prev.app != null && prev.app.thread != null) {
-            if (DEBUG_PAUSE) Slog.v(TAG, "Enqueueing pending pause: " + prev);
-            try {
-                EventLog.writeEvent(EventLogTags.AM_PAUSE_ACTIVITY,
-                        System.identityHashCode(prev),
-                        prev.shortComponentName);
-                prev.app.thread.schedulePauseActivity(prev, prev.finishing, userLeaving,
-                        prev.configChangeFlags);
-                updateUsageStats(prev, false);
-            } catch (Exception e) {
-                // Ignore exception, if process died other code will cleanup.
-                Slog.w(TAG, "Exception thrown during pause", e);
-                mPausingActivity = null;
-                mLastPausedActivity = null;
-            }
-        } else {
-            mPausingActivity = null;
-            mLastPausedActivity = null;
-        }
-
-        // If we are not going to sleep, we want to ensure the device is
-        // awake until the next activity is started.
-        if (!mSleeping && !mShuttingDown) {
-            mLaunchingActivity.acquire();
-            if (!mHandler.hasMessages(LAUNCH_TIMEOUT_MSG)) {
-                // To be safe, don't allow the wake lock to be held for too long.
-                Message msg = mHandler.obtainMessage(LAUNCH_TIMEOUT_MSG);
-                mHandler.sendMessageDelayed(msg, LAUNCH_TIMEOUT);
-            }
-        }
-
-
-        if (mPausingActivity != null) {
-            // Have the window manager pause its key dispatching until the new
-            // activity has started.  If we're pausing the activity just because
-            // the screen is being turned off and the UI is sleeping, don't interrupt
-            // key dispatch; the same activity will pick it up again on wakeup.
-            if (!uiSleeping) {
-                prev.pauseKeyDispatchingLocked();
-            } else {
-                if (DEBUG_PAUSE) Slog.v(TAG, "Key dispatch not paused for screen off");
-            }
-
-            // Schedule a pause timeout in case the app doesn't respond.
-            // We don't give it much time because this directly impacts the
-            // responsiveness seen by the user.
-            Message msg = mHandler.obtainMessage(PAUSE_TIMEOUT_MSG);
-            msg.obj = prev;
-            mHandler.sendMessageDelayed(msg, PAUSE_TIMEOUT);
-            if (DEBUG_PAUSE) Slog.v(TAG, "Waiting for pause to complete...");
-        } else {
-            // This activity failed to schedule the
-            // pause, so just treat it as being paused now.
-            if (DEBUG_PAUSE) Slog.v(TAG, "Activity not running, resuming next.");
-            resumeTopActivityLocked(null);
-        }
-    }
-
-    private final void completePauseLocked() {
-        ActivityRecord prev = mPausingActivity;
-        if (DEBUG_PAUSE) Slog.v(TAG, "Complete pause: " + prev);
-        
-        if (prev != null) {
-            if (prev.finishing) {
-                if (DEBUG_PAUSE) Slog.v(TAG, "Executing finish of activity: " + prev);
-                prev = finishCurrentActivityLocked(prev, FINISH_AFTER_VISIBLE);
-            } else if (prev.app != null) {
-                if (DEBUG_PAUSE) Slog.v(TAG, "Enqueueing pending stop: " + prev);
-                if (prev.waitingVisible) {
-                    prev.waitingVisible = false;
-                    mWaitingVisibleActivities.remove(prev);
-                    if (DEBUG_SWITCH || DEBUG_PAUSE) Slog.v(
-                            TAG, "Complete pause, no longer waiting: " + prev);
-                }
-                if (prev.configDestroy) {
-                    // The previous is being paused because the configuration
-                    // is changing, which means it is actually stopping...
-                    // To juggle the fact that we are also starting a new
-                    // instance right now, we need to first completely stop
-                    // the current instance before starting the new one.
-                    if (DEBUG_PAUSE) Slog.v(TAG, "Destroying after pause: " + prev);
-                    destroyActivityLocked(prev, true);
-                } else {
-                    mStoppingActivities.add(prev);
-                    if (mStoppingActivities.size() > 3) {
-                        // If we already have a few activities waiting to stop,
-                        // then give up on things going idle and start clearing
-                        // them out.
-                        if (DEBUG_PAUSE) Slog.v(TAG, "To many pending stops, forcing idle");
-                        Message msg = Message.obtain();
-                        msg.what = ActivityManagerService.IDLE_NOW_MSG;
-                        mHandler.sendMessage(msg);
-                    }
-                }
-            } else {
-                if (DEBUG_PAUSE) Slog.v(TAG, "App died during pause, not stopping: " + prev);
-                prev = null;
-            }
-            mPausingActivity = null;
-        }
-
-        if (!mSleeping && !mShuttingDown) {
-            resumeTopActivityLocked(prev);
-        } else {
-            if (mGoingToSleep.isHeld()) {
-                mGoingToSleep.release();
-            }
-            if (mShuttingDown) {
-                notifyAll();
-            }
-        }
-        
-        if (prev != null) {
-            prev.resumeKeyDispatchingLocked();
-        }
-
-        if (prev.app != null && prev.cpuTimeAtResume > 0 && mBatteryStatsService.isOnBattery()) {
-            long diff = 0;
-            synchronized (mProcessStatsThread) {
-                diff = mProcessStats.getCpuTimeForPid(prev.app.pid) - prev.cpuTimeAtResume;
-            }
-            if (diff > 0) {
-                BatteryStatsImpl bsi = mBatteryStatsService.getActiveStatistics();
-                synchronized (bsi) {
-                    BatteryStatsImpl.Uid.Proc ps =
-                            bsi.getProcessStatsLocked(prev.info.applicationInfo.uid,
-                            prev.info.packageName);
-                    if (ps != null) {
-                        ps.addForegroundTimeLocked(diff);
-                    }
-                }
-            }
-        }
-        prev.cpuTimeAtResume = 0; // reset it
-    }
-
-    /**
-     * Once we know that we have asked an application to put an activity in
-     * the resumed state (either by launching it or explicitly telling it),
-     * this function updates the rest of our state to match that fact.
-     */
-    private final void completeResumeLocked(ActivityRecord next) {
-        next.idle = false;
-        next.results = null;
-        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);
-        }
-
-        reportResumedActivityLocked(next);
-        
-        next.thumbnail = null;
-        setFocusedActivityLocked(next);
-        next.resumeKeyDispatchingLocked();
-        ensureActivitiesVisibleLocked(null, 0);
-        mWindowManager.executeAppTransition();
-        mNoAnimActivities.clear();
-
-        // Mark the point when the activity is resuming
-        // TODO: To be more accurate, the mark should be before the onCreate,
-        //       not after the onResume. But for subsequent starts, onResume is fine.
-        if (next.app != null) {
-            synchronized (mProcessStatsThread) {
-                next.cpuTimeAtResume = mProcessStats.getCpuTimeForPid(next.app.pid);
-            }
-        } else {
-            next.cpuTimeAtResume = 0; // Couldn't get the cpu time of process
-        }
-    }
-
-    /**
-     * Make sure that all activities that need to be visible (that is, they
-     * currently can be seen by the user) actually are.
-     */
-    private final void ensureActivitiesVisibleLocked(ActivityRecord top,
-            ActivityRecord starting, String onlyThisProcess, int configChanges) {
-        if (DEBUG_VISBILITY) Slog.v(
-                TAG, "ensureActivitiesVisible behind " + top
-                + " configChanges=0x" + Integer.toHexString(configChanges));
-
-        // If the top activity is not fullscreen, then we need to
-        // make sure any activities under it are now visible.
-        final int count = mHistory.size();
-        int i = count-1;
-        while (mHistory.get(i) != top) {
-            i--;
-        }
-        ActivityRecord r;
-        boolean behindFullscreen = false;
-        for (; i>=0; i--) {
-            r = (ActivityRecord)mHistory.get(i);
-            if (DEBUG_VISBILITY) Slog.v(
-                    TAG, "Make visible? " + r + " finishing=" + r.finishing
-                    + " state=" + r.state);
-            if (r.finishing) {
-                continue;
-            }
-            
-            final boolean doThisProcess = onlyThisProcess == null
-                    || onlyThisProcess.equals(r.processName);
-            
-            // First: if this is not the current activity being started, make
-            // sure it matches the current configuration.
-            if (r != starting && doThisProcess) {
-                ensureActivityConfigurationLocked(r, 0);
-            }
-            
-            if (r.app == null || r.app.thread == null) {
-                if (onlyThisProcess == null
-                        || onlyThisProcess.equals(r.processName)) {
-                    // This activity needs to be visible, but isn't even
-                    // running...  get it started, but don't resume it
-                    // at this point.
-                    if (DEBUG_VISBILITY) Slog.v(
-                            TAG, "Start and freeze screen for " + r);
-                    if (r != starting) {
-                        r.startFreezingScreenLocked(r.app, configChanges);
-                    }
-                    if (!r.visible) {
-                        if (DEBUG_VISBILITY) Slog.v(
-                                TAG, "Starting and making visible: " + r);
-                        mWindowManager.setAppVisibility(r, true);
-                    }
-                    if (r != starting) {
-                        startSpecificActivityLocked(r, false, false);
-                    }
-                }
-
-            } else if (r.visible) {
-                // If this activity is already visible, then there is nothing
-                // else to do here.
-                if (DEBUG_VISBILITY) Slog.v(
-                        TAG, "Skipping: already visible at " + r);
-                r.stopFreezingScreenLocked(false);
-
-            } else if (onlyThisProcess == null) {
-                // This activity is not currently visible, but is running.
-                // Tell it to become visible.
-                r.visible = true;
-                if (r.state != ActivityState.RESUMED && r != starting) {
-                    // If this activity is paused, tell it
-                    // to now show its window.
-                    if (DEBUG_VISBILITY) Slog.v(
-                            TAG, "Making visible and scheduling visibility: " + r);
-                    try {
-                        mWindowManager.setAppVisibility(r, true);
-                        r.app.thread.scheduleWindowVisibility(r, true);
-                        r.stopFreezingScreenLocked(false);
-                    } catch (Exception e) {
-                        // Just skip on any failure; we'll make it
-                        // visible when it next restarts.
-                        Slog.w(TAG, "Exception thrown making visibile: "
-                                + r.intent.getComponent(), e);
-                    }
-                }
-            }
-
-            // Aggregate current change flags.
-            configChanges |= r.configChangeFlags;
-
-            if (r.fullscreen) {
-                // At this point, nothing else needs to be shown
-                if (DEBUG_VISBILITY) Slog.v(
-                        TAG, "Stopping: fullscreen at " + r);
-                behindFullscreen = true;
-                i--;
-                break;
-            }
-        }
-
-        // Now for any activities that aren't visible to the user, make
-        // sure they no longer are keeping the screen frozen.
-        while (i >= 0) {
-            r = (ActivityRecord)mHistory.get(i);
-            if (DEBUG_VISBILITY) Slog.v(
-                    TAG, "Make invisible? " + r + " finishing=" + r.finishing
-                    + " state=" + r.state
-                    + " behindFullscreen=" + behindFullscreen);
-            if (!r.finishing) {
-                if (behindFullscreen) {
-                    if (r.visible) {
-                        if (DEBUG_VISBILITY) Slog.v(
-                                TAG, "Making invisible: " + r);
-                        r.visible = false;
-                        try {
-                            mWindowManager.setAppVisibility(r, false);
-                            if ((r.state == ActivityState.STOPPING
-                                    || r.state == ActivityState.STOPPED)
-                                    && r.app != null && r.app.thread != null) {
-                                if (DEBUG_VISBILITY) Slog.v(
-                                        TAG, "Scheduling invisibility: " + r);
-                                r.app.thread.scheduleWindowVisibility(r, false);
-                            }
-                        } catch (Exception e) {
-                            // Just skip on any failure; we'll make it
-                            // visible when it next restarts.
-                            Slog.w(TAG, "Exception thrown making hidden: "
-                                    + r.intent.getComponent(), e);
-                        }
-                    } else {
-                        if (DEBUG_VISBILITY) Slog.v(
-                                TAG, "Already invisible: " + r);
-                    }
-                } else if (r.fullscreen) {
-                    if (DEBUG_VISBILITY) Slog.v(
-                            TAG, "Now behindFullscreen: " + r);
-                    behindFullscreen = true;
-                }
-            }
-            i--;
-        }
-    }
-
-    /**
-     * Version of ensureActivitiesVisible that can easily be called anywhere.
-     */
-    private final void ensureActivitiesVisibleLocked(ActivityRecord starting,
-            int configChanges) {
-        ActivityRecord r = topRunningActivityLocked(null);
-        if (r != null) {
-            ensureActivitiesVisibleLocked(r, starting, null, configChanges);
-        }
-    }
-    
-    private void updateUsageStats(ActivityRecord resumedComponent, boolean resumed) {
+    void updateUsageStats(ActivityRecord resumedComponent, boolean resumed) {
         if (resumed) {
             mUsageStatsService.noteResumeComponent(resumedComponent.realActivity);
         } else {
@@ -2619,7 +1848,7 @@
         }
     }
 
-    private boolean startHomeActivityLocked() {
+    boolean startHomeActivityLocked() {
         if (mFactoryTest == SystemServer.FACTORY_TEST_LOW_LEVEL
                 && mTopAction == null) {
             // We are running in factory test mode, but unable to find
@@ -2646,7 +1875,7 @@
                     aInfo.applicationInfo.uid);
             if (app == null || app.instrumentationClass == null) {
                 intent.setFlags(intent.getFlags() | Intent.FLAG_ACTIVITY_NEW_TASK);
-                startActivityLocked(null, intent, null, null, 0, aInfo,
+                mMainStack.startActivityLocked(null, intent, null, null, 0, aInfo,
                         null, null, 0, 0, 0, false, false);
             }
         }
@@ -2658,7 +1887,7 @@
     /**
      * Starts the "new version setup screen" if appropriate.
      */
-    private void startSetupActivityLocked() {
+    void startSetupActivityLocked() {
         // Only do this once per boot.
         if (mCheckedForSetup) {
             return;
@@ -2702,14 +1931,14 @@
                     intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                     intent.setComponent(new ComponentName(
                             ri.activityInfo.packageName, ri.activityInfo.name));
-                    startActivityLocked(null, intent, null, null, 0, ri.activityInfo,
+                    mMainStack.startActivityLocked(null, intent, null, null, 0, ri.activityInfo,
                             null, null, 0, 0, 0, false, false);
                 }
             }
         }
     }
     
-    private void reportResumedActivityLocked(ActivityRecord r) {
+    void reportResumedActivityLocked(ActivityRecord r) {
         //Slog.i(TAG, "**** REPORT RESUME: " + r);
         
         final int identHash = System.identityHashCode(r);
@@ -2728,1278 +1957,27 @@
         }
         mWatchers.finishBroadcast();
     }
-    
-    /**
-     * Ensure that the top activity in the stack is resumed.
-     *
-     * @param prev The previously resumed activity, for when in the process
-     * of pausing; can be null to call from elsewhere.
-     *
-     * @return Returns true if something is being resumed, or false if
-     * nothing happened.
-     */
-    private final boolean resumeTopActivityLocked(ActivityRecord prev) {
-        // Find the first activity that is not finishing.
-        ActivityRecord next = topRunningActivityLocked(null);
 
-        // Remember how we'll process this pause/resume situation, and ensure
-        // that the state is reset however we wind up proceeding.
-        final boolean userLeaving = mUserLeaving;
-        mUserLeaving = false;
-
-        if (next == null) {
-            // There are no more activities!  Let's just start up the
-            // Launcher...
-            return startHomeActivityLocked();
-        }
-
-        next.delayedResume = false;
-        
-        // If the top activity is the resumed one, nothing to do.
-        if (mResumedActivity == next && next.state == ActivityState.RESUMED) {
-            // Make sure we have executed any pending transitions, since there
-            // should be nothing left to do at this point.
-            mWindowManager.executeAppTransition();
-            mNoAnimActivities.clear();
-            return false;
-        }
-
-        // If we are sleeping, and there is no resumed activity, and the top
-        // activity is paused, well that is the state we want.
-        if ((mSleeping || mShuttingDown)
-                && mLastPausedActivity == next && next.state == ActivityState.PAUSED) {
-            // Make sure we have executed any pending transitions, since there
-            // should be nothing left to do at this point.
-            mWindowManager.executeAppTransition();
-            mNoAnimActivities.clear();
-            return false;
-        }
-        
-        // The activity may be waiting for stop, but that is no longer
-        // appropriate for it.
-        mStoppingActivities.remove(next);
-        mWaitingVisibleActivities.remove(next);
-
-        if (DEBUG_SWITCH) Slog.v(TAG, "Resuming " + next);
-
-        // If we are currently pausing an activity, then don't do anything
-        // until that is done.
-        if (mPausingActivity != null) {
-            if (DEBUG_SWITCH) Slog.v(TAG, "Skip resume: pausing=" + mPausingActivity);
-            return false;
-        }
-
-        // We need to start pausing the current activity so the top one
-        // can be resumed...
-        if (mResumedActivity != null) {
-            if (DEBUG_SWITCH) Slog.v(TAG, "Skip resume: need to start pausing");
-            startPausingLocked(userLeaving, false);
-            return true;
-        }
-
-        if (prev != null && prev != next) {
-            if (!prev.waitingVisible && next != null && !next.nowVisible) {
-                prev.waitingVisible = true;
-                mWaitingVisibleActivities.add(prev);
-                if (DEBUG_SWITCH) Slog.v(
-                        TAG, "Resuming top, waiting visible to hide: " + prev);
-            } else {
-                // The next activity is already visible, so hide the previous
-                // activity's windows right now so we can show the new one ASAP.
-                // We only do this if the previous is finishing, which should mean
-                // it is on top of the one being resumed so hiding it quickly
-                // is good.  Otherwise, we want to do the normal route of allowing
-                // the resumed activity to be shown so we can decide if the
-                // previous should actually be hidden depending on whether the
-                // new one is found to be full-screen or not.
-                if (prev.finishing) {
-                    mWindowManager.setAppVisibility(prev, false);
-                    if (DEBUG_SWITCH) Slog.v(TAG, "Not waiting for visible to hide: "
-                            + prev + ", waitingVisible="
-                            + (prev != null ? prev.waitingVisible : null)
-                            + ", nowVisible=" + next.nowVisible);
-                } else {
-                    if (DEBUG_SWITCH) Slog.v(TAG, "Previous already visible but still waiting to hide: "
-                        + prev + ", waitingVisible="
-                        + (prev != null ? prev.waitingVisible : null)
-                        + ", nowVisible=" + next.nowVisible);
-                }
-            }
-        }
-
-        // We are starting up the next activity, so tell the window manager
-        // that the previous one will be hidden soon.  This way it can know
-        // to ignore it when computing the desired screen orientation.
-        if (prev != null) {
-            if (prev.finishing) {
-                if (DEBUG_TRANSITION) Slog.v(TAG,
-                        "Prepare close transition: prev=" + prev);
-                if (mNoAnimActivities.contains(prev)) {
-                    mWindowManager.prepareAppTransition(WindowManagerPolicy.TRANSIT_NONE);
-                } else {
-                    mWindowManager.prepareAppTransition(prev.task == next.task
-                            ? WindowManagerPolicy.TRANSIT_ACTIVITY_CLOSE
-                            : WindowManagerPolicy.TRANSIT_TASK_CLOSE);
-                }
-                mWindowManager.setAppWillBeHidden(prev);
-                mWindowManager.setAppVisibility(prev, false);
-            } else {
-                if (DEBUG_TRANSITION) Slog.v(TAG,
-                        "Prepare open transition: prev=" + prev);
-                if (mNoAnimActivities.contains(next)) {
-                    mWindowManager.prepareAppTransition(WindowManagerPolicy.TRANSIT_NONE);
-                } else {
-                    mWindowManager.prepareAppTransition(prev.task == next.task
-                            ? WindowManagerPolicy.TRANSIT_ACTIVITY_OPEN
-                            : WindowManagerPolicy.TRANSIT_TASK_OPEN);
-                }
-            }
-            if (false) {
-                mWindowManager.setAppWillBeHidden(prev);
-                mWindowManager.setAppVisibility(prev, false);
-            }
-        } else if (mHistory.size() > 1) {
-            if (DEBUG_TRANSITION) Slog.v(TAG,
-                    "Prepare open transition: no previous");
-            if (mNoAnimActivities.contains(next)) {
-                mWindowManager.prepareAppTransition(WindowManagerPolicy.TRANSIT_NONE);
-            } else {
-                mWindowManager.prepareAppTransition(WindowManagerPolicy.TRANSIT_ACTIVITY_OPEN);
-            }
-        }
-
-        if (next.app != null && next.app.thread != null) {
-            if (DEBUG_SWITCH) Slog.v(TAG, "Resume running: " + next);
-
-            // This activity is now becoming visible.
-            mWindowManager.setAppVisibility(next, true);
-
-            ActivityRecord lastResumedActivity = mResumedActivity;
-            ActivityState lastState = next.state;
-
-            updateCpuStats();
-            
-            next.state = ActivityState.RESUMED;
-            mResumedActivity = next;
-            next.task.touchActiveTime();
-            updateLruProcessLocked(next.app, true, true);
-            updateLRUListLocked(next);
-
-            // Have the window manager re-evaluate the orientation of
-            // the screen based on the new activity order.
-            boolean updated;
-            synchronized (this) {
-                Configuration config = mWindowManager.updateOrientationFromAppTokens(
-                        mConfiguration,
-                        next.mayFreezeScreenLocked(next.app) ? next : null);
-                if (config != null) {
-                    next.frozenBeforeDestroy = true;
-                }
-                updated = updateConfigurationLocked(config, next);
-            }
-            if (!updated) {
-                // The configuration update wasn't able to keep the existing
-                // instance of the activity, and instead started a new one.
-                // We should be all done, but let's just make sure our activity
-                // is still at the top and schedule another run if something
-                // weird happened.
-                ActivityRecord nextNext = topRunningActivityLocked(null);
-                if (DEBUG_SWITCH) Slog.i(TAG,
-                        "Activity config changed during resume: " + next
-                        + ", new next: " + nextNext);
-                if (nextNext != next) {
-                    // Do over!
-                    mHandler.sendEmptyMessage(RESUME_TOP_ACTIVITY_MSG);
-                }
-                setFocusedActivityLocked(next);
-                ensureActivitiesVisibleLocked(null, 0);
-                mWindowManager.executeAppTransition();
-                mNoAnimActivities.clear();
-                return true;
-            }
-            
-            try {
-                // Deliver all pending results.
-                ArrayList a = next.results;
-                if (a != null) {
-                    final int N = a.size();
-                    if (!next.finishing && N > 0) {
-                        if (DEBUG_RESULTS) Slog.v(
-                                TAG, "Delivering results to " + next
-                                + ": " + a);
-                        next.app.thread.scheduleSendResult(next, a);
-                    }
-                }
-
-                if (next.newIntents != null) {
-                    next.app.thread.scheduleNewIntent(next.newIntents, next);
-                }
-
-                EventLog.writeEvent(EventLogTags.AM_RESUME_ACTIVITY,
-                        System.identityHashCode(next),
-                        next.task.taskId, next.shortComponentName);
-                
-                next.app.thread.scheduleResumeActivity(next,
-                        isNextTransitionForward());
-                
-                pauseIfSleepingLocked();
-
-            } catch (Exception e) {
-                // Whoops, need to restart this activity!
-                next.state = lastState;
-                mResumedActivity = lastResumedActivity;
-                Slog.i(TAG, "Restarting because process died: " + next);
-                if (!next.hasBeenLaunched) {
-                    next.hasBeenLaunched = true;
-                } else {
-                    if (SHOW_APP_STARTING_ICON) {
-                        mWindowManager.setAppStartingWindow(
-                                next, next.packageName, next.theme,
-                                next.nonLocalizedLabel,
-                                next.labelRes, next.icon, null, true);
-                    }
-                }
-                startSpecificActivityLocked(next, true, false);
-                return true;
-            }
-
-            // From this point on, if something goes wrong there is no way
-            // to recover the activity.
-            try {
-                next.visible = true;
-                completeResumeLocked(next);
-            } catch (Exception e) {
-                // If any exception gets thrown, toss away this
-                // activity and try the next one.
-                Slog.w(TAG, "Exception thrown during resume of " + next, e);
-                requestFinishActivityLocked(next, Activity.RESULT_CANCELED, null,
-                        "resume-exception");
-                return true;
-            }
-
-            // Didn't need to use the icicle, and it is now out of date.
-            next.icicle = null;
-            next.haveState = false;
-            next.stopped = false;
-
-        } else {
-            // Whoops, need to restart this activity!
-            if (!next.hasBeenLaunched) {
-                next.hasBeenLaunched = true;
-            } else {
-                if (SHOW_APP_STARTING_ICON) {
-                    mWindowManager.setAppStartingWindow(
-                            next, next.packageName, next.theme,
-                            next.nonLocalizedLabel,
-                            next.labelRes, next.icon, null, true);
-                }
-                if (DEBUG_SWITCH) Slog.v(TAG, "Restarting: " + next);
-            }
-            startSpecificActivityLocked(next, true, true);
-        }
-
-        return true;
-    }
-
-    private final void startActivityLocked(ActivityRecord r, boolean newTask,
-            boolean doResume) {
-        final int NH = mHistory.size();
-
-        int addPos = -1;
-        
-        if (!newTask) {
-            // If starting in an existing task, find where that is...
-            ActivityRecord next = null;
-            boolean startIt = true;
-            for (int i = NH-1; i >= 0; i--) {
-                ActivityRecord p = (ActivityRecord)mHistory.get(i);
-                if (p.finishing) {
-                    continue;
-                }
-                if (p.task == r.task) {
-                    // Here it is!  Now, if this is not yet visible to the
-                    // user, then just add it without starting; it will
-                    // get started when the user navigates back to it.
-                    addPos = i+1;
-                    if (!startIt) {
-                        mHistory.add(addPos, r);
-                        r.inHistory = true;
-                        r.task.numActivities++;
-                        mWindowManager.addAppToken(addPos, r, r.task.taskId,
-                                r.info.screenOrientation, r.fullscreen);
-                        if (VALIDATE_TOKENS) {
-                            mWindowManager.validateAppTokens(mHistory);
-                        }
-                        return;
-                    }
-                    break;
-                }
-                if (p.fullscreen) {
-                    startIt = false;
-                }
-                next = p;
-            }
-        }
-
-        // Place a new activity at top of stack, so it is next to interact
-        // with the user.
-        if (addPos < 0) {
-            addPos = mHistory.size();
-        }
-        
-        // If we are not placing the new activity frontmost, we do not want
-        // to deliver the onUserLeaving callback to the actual frontmost
-        // activity
-        if (addPos < NH) {
-            mUserLeaving = false;
-            if (DEBUG_USER_LEAVING) Slog.v(TAG, "startActivity() behind front, mUserLeaving=false");
-        }
-        
-        // Slot the activity into the history stack and proceed
-        mHistory.add(addPos, r);
-        r.inHistory = true;
-        r.frontOfTask = newTask;
-        r.task.numActivities++;
-        if (NH > 0) {
-            // We want to show the starting preview window if we are
-            // switching to a new task, or the next activity's process is
-            // not currently running.
-            boolean showStartingIcon = newTask;
-            ProcessRecord proc = r.app;
-            if (proc == null) {
-                proc = mProcessNames.get(r.processName, r.info.applicationInfo.uid);
-            }
-            if (proc == null || proc.thread == null) {
-                showStartingIcon = true;
-            }
-            if (DEBUG_TRANSITION) Slog.v(TAG,
-                    "Prepare open transition: starting " + r);
-            if ((r.intent.getFlags()&Intent.FLAG_ACTIVITY_NO_ANIMATION) != 0) {
-                mWindowManager.prepareAppTransition(WindowManagerPolicy.TRANSIT_NONE);
-                mNoAnimActivities.add(r);
-            } else if ((r.intent.getFlags()&Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET) != 0) {
-                mWindowManager.prepareAppTransition(WindowManagerPolicy.TRANSIT_TASK_OPEN);
-                mNoAnimActivities.remove(r);
-            } else {
-                mWindowManager.prepareAppTransition(newTask
-                        ? WindowManagerPolicy.TRANSIT_TASK_OPEN
-                        : WindowManagerPolicy.TRANSIT_ACTIVITY_OPEN);
-                mNoAnimActivities.remove(r);
-            }
-            mWindowManager.addAppToken(
-                    addPos, r, r.task.taskId, r.info.screenOrientation, r.fullscreen);
-            boolean doShow = true;
-            if (newTask) {
-                // Even though this activity is starting fresh, we still need
-                // to reset it to make sure we apply affinities to move any
-                // existing activities from other tasks in to it.
-                // If the caller has requested that the target task be
-                // reset, then do so.
-                if ((r.intent.getFlags()
-                        &Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED) != 0) {
-                    resetTaskIfNeededLocked(r, r);
-                    doShow = topRunningNonDelayedActivityLocked(null) == r;
-                }
-            }
-            if (SHOW_APP_STARTING_ICON && doShow) {
-                // Figure out if we are transitioning from another activity that is
-                // "has the same starting icon" as the next one.  This allows the
-                // window manager to keep the previous window it had previously
-                // created, if it still had one.
-                ActivityRecord prev = mResumedActivity;
-                if (prev != null) {
-                    // We don't want to reuse the previous starting preview if:
-                    // (1) The current activity is in a different task.
-                    if (prev.task != r.task) prev = null;
-                    // (2) The current activity is already displayed.
-                    else if (prev.nowVisible) prev = null;
-                }
-                mWindowManager.setAppStartingWindow(
-                        r, r.packageName, r.theme, r.nonLocalizedLabel,
-                        r.labelRes, r.icon, prev, showStartingIcon);
-            }
-        } else {
-            // If this is the first activity, don't do any fancy animations,
-            // because there is nothing for it to animate on top of.
-            mWindowManager.addAppToken(addPos, r, r.task.taskId,
-                    r.info.screenOrientation, r.fullscreen);
-        }
-        if (VALIDATE_TOKENS) {
-            mWindowManager.validateAppTokens(mHistory);
-        }
-
-        if (doResume) {
-            resumeTopActivityLocked(null);
-        }
-    }
-
-    /**
-     * Perform clear operation as requested by
-     * {@link Intent#FLAG_ACTIVITY_CLEAR_TOP}: search from the top of the
-     * stack to the given task, then look for
-     * an instance of that activity in the stack and, if found, finish all
-     * activities on top of it and return the instance.
-     *
-     * @param newR Description of the new activity being started.
-     * @return Returns the old activity that should be continue to be used,
-     * or null if none was found.
-     */
-    private final ActivityRecord performClearTaskLocked(int taskId,
-            ActivityRecord newR, int launchFlags, boolean doClear) {
-        int i = mHistory.size();
-        
-        // First find the requested task.
-        while (i > 0) {
-            i--;
-            ActivityRecord r = (ActivityRecord)mHistory.get(i);
-            if (r.task.taskId == taskId) {
-                i++;
-                break;
-            }
-        }
-        
-        // Now clear it.
-        while (i > 0) {
-            i--;
-            ActivityRecord r = (ActivityRecord)mHistory.get(i);
-            if (r.finishing) {
-                continue;
-            }
-            if (r.task.taskId != taskId) {
-                return null;
-            }
-            if (r.realActivity.equals(newR.realActivity)) {
-                // Here it is!  Now finish everything in front...
-                ActivityRecord ret = r;
-                if (doClear) {
-                    while (i < (mHistory.size()-1)) {
-                        i++;
-                        r = (ActivityRecord)mHistory.get(i);
-                        if (r.finishing) {
-                            continue;
-                        }
-                        if (finishActivityLocked(r, i, Activity.RESULT_CANCELED,
-                                null, "clear")) {
-                            i--;
-                        }
-                    }
-                }
-                
-                // Finally, if this is a normal launch mode (that is, not
-                // expecting onNewIntent()), then we will finish the current
-                // instance of the activity so a new fresh one can be started.
-                if (ret.launchMode == ActivityInfo.LAUNCH_MULTIPLE
-                        && (launchFlags&Intent.FLAG_ACTIVITY_SINGLE_TOP) == 0) {
-                    if (!ret.finishing) {
-                        int index = indexOfTokenLocked(ret);
-                        if (index >= 0) {
-                            finishActivityLocked(ret, index, Activity.RESULT_CANCELED,
-                                    null, "clear");
-                        }
-                        return null;
-                    }
-                }
-                
-                return ret;
-            }
-        }
-
-        return null;
-    }
-
-    /**
-     * Find the activity in the history stack within the given task.  Returns
-     * the index within the history at which it's found, or < 0 if not found.
-     */
-    private final int findActivityInHistoryLocked(ActivityRecord r, int task) {
-        int i = mHistory.size();
-        while (i > 0) {
-            i--;
-            ActivityRecord candidate = (ActivityRecord)mHistory.get(i);
-            if (candidate.task.taskId != task) {
-                break;
-            }
-            if (candidate.realActivity.equals(r.realActivity)) {
-                return i;
-            }
-        }
-
-        return -1;
-    }
-
-    /**
-     * Reorder the history stack so that the activity at the given index is
-     * brought to the front.
-     */
-    private final ActivityRecord moveActivityToFrontLocked(int where) {
-        ActivityRecord newTop = (ActivityRecord)mHistory.remove(where);
-        int top = mHistory.size();
-        ActivityRecord oldTop = (ActivityRecord)mHistory.get(top-1);
-        mHistory.add(top, newTop);
-        oldTop.frontOfTask = false;
-        newTop.frontOfTask = true;
-        return newTop;
-    }
-
-    /**
-     * Deliver a new Intent to an existing activity, so that its onNewIntent()
-     * method will be called at the proper time.
-     */
-    private final void deliverNewIntentLocked(ActivityRecord r, Intent intent) {
-        boolean sent = false;
-        if (r.state == ActivityState.RESUMED
-                && r.app != null && r.app.thread != null) {
-            try {
-                ArrayList<Intent> ar = new ArrayList<Intent>();
-                ar.add(new Intent(intent));
-                r.app.thread.scheduleNewIntent(ar, r);
-                sent = true;
-            } catch (Exception e) {
-                Slog.w(TAG, "Exception thrown sending new intent to " + r, e);
-            }
-        }
-        if (!sent) {
-            r.addNewIntentLocked(new Intent(intent));
-        }
-    }
-
-    private final void logStartActivity(int tag, ActivityRecord r,
-            TaskRecord task) {
-        EventLog.writeEvent(tag,
-                System.identityHashCode(r), task.taskId,
-                r.shortComponentName, r.intent.getAction(),
-                r.intent.getType(), r.intent.getDataString(),
-                r.intent.getFlags());
-    }
-
-    private final int startActivityLocked(IApplicationThread caller,
-            Intent intent, String resolvedType,
-            Uri[] grantedUriPermissions,
-            int grantedMode, ActivityInfo aInfo, IBinder resultTo,
-            String resultWho, int requestCode,
-            int callingPid, int callingUid, boolean onlyIfNeeded,
-            boolean componentSpecified) {
-        Slog.i(TAG, "Starting activity: " + intent);
-
-        ActivityRecord sourceRecord = null;
-        ActivityRecord resultRecord = null;
-        if (resultTo != null) {
-            int index = indexOfTokenLocked(resultTo);
-            if (DEBUG_RESULTS) Slog.v(
-                TAG, "Sending result to " + resultTo + " (index " + index + ")");
-            if (index >= 0) {
-                sourceRecord = (ActivityRecord)mHistory.get(index);
-                if (requestCode >= 0 && !sourceRecord.finishing) {
-                    resultRecord = sourceRecord;
-                }
-            }
-        }
-
-        int launchFlags = intent.getFlags();
-
-        if ((launchFlags&Intent.FLAG_ACTIVITY_FORWARD_RESULT) != 0
-                && sourceRecord != null) {
-            // Transfer the result target from the source activity to the new
-            // one being started, including any failures.
-            if (requestCode >= 0) {
-                return START_FORWARD_AND_REQUEST_CONFLICT;
-            }
-            resultRecord = sourceRecord.resultTo;
-            resultWho = sourceRecord.resultWho;
-            requestCode = sourceRecord.requestCode;
-            sourceRecord.resultTo = null;
-            if (resultRecord != null) {
-                resultRecord.removeResultsLocked(
-                    sourceRecord, resultWho, requestCode);
-            }
-        }
-
-        int err = START_SUCCESS;
-
-        if (intent.getComponent() == null) {
-            // We couldn't find a class that can handle the given Intent.
-            // That's the end of that!
-            err = START_INTENT_NOT_RESOLVED;
-        }
-
-        if (err == START_SUCCESS && aInfo == null) {
-            // We couldn't find the specific class specified in the Intent.
-            // Also the end of the line.
-            err = START_CLASS_NOT_FOUND;
-        }
-
-        ProcessRecord callerApp = null;
-        if (err == START_SUCCESS && caller != null) {
-            callerApp = getRecordForAppLocked(caller);
-            if (callerApp != null) {
-                callingPid = callerApp.pid;
-                callingUid = callerApp.info.uid;
-            } else {
-                Slog.w(TAG, "Unable to find app for caller " + caller
-                      + " (pid=" + callingPid + ") when starting: "
-                      + intent.toString());
-                err = START_PERMISSION_DENIED;
-            }
-        }
-
-        if (err != START_SUCCESS) {
-            if (resultRecord != null) {
-                sendActivityResultLocked(-1,
-                    resultRecord, resultWho, requestCode,
-                    Activity.RESULT_CANCELED, null);
-            }
-            return err;
-        }
-
-        final int perm = checkComponentPermission(aInfo.permission, callingPid,
-                callingUid, aInfo.exported ? -1 : aInfo.applicationInfo.uid);
-        if (perm != PackageManager.PERMISSION_GRANTED) {
-            if (resultRecord != null) {
-                sendActivityResultLocked(-1,
-                    resultRecord, resultWho, requestCode,
-                    Activity.RESULT_CANCELED, null);
-            }
-            String msg = "Permission Denial: starting " + intent.toString()
-                    + " from " + callerApp + " (pid=" + callingPid
-                    + ", uid=" + callingUid + ")"
-                    + " requires " + aInfo.permission;
-            Slog.w(TAG, msg);
-            throw new SecurityException(msg);
-        }
-
-        if (mController != null) {
-            boolean abort = false;
-            try {
-                // The Intent we give to the watcher has the extra data
-                // stripped off, since it can contain private information.
-                Intent watchIntent = intent.cloneFilter();
-                abort = !mController.activityStarting(watchIntent,
-                        aInfo.applicationInfo.packageName);
-            } catch (RemoteException e) {
-                mController = null;
-            }
-
-            if (abort) {
-                if (resultRecord != null) {
-                    sendActivityResultLocked(-1,
-                        resultRecord, resultWho, requestCode,
-                        Activity.RESULT_CANCELED, null);
-                }
-                // We pretend to the caller that it was really started, but
-                // they will just get a cancel result.
-                return START_SUCCESS;
-            }
-        }
-
-        ActivityRecord r = new ActivityRecord(this, callerApp, callingUid,
-                intent, resolvedType, aInfo, mConfiguration,
-                resultRecord, resultWho, requestCode, componentSpecified);
-
-        if (mResumedActivity == null
-                || mResumedActivity.info.applicationInfo.uid != callingUid) {
-            if (!checkAppSwitchAllowedLocked(callingPid, callingUid, "Activity start")) {
-                PendingActivityLaunch pal = new PendingActivityLaunch();
-                pal.r = r;
-                pal.sourceRecord = sourceRecord;
-                pal.grantedUriPermissions = grantedUriPermissions;
-                pal.grantedMode = grantedMode;
-                pal.onlyIfNeeded = onlyIfNeeded;
-                mPendingActivityLaunches.add(pal);
-                return START_SWITCHES_CANCELED;
-            }
-        }
-        
-        if (mDidAppSwitch) {
-            // This is the second allowed switch since we stopped switches,
-            // so now just generally allow switches.  Use case: user presses
-            // home (switches disabled, switch to home, mDidAppSwitch now true);
-            // user taps a home icon (coming from home so allowed, we hit here
-            // and now allow anyone to switch again).
-            mAppSwitchesAllowedTime = 0;
-        } else {
-            mDidAppSwitch = true;
-        }
-     
-        doPendingActivityLaunchesLocked(false);
-        
-        return startActivityUncheckedLocked(r, sourceRecord,
-                grantedUriPermissions, grantedMode, onlyIfNeeded, true);
-    }
-  
-    private final void doPendingActivityLaunchesLocked(boolean doResume) {
+    final void doPendingActivityLaunchesLocked(boolean doResume) {
         final int N = mPendingActivityLaunches.size();
         if (N <= 0) {
             return;
         }
         for (int i=0; i<N; i++) {
             PendingActivityLaunch pal = mPendingActivityLaunches.get(i);
-            startActivityUncheckedLocked(pal.r, pal.sourceRecord,
+            mMainStack.startActivityUncheckedLocked(pal.r, pal.sourceRecord,
                     pal.grantedUriPermissions, pal.grantedMode, pal.onlyIfNeeded,
                     doResume && i == (N-1));
         }
         mPendingActivityLaunches.clear();
     }
     
-    private final int startActivityUncheckedLocked(ActivityRecord r,
-            ActivityRecord sourceRecord, Uri[] grantedUriPermissions,
-            int grantedMode, boolean onlyIfNeeded, boolean doResume) {
-        final Intent intent = r.intent;
-        final int callingUid = r.launchedFromUid;
-        
-        int launchFlags = intent.getFlags();
-        
-        // We'll invoke onUserLeaving before onPause only if the launching
-        // activity did not explicitly state that this is an automated launch.
-        mUserLeaving = (launchFlags&Intent.FLAG_ACTIVITY_NO_USER_ACTION) == 0;
-        if (DEBUG_USER_LEAVING) Slog.v(TAG,
-                "startActivity() => mUserLeaving=" + mUserLeaving);
-        
-        // If the caller has asked not to resume at this point, we make note
-        // of this in the record so that we can skip it when trying to find
-        // the top running activity.
-        if (!doResume) {
-            r.delayedResume = true;
-        }
-        
-        ActivityRecord notTop = (launchFlags&Intent.FLAG_ACTIVITY_PREVIOUS_IS_TOP)
-                != 0 ? r : null;
-
-        // If the onlyIfNeeded flag is set, then we can do this if the activity
-        // being launched is the same as the one making the call...  or, as
-        // a special case, if we do not know the caller then we count the
-        // current top activity as the caller.
-        if (onlyIfNeeded) {
-            ActivityRecord checkedCaller = sourceRecord;
-            if (checkedCaller == null) {
-                checkedCaller = topRunningNonDelayedActivityLocked(notTop);
-            }
-            if (!checkedCaller.realActivity.equals(r.realActivity)) {
-                // Caller is not the same as launcher, so always needed.
-                onlyIfNeeded = false;
-            }
-        }
-
-        if (grantedUriPermissions != null && callingUid > 0) {
-            for (int i=0; i<grantedUriPermissions.length; i++) {
-                grantUriPermissionLocked(callingUid, r.packageName,
-                        grantedUriPermissions[i], grantedMode, r);
-            }
-        }
-
-        grantUriPermissionFromIntentLocked(callingUid, r.packageName,
-                intent, r);
-
-        if (sourceRecord == null) {
-            // This activity is not being started from another...  in this
-            // case we -always- start a new task.
-            if ((launchFlags&Intent.FLAG_ACTIVITY_NEW_TASK) == 0) {
-                Slog.w(TAG, "startActivity called from non-Activity context; forcing Intent.FLAG_ACTIVITY_NEW_TASK for: "
-                      + intent);
-                launchFlags |= Intent.FLAG_ACTIVITY_NEW_TASK;
-            }
-        } else if (sourceRecord.launchMode == ActivityInfo.LAUNCH_SINGLE_INSTANCE) {
-            // The original activity who is starting us is running as a single
-            // instance...  this new activity it is starting must go on its
-            // own task.
-            launchFlags |= Intent.FLAG_ACTIVITY_NEW_TASK;
-        } else if (r.launchMode == ActivityInfo.LAUNCH_SINGLE_INSTANCE
-                || r.launchMode == ActivityInfo.LAUNCH_SINGLE_TASK) {
-            // The activity being started is a single instance...  it always
-            // gets launched into its own task.
-            launchFlags |= Intent.FLAG_ACTIVITY_NEW_TASK;
-        }
-
-        if (r.resultTo != null && (launchFlags&Intent.FLAG_ACTIVITY_NEW_TASK) != 0) {
-            // For whatever reason this activity is being launched into a new
-            // task...  yet the caller has requested a result back.  Well, that
-            // is pretty messed up, so instead immediately send back a cancel
-            // and let the new task continue launched as normal without a
-            // dependency on its originator.
-            Slog.w(TAG, "Activity is launching as a new task, so cancelling activity result.");
-            sendActivityResultLocked(-1,
-                    r.resultTo, r.resultWho, r.requestCode,
-                Activity.RESULT_CANCELED, null);
-            r.resultTo = null;
-        }
-
-        boolean addingToTask = false;
-        if (((launchFlags&Intent.FLAG_ACTIVITY_NEW_TASK) != 0 &&
-                (launchFlags&Intent.FLAG_ACTIVITY_MULTIPLE_TASK) == 0)
-                || r.launchMode == ActivityInfo.LAUNCH_SINGLE_TASK
-                || r.launchMode == ActivityInfo.LAUNCH_SINGLE_INSTANCE) {
-            // If bring to front is requested, and no result is requested, and
-            // we can find a task that was started with this same
-            // component, then instead of launching bring that one to the front.
-            if (r.resultTo == null) {
-                // See if there is a task to bring to the front.  If this is
-                // a SINGLE_INSTANCE activity, there can be one and only one
-                // instance of it in the history, and it is always in its own
-                // unique task, so we do a special search.
-                ActivityRecord taskTop = r.launchMode != ActivityInfo.LAUNCH_SINGLE_INSTANCE
-                        ? findTaskLocked(intent, r.info)
-                        : findActivityLocked(intent, r.info);
-                if (taskTop != null) {
-                    if (taskTop.task.intent == null) {
-                        // This task was started because of movement of
-                        // the activity based on affinity...  now that we
-                        // are actually launching it, we can assign the
-                        // base intent.
-                        taskTop.task.setIntent(intent, r.info);
-                    }
-                    // If the target task is not in the front, then we need
-                    // to bring it to the front...  except...  well, with
-                    // SINGLE_TASK_LAUNCH it's not entirely clear.  We'd like
-                    // to have the same behavior as if a new instance was
-                    // being started, which means not bringing it to the front
-                    // if the caller is not itself in the front.
-                    ActivityRecord curTop = topRunningNonDelayedActivityLocked(notTop);
-                    if (curTop.task != taskTop.task) {
-                        r.intent.addFlags(Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT);
-                        boolean callerAtFront = sourceRecord == null
-                                || curTop.task == sourceRecord.task;
-                        if (callerAtFront) {
-                            // We really do want to push this one into the
-                            // user's face, right now.
-                            moveTaskToFrontLocked(taskTop.task, r);
-                        }
-                    }
-                    // If the caller has requested that the target task be
-                    // reset, then do so.
-                    if ((launchFlags&Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED) != 0) {
-                        taskTop = resetTaskIfNeededLocked(taskTop, r);
-                    }
-                    if (onlyIfNeeded) {
-                        // We don't need to start a new activity, and
-                        // the client said not to do anything if that
-                        // is the case, so this is it!  And for paranoia, make
-                        // sure we have correctly resumed the top activity.
-                        if (doResume) {
-                            resumeTopActivityLocked(null);
-                        }
-                        return START_RETURN_INTENT_TO_CALLER;
-                    }
-                    if ((launchFlags&Intent.FLAG_ACTIVITY_CLEAR_TOP) != 0
-                            || r.launchMode == ActivityInfo.LAUNCH_SINGLE_TASK
-                            || r.launchMode == ActivityInfo.LAUNCH_SINGLE_INSTANCE) {
-                        // In this situation we want to remove all activities
-                        // from the task up to the one being started.  In most
-                        // cases this means we are resetting the task to its
-                        // initial state.
-                        ActivityRecord top = performClearTaskLocked(
-                                taskTop.task.taskId, r, launchFlags, true);
-                        if (top != null) {
-                            if (top.frontOfTask) {
-                                // Activity aliases may mean we use different
-                                // intents for the top activity, so make sure
-                                // the task now has the identity of the new
-                                // intent.
-                                top.task.setIntent(r.intent, r.info);
-                            }
-                            logStartActivity(EventLogTags.AM_NEW_INTENT, r, top.task);
-                            deliverNewIntentLocked(top, r.intent);
-                        } else {
-                            // A special case: we need to
-                            // start the activity because it is not currently
-                            // running, and the caller has asked to clear the
-                            // current task to have this activity at the top.
-                            addingToTask = true;
-                            // Now pretend like this activity is being started
-                            // by the top of its task, so it is put in the
-                            // right place.
-                            sourceRecord = taskTop;
-                        }
-                    } else if (r.realActivity.equals(taskTop.task.realActivity)) {
-                        // In this case the top activity on the task is the
-                        // same as the one being launched, so we take that
-                        // as a request to bring the task to the foreground.
-                        // If the top activity in the task is the root
-                        // activity, deliver this new intent to it if it
-                        // desires.
-                        if ((launchFlags&Intent.FLAG_ACTIVITY_SINGLE_TOP) != 0
-                                && taskTop.realActivity.equals(r.realActivity)) {
-                            logStartActivity(EventLogTags.AM_NEW_INTENT, r, taskTop.task);
-                            if (taskTop.frontOfTask) {
-                                taskTop.task.setIntent(r.intent, r.info);
-                            }
-                            deliverNewIntentLocked(taskTop, r.intent);
-                        } else if (!r.intent.filterEquals(taskTop.task.intent)) {
-                            // In this case we are launching the root activity
-                            // of the task, but with a different intent.  We
-                            // should start a new instance on top.
-                            addingToTask = true;
-                            sourceRecord = taskTop;
-                        }
-                    } else if ((launchFlags&Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED) == 0) {
-                        // In this case an activity is being launched in to an
-                        // existing task, without resetting that task.  This
-                        // is typically the situation of launching an activity
-                        // from a notification or shortcut.  We want to place
-                        // the new activity on top of the current task.
-                        addingToTask = true;
-                        sourceRecord = taskTop;
-                    } else if (!taskTop.task.rootWasReset) {
-                        // In this case we are launching in to an existing task
-                        // that has not yet been started from its front door.
-                        // The current task has been brought to the front.
-                        // Ideally, we'd probably like to place this new task
-                        // at the bottom of its stack, but that's a little hard
-                        // to do with the current organization of the code so
-                        // for now we'll just drop it.
-                        taskTop.task.setIntent(r.intent, r.info);
-                    }
-                    if (!addingToTask) {
-                        // We didn't do anything...  but it was needed (a.k.a., client
-                        // don't use that intent!)  And for paranoia, make
-                        // sure we have correctly resumed the top activity.
-                        if (doResume) {
-                            resumeTopActivityLocked(null);
-                        }
-                        return START_TASK_TO_FRONT;
-                    }
-                }
-            }
-        }
-
-        //String uri = r.intent.toURI();
-        //Intent intent2 = new Intent(uri);
-        //Slog.i(TAG, "Given intent: " + r.intent);
-        //Slog.i(TAG, "URI is: " + uri);
-        //Slog.i(TAG, "To intent: " + intent2);
-
-        if (r.packageName != null) {
-            // If the activity being launched is the same as the one currently
-            // at the top, then we need to check if it should only be launched
-            // once.
-            ActivityRecord top = topRunningNonDelayedActivityLocked(notTop);
-            if (top != null && r.resultTo == null) {
-                if (top.realActivity.equals(r.realActivity)) {
-                    if (top.app != null && top.app.thread != null) {
-                        if ((launchFlags&Intent.FLAG_ACTIVITY_SINGLE_TOP) != 0
-                            || r.launchMode == ActivityInfo.LAUNCH_SINGLE_TOP
-                            || r.launchMode == ActivityInfo.LAUNCH_SINGLE_TASK) {
-                            logStartActivity(EventLogTags.AM_NEW_INTENT, top, top.task);
-                            // For paranoia, make sure we have correctly
-                            // resumed the top activity.
-                            if (doResume) {
-                                resumeTopActivityLocked(null);
-                            }
-                            if (onlyIfNeeded) {
-                                // We don't need to start a new activity, and
-                                // the client said not to do anything if that
-                                // is the case, so this is it!
-                                return START_RETURN_INTENT_TO_CALLER;
-                            }
-                            deliverNewIntentLocked(top, r.intent);
-                            return START_DELIVERED_TO_TOP;
-                        }
-                    }
-                }
-            }
-
-        } else {
-            if (r.resultTo != null) {
-                sendActivityResultLocked(-1,
-                        r.resultTo, r.resultWho, r.requestCode,
-                    Activity.RESULT_CANCELED, null);
-            }
-            return START_CLASS_NOT_FOUND;
-        }
-
-        boolean newTask = false;
-
-        // Should this be considered a new task?
-        if (r.resultTo == null && !addingToTask
-                && (launchFlags&Intent.FLAG_ACTIVITY_NEW_TASK) != 0) {
-            // todo: should do better management of integers.
-            mCurTask++;
-            if (mCurTask <= 0) {
-                mCurTask = 1;
-            }
-            r.task = new TaskRecord(mCurTask, r.info, intent,
-                    (r.info.flags&ActivityInfo.FLAG_CLEAR_TASK_ON_LAUNCH) != 0);
-            if (DEBUG_TASKS) Slog.v(TAG, "Starting new activity " + r
-                    + " in new task " + r.task);
-            newTask = true;
-            addRecentTaskLocked(r.task);
-            
-        } else if (sourceRecord != null) {
-            if (!addingToTask &&
-                    (launchFlags&Intent.FLAG_ACTIVITY_CLEAR_TOP) != 0) {
-                // In this case, we are adding the activity to an existing
-                // task, but the caller has asked to clear that task if the
-                // activity is already running.
-                ActivityRecord top = performClearTaskLocked(
-                        sourceRecord.task.taskId, r, launchFlags, true);
-                if (top != null) {
-                    logStartActivity(EventLogTags.AM_NEW_INTENT, r, top.task);
-                    deliverNewIntentLocked(top, r.intent);
-                    // For paranoia, make sure we have correctly
-                    // resumed the top activity.
-                    if (doResume) {
-                        resumeTopActivityLocked(null);
-                    }
-                    return START_DELIVERED_TO_TOP;
-                }
-            } else if (!addingToTask &&
-                    (launchFlags&Intent.FLAG_ACTIVITY_REORDER_TO_FRONT) != 0) {
-                // In this case, we are launching an activity in our own task
-                // that may already be running somewhere in the history, and
-                // we want to shuffle it to the front of the stack if so.
-                int where = findActivityInHistoryLocked(r, sourceRecord.task.taskId);
-                if (where >= 0) {
-                    ActivityRecord top = moveActivityToFrontLocked(where);
-                    logStartActivity(EventLogTags.AM_NEW_INTENT, r, top.task);
-                    deliverNewIntentLocked(top, r.intent);
-                    if (doResume) {
-                        resumeTopActivityLocked(null);
-                    }
-                    return START_DELIVERED_TO_TOP;
-                }
-            }
-            // An existing activity is starting this new activity, so we want
-            // to keep the new one in the same task as the one that is starting
-            // it.
-            r.task = sourceRecord.task;
-            if (DEBUG_TASKS) Slog.v(TAG, "Starting new activity " + r
-                    + " in existing task " + r.task);
-
-        } else {
-            // This not being started from an existing activity, and not part
-            // of a new task...  just put it in the top task, though these days
-            // this case should never happen.
-            final int N = mHistory.size();
-            ActivityRecord prev =
-                N > 0 ? (ActivityRecord)mHistory.get(N-1) : null;
-            r.task = prev != null
-                ? prev.task
-                : new TaskRecord(mCurTask, r.info, intent,
-                        (r.info.flags&ActivityInfo.FLAG_CLEAR_TASK_ON_LAUNCH) != 0);
-            if (DEBUG_TASKS) Slog.v(TAG, "Starting new activity " + r
-                    + " in new guessed " + r.task);
-        }
-        if (newTask) {
-            EventLog.writeEvent(EventLogTags.AM_CREATE_TASK, r.task.taskId);
-        }
-        logStartActivity(EventLogTags.AM_CREATE_ACTIVITY, r, r.task);
-        startActivityLocked(r, newTask, doResume);
-        return START_SUCCESS;
-    }
-
-    void reportActivityLaunchedLocked(boolean timeout, ActivityRecord r,
-            long thisTime, long totalTime) {
-        for (int i=mWaitingActivityLaunched.size()-1; i>=0; i--) {
-            WaitResult w = mWaitingActivityLaunched.get(i);
-            w.timeout = timeout;
-            if (r != null) {
-                w.who = new ComponentName(r.info.packageName, r.info.name);
-            }
-            w.thisTime = thisTime;
-            w.totalTime = totalTime;
-        }
-        notify();
-    }
-    
-    void reportActivityVisibleLocked(ActivityRecord r) {
-        for (int i=mWaitingActivityVisible.size()-1; i>=0; i--) {
-            WaitResult w = mWaitingActivityVisible.get(i);
-            w.timeout = false;
-            if (r != null) {
-                w.who = new ComponentName(r.info.packageName, r.info.name);
-            }
-            w.totalTime = SystemClock.uptimeMillis() - w.thisTime;
-            w.thisTime = w.totalTime;
-        }
-        notify();
-    }
-    
-    private final int startActivityMayWait(IApplicationThread caller,
-            Intent intent, String resolvedType, Uri[] grantedUriPermissions,
-            int grantedMode, IBinder resultTo,
-            String resultWho, int requestCode, boolean onlyIfNeeded,
-            boolean debug, WaitResult outResult, Configuration config) {
-        // Refuse possible leaked file descriptors
-        if (intent != null && intent.hasFileDescriptors()) {
-            throw new IllegalArgumentException("File descriptors passed in Intent");
-        }
-
-        boolean componentSpecified = intent.getComponent() != null;
-        
-        // Don't modify the client's object!
-        intent = new Intent(intent);
-
-        // Collect information about the target of the Intent.
-        ActivityInfo aInfo;
-        try {
-            ResolveInfo rInfo =
-                AppGlobals.getPackageManager().resolveIntent(
-                        intent, resolvedType,
-                        PackageManager.MATCH_DEFAULT_ONLY
-                        | STOCK_PM_FLAGS);
-            aInfo = rInfo != null ? rInfo.activityInfo : null;
-        } catch (RemoteException e) {
-            aInfo = null;
-        }
-
-        if (aInfo != null) {
-            // Store the found target back into the intent, because now that
-            // we have it we never want to do this again.  For example, if the
-            // user navigates back to this point in the history, we should
-            // always restart the exact same activity.
-            intent.setComponent(new ComponentName(
-                    aInfo.applicationInfo.packageName, aInfo.name));
-
-            // Don't debug things in the system process
-            if (debug) {
-                if (!aInfo.processName.equals("system")) {
-                    setDebugApp(aInfo.processName, true, false);
-                }
-            }
-        }
-
-        synchronized (this) {
-            int callingPid;
-            int callingUid;
-            if (caller == null) {
-                callingPid = Binder.getCallingPid();
-                callingUid = Binder.getCallingUid();
-            } else {
-                callingPid = callingUid = -1;
-            }
-            
-            mConfigWillChange = config != null && mConfiguration.diff(config) != 0;
-            if (DEBUG_CONFIGURATION) Slog.v(TAG,
-                    "Starting activity when config will change = " + mConfigWillChange);
-            
-            final long origId = Binder.clearCallingIdentity();
-            
-            if (aInfo != null &&
-                    (aInfo.applicationInfo.flags&ApplicationInfo.FLAG_HEAVY_WEIGHT) != 0) {
-                // This may be a heavy-weight process!  Check to see if we already
-                // have another, different heavy-weight process running.
-                if (aInfo.processName.equals(aInfo.applicationInfo.packageName)) {
-                    if (mHeavyWeightProcess != null &&
-                            (mHeavyWeightProcess.info.uid != aInfo.applicationInfo.uid ||
-                            !mHeavyWeightProcess.processName.equals(aInfo.processName))) {
-                        int realCallingPid = callingPid;
-                        int realCallingUid = callingUid;
-                        if (caller != null) {
-                            ProcessRecord callerApp = getRecordForAppLocked(caller);
-                            if (callerApp != null) {
-                                realCallingPid = callerApp.pid;
-                                realCallingUid = callerApp.info.uid;
-                            } else {
-                                Slog.w(TAG, "Unable to find app for caller " + caller
-                                      + " (pid=" + realCallingPid + ") when starting: "
-                                      + intent.toString());
-                                return START_PERMISSION_DENIED;
-                            }
-                        }
-                        
-                        IIntentSender target = getIntentSenderLocked(
-                                IActivityManager.INTENT_SENDER_ACTIVITY, "android",
-                                realCallingUid, null, null, 0, intent,
-                                resolvedType, PendingIntent.FLAG_CANCEL_CURRENT
-                                | PendingIntent.FLAG_ONE_SHOT);
-                        
-                        Intent newIntent = new Intent();
-                        if (requestCode >= 0) {
-                            // Caller is requesting a result.
-                            newIntent.putExtra(HeavyWeightSwitcherActivity.KEY_HAS_RESULT, true);
-                        }
-                        newIntent.putExtra(HeavyWeightSwitcherActivity.KEY_INTENT,
-                                new IntentSender(target));
-                        if (mHeavyWeightProcess.activities.size() > 0) {
-                            ActivityRecord hist = mHeavyWeightProcess.activities.get(0);
-                            newIntent.putExtra(HeavyWeightSwitcherActivity.KEY_CUR_APP,
-                                    hist.packageName);
-                            newIntent.putExtra(HeavyWeightSwitcherActivity.KEY_CUR_TASK,
-                                    hist.task.taskId);
-                        }
-                        newIntent.putExtra(HeavyWeightSwitcherActivity.KEY_NEW_APP,
-                                aInfo.packageName);
-                        newIntent.setFlags(intent.getFlags());
-                        newIntent.setClassName("android",
-                                HeavyWeightSwitcherActivity.class.getName());
-                        intent = newIntent;
-                        resolvedType = null;
-                        caller = null;
-                        callingUid = Binder.getCallingUid();
-                        callingPid = Binder.getCallingPid();
-                        componentSpecified = true;
-                        try {
-                            ResolveInfo rInfo =
-                                AppGlobals.getPackageManager().resolveIntent(
-                                        intent, null,
-                                        PackageManager.MATCH_DEFAULT_ONLY
-                                        | STOCK_PM_FLAGS);
-                            aInfo = rInfo != null ? rInfo.activityInfo : null;
-                        } catch (RemoteException e) {
-                            aInfo = null;
-                        }
-                    }
-                }
-            }
-            
-            int res = startActivityLocked(caller, intent, resolvedType,
-                    grantedUriPermissions, grantedMode, aInfo,
-                    resultTo, resultWho, requestCode, callingPid, callingUid,
-                    onlyIfNeeded, componentSpecified);
-            
-            if (mConfigWillChange) {
-                // If the caller also wants to switch to a new configuration,
-                // do so now.  This allows a clean switch, as we are waiting
-                // for the current activity to pause (so we will not destroy
-                // it), and have not yet started the next activity.
-                enforceCallingPermission(android.Manifest.permission.CHANGE_CONFIGURATION,
-                        "updateConfiguration()");
-                mConfigWillChange = false;
-                if (DEBUG_CONFIGURATION) Slog.v(TAG,
-                        "Updating to new configuration after starting activity.");
-                updateConfigurationLocked(config, null);
-            }
-            
-            Binder.restoreCallingIdentity(origId);
-            
-            if (outResult != null) {
-                outResult.result = res;
-                if (res == IActivityManager.START_SUCCESS) {
-                    mWaitingActivityLaunched.add(outResult);
-                    do {
-                        try {
-                            wait();
-                        } catch (InterruptedException e) {
-                        }
-                    } while (!outResult.timeout && outResult.who == null);
-                } else if (res == IActivityManager.START_TASK_TO_FRONT) {
-                    ActivityRecord r = this.topRunningActivityLocked(null);
-                    if (r.nowVisible) {
-                        outResult.timeout = false;
-                        outResult.who = new ComponentName(r.info.packageName, r.info.name);
-                        outResult.totalTime = 0;
-                        outResult.thisTime = 0;
-                    } else {
-                        outResult.thisTime = SystemClock.uptimeMillis();
-                        mWaitingActivityVisible.add(outResult);
-                        do {
-                            try {
-                                wait();
-                            } catch (InterruptedException e) {
-                            }
-                        } while (!outResult.timeout && outResult.who == null);
-                    }
-                }
-            }
-            
-            return res;
-        }
-    }
-
     public final int startActivity(IApplicationThread caller,
             Intent intent, String resolvedType, Uri[] grantedUriPermissions,
             int grantedMode, IBinder resultTo,
             String resultWho, int requestCode, boolean onlyIfNeeded,
             boolean debug) {
-        return startActivityMayWait(caller, intent, resolvedType,
+        return mMainStack.startActivityMayWait(caller, intent, resolvedType,
                 grantedUriPermissions, grantedMode, resultTo, resultWho,
                 requestCode, onlyIfNeeded, debug, null, null);
     }
@@ -4010,7 +1988,7 @@
             String resultWho, int requestCode, boolean onlyIfNeeded,
             boolean debug) {
         WaitResult res = new WaitResult();
-        startActivityMayWait(caller, intent, resolvedType,
+        mMainStack.startActivityMayWait(caller, intent, resolvedType,
                 grantedUriPermissions, grantedMode, resultTo, resultWho,
                 requestCode, onlyIfNeeded, debug, res, null);
         return res;
@@ -4021,7 +1999,7 @@
             int grantedMode, IBinder resultTo,
             String resultWho, int requestCode, boolean onlyIfNeeded,
             boolean debug, Configuration config) {
-        return startActivityMayWait(caller, intent, resolvedType,
+        return mMainStack.startActivityMayWait(caller, intent, resolvedType,
                 grantedUriPermissions, grantedMode, resultTo, resultWho,
                 requestCode, onlyIfNeeded, debug, null, config);
     }
@@ -4045,8 +2023,8 @@
         synchronized (this) {
             // If this is coming from the currently resumed activity, it is
             // effectively saying that app switches are allowed at this point.
-            if (mResumedActivity != null
-                    && mResumedActivity.info.applicationInfo.uid ==
+            if (mMainStack.mResumedActivity != null
+                    && mMainStack.mResumedActivity.info.applicationInfo.uid ==
                             Binder.getCallingUid()) {
                 mAppSwitchesAllowedTime = 0;
             }
@@ -4064,11 +2042,11 @@
         }
 
         synchronized (this) {
-            int index = indexOfTokenLocked(callingActivity);
+            int index = mMainStack.indexOfTokenLocked(callingActivity);
             if (index < 0) {
                 return false;
             }
-            ActivityRecord r = (ActivityRecord)mHistory.get(index);
+            ActivityRecord r = (ActivityRecord)mMainStack.mHistory.get(index);
             if (r.app == null || r.app.thread == null) {
                 // The caller is not running...  d'oh!
                 return false;
@@ -4137,7 +2115,7 @@
             final long origId = Binder.clearCallingIdentity();
             // XXX we are not dealing with propagating grantedUriPermissions...
             // those are not yet exposed to user code, so there is no need.
-            int res = startActivityLocked(r.app.thread, intent,
+            int res = mMainStack.startActivityLocked(r.app.thread, intent,
                     r.resolvedType, null, 0, aInfo, resultTo, resultWho,
                     requestCode, -1, r.launchedFromUid, false, false);
             Binder.restoreCallingIdentity(origId);
@@ -4189,13 +2167,13 @@
         }
 
         synchronized(this) {
-            return startActivityLocked(null, intent, resolvedType,
+            return mMainStack.startActivityLocked(null, intent, resolvedType,
                     null, 0, aInfo, resultTo, resultWho, requestCode, -1, uid,
                     onlyIfNeeded, componentSpecified);
         }
     }
 
-    private final void addRecentTaskLocked(TaskRecord task) {
+    final void addRecentTaskLocked(TaskRecord task) {
         // Remove any existing entries that are the same kind of task.
         int N = mRecentTasks.size();
         for (int i=0; i<N; i++) {
@@ -4221,11 +2199,11 @@
     public void setRequestedOrientation(IBinder token,
             int requestedOrientation) {
         synchronized (this) {
-            int index = indexOfTokenLocked(token);
+            int index = mMainStack.indexOfTokenLocked(token);
             if (index < 0) {
                 return;
             }
-            ActivityRecord r = (ActivityRecord)mHistory.get(index);
+            ActivityRecord r = (ActivityRecord)mMainStack.mHistory.get(index);
             final long origId = Binder.clearCallingIdentity();
             mWindowManager.setAppOrientation(r, requestedOrientation);
             Configuration config = mWindowManager.updateOrientationFromAppTokens(
@@ -4234,7 +2212,7 @@
             if (config != null) {
                 r.frozenBeforeDestroy = true;
                 if (!updateConfigurationLocked(config, r)) {
-                    resumeTopActivityLocked(null);
+                    mMainStack.resumeTopActivityLocked(null);
                 }
             }
             Binder.restoreCallingIdentity(origId);
@@ -4243,250 +2221,15 @@
 
     public int getRequestedOrientation(IBinder token) {
         synchronized (this) {
-            int index = indexOfTokenLocked(token);
+            int index = mMainStack.indexOfTokenLocked(token);
             if (index < 0) {
                 return ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
             }
-            ActivityRecord r = (ActivityRecord)mHistory.get(index);
+            ActivityRecord r = (ActivityRecord)mMainStack.mHistory.get(index);
             return mWindowManager.getAppOrientation(r);
         }
     }
 
-    private 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) {
-            if (!r.finishing) {
-                requestFinishActivityLocked(r, Activity.RESULT_CANCELED, null,
-                        "no-history");
-            }
-        } else if (r.app != null && r.app.thread != null) {
-            if (mFocusedActivity == r) {
-                setFocusedActivityLocked(topRunningActivityLocked(null));
-            }
-            r.resumeKeyDispatchingLocked();
-            try {
-                r.stopped = false;
-                r.state = ActivityState.STOPPING;
-                if (DEBUG_VISBILITY) Slog.v(
-                        TAG, "Stopping visible=" + r.visible + " for " + r);
-                if (!r.visible) {
-                    mWindowManager.setAppVisibility(r, false);
-                }
-                r.app.thread.scheduleStopActivity(r, r.visible, r.configChangeFlags);
-            } catch (Exception e) {
-                // Maybe just ignore exceptions here...  if the process
-                // has crashed, our death notification will clean things
-                // up.
-                Slog.w(TAG, "Exception thrown during pause", e);
-                // Just in case, assume it to be stopped.
-                r.stopped = true;
-                r.state = ActivityState.STOPPED;
-                if (r.configDestroy) {
-                    destroyActivityLocked(r, true);
-                }
-            }
-        }
-    }
-
-    /**
-     * @return Returns true if the activity is being finished, false if for
-     * some reason it is being left as-is.
-     */
-    private final boolean requestFinishActivityLocked(IBinder token, int resultCode,
-            Intent resultData, String reason) {
-        if (DEBUG_RESULTS) Slog.v(
-            TAG, "Finishing activity: token=" + token
-            + ", result=" + resultCode + ", data=" + resultData);
-
-        int index = indexOfTokenLocked(token);
-        if (index < 0) {
-            return false;
-        }
-        ActivityRecord r = (ActivityRecord)mHistory.get(index);
-
-        // Is this the last activity left?
-        boolean lastActivity = true;
-        for (int i=mHistory.size()-1; i>=0; i--) {
-            ActivityRecord p = (ActivityRecord)mHistory.get(i);
-            if (!p.finishing && p != r) {
-                lastActivity = false;
-                break;
-            }
-        }
-        
-        // If this is the last activity, but it is the home activity, then
-        // just don't finish it.
-        if (lastActivity) {
-            if (r.intent.hasCategory(Intent.CATEGORY_HOME)) {
-                return false;
-            }
-        }
-        
-        finishActivityLocked(r, index, resultCode, resultData, reason);
-        return true;
-    }
-
-    /**
-     * @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.
-     */
-    private final boolean finishActivityLocked(ActivityRecord r, int index,
-            int resultCode, Intent resultData, String reason) {
-        if (r.finishing) {
-            Slog.w(TAG, "Duplicate finish request for " + r);
-            return false;
-        }
-
-        r.finishing = true;
-        EventLog.writeEvent(EventLogTags.AM_FINISH_ACTIVITY,
-                System.identityHashCode(r),
-                r.task.taskId, r.shortComponentName, reason);
-        r.task.numActivities--;
-        if (index < (mHistory.size()-1)) {
-            ActivityRecord next = (ActivityRecord)mHistory.get(index+1);
-            if (next.task == r.task) {
-                if (r.frontOfTask) {
-                    // The next activity is now the front of the task.
-                    next.frontOfTask = true;
-                }
-                if ((r.intent.getFlags()&Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET) != 0) {
-                    // If the caller asked that this activity (and all above it)
-                    // be cleared when the task is reset, don't lose that information,
-                    // but propagate it up to the next activity.
-                    next.intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET);
-                }
-            }
-        }
-
-        r.pauseKeyDispatchingLocked();
-        if (mFocusedActivity == r) {
-            setFocusedActivityLocked(topRunningActivityLocked(null));
-        }
-
-        // send the result
-        ActivityRecord resultTo = r.resultTo;
-        if (resultTo != null) {
-            if (DEBUG_RESULTS) Slog.v(TAG, "Adding result to " + resultTo
-                    + " who=" + r.resultWho + " req=" + r.requestCode
-                    + " res=" + resultCode + " data=" + resultData);
-            if (r.info.applicationInfo.uid > 0) {
-                grantUriPermissionFromIntentLocked(r.info.applicationInfo.uid,
-                        r.packageName, resultData, r);
-            }
-            resultTo.addResultLocked(r, r.resultWho, r.requestCode, resultCode,
-                                     resultData);
-            r.resultTo = null;
-        }
-        else if (DEBUG_RESULTS) Slog.v(TAG, "No result destination from " + r);
-
-        // Make sure this HistoryRecord is not holding on to other resources,
-        // because clients have remote IPC references to this object so we
-        // can't assume that will go away and want to avoid circular IPC refs.
-        r.results = null;
-        r.pendingResults = null;
-        r.newIntents = null;
-        r.icicle = null;
-        
-        if (mPendingThumbnails.size() > 0) {
-            // 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);
-        }
-
-        if (mResumedActivity == r) {
-            boolean endTask = index <= 0
-                    || ((ActivityRecord)mHistory.get(index-1)).task != r.task;
-            if (DEBUG_TRANSITION) Slog.v(TAG,
-                    "Prepare close transition: finishing " + r);
-            mWindowManager.prepareAppTransition(endTask
-                    ? WindowManagerPolicy.TRANSIT_TASK_CLOSE
-                    : WindowManagerPolicy.TRANSIT_ACTIVITY_CLOSE);
-    
-            // Tell window manager to prepare for this one to be removed.
-            mWindowManager.setAppVisibility(r, false);
-                
-            if (mPausingActivity == null) {
-                if (DEBUG_PAUSE) Slog.v(TAG, "Finish needs to pause: " + r);
-                if (DEBUG_USER_LEAVING) Slog.v(TAG, "finish() => pause with userLeaving=false");
-                startPausingLocked(false, false);
-            }
-
-        } else if (r.state != ActivityState.PAUSING) {
-            // If the activity is PAUSING, we will complete the finish once
-            // it is done pausing; else we can just directly finish it here.
-            if (DEBUG_PAUSE) Slog.v(TAG, "Finish not pausing: " + r);
-            return finishCurrentActivityLocked(r, index,
-                    FINISH_AFTER_PAUSE) == null;
-        } else {
-            if (DEBUG_PAUSE) Slog.v(TAG, "Finish waiting for pause of: " + r);
-        }
-
-        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;
-
-    private final ActivityRecord finishCurrentActivityLocked(ActivityRecord r,
-            int mode) {
-        final int index = indexOfTokenLocked(r);
-        if (index < 0) {
-            return null;
-        }
-
-        return finishCurrentActivityLocked(r, index, mode);
-    }
-
-    private final ActivityRecord finishCurrentActivityLocked(ActivityRecord r,
-            int index, int mode) {
-        // 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.
-        if (mode == FINISH_AFTER_VISIBLE && r.nowVisible) {
-            if (!mStoppingActivities.contains(r)) {
-                mStoppingActivities.add(r);
-                if (mStoppingActivities.size() > 3) {
-                    // If we already have a few activities waiting to stop,
-                    // then give up on things going idle and start clearing
-                    // them out.
-                    Message msg = Message.obtain();
-                    msg.what = ActivityManagerService.IDLE_NOW_MSG;
-                    mHandler.sendMessage(msg);
-                }
-            }
-            r.state = ActivityState.STOPPING;
-            updateOomAdjLocked();
-            return r;
-        }
-
-        // make sure the record is cleaned out of other places.
-        mStoppingActivities.remove(r);
-        mWaitingVisibleActivities.remove(r);
-        if (mResumedActivity == r) {
-            mResumedActivity = null;
-        }
-        final ActivityState prevState = r.state;
-        r.state = ActivityState.FINISHING;
-
-        if (mode == FINISH_IMMEDIATELY
-                || prevState == ActivityState.STOPPED
-                || prevState == ActivityState.INITIALIZING) {
-            // If this activity is already stopped, we can just finish
-            // it right now.
-            return destroyActivityLocked(r, true) ? null : r;
-        } else {
-            // 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);
-            resumeTopActivityLocked(null);
-        }
-        return r;
-    }
-
     /**
      * This is the internal entry point for handling Activity.finish().
      * 
@@ -4505,7 +2248,7 @@
         synchronized(this) {
             if (mController != null) {
                 // Find the first activity that is not finishing.
-                ActivityRecord next = topRunningActivityLocked(token, 0);
+                ActivityRecord next = mMainStack.topRunningActivityLocked(token, 0);
                 if (next != null) {
                     // ask watcher if this is allowed
                     boolean resumeOK = true;
@@ -4521,7 +2264,7 @@
                 }
             }
             final long origId = Binder.clearCallingIdentity();
-            boolean res = requestFinishActivityLocked(token, resultCode,
+            boolean res = mMainStack.requestFinishActivityLocked(token, resultCode,
                     resultData, "app-request");
             Binder.restoreCallingIdentity(origId);
             return res;
@@ -4549,9 +2292,9 @@
             for (int i=0; i<activities.size(); i++) {
                 ActivityRecord r = activities.get(i);
                 if (!r.finishing) {
-                    int index = indexOfTokenLocked(r);
+                    int index = mMainStack.indexOfTokenLocked(r);
                     if (index >= 0) {
-                        finishActivityLocked(r, index, Activity.RESULT_CANCELED,
+                        mMainStack.finishActivityLocked(r, index, Activity.RESULT_CANCELED,
                                 null, "finish-heavy");
                     }
                 }
@@ -4616,50 +2359,24 @@
         }
     }
     
-    void sendActivityResultLocked(int callingUid, ActivityRecord r,
-            String resultWho, int requestCode, int resultCode, Intent data) {
-
-        if (callingUid > 0) {
-            grantUriPermissionFromIntentLocked(callingUid, r.packageName,
-                    data, r);
-        }
-
-        if (DEBUG_RESULTS) Slog.v(TAG, "Send activity result to " + r
-                + " : who=" + resultWho + " req=" + requestCode
-                + " res=" + resultCode + " data=" + data);
-        if (mResumedActivity == r && r.app != null && r.app.thread != null) {
-            try {
-                ArrayList<ResultInfo> list = new ArrayList<ResultInfo>();
-                list.add(new ResultInfo(resultWho, requestCode,
-                        resultCode, data));
-                r.app.thread.scheduleSendResult(r, list);
-                return;
-            } catch (Exception e) {
-                Slog.w(TAG, "Exception thrown sending result to " + r, e);
-            }
-        }
-
-        r.addResultLocked(null, resultWho, requestCode, resultCode, data);
-    }
-
     public final void finishSubActivity(IBinder token, String resultWho,
             int requestCode) {
         synchronized(this) {
-            int index = indexOfTokenLocked(token);
+            int index = mMainStack.indexOfTokenLocked(token);
             if (index < 0) {
                 return;
             }
-            ActivityRecord self = (ActivityRecord)mHistory.get(index);
+            ActivityRecord self = (ActivityRecord)mMainStack.mHistory.get(index);
 
             final long origId = Binder.clearCallingIdentity();
 
             int i;
-            for (i=mHistory.size()-1; i>=0; i--) {
-                ActivityRecord r = (ActivityRecord)mHistory.get(i);
+            for (i=mMainStack.mHistory.size()-1; i>=0; i--) {
+                ActivityRecord r = (ActivityRecord)mMainStack.mHistory.get(i);
                 if (r.resultTo == self && r.requestCode == requestCode) {
                     if ((r.resultWho == null && resultWho == null) ||
                         (r.resultWho != null && r.resultWho.equals(resultWho))) {
-                        finishActivityLocked(r, i,
+                        mMainStack.finishActivityLocked(r, i,
                                 Activity.RESULT_CANCELED, null, "request-sub");
                     }
                 }
@@ -4672,8 +2389,8 @@
     public boolean willActivityBeVisible(IBinder token) {
         synchronized(this) {
             int i;
-            for (i=mHistory.size()-1; i>=0; i--) {
-                ActivityRecord r = (ActivityRecord)mHistory.get(i);
+            for (i=mMainStack.mHistory.size()-1; i>=0; i--) {
+                ActivityRecord r = (ActivityRecord)mMainStack.mHistory.get(i);
                 if (r == token) {
                     return true;
                 }
@@ -4688,11 +2405,11 @@
     public void overridePendingTransition(IBinder token, String packageName,
             int enterAnim, int exitAnim) {
         synchronized(this) {
-            int index = indexOfTokenLocked(token);
+            int index = mMainStack.indexOfTokenLocked(token);
             if (index < 0) {
                 return;
             }
-            ActivityRecord self = (ActivityRecord)mHistory.get(index);
+            ActivityRecord self = (ActivityRecord)mMainStack.mHistory.get(index);
 
             final long origId = Binder.clearCallingIdentity();
             
@@ -4707,192 +2424,6 @@
     }
     
     /**
-     * Perform clean-up of service connections in an activity record.
-     */
-    private final void cleanUpActivityServicesLocked(ActivityRecord r) {
-        // Throw away any services that have been bound by this activity.
-        if (r.connections != null) {
-            Iterator<ConnectionRecord> it = r.connections.iterator();
-            while (it.hasNext()) {
-                ConnectionRecord c = it.next();
-                removeConnectionLocked(c, null, r);
-            }
-            r.connections = null;
-        }
-    }
-    
-    /**
-     * Perform the common clean-up of an activity record.  This is called both
-     * as part of destroyActivityLocked() (when destroying the client-side
-     * representation) and cleaning things up as a result of its hosting
-     * processing going away, in which case there is no remaining client-side
-     * state to destroy so only the cleanup here is needed.
-     */
-    private final void cleanUpActivityLocked(ActivityRecord r, boolean cleanServices) {
-        if (mResumedActivity == r) {
-            mResumedActivity = null;
-        }
-        if (mFocusedActivity == r) {
-            mFocusedActivity = null;
-        }
-
-        r.configDestroy = false;
-        r.frozenBeforeDestroy = false;
-
-        // 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);
-        mWaitingVisibleActivities.remove(r);
-        
-        // Remove any pending results.
-        if (r.finishing && r.pendingResults != null) {
-            for (WeakReference<PendingIntentRecord> apr : r.pendingResults) {
-                PendingIntentRecord rec = apr.get();
-                if (rec != null) {
-                    cancelIntentSenderLocked(rec, false);
-                }
-            }
-            r.pendingResults = null;
-        }
-
-        if (cleanServices) {
-            cleanUpActivityServicesLocked(r);            
-        }
-
-        if (mPendingThumbnails.size() > 0) {
-            // 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);
-        }
-
-        // Get rid of any pending idle timeouts.
-        mHandler.removeMessages(PAUSE_TIMEOUT_MSG, r);
-        mHandler.removeMessages(IDLE_TIMEOUT_MSG, r);
-    }
-
-    private final void removeActivityFromHistoryLocked(ActivityRecord r) {
-        if (r.state != ActivityState.DESTROYED) {
-            mHistory.remove(r);
-            r.inHistory = false;
-            r.state = ActivityState.DESTROYED;
-            mWindowManager.removeAppToken(r);
-            if (VALIDATE_TOKENS) {
-                mWindowManager.validateAppTokens(mHistory);
-            }
-            cleanUpActivityServicesLocked(r);
-            removeActivityUriPermissionsLocked(r);
-        }
-    }
-    
-    /**
-     * Destroy the current CLIENT SIDE instance of an activity.  This may be
-     * called both when actually finishing an activity, or when performing
-     * a configuration switch where we destroy the current client-side object
-     * but then create a new client-side object for this same HistoryRecord.
-     */
-    private final boolean destroyActivityLocked(ActivityRecord r,
-            boolean removeFromApp) {
-        if (DEBUG_SWITCH) Slog.v(
-            TAG, "Removing activity: token=" + r
-              + ", app=" + (r.app != null ? r.app.processName : "(null)"));
-        EventLog.writeEvent(EventLogTags.AM_DESTROY_ACTIVITY,
-                System.identityHashCode(r),
-                r.task.taskId, r.shortComponentName);
-
-        boolean removedFromHistory = false;
-        
-        cleanUpActivityLocked(r, false);
-
-        final boolean hadApp = r.app != null;
-        
-        if (hadApp) {
-            if (removeFromApp) {
-                int idx = r.app.activities.indexOf(r);
-                if (idx >= 0) {
-                    r.app.activities.remove(idx);
-                }
-                if (mHeavyWeightProcess == r.app && r.app.activities.size() <= 0) {
-                    mHeavyWeightProcess = null;
-                    mHandler.sendEmptyMessage(CANCEL_HEAVY_NOTIFICATION_MSG);
-                }
-                if (r.persistent) {
-                    decPersistentCountLocked(r.app);
-                }
-                if (r.app.activities.size() == 0) {
-                    // No longer have activities, so update location in
-                    // LRU list.
-                    updateLruProcessLocked(r.app, true, false);
-                }
-            }
-
-            boolean skipDestroy = false;
-            
-            try {
-                if (DEBUG_SWITCH) Slog.i(TAG, "Destroying: " + r);
-                r.app.thread.scheduleDestroyActivity(r, r.finishing,
-                        r.configChangeFlags);
-            } catch (Exception e) {
-                // We can just ignore exceptions here...  if the process
-                // has crashed, our death notification will clean things
-                // up.
-                //Slog.w(TAG, "Exception thrown during finish", e);
-                if (r.finishing) {
-                    removeActivityFromHistoryLocked(r);
-                    removedFromHistory = true;
-                    skipDestroy = true;
-                }
-            }
-
-            r.app = null;
-            r.nowVisible = false;
-            
-            if (r.finishing && !skipDestroy) {
-                r.state = ActivityState.DESTROYING;
-                Message msg = mHandler.obtainMessage(DESTROY_TIMEOUT_MSG);
-                msg.obj = r;
-                mHandler.sendMessageDelayed(msg, DESTROY_TIMEOUT);
-            } else {
-                r.state = ActivityState.DESTROYED;
-            }
-        } else {
-            // remove this record from the history.
-            if (r.finishing) {
-                removeActivityFromHistoryLocked(r);
-                removedFromHistory = true;
-            } else {
-                r.state = ActivityState.DESTROYED;
-            }
-        }
-
-        r.configChangeFlags = 0;
-        
-        if (!mLRUActivities.remove(r) && hadApp) {
-            Slog.w(TAG, "Activity " + r + " being finished, but not in LRU list");
-        }
-        
-        return removedFromHistory;
-    }
-
-    private static void removeHistoryRecordsForAppLocked(ArrayList list, ProcessRecord app) {
-        int i = list.size();
-        if (localLOGV) Slog.v(
-            TAG, "Removing app " + app + " from list " + list
-            + " with " + i + " entries");
-        while (i > 0) {
-            i--;
-            ActivityRecord r = (ActivityRecord)list.get(i);
-            if (localLOGV) Slog.v(
-                TAG, "Record #" + i + " " + r + ": app=" + r.app);
-            if (r.app == app) {
-                if (localLOGV) Slog.v(TAG, "Removing this entry!");
-                list.remove(i);
-            }
-        }
-    }
-
-    /**
      * Main function for removing an existing process from the activity manager
      * as a result of that process going away.  Clears out all connections
      * to the process.
@@ -4905,30 +2436,27 @@
         }
 
         // Just in case...
-        if (mPausingActivity != null && mPausingActivity.app == app) {
-            if (DEBUG_PAUSE) Slog.v(TAG, "App died while pausing: " + mPausingActivity);
-            mPausingActivity = null;
+        if (mMainStack.mPausingActivity != null && mMainStack.mPausingActivity.app == app) {
+            if (DEBUG_PAUSE) Slog.v(TAG, "App died while pausing: " +mMainStack.mPausingActivity);
+            mMainStack.mPausingActivity = null;
         }
-        if (mLastPausedActivity != null && mLastPausedActivity.app == app) {
-            mLastPausedActivity = null;
+        if (mMainStack.mLastPausedActivity != null && mMainStack.mLastPausedActivity.app == app) {
+            mMainStack.mLastPausedActivity = null;
         }
 
         // Remove this application's activities from active lists.
-        removeHistoryRecordsForAppLocked(mLRUActivities, app);
-        removeHistoryRecordsForAppLocked(mStoppingActivities, app);
-        removeHistoryRecordsForAppLocked(mWaitingVisibleActivities, app);
-        removeHistoryRecordsForAppLocked(mFinishingActivities, app);
+        mMainStack.removeHistoryRecordsForAppLocked(app);
 
         boolean atTop = true;
         boolean hasVisibleActivities = false;
 
         // Clean out the history list.
-        int i = mHistory.size();
+        int i = mMainStack.mHistory.size();
         if (localLOGV) Slog.v(
             TAG, "Removing app " + app + " from history with " + i + " entries");
         while (i > 0) {
             i--;
-            ActivityRecord r = (ActivityRecord)mHistory.get(i);
+            ActivityRecord r = (ActivityRecord)mMainStack.mHistory.get(i);
             if (localLOGV) Slog.v(
                 TAG, "Record #" + i + " " + r + ": app=" + r.app);
             if (r.app == app) {
@@ -4936,14 +2464,14 @@
                     if (localLOGV) Slog.v(
                         TAG, "Removing this entry!  frozen=" + r.haveState
                         + " finishing=" + r.finishing);
-                    mHistory.remove(i);
+                    mMainStack.mHistory.remove(i);
 
                     r.inHistory = false;
                     mWindowManager.removeAppToken(r);
                     if (VALIDATE_TOKENS) {
-                        mWindowManager.validateAppTokens(mHistory);
+                        mWindowManager.validateAppTokens(mMainStack.mHistory);
                     }
-                    removeActivityUriPermissionsLocked(r);
+                    r.removeUriPermissionsLocked();
 
                 } else {
                     // We have the current state for this activity, so
@@ -4960,7 +2488,7 @@
                     }
                 }
 
-                cleanUpActivityLocked(r, true);
+                r.stack.cleanUpActivityLocked(r, true);
                 r.state = ActivityState.STOPPED;
             }
             atTop = false;
@@ -4977,14 +2505,14 @@
         }
 
         if (!restarting) {
-            if (!resumeTopActivityLocked(null)) {
+            if (!mMainStack.resumeTopActivityLocked(null)) {
                 // If there was nothing to resume, and we are not already
                 // restarting this process, but there is a visible activity that
                 // is hosted by the process...  then make sure all visible
                 // activities are running, taking care of restarting this
                 // process.
                 if (hasVisibleActivities) {
-                    ensureActivitiesVisibleLocked(null, 0);
+                    mMainStack.ensureActivitiesVisibleLocked(null, 0);
                 }
             }
         }
@@ -5003,7 +2531,7 @@
         return -1;
     }
 
-    private final ProcessRecord getRecordForAppLocked(
+    final ProcessRecord getRecordForAppLocked(
             IApplicationThread thread) {
         if (thread == null) {
             return null;
@@ -5013,7 +2541,7 @@
         return appIndex >= 0 ? mLruProcesses.get(appIndex) : null;
     }
 
-    private final void appDiedLocked(ProcessRecord app, int pid,
+    final void appDiedLocked(ProcessRecord app, int pid,
             IApplicationThread thread) {
 
         mProcDeaths[0]++;
@@ -5253,8 +2781,7 @@
         }
     }
 
-    private final void decPersistentCountLocked(ProcessRecord app)
-    {
+    final void decPersistentCountLocked(ProcessRecord app) {
         app.persistentActivities--;
         if (app.persistentActivities > 0) {
             // Still more of 'em...
@@ -5282,11 +2809,11 @@
         }
 
         synchronized(this) {
-            int index = indexOfTokenLocked(token);
+            int index = mMainStack.indexOfTokenLocked(token);
             if (index < 0) {
                 return;
             }
-            ActivityRecord r = (ActivityRecord)mHistory.get(index);
+            ActivityRecord r = (ActivityRecord)mMainStack.mHistory.get(index);
             ProcessRecord app = r.app;
 
             if (localLOGV) Slog.v(
@@ -5497,10 +3024,10 @@
             
             mWindowManager.closeSystemDialogs(reason);
             
-            for (i=mHistory.size()-1; i>=0; i--) {
-                ActivityRecord r = (ActivityRecord)mHistory.get(i);
+            for (i=mMainStack.mHistory.size()-1; i>=0; i--) {
+                ActivityRecord r = (ActivityRecord)mMainStack.mHistory.get(i);
                 if ((r.info.flags&ActivityInfo.FLAG_FINISH_ON_CLOSE_SYSTEM_DIALOGS) != 0) {
-                    finishActivityLocked(r, i,
+                    r.stack.finishActivityLocked(r, i,
                             Activity.RESULT_CANCELED, null, "close-sys");
                 }
             }
@@ -5621,8 +3148,8 @@
         boolean didSomething = killPackageProcessesLocked(name, uid, -100,
                 callerWillRestart, doit);
         
-        for (i=mHistory.size()-1; i>=0; i--) {
-            ActivityRecord r = (ActivityRecord)mHistory.get(i);
+        for (i=mMainStack.mHistory.size()-1; i>=0; i--) {
+            ActivityRecord r = (ActivityRecord)mMainStack.mHistory.get(i);
             if (r.packageName.equals(name)) {
                 if (!doit) {
                     return true;
@@ -5633,7 +3160,7 @@
                     r.app.removed = true;
                 }
                 r.app = null;
-                finishActivityLocked(r, i, Activity.RESULT_CANCELED, null, "uninstall");
+                r.stack.finishActivityLocked(r, i, Activity.RESULT_CANCELED, null, "uninstall");
             }
         }
 
@@ -5665,7 +3192,7 @@
                     ac.removePackage(name);
                 }
             }
-            resumeTopActivityLocked(null);
+            mMainStack.resumeTopActivityLocked(null);
         }
         
         return didSomething;
@@ -5894,12 +3421,12 @@
         boolean didSomething = false;
 
         // See if the top visible activity is waiting to run in this process...
-        ActivityRecord hr = topRunningActivityLocked(null);
+        ActivityRecord hr = mMainStack.topRunningActivityLocked(null);
         if (hr != null && normalMode) {
             if (hr.app == null && app.info.uid == hr.info.applicationInfo.uid
                     && processName.equals(hr.processName)) {
                 try {
-                    if (realStartActivityLocked(hr, app, true, true)) {
+                    if (mMainStack.realStartActivityLocked(hr, app, true, true)) {
                         didSomething = true;
                     }
                 } catch (Exception e) {
@@ -5908,7 +3435,7 @@
                     badApp = true;
                 }
             } else {
-                ensureActivitiesVisibleLocked(hr, null, processName, 0);
+                mMainStack.ensureActivitiesVisibleLocked(hr, null, processName, 0);
             }
         }
 
@@ -5992,195 +3519,16 @@
 
     public final void activityIdle(IBinder token, Configuration config) {
         final long origId = Binder.clearCallingIdentity();
-        activityIdleInternal(token, false, config);
+        mMainStack.activityIdleInternal(token, false, config);
         Binder.restoreCallingIdentity(origId);
     }
 
-    final ArrayList<ActivityRecord> processStoppingActivitiesLocked(
-            boolean remove) {
-        int N = mStoppingActivities.size();
-        if (N <= 0) return null;
-
-        ArrayList<ActivityRecord> stops = null;
-
-        final boolean nowVisible = mResumedActivity != null
-                && mResumedActivity.nowVisible
-                && !mResumedActivity.waitingVisible;
-        for (int i=0; i<N; i++) {
-            ActivityRecord s = mStoppingActivities.get(i);
-            if (localLOGV) Slog.v(TAG, "Stopping " + s + ": nowVisible="
-                    + nowVisible + " waitingVisible=" + s.waitingVisible
-                    + " finishing=" + s.finishing);
-            if (s.waitingVisible && nowVisible) {
-                mWaitingVisibleActivities.remove(s);
-                s.waitingVisible = false;
-                if (s.finishing) {
-                    // If this activity is finishing, it is sitting on top of
-                    // everyone else but we now know it is no longer needed...
-                    // so get rid of it.  Otherwise, we need to go through the
-                    // normal flow and hide it once we determine that it is
-                    // hidden by the activities in front of it.
-                    if (localLOGV) Slog.v(TAG, "Before stopping, can hide: " + s);
-                    mWindowManager.setAppVisibility(s, false);
-                }
-            }
-            if (!s.waitingVisible && remove) {
-                if (localLOGV) Slog.v(TAG, "Ready to stop: " + s);
-                if (stops == null) {
-                    stops = new ArrayList<ActivityRecord>();
-                }
-                stops.add(s);
-                mStoppingActivities.remove(i);
-                N--;
-                i--;
-            }
-        }
-
-        return stops;
-    }
-
     void enableScreenAfterBoot() {
         EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_ENABLE_SCREEN,
                 SystemClock.uptimeMillis());
         mWindowManager.enableScreenAfterBoot();
     }
 
-    final void activityIdleInternal(IBinder token, boolean fromTimeout,
-            Configuration config) {
-        if (localLOGV) Slog.v(TAG, "Activity idle: " + token);
-
-        ArrayList<ActivityRecord> stops = null;
-        ArrayList<ActivityRecord> finishes = null;
-        ArrayList<ActivityRecord> thumbnails = null;
-        int NS = 0;
-        int NF = 0;
-        int NT = 0;
-        IApplicationThread sendThumbnail = null;
-        boolean booting = false;
-        boolean enableScreen = false;
-
-        synchronized (this) {
-            if (token != null) {
-                mHandler.removeMessages(IDLE_TIMEOUT_MSG, token);
-            }
-
-            // Get the activity record.
-            int index = indexOfTokenLocked(token);
-            if (index >= 0) {
-                ActivityRecord r = (ActivityRecord)mHistory.get(index);
-
-                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;
-                }
-                
-                // No longer need to keep the device awake.
-                if (mResumedActivity == r && 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;
-                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);
-                ensureActivitiesVisibleLocked(null, 0);
-
-                //Slog.i(TAG, "IDLE: mBooted=" + mBooted + ", fromTimeout=" + fromTimeout);
-                if (!mBooted && !fromTimeout) {
-                    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();
-            }
-            if ((NT=mCancelledThumbnails.size()) > 0) {
-                thumbnails = new ArrayList<ActivityRecord>(mCancelledThumbnails);
-                mCancelledThumbnails.clear();
-            }
-
-            booting = mBooting;
-            mBooting = false;
-        }
-
-        int i;
-
-        // Send thumbnail if requested.
-        if (sendThumbnail != null) {
-            try {
-                sendThumbnail.requestThumbnail(token);
-            } catch (Exception e) {
-                Slog.w(TAG, "Exception thrown when requesting thumbnail", e);
-                sendPendingThumbnail(null, token, null, null, true);
-            }
-        }
-
-        // Stop any activities that are scheduled to do so but have been
-        // waiting for the next one to start.
-        for (i=0; i<NS; i++) {
-            ActivityRecord r = (ActivityRecord)stops.get(i);
-            synchronized (this) {
-                if (r.finishing) {
-                    finishCurrentActivityLocked(r, FINISH_IMMEDIATELY);
-                } else {
-                    stopActivityLocked(r);
-                }
-            }
-        }
-
-        // Finish any activities that are scheduled to do so but have been
-        // waiting for the next one to start.
-        for (i=0; i<NF; i++) {
-            ActivityRecord r = (ActivityRecord)finishes.get(i);
-            synchronized (this) {
-                destroyActivityLocked(r, true);
-            }
-        }
-
-        // Report back to any thumbnail receivers.
-        for (i=0; i<NT; i++) {
-            ActivityRecord r = (ActivityRecord)thumbnails.get(i);
-            sendPendingThumbnail(r, null, null, null, true);
-        }
-
-        if (booting) {
-            finishBooting();
-        }
-
-        trimApplications();
-        //dump();
-        //mWindowManager.dump();
-
-        if (enableScreen) {
-            enableScreenAfterBoot();
-        }
-    }
-
     final void finishBooting() {
         IntentFilter pkgFilter = new IntentFilter();
         pkgFilter.addAction(Intent.ACTION_QUERY_PACKAGE_RESTART);
@@ -6249,39 +3597,10 @@
         }
 
         final long origId = Binder.clearCallingIdentity();
-        activityPaused(token, icicle, false);
+        mMainStack.activityPaused(token, icicle, false);
         Binder.restoreCallingIdentity(origId);
     }
 
-    final void activityPaused(IBinder token, Bundle icicle, boolean timeout) {
-        if (DEBUG_PAUSE) Slog.v(
-            TAG, "Activity paused: token=" + token + ", icicle=" + icicle
-            + ", timeout=" + timeout);
-
-        ActivityRecord r = null;
-
-        synchronized (this) {
-            int index = indexOfTokenLocked(token);
-            if (index >= 0) {
-                r = (ActivityRecord)mHistory.get(index);
-                if (!timeout) {
-                    r.icicle = icicle;
-                    r.haveState = true;
-                }
-                mHandler.removeMessages(PAUSE_TIMEOUT_MSG, r);
-                if (mPausingActivity == r) {
-                    r.state = ActivityState.PAUSED;
-                    completePauseLocked();
-                } else {
-                	EventLog.writeEvent(EventLogTags.AM_FAILED_TO_PAUSE,
-                	        System.identityHashCode(r), r.shortComponentName, 
-                			mPausingActivity != null
-                			    ? mPausingActivity.shortComponentName : "(none)");
-                }
-            }
-        }
-    }
-
     public final void activityStopped(IBinder token, Bitmap thumbnail,
             CharSequence description) {
         if (localLOGV) Slog.v(
@@ -6292,17 +3611,17 @@
         final long origId = Binder.clearCallingIdentity();
 
         synchronized (this) {
-            int index = indexOfTokenLocked(token);
+            int index = mMainStack.indexOfTokenLocked(token);
             if (index >= 0) {
-                r = (ActivityRecord)mHistory.get(index);
+                r = (ActivityRecord)mMainStack.mHistory.get(index);
                 r.thumbnail = thumbnail;
                 r.description = description;
                 r.stopped = true;
                 r.state = ActivityState.STOPPED;
                 if (!r.finishing) {
                     if (r.configDestroy) {
-                        destroyActivityLocked(r, true);
-                        resumeTopActivityLocked(null);
+                        r.stack.destroyActivityLocked(r, true);
+                        r.stack.resumeTopActivityLocked(null);
                     }
                 }
             }
@@ -6319,19 +3638,7 @@
 
     public final void activityDestroyed(IBinder token) {
         if (DEBUG_SWITCH) Slog.v(TAG, "ACTIVITY DESTROYED: " + token);
-        synchronized (this) {
-            mHandler.removeMessages(DESTROY_TIMEOUT_MSG, token);
-            
-            int index = indexOfTokenLocked(token);
-            if (index >= 0) {
-                ActivityRecord r = (ActivityRecord)mHistory.get(index);
-                if (r.state == ActivityState.DESTROYING) {
-                    final long origId = Binder.clearCallingIdentity();
-                    removeActivityFromHistoryLocked(r);
-                    Binder.restoreCallingIdentity(origId);
-                }
-            }
-        }
+        mMainStack.activityDestroyed(token);
     }
     
     public String getCallingPackage(IBinder token) {
@@ -6349,9 +3656,9 @@
     }
 
     private ActivityRecord getCallingRecordLocked(IBinder token) {
-        int index = indexOfTokenLocked(token);
+        int index = mMainStack.indexOfTokenLocked(token);
         if (index >= 0) {
-            ActivityRecord r = (ActivityRecord)mHistory.get(index);
+            ActivityRecord r = (ActivityRecord)mMainStack.mHistory.get(index);
             if (r != null) {
                 return r.resultTo;
             }
@@ -6361,9 +3668,9 @@
 
     public ComponentName getActivityClassForToken(IBinder token) {
         synchronized(this) {
-            int index = indexOfTokenLocked(token);
+            int index = mMainStack.indexOfTokenLocked(token);
             if (index >= 0) {
-                ActivityRecord r = (ActivityRecord)mHistory.get(index);
+                ActivityRecord r = (ActivityRecord)mMainStack.mHistory.get(index);
                 return r.intent.getComponent();
             }
             return null;
@@ -6372,9 +3679,9 @@
 
     public String getPackageForToken(IBinder token) {
         synchronized(this) {
-            int index = indexOfTokenLocked(token);
+            int index = mMainStack.indexOfTokenLocked(token);
             if (index >= 0) {
-                ActivityRecord r = (ActivityRecord)mHistory.get(index);
+                ActivityRecord r = (ActivityRecord)mMainStack.mHistory.get(index);
                 return r.packageName;
             }
             return null;
@@ -6428,11 +3735,11 @@
             int requestCode, Intent intent, String resolvedType, int flags) {
         ActivityRecord activity = null;
         if (type == INTENT_SENDER_ACTIVITY_RESULT) {
-            int index = indexOfTokenLocked(token);
+            int index = mMainStack.indexOfTokenLocked(token);
             if (index < 0) {
                 return null;
             }
-            activity = (ActivityRecord)mHistory.get(index);
+            activity = (ActivityRecord)mMainStack.mHistory.get(index);
             if (activity.finishing) {
                 return null;
             }
@@ -6749,7 +4056,7 @@
         }
     }
 
-    private void grantUriPermissionLocked(int callingUid,
+    void grantUriPermissionLocked(int callingUid,
             String targetPkg, Uri uri, int modeFlags, ActivityRecord activity) {
         modeFlags &= (Intent.FLAG_GRANT_READ_URI_PERMISSION
                 | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
@@ -6878,7 +4185,7 @@
         }
     }
 
-    private void grantUriPermissionFromIntentLocked(int callingUid,
+    void grantUriPermissionFromIntentLocked(int callingUid,
             String targetPkg, Intent intent, ActivityRecord activity) {
         if (intent == null) {
             return;
@@ -6914,7 +4221,7 @@
         }
     }
 
-    private void removeUriPermissionIfNeededLocked(UriPermission perm) {
+    void removeUriPermissionIfNeededLocked(UriPermission perm) {
         if ((perm.modeFlags&(Intent.FLAG_GRANT_READ_URI_PERMISSION
                 |Intent.FLAG_GRANT_WRITE_URI_PERMISSION)) == 0) {
             HashMap<Uri, UriPermission> perms
@@ -6930,29 +4237,6 @@
         }
     }
 
-    private void removeActivityUriPermissionsLocked(ActivityRecord activity) {
-        if (activity.readUriPermissions != null) {
-            for (UriPermission perm : activity.readUriPermissions) {
-                perm.readActivities.remove(activity);
-                if (perm.readActivities.size() == 0 && (perm.globalModeFlags
-                        &Intent.FLAG_GRANT_READ_URI_PERMISSION) == 0) {
-                    perm.modeFlags &= ~Intent.FLAG_GRANT_READ_URI_PERMISSION;
-                    removeUriPermissionIfNeededLocked(perm);
-                }
-            }
-        }
-        if (activity.writeUriPermissions != null) {
-            for (UriPermission perm : activity.writeUriPermissions) {
-                perm.writeActivities.remove(activity);
-                if (perm.writeActivities.size() == 0 && (perm.globalModeFlags
-                        &Intent.FLAG_GRANT_WRITE_URI_PERMISSION) == 0) {
-                    perm.modeFlags &= ~Intent.FLAG_GRANT_WRITE_URI_PERMISSION;
-                    removeUriPermissionIfNeededLocked(perm);
-                }
-            }
-        }
-    }
-
     private void revokeUriPermissionLocked(int callingUid, Uri uri,
             int modeFlags) {
         modeFlags &= (Intent.FLAG_GRANT_READ_URI_PERMISSION
@@ -7137,9 +4421,9 @@
                 throw new SecurityException(msg);
             }
 
-            int pos = mHistory.size()-1;
+            int pos = mMainStack.mHistory.size()-1;
             ActivityRecord next =
-                pos >= 0 ? (ActivityRecord)mHistory.get(pos) : null;
+                pos >= 0 ? (ActivityRecord)mMainStack.mHistory.get(pos) : null;
             ActivityRecord top = null;
             CharSequence topDescription = null;
             TaskRecord curTask = null;
@@ -7148,7 +4432,7 @@
             while (pos >= 0 && maxNum > 0) {
                 final ActivityRecord r = next;
                 pos--;
-                next = pos >= 0 ? (ActivityRecord)mHistory.get(pos) : null;
+                next = pos >= 0 ? (ActivityRecord)mMainStack.mHistory.get(pos) : null;
 
                 // Initialize state for next task if needed.
                 if (top == null ||
@@ -7297,12 +4581,12 @@
 
     private final int findAffinityTaskTopLocked(int startIndex, String affinity) {
         int j;
-        TaskRecord startTask = ((ActivityRecord)mHistory.get(startIndex)).task; 
+        TaskRecord startTask = ((ActivityRecord)mMainStack.mHistory.get(startIndex)).task; 
         TaskRecord jt = startTask;
         
         // First look backwards
         for (j=startIndex-1; j>=0; j--) {
-            ActivityRecord r = (ActivityRecord)mHistory.get(j);
+            ActivityRecord r = (ActivityRecord)mMainStack.mHistory.get(j);
             if (r.task != jt) {
                 jt = r.task;
                 if (affinity.equals(jt.affinity)) {
@@ -7312,10 +4596,10 @@
         }
         
         // Now look forwards
-        final int N = mHistory.size();
+        final int N = mMainStack.mHistory.size();
         jt = startTask;
         for (j=startIndex+1; j<N; j++) {
-            ActivityRecord r = (ActivityRecord)mHistory.get(j);
+            ActivityRecord r = (ActivityRecord)mMainStack.mHistory.get(j);
             if (r.task != jt) {
                 if (affinity.equals(jt.affinity)) {
                     return j;
@@ -7325,7 +4609,7 @@
         }
         
         // Might it be at the top?
-        if (affinity.equals(((ActivityRecord)mHistory.get(N-1)).task.affinity)) {
+        if (affinity.equals(((ActivityRecord)mMainStack.mHistory.get(N-1)).task.affinity)) {
             return N-1;
         }
         
@@ -7333,293 +4617,6 @@
     }
     
     /**
-     * Perform a reset of the given task, if needed as part of launching it.
-     * Returns the new HistoryRecord at the top of the task.
-     */
-    private final ActivityRecord resetTaskIfNeededLocked(ActivityRecord taskTop,
-            ActivityRecord newActivity) {
-        boolean forceReset = (newActivity.info.flags
-                &ActivityInfo.FLAG_CLEAR_TASK_ON_LAUNCH) != 0;
-        if (taskTop.task.getInactiveDuration() > ACTIVITY_INACTIVE_RESET_TIME) {
-            if ((newActivity.info.flags
-                    &ActivityInfo.FLAG_ALWAYS_RETAIN_TASK_STATE) == 0) {
-                forceReset = true;
-            }
-        }
-        
-        final TaskRecord task = taskTop.task;
-        
-        // We are going to move through the history list so that we can look
-        // at each activity 'target' with 'below' either the interesting
-        // activity immediately below it in the stack or null.
-        ActivityRecord target = null;
-        int targetI = 0;
-        int taskTopI = -1;
-        int replyChainEnd = -1;
-        int lastReparentPos = -1;
-        for (int i=mHistory.size()-1; i>=-1; i--) {
-            ActivityRecord below = i >= 0 ? (ActivityRecord)mHistory.get(i) : null;
-            
-            if (below != null && below.finishing) {
-                continue;
-            }
-            if (target == null) {
-                target = below;
-                targetI = i;
-                // If we were in the middle of a reply chain before this
-                // task, it doesn't appear like the root of the chain wants
-                // anything interesting, so drop it.
-                replyChainEnd = -1;
-                continue;
-            }
-        
-            final int flags = target.info.flags;
-            
-            final boolean finishOnTaskLaunch =
-                (flags&ActivityInfo.FLAG_FINISH_ON_TASK_LAUNCH) != 0;
-            final boolean allowTaskReparenting =
-                (flags&ActivityInfo.FLAG_ALLOW_TASK_REPARENTING) != 0;
-            
-            if (target.task == task) {
-                // We are inside of the task being reset...  we'll either
-                // finish this activity, push it out for another task,
-                // or leave it as-is.  We only do this
-                // for activities that are not the root of the task (since
-                // if we finish the root, we may no longer have the task!).
-                if (taskTopI < 0) {
-                    taskTopI = targetI;
-                }
-                if (below != null && below.task == task) {
-                    final boolean clearWhenTaskReset =
-                            (target.intent.getFlags()
-                                    &Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET) != 0;
-                    if (!finishOnTaskLaunch && !clearWhenTaskReset && target.resultTo != null) {
-                        // If this activity is sending a reply to a previous
-                        // activity, we can't do anything with it now until
-                        // we reach the start of the reply chain.
-                        // XXX note that we are assuming the result is always
-                        // to the previous activity, which is almost always
-                        // the case but we really shouldn't count on.
-                        if (replyChainEnd < 0) {
-                            replyChainEnd = targetI;
-                        }
-                    } else if (!finishOnTaskLaunch && !clearWhenTaskReset && allowTaskReparenting
-                            && target.taskAffinity != null
-                            && !target.taskAffinity.equals(task.affinity)) {
-                        // If this activity has an affinity for another
-                        // task, then we need to move it out of here.  We will
-                        // move it as far out of the way as possible, to the
-                        // bottom of the activity stack.  This also keeps it
-                        // correctly ordered with any activities we previously
-                        // moved.
-                        ActivityRecord p = (ActivityRecord)mHistory.get(0);
-                        if (target.taskAffinity != null
-                                && target.taskAffinity.equals(p.task.affinity)) {
-                            // If the activity currently at the bottom has the
-                            // same task affinity as the one we are moving,
-                            // then merge it into the same task.
-                            target.task = p.task;
-                            if (DEBUG_TASKS) Slog.v(TAG, "Start pushing activity " + target
-                                    + " out to bottom task " + p.task);
-                        } else {
-                            mCurTask++;
-                            if (mCurTask <= 0) {
-                                mCurTask = 1;
-                            }
-                            target.task = new TaskRecord(mCurTask, target.info, null,
-                                    (target.info.flags&ActivityInfo.FLAG_CLEAR_TASK_ON_LAUNCH) != 0);
-                            target.task.affinityIntent = target.intent;
-                            if (DEBUG_TASKS) Slog.v(TAG, "Start pushing activity " + target
-                                    + " out to new task " + target.task);
-                        }
-                        mWindowManager.setAppGroupId(target, task.taskId);
-                        if (replyChainEnd < 0) {
-                            replyChainEnd = targetI;
-                        }
-                        int dstPos = 0;
-                        for (int srcPos=targetI; srcPos<=replyChainEnd; srcPos++) {
-                            p = (ActivityRecord)mHistory.get(srcPos);
-                            if (p.finishing) {
-                                continue;
-                            }
-                            if (DEBUG_TASKS) Slog.v(TAG, "Pushing next activity " + p
-                                    + " out to target's task " + target.task);
-                            task.numActivities--;
-                            p.task = target.task;
-                            target.task.numActivities++;
-                            mHistory.remove(srcPos);
-                            mHistory.add(dstPos, p);
-                            mWindowManager.moveAppToken(dstPos, p);
-                            mWindowManager.setAppGroupId(p, p.task.taskId);
-                            dstPos++;
-                            if (VALIDATE_TOKENS) {
-                                mWindowManager.validateAppTokens(mHistory);
-                            }
-                            i++;
-                        }
-                        if (taskTop == p) {
-                            taskTop = below;
-                        }
-                        if (taskTopI == replyChainEnd) {
-                            taskTopI = -1;
-                        }
-                        replyChainEnd = -1;
-                        addRecentTaskLocked(target.task);
-                    } else if (forceReset || finishOnTaskLaunch
-                            || clearWhenTaskReset) {
-                        // If the activity should just be removed -- either
-                        // because it asks for it, or the task should be
-                        // cleared -- then finish it and anything that is
-                        // part of its reply chain.
-                        if (clearWhenTaskReset) {
-                            // In this case, we want to finish this activity
-                            // and everything above it, so be sneaky and pretend
-                            // like these are all in the reply chain.
-                            replyChainEnd = targetI+1;
-                            while (replyChainEnd < mHistory.size() &&
-                                    ((ActivityRecord)mHistory.get(
-                                                replyChainEnd)).task == task) {
-                                replyChainEnd++;
-                            }
-                            replyChainEnd--;
-                        } else if (replyChainEnd < 0) {
-                            replyChainEnd = targetI;
-                        }
-                        ActivityRecord p = null;
-                        for (int srcPos=targetI; srcPos<=replyChainEnd; srcPos++) {
-                            p = (ActivityRecord)mHistory.get(srcPos);
-                            if (p.finishing) {
-                                continue;
-                            }
-                            if (finishActivityLocked(p, srcPos,
-                                    Activity.RESULT_CANCELED, null, "reset")) {
-                                replyChainEnd--;
-                                srcPos--;
-                            }
-                        }
-                        if (taskTop == p) {
-                            taskTop = below;
-                        }
-                        if (taskTopI == replyChainEnd) {
-                            taskTopI = -1;
-                        }
-                        replyChainEnd = -1;
-                    } else {
-                        // If we were in the middle of a chain, well the
-                        // activity that started it all doesn't want anything
-                        // special, so leave it all as-is.
-                        replyChainEnd = -1;
-                    }
-                } else {
-                    // Reached the bottom of the task -- any reply chain
-                    // should be left as-is.
-                    replyChainEnd = -1;
-                }
-                
-            } else if (target.resultTo != null) {
-                // If this activity is sending a reply to a previous
-                // activity, we can't do anything with it now until
-                // we reach the start of the reply chain.
-                // XXX note that we are assuming the result is always
-                // to the previous activity, which is almost always
-                // the case but we really shouldn't count on.
-                if (replyChainEnd < 0) {
-                    replyChainEnd = targetI;
-                }
-
-            } else if (taskTopI >= 0 && allowTaskReparenting
-                    && task.affinity != null
-                    && task.affinity.equals(target.taskAffinity)) {
-                // We are inside of another task...  if this activity has
-                // an affinity for our task, then either remove it if we are
-                // clearing or move it over to our task.  Note that
-                // we currently punt on the case where we are resetting a
-                // task that is not at the top but who has activities above
-                // with an affinity to it...  this is really not a normal
-                // case, and we will need to later pull that task to the front
-                // and usually at that point we will do the reset and pick
-                // up those remaining activities.  (This only happens if
-                // someone starts an activity in a new task from an activity
-                // in a task that is not currently on top.)
-                if (forceReset || finishOnTaskLaunch) {
-                    if (replyChainEnd < 0) {
-                        replyChainEnd = targetI;
-                    }
-                    ActivityRecord p = null;
-                    for (int srcPos=targetI; srcPos<=replyChainEnd; srcPos++) {
-                        p = (ActivityRecord)mHistory.get(srcPos);
-                        if (p.finishing) {
-                            continue;
-                        }
-                        if (finishActivityLocked(p, srcPos,
-                                Activity.RESULT_CANCELED, null, "reset")) {
-                            taskTopI--;
-                            lastReparentPos--;
-                            replyChainEnd--;
-                            srcPos--;
-                        }
-                    }
-                    replyChainEnd = -1;
-                } else {
-                    if (replyChainEnd < 0) {
-                        replyChainEnd = targetI;
-                    }
-                    for (int srcPos=replyChainEnd; srcPos>=targetI; srcPos--) {
-                        ActivityRecord p = (ActivityRecord)mHistory.get(srcPos);
-                        if (p.finishing) {
-                            continue;
-                        }
-                        if (lastReparentPos < 0) {
-                            lastReparentPos = taskTopI;
-                            taskTop = p;
-                        } else {
-                            lastReparentPos--;
-                        }
-                        mHistory.remove(srcPos);
-                        p.task.numActivities--;
-                        p.task = task;
-                        mHistory.add(lastReparentPos, p);
-                        if (DEBUG_TASKS) Slog.v(TAG, "Pulling activity " + p
-                                + " in to resetting task " + task);
-                        task.numActivities++;
-                        mWindowManager.moveAppToken(lastReparentPos, p);
-                        mWindowManager.setAppGroupId(p, p.task.taskId);
-                        if (VALIDATE_TOKENS) {
-                            mWindowManager.validateAppTokens(mHistory);
-                        }
-                    }
-                    replyChainEnd = -1;
-                    
-                    // Now we've moved it in to place...  but what if this is
-                    // a singleTop activity and we have put it on top of another
-                    // instance of the same activity?  Then we drop the instance
-                    // below so it remains singleTop.
-                    if (target.info.launchMode == ActivityInfo.LAUNCH_SINGLE_TOP) {
-                        for (int j=lastReparentPos-1; j>=0; j--) {
-                            ActivityRecord p = (ActivityRecord)mHistory.get(j);
-                            if (p.finishing) {
-                                continue;
-                            }
-                            if (p.intent.getComponent().equals(target.intent.getComponent())) {
-                                if (finishActivityLocked(p, j,
-                                        Activity.RESULT_CANCELED, null, "replace")) {
-                                    taskTopI--;
-                                    lastReparentPos--;
-                                }
-                            }
-                        }
-                    }
-                }
-            }
-            
-            target = below;
-            targetI = i;
-        }
-        
-        return taskTop;
-    }
-    
-    /**
      * TODO: Add mController hook
      */
     public void moveTaskToFront(int task) {
@@ -7637,14 +4634,14 @@
                 for (int i=0; i<N; i++) {
                     TaskRecord tr = mRecentTasks.get(i);
                     if (tr.taskId == task) {
-                        moveTaskToFrontLocked(tr, null);
+                        mMainStack.moveTaskToFrontLocked(tr, null);
                         return;
                     }
                 }
-                for (int i=mHistory.size()-1; i>=0; i--) {
-                    ActivityRecord hr = (ActivityRecord)mHistory.get(i);
+                for (int i=mMainStack.mHistory.size()-1; i>=0; i--) {
+                    ActivityRecord hr = (ActivityRecord)mMainStack.mHistory.get(i);
                     if (hr.task.taskId == task) {
-                        moveTaskToFrontLocked(hr.task, null);
+                        mMainStack.moveTaskToFrontLocked(hr.task, null);
                         return;
                     }
                 }
@@ -7654,84 +4651,20 @@
         }
     }
 
-    private final void moveTaskToFrontLocked(TaskRecord tr, ActivityRecord reason) {
-        if (DEBUG_SWITCH) Slog.v(TAG, "moveTaskToFront: " + tr);
-
-        final int task = tr.taskId;
-        int top = mHistory.size()-1;
-
-        if (top < 0 || ((ActivityRecord)mHistory.get(top)).task.taskId == task) {
-            // nothing to do!
-            return;
-        }
-
-        ArrayList moved = new ArrayList();
-
-        // Applying the affinities may have removed entries from the history,
-        // so get the size again.
-        top = mHistory.size()-1;
-        int pos = top;
-
-        // Shift all activities with this task up to the top
-        // of the stack, keeping them in the same internal order.
-        while (pos >= 0) {
-            ActivityRecord r = (ActivityRecord)mHistory.get(pos);
-            if (localLOGV) Slog.v(
-                TAG, "At " + pos + " ckp " + r.task + ": " + r);
-            boolean first = true;
-            if (r.task.taskId == task) {
-                if (localLOGV) Slog.v(TAG, "Removing and adding at " + top);
-                mHistory.remove(pos);
-                mHistory.add(top, r);
-                moved.add(0, r);
-                top--;
-                if (first) {
-                    addRecentTaskLocked(r.task);
-                    first = false;
-                }
-            }
-            pos--;
-        }
-
-        if (DEBUG_TRANSITION) Slog.v(TAG,
-                "Prepare to front transition: task=" + tr);
-        if (reason != null &&
-                (reason.intent.getFlags()&Intent.FLAG_ACTIVITY_NO_ANIMATION) != 0) {
-            mWindowManager.prepareAppTransition(WindowManagerPolicy.TRANSIT_NONE);
-            ActivityRecord r = topRunningActivityLocked(null);
-            if (r != null) {
-                mNoAnimActivities.add(r);
-            }
-        } else {
-            mWindowManager.prepareAppTransition(WindowManagerPolicy.TRANSIT_TASK_TO_FRONT);
-        }
-        
-        mWindowManager.moveAppTokensToTop(moved);
-        if (VALIDATE_TOKENS) {
-            mWindowManager.validateAppTokens(mHistory);
-        }
-
-        finishTaskMoveLocked(task);
-        EventLog.writeEvent(EventLogTags.AM_TASK_TO_FRONT, task);
-    }
-
-    private final void finishTaskMoveLocked(int task) {
-        resumeTopActivityLocked(null);
-    }
-
     public void moveTaskToBack(int task) {
         enforceCallingPermission(android.Manifest.permission.REORDER_TASKS,
                 "moveTaskToBack()");
 
         synchronized(this) {
-            if (mResumedActivity != null && mResumedActivity.task.taskId == task) {
+            if (mMainStack.mResumedActivity != null
+                    && mMainStack.mResumedActivity.task.taskId == task) {
                 if (!checkAppSwitchAllowedLocked(Binder.getCallingPid(),
                         Binder.getCallingUid(), "Task to back")) {
                     return;
                 }
             }
             final long origId = Binder.clearCallingIdentity();
-            moveTaskToBackLocked(task, null);
+            mMainStack.moveTaskToBackLocked(task, null);
             Binder.restoreCallingIdentity(origId);
         }
     }
@@ -7750,93 +4683,13 @@
             final long origId = Binder.clearCallingIdentity();
             int taskId = getTaskForActivityLocked(token, !nonRoot);
             if (taskId >= 0) {
-                return moveTaskToBackLocked(taskId, null);
+                return mMainStack.moveTaskToBackLocked(taskId, null);
             }
             Binder.restoreCallingIdentity(origId);
         }
         return false;
     }
 
-    /**
-     * Worker method for rearranging history stack.  Implements the function of moving all 
-     * activities for a specific task (gathering them if disjoint) into a single group at the 
-     * bottom of the stack.
-     * 
-     * If a watcher is installed, the action is preflighted and the watcher has an opportunity
-     * to premeptively cancel the move.
-     * 
-     * @param task The taskId to collect and move to the bottom.
-     * @return Returns true if the move completed, false if not.
-     */
-    private final boolean moveTaskToBackLocked(int task, ActivityRecord reason) {
-        Slog.i(TAG, "moveTaskToBack: " + task);
-        
-        // If we have a watcher, preflight the move before committing to it.  First check
-        // for *other* available tasks, but if none are available, then try again allowing the
-        // current task to be selected.
-        if (mController != null) {
-            ActivityRecord next = topRunningActivityLocked(null, task);
-            if (next == null) {
-                next = topRunningActivityLocked(null, 0);
-            }
-            if (next != null) {
-                // ask watcher if this is allowed
-                boolean moveOK = true;
-                try {
-                    moveOK = mController.activityResuming(next.packageName);
-                } catch (RemoteException e) {
-                    mController = null;
-                }
-                if (!moveOK) {
-                    return false;
-                }
-            }
-        }
-
-        ArrayList moved = new ArrayList();
-
-        if (DEBUG_TRANSITION) Slog.v(TAG,
-                "Prepare to back transition: task=" + task);
-        
-        final int N = mHistory.size();
-        int bottom = 0;
-        int pos = 0;
-
-        // Shift all activities with this task down to the bottom
-        // of the stack, keeping them in the same internal order.
-        while (pos < N) {
-            ActivityRecord r = (ActivityRecord)mHistory.get(pos);
-            if (localLOGV) Slog.v(
-                TAG, "At " + pos + " ckp " + r.task + ": " + r);
-            if (r.task.taskId == task) {
-                if (localLOGV) Slog.v(TAG, "Removing and adding at " + (N-1));
-                mHistory.remove(pos);
-                mHistory.add(bottom, r);
-                moved.add(r);
-                bottom++;
-            }
-            pos++;
-        }
-
-        if (reason != null &&
-                (reason.intent.getFlags()&Intent.FLAG_ACTIVITY_NO_ANIMATION) != 0) {
-            mWindowManager.prepareAppTransition(WindowManagerPolicy.TRANSIT_NONE);
-            ActivityRecord r = topRunningActivityLocked(null);
-            if (r != null) {
-                mNoAnimActivities.add(r);
-            }
-        } else {
-            mWindowManager.prepareAppTransition(WindowManagerPolicy.TRANSIT_TASK_TO_BACK);
-        }
-        mWindowManager.moveAppTokensToBottom(moved);
-        if (VALIDATE_TOKENS) {
-            mWindowManager.validateAppTokens(mHistory);
-        }
-
-        finishTaskMoveLocked(task);
-        return true;
-    }
-
     public void moveTaskBackwards(int task) {
         enforceCallingPermission(android.Manifest.permission.REORDER_TASKS,
                 "moveTaskBackwards()");
@@ -7863,10 +4716,10 @@
     }
 
     int getTaskForActivityLocked(IBinder token, boolean onlyRoot) {
-        final int N = mHistory.size();
+        final int N = mMainStack.mHistory.size();
         TaskRecord lastTask = null;
         for (int i=0; i<N; i++) {
-            ActivityRecord r = (ActivityRecord)mHistory.get(i);
+            ActivityRecord r = (ActivityRecord)mMainStack.mHistory.get(i);
             if (r == token) {
                 if (!onlyRoot || lastTask != r.task) {
                     return r.task.taskId;
@@ -7879,89 +4732,17 @@
         return -1;
     }
 
-    /**
-     * Returns the top activity in any existing task matching the given
-     * Intent.  Returns null if no such task is found.
-     */
-    private ActivityRecord findTaskLocked(Intent intent, ActivityInfo info) {
-        ComponentName cls = intent.getComponent();
-        if (info.targetActivity != null) {
-            cls = new ComponentName(info.packageName, info.targetActivity);
-        }
-
-        TaskRecord cp = null;
-
-        final int N = mHistory.size();
-        for (int i=(N-1); i>=0; i--) {
-            ActivityRecord r = (ActivityRecord)mHistory.get(i);
-            if (!r.finishing && r.task != cp
-                    && r.launchMode != ActivityInfo.LAUNCH_SINGLE_INSTANCE) {
-                cp = r.task;
-                //Slog.i(TAG, "Comparing existing cls=" + r.task.intent.getComponent().flattenToShortString()
-                //        + "/aff=" + r.task.affinity + " to new cls="
-                //        + intent.getComponent().flattenToShortString() + "/aff=" + taskAffinity);
-                if (r.task.affinity != null) {
-                    if (r.task.affinity.equals(info.taskAffinity)) {
-                        //Slog.i(TAG, "Found matching affinity!");
-                        return r;
-                    }
-                } else if (r.task.intent != null
-                        && r.task.intent.getComponent().equals(cls)) {
-                    //Slog.i(TAG, "Found matching class!");
-                    //dump();
-                    //Slog.i(TAG, "For Intent " + intent + " bringing to top: " + r.intent);
-                    return r;
-                } else if (r.task.affinityIntent != null
-                        && r.task.affinityIntent.getComponent().equals(cls)) {
-                    //Slog.i(TAG, "Found matching class!");
-                    //dump();
-                    //Slog.i(TAG, "For Intent " + intent + " bringing to top: " + r.intent);
-                    return r;
-                }
-            }
-        }
-
-        return null;
-    }
-
-    /**
-     * Returns the first activity (starting from the top of the stack) that
-     * is the same as the given activity.  Returns null if no such activity
-     * is found.
-     */
-    private ActivityRecord findActivityLocked(Intent intent, ActivityInfo info) {
-        ComponentName cls = intent.getComponent();
-        if (info.targetActivity != null) {
-            cls = new ComponentName(info.packageName, info.targetActivity);
-        }
-
-        final int N = mHistory.size();
-        for (int i=(N-1); i>=0; i--) {
-            ActivityRecord r = (ActivityRecord)mHistory.get(i);
-            if (!r.finishing) {
-                if (r.intent.getComponent().equals(cls)) {
-                    //Slog.i(TAG, "Found matching class!");
-                    //dump();
-                    //Slog.i(TAG, "For Intent " + intent + " bringing to top: " + r.intent);
-                    return r;
-                }
-            }
-        }
-
-        return null;
-    }
-
     public void finishOtherInstances(IBinder token, ComponentName className) {
         synchronized(this) {
             final long origId = Binder.clearCallingIdentity();
 
-            int N = mHistory.size();
+            int N = mMainStack.mHistory.size();
             TaskRecord lastTask = null;
             for (int i=0; i<N; i++) {
-                ActivityRecord r = (ActivityRecord)mHistory.get(i);
+                ActivityRecord r = (ActivityRecord)mMainStack.mHistory.get(i);
                 if (r.realActivity.equals(className)
                         && r != token && lastTask != r.task) {
-                    if (finishActivityLocked(r, i, Activity.RESULT_CANCELED,
+                    if (r.stack.finishActivityLocked(r, i, Activity.RESULT_CANCELED,
                             null, "others")) {
                         i--;
                         N--;
@@ -7995,11 +4776,11 @@
 
         synchronized(this) {
             if (r == null) {
-                int index = indexOfTokenLocked(token);
+                int index = mMainStack.indexOfTokenLocked(token);
                 if (index < 0) {
                     return;
                 }
-                r = (ActivityRecord)mHistory.get(index);
+                r = (ActivityRecord)mMainStack.mHistory.get(index);
             }
             if (thumbnail == null) {
                 thumbnail = r.thumbnail;
@@ -8531,12 +5312,12 @@
                 "unhandledBack()");
 
         synchronized(this) {
-            int count = mHistory.size();
+            int count = mMainStack.mHistory.size();
             if (DEBUG_SWITCH) Slog.d(
                 TAG, "Performing unhandledBack(): stack size = " + count);
             if (count > 1) {
                 final long origId = Binder.clearCallingIdentity();
-                finishActivityLocked((ActivityRecord)mHistory.get(count-1),
+                mMainStack.finishActivityLocked((ActivityRecord)mMainStack.mHistory.get(count-1),
                         count-1, Activity.RESULT_CANCELED, null, "unhandled-back");
                 Binder.restoreCallingIdentity(origId);
             }
@@ -8579,8 +5360,8 @@
             mSleeping = true;
             mWindowManager.setEventDispatching(false);
 
-            if (mResumedActivity != null) {
-                pauseIfSleepingLocked();
+            if (mMainStack.mResumedActivity != null) {
+                mMainStack.pauseIfSleepingLocked();
             } else {
                 Slog.w(TAG, "goingToSleep with no resumed activity!");
             }
@@ -8600,10 +5381,11 @@
             mShuttingDown = true;
             mWindowManager.setEventDispatching(false);
 
-            if (mResumedActivity != null) {
-                pauseIfSleepingLocked();
+            if (mMainStack.mResumedActivity != null) {
+                mMainStack.pauseIfSleepingLocked();
                 final long endTime = System.currentTimeMillis() + timeout;
-                while (mResumedActivity != null || mPausingActivity != null) {
+                while (mMainStack.mResumedActivity != null
+                        || mMainStack.mPausingActivity != null) {
                     long delay = endTime - System.currentTimeMillis();
                     if (delay <= 0) {
                         Slog.w(TAG, "Activity manager shutdown timed out");
@@ -8624,35 +5406,14 @@
         return timedout;
     }
     
-    void pauseIfSleepingLocked() {
-        if (mSleeping || mShuttingDown) {
-            if (!mGoingToSleep.isHeld()) {
-                mGoingToSleep.acquire();
-                if (mLaunchingActivity.isHeld()) {
-                    mLaunchingActivity.release();
-                    mHandler.removeMessages(LAUNCH_TIMEOUT_MSG);
-                }
-            }
-
-            // If we are not currently pausing an activity, get the current
-            // one to pause.  If we are pausing one, we will just let that stuff
-            // run and release the wake lock when all done.
-            if (mPausingActivity == null) {
-                if (DEBUG_PAUSE) Slog.v(TAG, "Sleep needs to pause...");
-                if (DEBUG_USER_LEAVING) Slog.v(TAG, "Sleep => pause with userLeaving=false");
-                startPausingLocked(false, true);
-            }
-        }
-    }
-    
     public void wakingUp() {
         synchronized(this) {
-            if (mGoingToSleep.isHeld()) {
-                mGoingToSleep.release();
+            if (mMainStack.mGoingToSleep.isHeld()) {
+                mMainStack.mGoingToSleep.release();
             }
             mWindowManager.setEventDispatching(true);
             mSleeping = false;
-            resumeTopActivityLocked(null);
+            mMainStack.resumeTopActivityLocked(null);
         }
     }
 
@@ -8782,29 +5543,29 @@
 
     public void setImmersive(IBinder token, boolean immersive) {
         synchronized(this) {
-            int index = (token != null) ? indexOfTokenLocked(token) : -1;
+            int index = (token != null) ? mMainStack.indexOfTokenLocked(token) : -1;
             if (index < 0) {
                 throw new IllegalArgumentException();
             }
-            ActivityRecord r = (ActivityRecord)mHistory.get(index);
+            ActivityRecord r = (ActivityRecord)mMainStack.mHistory.get(index);
             r.immersive = immersive;
         }
     }
 
     public boolean isImmersive(IBinder token) {
         synchronized (this) {
-            int index = (token != null) ? indexOfTokenLocked(token) : -1;
+            int index = (token != null) ? mMainStack.indexOfTokenLocked(token) : -1;
             if (index < 0) {
                 throw new IllegalArgumentException();
             }
-            ActivityRecord r = (ActivityRecord)mHistory.get(index);
+            ActivityRecord r = (ActivityRecord)mMainStack.mHistory.get(index);
             return r.immersive;
         }
     }
 
     public boolean isTopActivityImmersive() {
         synchronized (this) {
-            ActivityRecord r = topRunningActivityLocked(null);
+            ActivityRecord r = mMainStack.topRunningActivityLocked(null);
             return (r != null) ? r.immersive : false;
         }
     }
@@ -9238,7 +5999,7 @@
             } catch (RemoteException e) {
             }
 
-            resumeTopActivityLocked(null);
+            mMainStack.resumeTopActivityLocked(null);
         }
     }
 
@@ -9326,12 +6087,12 @@
             EventLog.writeEvent(EventLogTags.AM_PROCESS_CRASHED_TOO_MUCH,
                     app.info.processName, app.info.uid);
             killServicesLocked(app, false);
-            for (int i=mHistory.size()-1; i>=0; i--) {
-                ActivityRecord r = (ActivityRecord)mHistory.get(i);
+            for (int i=mMainStack.mHistory.size()-1; i>=0; i--) {
+                ActivityRecord r = (ActivityRecord)mMainStack.mHistory.get(i);
                 if (r.app == app) {
                     Slog.w(TAG, "  Force finishing activity "
                         + r.intent.getComponent().flattenToShortString());
-                    finishActivityLocked(r, i, Activity.RESULT_CANCELED, null, "crashed");
+                    r.stack.finishActivityLocked(r, i, Activity.RESULT_CANCELED, null, "crashed");
                 }
             }
             if (!app.persistent) {
@@ -9349,28 +6110,28 @@
                 return false;
             }
         } else {
-            ActivityRecord r = topRunningActivityLocked(null);
+            ActivityRecord r = mMainStack.topRunningActivityLocked(null);
             if (r.app == app) {
                 // If the top running activity is from this crashing
                 // process, then terminate it to avoid getting in a loop.
                 Slog.w(TAG, "  Force finishing activity "
                         + r.intent.getComponent().flattenToShortString());
-                int index = indexOfTokenLocked(r);
-                finishActivityLocked(r, index,
+                int index = mMainStack.indexOfTokenLocked(r);
+                r.stack.finishActivityLocked(r, index,
                         Activity.RESULT_CANCELED, null, "crashed");
                 // Also terminate an activities below it that aren't yet
                 // stopped, to avoid a situation where one will get
                 // re-start our crashing activity once it gets resumed again.
                 index--;
                 if (index >= 0) {
-                    r = (ActivityRecord)mHistory.get(index);
+                    r = (ActivityRecord)mMainStack.mHistory.get(index);
                     if (r.state == ActivityState.RESUMED
                             || r.state == ActivityState.PAUSING
                             || r.state == ActivityState.PAUSED) {
                         if (!r.isHomeActivity) {
                             Slog.w(TAG, "  Force finishing activity "
                                     + r.intent.getComponent().flattenToShortString());
-                            finishActivityLocked(r, index,
+                            r.stack.finishActivityLocked(r, index,
                                     Activity.RESULT_CANCELED, null, "crashed");
                         }
                     }
@@ -10075,31 +6836,31 @@
         if (needHeader) {
             pw.println("  Activity stack:");
         }
-        dumpHistoryList(pw, mHistory, "  ", "Hist", true);
+        dumpHistoryList(pw, mMainStack.mHistory, "  ", "Hist", true);
         pw.println(" ");
         pw.println("  Running activities (most recent first):");
-        dumpHistoryList(pw, mLRUActivities, "  ", "Run", false);
-        if (mWaitingVisibleActivities.size() > 0) {
+        dumpHistoryList(pw, mMainStack.mLRUActivities, "  ", "Run", false);
+        if (mMainStack.mWaitingVisibleActivities.size() > 0) {
             pw.println(" ");
             pw.println("  Activities waiting for another to become visible:");
-            dumpHistoryList(pw, mWaitingVisibleActivities, "  ", "Wait", false);
+            dumpHistoryList(pw, mMainStack.mWaitingVisibleActivities, "  ", "Wait", false);
         }
-        if (mStoppingActivities.size() > 0) {
+        if (mMainStack.mStoppingActivities.size() > 0) {
             pw.println(" ");
             pw.println("  Activities waiting to stop:");
-            dumpHistoryList(pw, mStoppingActivities, "  ", "Stop", false);
+            dumpHistoryList(pw, mMainStack.mStoppingActivities, "  ", "Stop", false);
         }
-        if (mFinishingActivities.size() > 0) {
+        if (mMainStack.mFinishingActivities.size() > 0) {
             pw.println(" ");
             pw.println("  Activities waiting to finish:");
-            dumpHistoryList(pw, mFinishingActivities, "  ", "Fin", false);
+            dumpHistoryList(pw, mMainStack.mFinishingActivities, "  ", "Fin", false);
         }
 
         pw.println(" ");
-        pw.println("  mPausingActivity: " + mPausingActivity);
-        pw.println("  mResumedActivity: " + mResumedActivity);
+        pw.println("  mPausingActivity: " + mMainStack.mPausingActivity);
+        pw.println("  mResumedActivity: " + mMainStack.mResumedActivity);
         pw.println("  mFocusedActivity: " + mFocusedActivity);
-        pw.println("  mLastPausedActivity: " + mLastPausedActivity);
+        pw.println("  mLastPausedActivity: " + mMainStack.mLastPausedActivity);
 
         if (dumpAll && mRecentTasks.size() > 0) {
             pw.println(" ");
@@ -10268,7 +7029,7 @@
             pw.println("  mHeavyWeightProcess: " + mHeavyWeightProcess);
         }
         pw.println("  mConfiguration: " + mConfiguration);
-        pw.println("  mConfigWillChange: " + mConfigWillChange);
+        pw.println("  mConfigWillChange: " + mMainStack.mConfigWillChange);
         pw.println("  mSleeping=" + mSleeping + " mShuttingDown=" + mShuttingDown);
         if (mDebugApp != null || mOrigDebugApp != null || mDebugTransient
                 || mOrigWaitForDebugger) {
@@ -10287,8 +7048,8 @@
                     + " mBooting=" + mBooting
                     + " mBooted=" + mBooted
                     + " mFactoryTest=" + mFactoryTest);
-            pw.println("  mGoingToSleep=" + mGoingToSleep);
-            pw.println("  mLaunchingActivity=" + mLaunchingActivity);
+            pw.println("  mGoingToSleep=" + mMainStack.mGoingToSleep);
+            pw.println("  mLaunchingActivity=" + mMainStack.mLaunchingActivity);
             pw.println("  mAdjSeq=" + mAdjSeq + " mLruSeq=" + mLruSeq);
         }
         
@@ -10778,22 +7539,6 @@
         return false;
     }
 
-    private final int indexOfTokenLocked(IBinder token) {
-        int count = mHistory.size();
-
-        // convert the token to an entry in the history.
-        int index = -1;
-        for (int i=count-1; i>=0; i--) {
-            Object o = mHistory.get(i);
-            if (o == token) {
-                index = i;
-                break;
-            }
-        }
-
-        return index;
-    }
-
     private final void killServicesLocked(ProcessRecord app,
             boolean allowRestart) {
         // Report disconnected services.
@@ -12071,12 +8816,12 @@
 
             ActivityRecord activity = null;
             if (token != null) {
-                int aindex = indexOfTokenLocked(token);
+                int aindex = mMainStack.indexOfTokenLocked(token);
                 if (aindex < 0) {
                     Slog.w(TAG, "Binding with unknown activity: " + token);
                     return 0;
                 }
-                activity = (ActivityRecord)mHistory.get(aindex);
+                activity = (ActivityRecord)mMainStack.mHistory.get(aindex);
             }
 
             int clientLabel = 0;
@@ -12180,7 +8925,7 @@
         return 1;
     }
 
-    private void removeConnectionLocked(
+    void removeConnectionLocked(
         ConnectionRecord c, ProcessRecord skipApp, ActivityRecord skipAct) {
         IBinder binder = c.conn.asBinder();
         AppBindRecord b = c.binding;
@@ -14012,18 +10757,18 @@
             // If the configuration changed, and the caller is not already
             // in the process of starting an activity, then find the top
             // activity to check if its configuration needs to change.
-            starting = topRunningActivityLocked(null);
+            starting = mMainStack.topRunningActivityLocked(null);
         }
         
         if (starting != null) {
-            kept = ensureActivityConfigurationLocked(starting, changes);
+            kept = mMainStack.ensureActivityConfigurationLocked(starting, changes);
             if (kept) {
                 // If this didn't result in the starting activity being
                 // destroyed, then we need to make sure at this point that all
                 // other activities are made visible.
                 if (DEBUG_SWITCH) Slog.i(TAG, "Config didn't destroy " + starting
                         + ", ensuring others are correct.");
-                ensureActivitiesVisibleLocked(starting, changes);
+                mMainStack.ensureActivitiesVisibleLocked(starting, changes);
             }
         }
         
@@ -14033,160 +10778,6 @@
         
         return kept;
     }
-
-    private final boolean relaunchActivityLocked(ActivityRecord r,
-            int changes, boolean andResume) {
-        List<ResultInfo> results = null;
-        List<Intent> newIntents = null;
-        if (andResume) {
-            results = r.results;
-            newIntents = r.newIntents;
-        }
-        if (DEBUG_SWITCH) Slog.v(TAG, "Relaunching: " + r
-                + " with results=" + results + " newIntents=" + newIntents
-                + " andResume=" + andResume);
-        EventLog.writeEvent(andResume ? EventLogTags.AM_RELAUNCH_RESUME_ACTIVITY
-                : EventLogTags.AM_RELAUNCH_ACTIVITY, System.identityHashCode(r),
-                r.task.taskId, r.shortComponentName);
-        
-        r.startFreezingScreenLocked(r.app, 0);
-        
-        try {
-            if (DEBUG_SWITCH) Slog.i(TAG, "Switch is restarting resumed " + r);
-            r.app.thread.scheduleRelaunchActivity(r, results, newIntents,
-                    changes, !andResume, mConfiguration);
-            // Note: don't need to call pauseIfSleepingLocked() here, because
-            // the caller will only pass in 'andResume' if this activity is
-            // currently resumed, which implies we aren't sleeping.
-        } catch (RemoteException e) {
-            return false;
-        }
-
-        if (andResume) {
-            r.results = null;
-            r.newIntents = null;
-            reportResumedActivityLocked(r);
-        }
-
-        return true;
-    }
-
-    /**
-     * Make sure the given activity matches the current configuration.  Returns
-     * false if the activity had to be destroyed.  Returns true if the
-     * configuration is the same, or the activity will remain running as-is
-     * for whatever reason.  Ensures the HistoryRecord is updated with the
-     * correct configuration and all other bookkeeping is handled.
-     */
-    private final boolean ensureActivityConfigurationLocked(ActivityRecord r,
-            int globalChanges) {
-        if (mConfigWillChange) {
-            if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG,
-                    "Skipping config check (will change): " + r);
-            return true;
-        }
-        
-        if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG,
-                "Ensuring correct configuration: " + r);
-        
-        // Short circuit: if the two configurations are the exact same
-        // object (the common case), then there is nothing to do.
-        Configuration newConfig = mConfiguration;
-        if (r.configuration == newConfig) {
-            if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG,
-                    "Configuration unchanged in " + r);
-            return true;
-        }
-        
-        // We don't worry about activities that are finishing.
-        if (r.finishing) {
-            if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG,
-                    "Configuration doesn't matter in finishing " + r);
-            r.stopFreezingScreenLocked(false);
-            return true;
-        }
-        
-        // Okay we now are going to make this activity have the new config.
-        // But then we need to figure out how it needs to deal with that.
-        Configuration oldConfig = r.configuration;
-        r.configuration = newConfig;
-        
-        // If the activity isn't currently running, just leave the new
-        // configuration and it will pick that up next time it starts.
-        if (r.app == null || r.app.thread == null) {
-            if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG,
-                    "Configuration doesn't matter not running " + r);
-            r.stopFreezingScreenLocked(false);
-            return true;
-        }
-        
-        // If the activity isn't persistent, there is a chance we will
-        // need to restart it.
-        if (!r.persistent) {
-
-            // Figure out what has changed between the two configurations.
-            int changes = oldConfig.diff(newConfig);
-            if (DEBUG_SWITCH || DEBUG_CONFIGURATION) {
-                Slog.v(TAG, "Checking to restart " + r.info.name + ": changed=0x"
-                        + Integer.toHexString(changes) + ", handles=0x"
-                        + Integer.toHexString(r.info.configChanges)
-                        + ", newConfig=" + newConfig);
-            }
-            if ((changes&(~r.info.configChanges)) != 0) {
-                // Aha, the activity isn't handling the change, so DIE DIE DIE.
-                r.configChangeFlags |= changes;
-                r.startFreezingScreenLocked(r.app, globalChanges);
-                if (r.app == null || r.app.thread == null) {
-                    if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG,
-                            "Switch is destroying non-running " + r);
-                    destroyActivityLocked(r, true);
-                } else if (r.state == ActivityState.PAUSING) {
-                    // A little annoying: we are waiting for this activity to
-                    // finish pausing.  Let's not do anything now, but just
-                    // flag that it needs to be restarted when done pausing.
-                    if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG,
-                            "Switch is skipping already pausing " + r);
-                    r.configDestroy = true;
-                    return true;
-                } else if (r.state == ActivityState.RESUMED) {
-                    // Try to optimize this case: the configuration is changing
-                    // and we need to restart the top, resumed activity.
-                    // Instead of doing the normal handshaking, just say
-                    // "restart!".
-                    if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG,
-                            "Switch is restarting resumed " + r);
-                    relaunchActivityLocked(r, r.configChangeFlags, true);
-                    r.configChangeFlags = 0;
-                } else {
-                    if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG,
-                            "Switch is restarting non-resumed " + r);
-                    relaunchActivityLocked(r, r.configChangeFlags, false);
-                    r.configChangeFlags = 0;
-                }
-                
-                // All done...  tell the caller we weren't able to keep this
-                // activity around.
-                return false;
-            }
-        }
-        
-        // Default case: the activity can handle this new configuration, so
-        // hand it over.  Note that we don't need to give it the new
-        // configuration, since we always send configuration changes to all
-        // process when they happen so it can just use whatever configuration
-        // it last got.
-        if (r.app != null && r.app.thread != null) {
-            try {
-                if (DEBUG_CONFIGURATION) Slog.v(TAG, "Sending new config to " + r);
-                r.app.thread.scheduleActivityConfigurationChanged(r);
-            } catch (RemoteException e) {
-                // If process died, whatever.
-            }
-        }
-        r.stopFreezingScreenLocked(false);
-        
-        return true;
-    }
     
     /**
      * Save the locale.  You must be inside a synchronized (this) block.
@@ -14542,8 +11133,8 @@
     private final boolean canGcNowLocked() {
         return mParallelBroadcasts.size() == 0
                 && mOrderedBroadcasts.size() == 0
-                && (mSleeping || (mResumedActivity != null &&
-                        mResumedActivity.idle));
+                && (mSleeping || (mMainStack.mResumedActivity != null &&
+                        mMainStack.mResumedActivity.idle));
     }
     
     /**
@@ -14714,11 +11305,11 @@
     }
 
     private final ActivityRecord resumedAppLocked() {
-        ActivityRecord resumedActivity = mResumedActivity;
+        ActivityRecord resumedActivity = mMainStack.mResumedActivity;
         if (resumedActivity == null || resumedActivity.app == null) {
-            resumedActivity = mPausingActivity;
+            resumedActivity = mMainStack.mPausingActivity;
             if (resumedActivity == null || resumedActivity.app == null) {
-                resumedActivity = topRunningActivityLocked(null);
+                resumedActivity = mMainStack.topRunningActivityLocked(null);
             }
         }
         return resumedActivity;
@@ -14746,7 +11337,7 @@
         return res;
     }
 
-    private final boolean updateOomAdjLocked() {
+    final boolean updateOomAdjLocked() {
         boolean didOomAdj = true;
         final ActivityRecord TOP_ACT = resumedAppLocked();
         final ProcessRecord TOP_APP = TOP_ACT != null ? TOP_ACT.app : null;
@@ -14810,7 +11401,7 @@
         return ENFORCE_PROCESS_LIMIT || mProcessLimit > 0 ? false : didOomAdj;
     }
 
-    private final void trimApplications() {
+    final void trimApplications() {
         synchronized (this) {
             int i;
 
@@ -14942,7 +11533,7 @@
                         for (j=0; j<NUMA; j++) {
                             ActivityRecord r = app.activities.get(j);
                             if (!r.finishing) {
-                                destroyActivityLocked(r, false);
+                                r.stack.destroyActivityLocked(r, false);
                             }
                             r.resultTo = null;
                         }
@@ -14979,25 +11570,25 @@
             // Finally, if there are too many activities now running, try to
             // finish as many as we can to get back down to the limit.
             for (   i=0;
-                    i<mLRUActivities.size()
-                        && mLRUActivities.size() > curMaxActivities;
+                    i<mMainStack.mLRUActivities.size()
+                        && mMainStack.mLRUActivities.size() > curMaxActivities;
                     i++) {
                 final ActivityRecord r
-                    = (ActivityRecord)mLRUActivities.get(i);
+                    = (ActivityRecord)mMainStack.mLRUActivities.get(i);
 
                 // We can finish this one if we have its icicle saved and
                 // it is not persistent.
                 if ((r.haveState || !r.stateNotNeeded) && !r.visible
                         && r.stopped && !r.persistent && !r.finishing) {
-                    final int origSize = mLRUActivities.size();
-                    destroyActivityLocked(r, true);
+                    final int origSize = mMainStack.mLRUActivities.size();
+                    r.stack.destroyActivityLocked(r, true);
 
                     // This will remove it from the LRU list, so keep
                     // our index at the same value.  Note that this check to
                     // see if the size changes is just paranoia -- if
                     // something unexpected happens, we don't want to end up
                     // in an infinite loop.
-                    if (origSize > mLRUActivities.size()) {
+                    if (origSize > mMainStack.mLRUActivities.size()) {
                         i--;
                     }
                 }
diff --git a/services/java/com/android/server/am/ActivityRecord.java b/services/java/com/android/server/am/ActivityRecord.java
index 22ac58d..79756a7 100644
--- a/services/java/com/android/server/am/ActivityRecord.java
+++ b/services/java/com/android/server/am/ActivityRecord.java
@@ -17,7 +17,7 @@
 package com.android.server.am;
 
 import com.android.server.AttributeCache;
-import com.android.server.am.ActivityManagerService.ActivityState;
+import com.android.server.am.ActivityStack.ActivityState;
 
 import android.app.Activity;
 import android.content.ComponentName;
@@ -32,6 +32,7 @@
 import android.os.SystemClock;
 import android.util.EventLog;
 import android.util.Log;
+import android.util.Slog;
 import android.view.IApplicationToken;
 
 import java.io.PrintWriter;
@@ -44,6 +45,7 @@
  */
 class ActivityRecord extends IApplicationToken.Stub {
     final ActivityManagerService service; // owner
+    final ActivityStack stack; // owner
     final ActivityInfo info; // all about me
     final int launchedFromUid; // always the uid who started the activity.
     final Intent intent;    // the original intent that generated us
@@ -80,7 +82,7 @@
     ProcessRecord app;  // if non-null, hosting application
     Bitmap thumbnail;       // icon representation of paused screen
     CharSequence description; // textual description of paused screen
-    ActivityManagerService.ActivityState state;    // current state we are in
+    ActivityState state;    // current state we are in
     Bundle  icicle;         // last saved activity state
     boolean frontOfTask;    // is this the root activity of its task?
     boolean launchFailed;   // set if a launched failed, to abort on 2nd try
@@ -175,12 +177,13 @@
         }
     }
 
-    ActivityRecord(ActivityManagerService _service, ProcessRecord _caller,
+    ActivityRecord(ActivityManagerService _service, ActivityStack _stack, ProcessRecord _caller,
             int _launchedFromUid, Intent _intent, String _resolvedType,
             ActivityInfo aInfo, Configuration _configuration,
             ActivityRecord _resultTo, String _resultWho, int _reqCode,
             boolean _componentSpecified) {
         service = _service;
+        stack = _stack;
         info = aInfo;
         launchedFromUid = _launchedFromUid;
         intent = _intent;
@@ -191,7 +194,7 @@
         resultTo = _resultTo;
         resultWho = _resultWho;
         requestCode = _reqCode;
-        state = ActivityManagerService.ActivityState.INITIALIZING;
+        state = ActivityState.INITIALIZING;
         frontOfTask = false;
         launchFailed = false;
         haveState = false;
@@ -332,6 +335,52 @@
         }
         newIntents.add(intent);
     }
+    
+    /**
+     * Deliver a new Intent to an existing activity, so that its onNewIntent()
+     * method will be called at the proper time.
+     */
+    final void deliverNewIntentLocked(Intent intent) {
+        boolean sent = false;
+        if (state == ActivityState.RESUMED
+                && app != null && app.thread != null) {
+            try {
+                ArrayList<Intent> ar = new ArrayList<Intent>();
+                ar.add(new Intent(intent));
+                app.thread.scheduleNewIntent(ar, this);
+                sent = true;
+            } catch (Exception e) {
+                Slog.w(ActivityManagerService.TAG,
+                        "Exception thrown sending new intent to " + this, e);
+            }
+        }
+        if (!sent) {
+            addNewIntentLocked(new Intent(intent));
+        }
+    }
+
+    void removeUriPermissionsLocked() {
+        if (readUriPermissions != null) {
+            for (UriPermission perm : readUriPermissions) {
+                perm.readActivities.remove(this);
+                if (perm.readActivities.size() == 0 && (perm.globalModeFlags
+                        &Intent.FLAG_GRANT_READ_URI_PERMISSION) == 0) {
+                    perm.modeFlags &= ~Intent.FLAG_GRANT_READ_URI_PERMISSION;
+                   service.removeUriPermissionIfNeededLocked(perm);
+                }
+            }
+        }
+        if (writeUriPermissions != null) {
+            for (UriPermission perm : writeUriPermissions) {
+                perm.writeActivities.remove(this);
+                if (perm.writeActivities.size() == 0 && (perm.globalModeFlags
+                        &Intent.FLAG_GRANT_WRITE_URI_PERMISSION) == 0) {
+                    perm.modeFlags &= ~Intent.FLAG_GRANT_WRITE_URI_PERMISSION;
+                    service.removeUriPermissionIfNeededLocked(perm);
+                }
+            }
+        }
+    }
 
     void pauseKeyDispatchingLocked() {
         if (!keysPaused) {
@@ -375,8 +424,8 @@
             if (startTime != 0) {
                 final long curTime = SystemClock.uptimeMillis();
                 final long thisTime = curTime - startTime;
-                final long totalTime = service.mInitialStartTime != 0
-                        ? (curTime - service.mInitialStartTime) : thisTime;
+                final long totalTime = stack.mInitialStartTime != 0
+                        ? (curTime - stack.mInitialStartTime) : thisTime;
                 if (ActivityManagerService.SHOW_ACTIVITY_START_TIME) {
                     EventLog.writeEvent(EventLogTags.ACTIVITY_LAUNCH_TIME,
                             System.identityHashCode(this), shortComponentName,
@@ -392,14 +441,14 @@
                     sb.append(" ms)");
                     Log.i(ActivityManagerService.TAG, sb.toString());
                 }
-                service.reportActivityLaunchedLocked(false, this, thisTime, totalTime);
+                stack.reportActivityLaunchedLocked(false, this, thisTime, totalTime);
                 if (totalTime > 0) {
                     service.mUsageStatsService.noteLaunchTime(realActivity, (int)totalTime);
                 }
                 startTime = 0;
-                service.mInitialStartTime = 0;
+                stack.mInitialStartTime = 0;
             }
-            service.reportActivityVisibleLocked(this);
+            stack.reportActivityVisibleLocked(this);
             if (ActivityManagerService.DEBUG_SWITCH) Log.v(
                     ActivityManagerService.TAG, "windowsVisible(): " + this);
             if (!nowVisible) {
@@ -408,27 +457,27 @@
                     // Instead of doing the full stop routine here, let's just
                     // hide any activities we now can, and let them stop when
                     // the normal idle happens.
-                    service.processStoppingActivitiesLocked(false);
+                    stack.processStoppingActivitiesLocked(false);
                 } else {
                     // If this activity was already idle, then we now need to
                     // make sure we perform the full stop of any activities
                     // that are waiting to do so.  This is because we won't
                     // do that while they are still waiting for this one to
                     // become visible.
-                    final int N = service.mWaitingVisibleActivities.size();
+                    final int N = stack.mWaitingVisibleActivities.size();
                     if (N > 0) {
                         for (int i=0; i<N; i++) {
                             ActivityRecord r = (ActivityRecord)
-                                service.mWaitingVisibleActivities.get(i);
+                                stack.mWaitingVisibleActivities.get(i);
                             r.waitingVisible = false;
                             if (ActivityManagerService.DEBUG_SWITCH) Log.v(
                                     ActivityManagerService.TAG,
                                     "Was waiting for visible: " + r);
                         }
-                        service.mWaitingVisibleActivities.clear();
+                        stack.mWaitingVisibleActivities.clear();
                         Message msg = Message.obtain();
-                        msg.what = ActivityManagerService.IDLE_NOW_MSG;
-                        service.mHandler.sendMessage(msg);
+                        msg.what = ActivityStack.IDLE_NOW_MSG;
+                        stack.mHandler.sendMessage(msg);
                     }
                 }
                 service.scheduleAppGcsLocked();
@@ -449,9 +498,9 @@
         ActivityRecord r = this;
         if (r.waitingVisible) {
             // Hmmm, who might we be waiting for?
-            r = service.mResumedActivity;
+            r = stack.mResumedActivity;
             if (r == null) {
-                r = service.mPausingActivity;
+                r = stack.mPausingActivity;
             }
             // Both of those null?  Fall back to 'this' again
             if (r == null) {
diff --git a/services/java/com/android/server/am/ActivityStack.java b/services/java/com/android/server/am/ActivityStack.java
new file mode 100644
index 0000000..de7b15c
--- /dev/null
+++ b/services/java/com/android/server/am/ActivityStack.java
@@ -0,0 +1,3521 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.am;
+
+import com.android.internal.app.HeavyWeightSwitcherActivity;
+import com.android.internal.os.BatteryStatsImpl;
+import com.android.server.am.ActivityManagerService.PendingActivityLaunch;
+
+import android.app.Activity;
+import android.app.AppGlobals;
+import android.app.IActivityManager;
+import static android.app.IActivityManager.START_CLASS_NOT_FOUND;
+import static android.app.IActivityManager.START_DELIVERED_TO_TOP;
+import static android.app.IActivityManager.START_FORWARD_AND_REQUEST_CONFLICT;
+import static android.app.IActivityManager.START_INTENT_NOT_RESOLVED;
+import static android.app.IActivityManager.START_PERMISSION_DENIED;
+import static android.app.IActivityManager.START_RETURN_INTENT_TO_CALLER;
+import static android.app.IActivityManager.START_SUCCESS;
+import static android.app.IActivityManager.START_SWITCHES_CANCELED;
+import static android.app.IActivityManager.START_TASK_TO_FRONT;
+import android.app.IApplicationThread;
+import android.app.PendingIntent;
+import android.app.ResultInfo;
+import android.app.IActivityManager.WaitResult;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.IIntentSender;
+import android.content.Intent;
+import android.content.IntentSender;
+import android.content.pm.ActivityInfo;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.content.res.Configuration;
+import android.net.Uri;
+import android.os.Binder;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Message;
+import android.os.PowerManager;
+import android.os.RemoteException;
+import android.os.SystemClock;
+import android.util.EventLog;
+import android.util.Log;
+import android.util.Slog;
+import android.view.WindowManagerPolicy;
+
+import java.lang.ref.WeakReference;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * State and management of a single stack of activities.
+ */
+public class ActivityStack {
+    static final String TAG = ActivityManagerService.TAG;
+    static final boolean localLOGV = ActivityManagerService.localLOGV;
+    static final boolean DEBUG_SWITCH = ActivityManagerService.DEBUG_SWITCH;
+    static final boolean DEBUG_PAUSE = ActivityManagerService.DEBUG_PAUSE;
+    static final boolean DEBUG_VISBILITY = ActivityManagerService.DEBUG_VISBILITY;
+    static final boolean DEBUG_USER_LEAVING = ActivityManagerService.DEBUG_USER_LEAVING;
+    static final boolean DEBUG_TRANSITION = ActivityManagerService.DEBUG_TRANSITION;
+    static final boolean DEBUG_RESULTS = ActivityManagerService.DEBUG_RESULTS;
+    static final boolean DEBUG_CONFIGURATION = ActivityManagerService.DEBUG_CONFIGURATION;
+    static final boolean DEBUG_TASKS = ActivityManagerService.DEBUG_TASKS;
+    
+    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;
+    
+    // How long we wait until giving up on the last activity to pause.  This
+    // is short because it directly impacts the responsiveness of starting the
+    // next activity.
+    static final int PAUSE_TIMEOUT = 500;
+
+    // How long we can hold the launch wake lock before giving up.
+    static final int LAUNCH_TIMEOUT = 10*1000;
+
+    // How long we wait until giving up on an activity telling us it has
+    // finished destroying itself.
+    static final int DESTROY_TIMEOUT = 10*1000;
+    
+    // How long until we reset a task when the user returns to it.  Currently
+    // 30 minutes.
+    static final long ACTIVITY_INACTIVE_RESET_TIME = 1000*60*30;
+    
+    // Set to false to disable the preview that is shown while a new activity
+    // is being started.
+    static final boolean SHOW_APP_STARTING_PREVIEW = true;
+    
+    enum ActivityState {
+        INITIALIZING,
+        RESUMED,
+        PAUSING,
+        PAUSED,
+        STOPPING,
+        STOPPED,
+        FINISHING,
+        DESTROYING,
+        DESTROYED
+    }
+
+    final ActivityManagerService mService;
+    final boolean mMainStack;
+    
+    final Context mContext;
+    
+    /**
+     * The back history of all previous (and possibly still
+     * running) activities.  It contains HistoryRecord objects.
+     */
+    final ArrayList mHistory = new ArrayList();
+    
+    /**
+     * List of running activities, sorted by recent usage.
+     * The first entry in the list is the least recently used.
+     * It contains HistoryRecord objects.
+     */
+    final ArrayList mLRUActivities = new ArrayList();
+
+    /**
+     * List of activities that are waiting for a new activity
+     * to become visible before completing whatever operation they are
+     * supposed to do.
+     */
+    final ArrayList<ActivityRecord> mWaitingVisibleActivities
+            = new ArrayList<ActivityRecord>();
+
+    /**
+     * List of activities that are ready to be stopped, but waiting
+     * for the next activity to settle down before doing so.  It contains
+     * HistoryRecord objects.
+     */
+    final ArrayList<ActivityRecord> mStoppingActivities
+            = new ArrayList<ActivityRecord>();
+
+    /**
+     * Animations that for the current transition have requested not to
+     * be considered for the transition animation.
+     */
+    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>();
+    
+    /**
+     * List of people waiting to find out about the next launched activity.
+     */
+    final ArrayList<IActivityManager.WaitResult> mWaitingActivityLaunched
+            = new ArrayList<IActivityManager.WaitResult>();
+    
+    /**
+     * List of people waiting to find out about the next visible activity.
+     */
+    final ArrayList<IActivityManager.WaitResult> mWaitingActivityVisible
+            = new ArrayList<IActivityManager.WaitResult>();
+    
+    /**
+     * 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.
+     */
+    final PowerManager.WakeLock mGoingToSleep;
+
+    /**
+     * We don't want to allow the device to go to sleep while in the process
+     * of launching an activity.  This is primarily to allow alarm intent
+     * receivers to launch an activity and get that to run before the device
+     * goes back to sleep.
+     */
+    final PowerManager.WakeLock mLaunchingActivity;
+
+    /**
+     * When we are in the process of pausing an activity, before starting the
+     * next one, this variable holds the activity that is currently being paused.
+     */
+    ActivityRecord mPausingActivity = null;
+
+    /**
+     * This is the last activity that we put into the paused state.  This is
+     * used to determine if we need to do an activity transition while sleeping,
+     * when we normally hold the top activity paused.
+     */
+    ActivityRecord mLastPausedActivity = null;
+
+    /**
+     * Current activity that is resumed, or null if there is none.
+     */
+    ActivityRecord mResumedActivity = null;
+    
+    /**
+     * Set when we know we are going to be calling updateConfiguration()
+     * soon, so want to skip intermediate config checks.
+     */
+    boolean mConfigWillChange;
+
+    /**
+     * Set to indicate whether to issue an onUserLeaving callback when a
+     * newly launched activity is being brought in front of us.
+     */
+    boolean mUserLeaving = false;
+    
+    long mInitialStartTime = 0;
+    
+    static final int PAUSE_TIMEOUT_MSG = 9;
+    static final int IDLE_TIMEOUT_MSG = 10;
+    static final int IDLE_NOW_MSG = 11;
+    static final int LAUNCH_TIMEOUT_MSG = 16;
+    static final int DESTROY_TIMEOUT_MSG = 17;
+    static final int RESUME_TOP_ACTIVITY_MSG = 19;
+    
+    final Handler mHandler = new Handler() {
+        //public Handler() {
+        //    if (localLOGV) Slog.v(TAG, "Handler started!");
+        //}
+
+        public void handleMessage(Message msg) {
+            switch (msg.what) {
+                case PAUSE_TIMEOUT_MSG: {
+                    IBinder token = (IBinder)msg.obj;
+                    // We don't at this point know if the activity is fullscreen,
+                    // so we need to be conservative and assume it isn't.
+                    Slog.w(TAG, "Activity pause timeout for " + token);
+                    activityPaused(token, 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.
+                    IBinder token = (IBinder)msg.obj;
+                    Slog.w(TAG, "Activity idle timeout for " + token);
+                    activityIdleInternal(token, true, null);
+                } break;
+                case DESTROY_TIMEOUT_MSG: {
+                    IBinder token = (IBinder)msg.obj;
+                    // We don't at this point know if the activity is fullscreen,
+                    // so we need to be conservative and assume it isn't.
+                    Slog.w(TAG, "Activity destroy timeout for " + token);
+                    activityDestroyed(token);
+                } break;
+                case IDLE_NOW_MSG: {
+                    IBinder token = (IBinder)msg.obj;
+                    activityIdleInternal(token, false, null);
+                } break;
+                case LAUNCH_TIMEOUT_MSG: {
+                    if (mService.mDidDexOpt) {
+                        mService.mDidDexOpt = false;
+                        Message nmsg = mHandler.obtainMessage(LAUNCH_TIMEOUT_MSG);
+                        mHandler.sendMessageDelayed(nmsg, LAUNCH_TIMEOUT);
+                        return;
+                    }
+                    synchronized (mService) {
+                        if (mLaunchingActivity.isHeld()) {
+                            Slog.w(TAG, "Launch timeout has expired, giving up wake lock!");
+                            mLaunchingActivity.release();
+                        }
+                    }
+                } break;
+                case RESUME_TOP_ACTIVITY_MSG: {
+                    synchronized (mService) {
+                        resumeTopActivityLocked(null);
+                    }
+                } break;
+            }
+        }
+    };
+    
+    ActivityStack(ActivityManagerService service, Context context, boolean mainStack) {
+        mService = service;
+        mContext = context;
+        mMainStack = mainStack;
+        PowerManager pm =
+            (PowerManager)context.getSystemService(Context.POWER_SERVICE);
+        mGoingToSleep = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "ActivityManager-Sleep");
+        mLaunchingActivity = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "ActivityManager-Launch");
+        mLaunchingActivity.setReferenceCounted(false);
+    }
+    
+    final ActivityRecord topRunningActivityLocked(ActivityRecord notTop) {
+        int i = mHistory.size()-1;
+        while (i >= 0) {
+            ActivityRecord r = (ActivityRecord)mHistory.get(i);
+            if (!r.finishing && r != notTop) {
+                return r;
+            }
+            i--;
+        }
+        return null;
+    }
+
+    final ActivityRecord topRunningNonDelayedActivityLocked(ActivityRecord notTop) {
+        int i = mHistory.size()-1;
+        while (i >= 0) {
+            ActivityRecord r = (ActivityRecord)mHistory.get(i);
+            if (!r.finishing && !r.delayedResume && r != notTop) {
+                return r;
+            }
+            i--;
+        }
+        return null;
+    }
+
+    /**
+     * This is a simplified version of topRunningActivityLocked that provides a number of
+     * optional skip-over modes.  It is intended for use with the ActivityController hook only.
+     * 
+     * @param token If non-null, any history records matching this token will be skipped.
+     * @param taskId If non-zero, we'll attempt to skip over records with the same task ID.
+     * 
+     * @return Returns the HistoryRecord of the next activity on the stack.
+     */
+    final ActivityRecord topRunningActivityLocked(IBinder token, int taskId) {
+        int i = mHistory.size()-1;
+        while (i >= 0) {
+            ActivityRecord r = (ActivityRecord)mHistory.get(i);
+            // Note: the taskId check depends on real taskId fields being non-zero
+            if (!r.finishing && (token != r) && (taskId != r.task.taskId)) {
+                return r;
+            }
+            i--;
+        }
+        return null;
+    }
+
+    final int indexOfTokenLocked(IBinder token) {
+        int count = mHistory.size();
+
+        // convert the token to an entry in the history.
+        int index = -1;
+        for (int i=count-1; i>=0; i--) {
+            Object o = mHistory.get(i);
+            if (o == token) {
+                index = i;
+                break;
+            }
+        }
+
+        return index;
+    }
+
+    private final boolean updateLRUListLocked(ActivityRecord r) {
+        final boolean hadit = mLRUActivities.remove(r);
+        mLRUActivities.add(r);
+        return hadit;
+    }
+
+    /**
+     * Returns the top activity in any existing task matching the given
+     * Intent.  Returns null if no such task is found.
+     */
+    private ActivityRecord findTaskLocked(Intent intent, ActivityInfo info) {
+        ComponentName cls = intent.getComponent();
+        if (info.targetActivity != null) {
+            cls = new ComponentName(info.packageName, info.targetActivity);
+        }
+
+        TaskRecord cp = null;
+
+        final int N = mHistory.size();
+        for (int i=(N-1); i>=0; i--) {
+            ActivityRecord r = (ActivityRecord)mHistory.get(i);
+            if (!r.finishing && r.task != cp
+                    && r.launchMode != ActivityInfo.LAUNCH_SINGLE_INSTANCE) {
+                cp = r.task;
+                //Slog.i(TAG, "Comparing existing cls=" + r.task.intent.getComponent().flattenToShortString()
+                //        + "/aff=" + r.task.affinity + " to new cls="
+                //        + intent.getComponent().flattenToShortString() + "/aff=" + taskAffinity);
+                if (r.task.affinity != null) {
+                    if (r.task.affinity.equals(info.taskAffinity)) {
+                        //Slog.i(TAG, "Found matching affinity!");
+                        return r;
+                    }
+                } else if (r.task.intent != null
+                        && r.task.intent.getComponent().equals(cls)) {
+                    //Slog.i(TAG, "Found matching class!");
+                    //dump();
+                    //Slog.i(TAG, "For Intent " + intent + " bringing to top: " + r.intent);
+                    return r;
+                } else if (r.task.affinityIntent != null
+                        && r.task.affinityIntent.getComponent().equals(cls)) {
+                    //Slog.i(TAG, "Found matching class!");
+                    //dump();
+                    //Slog.i(TAG, "For Intent " + intent + " bringing to top: " + r.intent);
+                    return r;
+                }
+            }
+        }
+
+        return null;
+    }
+
+    /**
+     * Returns the first activity (starting from the top of the stack) that
+     * is the same as the given activity.  Returns null if no such activity
+     * is found.
+     */
+    private ActivityRecord findActivityLocked(Intent intent, ActivityInfo info) {
+        ComponentName cls = intent.getComponent();
+        if (info.targetActivity != null) {
+            cls = new ComponentName(info.packageName, info.targetActivity);
+        }
+
+        final int N = mHistory.size();
+        for (int i=(N-1); i>=0; i--) {
+            ActivityRecord r = (ActivityRecord)mHistory.get(i);
+            if (!r.finishing) {
+                if (r.intent.getComponent().equals(cls)) {
+                    //Slog.i(TAG, "Found matching class!");
+                    //dump();
+                    //Slog.i(TAG, "For Intent " + intent + " bringing to top: " + r.intent);
+                    return r;
+                }
+            }
+        }
+
+        return null;
+    }
+
+    final boolean realStartActivityLocked(ActivityRecord r,
+            ProcessRecord app, boolean andResume, boolean checkConfig)
+            throws RemoteException {
+
+        r.startFreezingScreenLocked(app, 0);
+        mService.mWindowManager.setAppVisibility(r, true);
+
+        // 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 : null);
+            mService.updateConfigurationLocked(config, r);
+        }
+
+        r.app = app;
+
+        if (localLOGV) Slog.v(TAG, "Launching: " + r);
+
+        int idx = app.activities.indexOf(r);
+        if (idx < 0) {
+            app.activities.add(r);
+        }
+        mService.updateLruProcessLocked(app, true, true);
+
+        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,
+                        System.identityHashCode(r),
+                        r.task.taskId, r.shortComponentName);
+            }
+            if (r.isHomeActivity) {
+                mService.mHomeProcess = app;
+            }
+            mService.ensurePackageDexOpt(r.intent.getComponent().getPackageName());
+            app.thread.scheduleLaunchActivity(new Intent(r.intent), r,
+                    System.identityHashCode(r),
+                    r.info, r.icicle, results, newIntents, !andResume,
+                    mService.isNextTransitionForward());
+            
+            if ((app.info.flags&ApplicationInfo.FLAG_HEAVY_WEIGHT) != 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) {
+                        Log.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);
+                requestFinishActivityLocked(r, Activity.RESULT_CANCELED, null,
+                        "2nd-crash");
+                return false;
+            }
+
+            // This is the first time we failed -- restart process and
+            // retry.
+            app.activities.remove(r);
+            throw e;
+        }
+
+        r.launchFailed = false;
+        if (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.
+            r.state = ActivityState.RESUMED;
+            r.icicle = null;
+            r.haveState = false;
+            r.stopped = false;
+            mResumedActivity = r;
+            r.task.touchActiveTime();
+            completeResumeLocked(r);
+            pauseIfSleepingLocked();                
+        } 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.
+            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 (mMainStack) {
+            mService.startSetupActivityLocked();
+        }
+        
+        return true;
+    }
+
+    private final void startSpecificActivityLocked(ActivityRecord r,
+            boolean andResume, boolean checkConfig) {
+        // Is this activity's application already running?
+        ProcessRecord app = mService.getProcessRecordLocked(r.processName,
+                r.info.applicationInfo.uid);
+        
+        if (r.startTime == 0) {
+            r.startTime = SystemClock.uptimeMillis();
+            if (mInitialStartTime == 0) {
+                mInitialStartTime = r.startTime;
+            }
+        } else if (mInitialStartTime == 0) {
+            mInitialStartTime = SystemClock.uptimeMillis();
+        }
+        
+        if (app != null && app.thread != null) {
+            try {
+                realStartActivityLocked(r, app, andResume, checkConfig);
+                return;
+            } catch (RemoteException e) {
+                Slog.w(TAG, "Exception when starting activity "
+                        + r.intent.getComponent().flattenToShortString(), e);
+            }
+
+            // If a dead object exception was thrown -- fall through to
+            // restart the application.
+        }
+
+        mService.startProcessLocked(r.processName, r.info.applicationInfo, true, 0,
+                "activity", r.intent.getComponent(), false);
+    }
+    
+    void pauseIfSleepingLocked() {
+        if (mService.mSleeping || mService.mShuttingDown) {
+            if (!mGoingToSleep.isHeld()) {
+                mGoingToSleep.acquire();
+                if (mLaunchingActivity.isHeld()) {
+                    mLaunchingActivity.release();
+                    mService.mHandler.removeMessages(LAUNCH_TIMEOUT_MSG);
+                }
+            }
+
+            // If we are not currently pausing an activity, get the current
+            // one to pause.  If we are pausing one, we will just let that stuff
+            // run and release the wake lock when all done.
+            if (mPausingActivity == null) {
+                if (DEBUG_PAUSE) Slog.v(TAG, "Sleep needs to pause...");
+                if (DEBUG_USER_LEAVING) Slog.v(TAG, "Sleep => pause with userLeaving=false");
+                startPausingLocked(false, true);
+            }
+        }
+    }
+    
+    private final void startPausingLocked(boolean userLeaving, boolean uiSleeping) {
+        if (mPausingActivity != null) {
+            RuntimeException e = new RuntimeException();
+            Slog.e(TAG, "Trying to pause when pause is already pending for "
+                  + mPausingActivity, e);
+        }
+        ActivityRecord prev = mResumedActivity;
+        if (prev == null) {
+            RuntimeException e = new RuntimeException();
+            Slog.e(TAG, "Trying to pause when nothing is resumed", e);
+            resumeTopActivityLocked(null);
+            return;
+        }
+        if (DEBUG_PAUSE) Slog.v(TAG, "Start pausing: " + prev);
+        mResumedActivity = null;
+        mPausingActivity = prev;
+        mLastPausedActivity = prev;
+        prev.state = ActivityState.PAUSING;
+        prev.task.touchActiveTime();
+
+        mService.updateCpuStats();
+        
+        if (prev.app != null && prev.app.thread != null) {
+            if (DEBUG_PAUSE) Slog.v(TAG, "Enqueueing pending pause: " + prev);
+            try {
+                EventLog.writeEvent(EventLogTags.AM_PAUSE_ACTIVITY,
+                        System.identityHashCode(prev),
+                        prev.shortComponentName);
+                prev.app.thread.schedulePauseActivity(prev, prev.finishing, userLeaving,
+                        prev.configChangeFlags);
+                if (mMainStack) {
+                    mService.updateUsageStats(prev, false);
+                }
+            } catch (Exception e) {
+                // Ignore exception, if process died other code will cleanup.
+                Slog.w(TAG, "Exception thrown during pause", e);
+                mPausingActivity = null;
+                mLastPausedActivity = null;
+            }
+        } else {
+            mPausingActivity = null;
+            mLastPausedActivity = null;
+        }
+
+        // If we are not going to sleep, we want to ensure the device is
+        // awake until the next activity is started.
+        if (!mService.mSleeping && !mService.mShuttingDown) {
+            mLaunchingActivity.acquire();
+            if (!mHandler.hasMessages(LAUNCH_TIMEOUT_MSG)) {
+                // To be safe, don't allow the wake lock to be held for too long.
+                Message msg = mHandler.obtainMessage(LAUNCH_TIMEOUT_MSG);
+                mHandler.sendMessageDelayed(msg, LAUNCH_TIMEOUT);
+            }
+        }
+
+
+        if (mPausingActivity != null) {
+            // Have the window manager pause its key dispatching until the new
+            // activity has started.  If we're pausing the activity just because
+            // the screen is being turned off and the UI is sleeping, don't interrupt
+            // key dispatch; the same activity will pick it up again on wakeup.
+            if (!uiSleeping) {
+                prev.pauseKeyDispatchingLocked();
+            } else {
+                if (DEBUG_PAUSE) Slog.v(TAG, "Key dispatch not paused for screen off");
+            }
+
+            // Schedule a pause timeout in case the app doesn't respond.
+            // We don't give it much time because this directly impacts the
+            // responsiveness seen by the user.
+            Message msg = mHandler.obtainMessage(PAUSE_TIMEOUT_MSG);
+            msg.obj = prev;
+            mHandler.sendMessageDelayed(msg, PAUSE_TIMEOUT);
+            if (DEBUG_PAUSE) Slog.v(TAG, "Waiting for pause to complete...");
+        } else {
+            // This activity failed to schedule the
+            // pause, so just treat it as being paused now.
+            if (DEBUG_PAUSE) Slog.v(TAG, "Activity not running, resuming next.");
+            resumeTopActivityLocked(null);
+        }
+    }
+    
+    final void activityPaused(IBinder token, Bundle icicle, boolean timeout) {
+        if (DEBUG_PAUSE) Slog.v(
+            TAG, "Activity paused: token=" + token + ", icicle=" + icicle
+            + ", timeout=" + timeout);
+
+        ActivityRecord r = null;
+
+        synchronized (mService) {
+            int index = indexOfTokenLocked(token);
+            if (index >= 0) {
+                r = (ActivityRecord)mHistory.get(index);
+                if (!timeout) {
+                    r.icicle = icicle;
+                    r.haveState = true;
+                }
+                mHandler.removeMessages(PAUSE_TIMEOUT_MSG, r);
+                if (mPausingActivity == r) {
+                    r.state = ActivityState.PAUSED;
+                    completePauseLocked();
+                } else {
+                    EventLog.writeEvent(EventLogTags.AM_FAILED_TO_PAUSE,
+                            System.identityHashCode(r), r.shortComponentName, 
+                            mPausingActivity != null
+                                ? mPausingActivity.shortComponentName : "(none)");
+                }
+            }
+        }
+    }
+
+    private final void completePauseLocked() {
+        ActivityRecord prev = mPausingActivity;
+        if (DEBUG_PAUSE) Slog.v(TAG, "Complete pause: " + prev);
+        
+        if (prev != null) {
+            if (prev.finishing) {
+                if (DEBUG_PAUSE) Slog.v(TAG, "Executing finish of activity: " + prev);
+                prev = finishCurrentActivityLocked(prev, FINISH_AFTER_VISIBLE);
+            } else if (prev.app != null) {
+                if (DEBUG_PAUSE) Slog.v(TAG, "Enqueueing pending stop: " + prev);
+                if (prev.waitingVisible) {
+                    prev.waitingVisible = false;
+                    mWaitingVisibleActivities.remove(prev);
+                    if (DEBUG_SWITCH || DEBUG_PAUSE) Slog.v(
+                            TAG, "Complete pause, no longer waiting: " + prev);
+                }
+                if (prev.configDestroy) {
+                    // The previous is being paused because the configuration
+                    // is changing, which means it is actually stopping...
+                    // To juggle the fact that we are also starting a new
+                    // instance right now, we need to first completely stop
+                    // the current instance before starting the new one.
+                    if (DEBUG_PAUSE) Slog.v(TAG, "Destroying after pause: " + prev);
+                    destroyActivityLocked(prev, true);
+                } else {
+                    mStoppingActivities.add(prev);
+                    if (mStoppingActivities.size() > 3) {
+                        // If we already have a few activities waiting to stop,
+                        // then give up on things going idle and start clearing
+                        // them out.
+                        if (DEBUG_PAUSE) Slog.v(TAG, "To many pending stops, forcing idle");
+                        Message msg = Message.obtain();
+                        msg.what = IDLE_NOW_MSG;
+                        mHandler.sendMessage(msg);
+                    }
+                }
+            } else {
+                if (DEBUG_PAUSE) Slog.v(TAG, "App died during pause, not stopping: " + prev);
+                prev = null;
+            }
+            mPausingActivity = null;
+        }
+
+        if (!mService.mSleeping && !mService.mShuttingDown) {
+            resumeTopActivityLocked(prev);
+        } else {
+            if (mGoingToSleep.isHeld()) {
+                mGoingToSleep.release();
+            }
+            if (mService.mShuttingDown) {
+                mService.notifyAll();
+            }
+        }
+        
+        if (prev != null) {
+            prev.resumeKeyDispatchingLocked();
+        }
+
+        if (prev.app != null && prev.cpuTimeAtResume > 0
+                && mService.mBatteryStatsService.isOnBattery()) {
+            long diff = 0;
+            synchronized (mService.mProcessStatsThread) {
+                diff = mService.mProcessStats.getCpuTimeForPid(prev.app.pid)
+                        - prev.cpuTimeAtResume;
+            }
+            if (diff > 0) {
+                BatteryStatsImpl bsi = mService.mBatteryStatsService.getActiveStatistics();
+                synchronized (bsi) {
+                    BatteryStatsImpl.Uid.Proc ps =
+                            bsi.getProcessStatsLocked(prev.info.applicationInfo.uid,
+                            prev.info.packageName);
+                    if (ps != null) {
+                        ps.addForegroundTimeLocked(diff);
+                    }
+                }
+            }
+        }
+        prev.cpuTimeAtResume = 0; // reset it
+    }
+
+    /**
+     * Once we know that we have asked an application to put an activity in
+     * the resumed state (either by launching it or explicitly telling it),
+     * this function updates the rest of our state to match that fact.
+     */
+    private final void completeResumeLocked(ActivityRecord next) {
+        next.idle = false;
+        next.results = null;
+        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);
+        }
+
+        if (mMainStack) {
+            mService.reportResumedActivityLocked(next);
+        }
+        
+        next.thumbnail = null;
+        if (mMainStack) {
+            mService.setFocusedActivityLocked(next);
+        }
+        next.resumeKeyDispatchingLocked();
+        ensureActivitiesVisibleLocked(null, 0);
+        mService.mWindowManager.executeAppTransition();
+        mNoAnimActivities.clear();
+
+        // Mark the point when the activity is resuming
+        // TODO: To be more accurate, the mark should be before the onCreate,
+        //       not after the onResume. But for subsequent starts, onResume is fine.
+        if (next.app != null) {
+            synchronized (mService.mProcessStatsThread) {
+                next.cpuTimeAtResume = mService.mProcessStats.getCpuTimeForPid(next.app.pid);
+            }
+        } else {
+            next.cpuTimeAtResume = 0; // Couldn't get the cpu time of process
+        }
+    }
+
+    /**
+     * Make sure that all activities that need to be visible (that is, they
+     * currently can be seen by the user) actually are.
+     */
+    final void ensureActivitiesVisibleLocked(ActivityRecord top,
+            ActivityRecord starting, String onlyThisProcess, int configChanges) {
+        if (DEBUG_VISBILITY) Slog.v(
+                TAG, "ensureActivitiesVisible behind " + top
+                + " configChanges=0x" + Integer.toHexString(configChanges));
+
+        // If the top activity is not fullscreen, then we need to
+        // make sure any activities under it are now visible.
+        final int count = mHistory.size();
+        int i = count-1;
+        while (mHistory.get(i) != top) {
+            i--;
+        }
+        ActivityRecord r;
+        boolean behindFullscreen = false;
+        for (; i>=0; i--) {
+            r = (ActivityRecord)mHistory.get(i);
+            if (DEBUG_VISBILITY) Slog.v(
+                    TAG, "Make visible? " + r + " finishing=" + r.finishing
+                    + " state=" + r.state);
+            if (r.finishing) {
+                continue;
+            }
+            
+            final boolean doThisProcess = onlyThisProcess == null
+                    || onlyThisProcess.equals(r.processName);
+            
+            // First: if this is not the current activity being started, make
+            // sure it matches the current configuration.
+            if (r != starting && doThisProcess) {
+                ensureActivityConfigurationLocked(r, 0);
+            }
+            
+            if (r.app == null || r.app.thread == null) {
+                if (onlyThisProcess == null
+                        || onlyThisProcess.equals(r.processName)) {
+                    // This activity needs to be visible, but isn't even
+                    // running...  get it started, but don't resume it
+                    // at this point.
+                    if (DEBUG_VISBILITY) Slog.v(
+                            TAG, "Start and freeze screen for " + r);
+                    if (r != starting) {
+                        r.startFreezingScreenLocked(r.app, configChanges);
+                    }
+                    if (!r.visible) {
+                        if (DEBUG_VISBILITY) Slog.v(
+                                TAG, "Starting and making visible: " + r);
+                        mService.mWindowManager.setAppVisibility(r, true);
+                    }
+                    if (r != starting) {
+                        startSpecificActivityLocked(r, false, false);
+                    }
+                }
+
+            } else if (r.visible) {
+                // If this activity is already visible, then there is nothing
+                // else to do here.
+                if (DEBUG_VISBILITY) Slog.v(
+                        TAG, "Skipping: already visible at " + r);
+                r.stopFreezingScreenLocked(false);
+
+            } else if (onlyThisProcess == null) {
+                // This activity is not currently visible, but is running.
+                // Tell it to become visible.
+                r.visible = true;
+                if (r.state != ActivityState.RESUMED && r != starting) {
+                    // If this activity is paused, tell it
+                    // to now show its window.
+                    if (DEBUG_VISBILITY) Slog.v(
+                            TAG, "Making visible and scheduling visibility: " + r);
+                    try {
+                        mService.mWindowManager.setAppVisibility(r, true);
+                        r.app.thread.scheduleWindowVisibility(r, true);
+                        r.stopFreezingScreenLocked(false);
+                    } catch (Exception e) {
+                        // Just skip on any failure; we'll make it
+                        // visible when it next restarts.
+                        Slog.w(TAG, "Exception thrown making visibile: "
+                                + r.intent.getComponent(), e);
+                    }
+                }
+            }
+
+            // Aggregate current change flags.
+            configChanges |= r.configChangeFlags;
+
+            if (r.fullscreen) {
+                // At this point, nothing else needs to be shown
+                if (DEBUG_VISBILITY) Slog.v(
+                        TAG, "Stopping: fullscreen at " + r);
+                behindFullscreen = true;
+                i--;
+                break;
+            }
+        }
+
+        // Now for any activities that aren't visible to the user, make
+        // sure they no longer are keeping the screen frozen.
+        while (i >= 0) {
+            r = (ActivityRecord)mHistory.get(i);
+            if (DEBUG_VISBILITY) Slog.v(
+                    TAG, "Make invisible? " + r + " finishing=" + r.finishing
+                    + " state=" + r.state
+                    + " behindFullscreen=" + behindFullscreen);
+            if (!r.finishing) {
+                if (behindFullscreen) {
+                    if (r.visible) {
+                        if (DEBUG_VISBILITY) Slog.v(
+                                TAG, "Making invisible: " + r);
+                        r.visible = false;
+                        try {
+                            mService.mWindowManager.setAppVisibility(r, false);
+                            if ((r.state == ActivityState.STOPPING
+                                    || r.state == ActivityState.STOPPED)
+                                    && r.app != null && r.app.thread != null) {
+                                if (DEBUG_VISBILITY) Slog.v(
+                                        TAG, "Scheduling invisibility: " + r);
+                                r.app.thread.scheduleWindowVisibility(r, false);
+                            }
+                        } catch (Exception e) {
+                            // Just skip on any failure; we'll make it
+                            // visible when it next restarts.
+                            Slog.w(TAG, "Exception thrown making hidden: "
+                                    + r.intent.getComponent(), e);
+                        }
+                    } else {
+                        if (DEBUG_VISBILITY) Slog.v(
+                                TAG, "Already invisible: " + r);
+                    }
+                } else if (r.fullscreen) {
+                    if (DEBUG_VISBILITY) Slog.v(
+                            TAG, "Now behindFullscreen: " + r);
+                    behindFullscreen = true;
+                }
+            }
+            i--;
+        }
+    }
+
+    /**
+     * Version of ensureActivitiesVisible that can easily be called anywhere.
+     */
+    final void ensureActivitiesVisibleLocked(ActivityRecord starting,
+            int configChanges) {
+        ActivityRecord r = topRunningActivityLocked(null);
+        if (r != null) {
+            ensureActivitiesVisibleLocked(r, starting, null, configChanges);
+        }
+    }
+    
+    /**
+     * Ensure that the top activity in the stack is resumed.
+     *
+     * @param prev The previously resumed activity, for when in the process
+     * of pausing; can be null to call from elsewhere.
+     *
+     * @return Returns true if something is being resumed, or false if
+     * nothing happened.
+     */
+    final boolean resumeTopActivityLocked(ActivityRecord prev) {
+        // Find the first activity that is not finishing.
+        ActivityRecord next = topRunningActivityLocked(null);
+
+        // Remember how we'll process this pause/resume situation, and ensure
+        // that the state is reset however we wind up proceeding.
+        final boolean userLeaving = mUserLeaving;
+        mUserLeaving = false;
+
+        if (next == null) {
+            // There are no more activities!  Let's just start up the
+            // Launcher...
+            if (mMainStack) {
+                return mService.startHomeActivityLocked();
+            }
+        }
+
+        next.delayedResume = false;
+        
+        // If the top activity is the resumed one, nothing to do.
+        if (mResumedActivity == next && next.state == ActivityState.RESUMED) {
+            // Make sure we have executed any pending transitions, since there
+            // should be nothing left to do at this point.
+            mService.mWindowManager.executeAppTransition();
+            mNoAnimActivities.clear();
+            return false;
+        }
+
+        // If we are sleeping, and there is no resumed activity, and the top
+        // activity is paused, well that is the state we want.
+        if ((mService.mSleeping || mService.mShuttingDown)
+                && mLastPausedActivity == next && next.state == ActivityState.PAUSED) {
+            // Make sure we have executed any pending transitions, since there
+            // should be nothing left to do at this point.
+            mService.mWindowManager.executeAppTransition();
+            mNoAnimActivities.clear();
+            return false;
+        }
+        
+        // The activity may be waiting for stop, but that is no longer
+        // appropriate for it.
+        mStoppingActivities.remove(next);
+        mWaitingVisibleActivities.remove(next);
+
+        if (DEBUG_SWITCH) Slog.v(TAG, "Resuming " + next);
+
+        // If we are currently pausing an activity, then don't do anything
+        // until that is done.
+        if (mPausingActivity != null) {
+            if (DEBUG_SWITCH) Slog.v(TAG, "Skip resume: pausing=" + mPausingActivity);
+            return false;
+        }
+
+        // We need to start pausing the current activity so the top one
+        // can be resumed...
+        if (mResumedActivity != null) {
+            if (DEBUG_SWITCH) Slog.v(TAG, "Skip resume: need to start pausing");
+            startPausingLocked(userLeaving, false);
+            return true;
+        }
+
+        if (prev != null && prev != next) {
+            if (!prev.waitingVisible && next != null && !next.nowVisible) {
+                prev.waitingVisible = true;
+                mWaitingVisibleActivities.add(prev);
+                if (DEBUG_SWITCH) Slog.v(
+                        TAG, "Resuming top, waiting visible to hide: " + prev);
+            } else {
+                // The next activity is already visible, so hide the previous
+                // activity's windows right now so we can show the new one ASAP.
+                // We only do this if the previous is finishing, which should mean
+                // it is on top of the one being resumed so hiding it quickly
+                // is good.  Otherwise, we want to do the normal route of allowing
+                // the resumed activity to be shown so we can decide if the
+                // previous should actually be hidden depending on whether the
+                // new one is found to be full-screen or not.
+                if (prev.finishing) {
+                    mService.mWindowManager.setAppVisibility(prev, false);
+                    if (DEBUG_SWITCH) Slog.v(TAG, "Not waiting for visible to hide: "
+                            + prev + ", waitingVisible="
+                            + (prev != null ? prev.waitingVisible : null)
+                            + ", nowVisible=" + next.nowVisible);
+                } else {
+                    if (DEBUG_SWITCH) Slog.v(TAG, "Previous already visible but still waiting to hide: "
+                        + prev + ", waitingVisible="
+                        + (prev != null ? prev.waitingVisible : null)
+                        + ", nowVisible=" + next.nowVisible);
+                }
+            }
+        }
+
+        // We are starting up the next activity, so tell the window manager
+        // that the previous one will be hidden soon.  This way it can know
+        // to ignore it when computing the desired screen orientation.
+        if (prev != null) {
+            if (prev.finishing) {
+                if (DEBUG_TRANSITION) Slog.v(TAG,
+                        "Prepare close transition: prev=" + prev);
+                if (mNoAnimActivities.contains(prev)) {
+                    mService.mWindowManager.prepareAppTransition(WindowManagerPolicy.TRANSIT_NONE);
+                } else {
+                    mService.mWindowManager.prepareAppTransition(prev.task == next.task
+                            ? WindowManagerPolicy.TRANSIT_ACTIVITY_CLOSE
+                            : WindowManagerPolicy.TRANSIT_TASK_CLOSE);
+                }
+                mService.mWindowManager.setAppWillBeHidden(prev);
+                mService.mWindowManager.setAppVisibility(prev, false);
+            } else {
+                if (DEBUG_TRANSITION) Slog.v(TAG,
+                        "Prepare open transition: prev=" + prev);
+                if (mNoAnimActivities.contains(next)) {
+                    mService.mWindowManager.prepareAppTransition(WindowManagerPolicy.TRANSIT_NONE);
+                } else {
+                    mService.mWindowManager.prepareAppTransition(prev.task == next.task
+                            ? WindowManagerPolicy.TRANSIT_ACTIVITY_OPEN
+                            : WindowManagerPolicy.TRANSIT_TASK_OPEN);
+                }
+            }
+            if (false) {
+                mService.mWindowManager.setAppWillBeHidden(prev);
+                mService.mWindowManager.setAppVisibility(prev, false);
+            }
+        } else if (mHistory.size() > 1) {
+            if (DEBUG_TRANSITION) Slog.v(TAG,
+                    "Prepare open transition: no previous");
+            if (mNoAnimActivities.contains(next)) {
+                mService.mWindowManager.prepareAppTransition(WindowManagerPolicy.TRANSIT_NONE);
+            } else {
+                mService.mWindowManager.prepareAppTransition(WindowManagerPolicy.TRANSIT_ACTIVITY_OPEN);
+            }
+        }
+
+        if (next.app != null && next.app.thread != null) {
+            if (DEBUG_SWITCH) Slog.v(TAG, "Resume running: " + next);
+
+            // This activity is now becoming visible.
+            mService.mWindowManager.setAppVisibility(next, true);
+
+            ActivityRecord lastResumedActivity = mResumedActivity;
+            ActivityState lastState = next.state;
+
+            mService.updateCpuStats();
+            
+            next.state = ActivityState.RESUMED;
+            mResumedActivity = next;
+            next.task.touchActiveTime();
+            mService.updateLruProcessLocked(next.app, true, true);
+            updateLRUListLocked(next);
+
+            // Have the window manager re-evaluate the orientation of
+            // the screen based on the new activity order.
+            boolean updated = false;
+            if (mMainStack) {
+                synchronized (mService) {
+                    Configuration config = mService.mWindowManager.updateOrientationFromAppTokens(
+                            mService.mConfiguration,
+                            next.mayFreezeScreenLocked(next.app) ? next : null);
+                    if (config != null) {
+                        next.frozenBeforeDestroy = true;
+                    }
+                    updated = mService.updateConfigurationLocked(config, next);
+                }
+            }
+            if (!updated) {
+                // The configuration update wasn't able to keep the existing
+                // instance of the activity, and instead started a new one.
+                // We should be all done, but let's just make sure our activity
+                // is still at the top and schedule another run if something
+                // weird happened.
+                ActivityRecord nextNext = topRunningActivityLocked(null);
+                if (DEBUG_SWITCH) Slog.i(TAG,
+                        "Activity config changed during resume: " + next
+                        + ", new next: " + nextNext);
+                if (nextNext != next) {
+                    // Do over!
+                    mHandler.sendEmptyMessage(RESUME_TOP_ACTIVITY_MSG);
+                }
+                if (mMainStack) {
+                    mService.setFocusedActivityLocked(next);
+                }
+                ensureActivitiesVisibleLocked(null, 0);
+                mService.mWindowManager.executeAppTransition();
+                mNoAnimActivities.clear();
+                return true;
+            }
+            
+            try {
+                // Deliver all pending results.
+                ArrayList a = next.results;
+                if (a != null) {
+                    final int N = a.size();
+                    if (!next.finishing && N > 0) {
+                        if (DEBUG_RESULTS) Slog.v(
+                                TAG, "Delivering results to " + next
+                                + ": " + a);
+                        next.app.thread.scheduleSendResult(next, a);
+                    }
+                }
+
+                if (next.newIntents != null) {
+                    next.app.thread.scheduleNewIntent(next.newIntents, next);
+                }
+
+                EventLog.writeEvent(EventLogTags.AM_RESUME_ACTIVITY,
+                        System.identityHashCode(next),
+                        next.task.taskId, next.shortComponentName);
+                
+                next.app.thread.scheduleResumeActivity(next,
+                        mService.isNextTransitionForward());
+                
+                pauseIfSleepingLocked();
+
+            } catch (Exception e) {
+                // Whoops, need to restart this activity!
+                next.state = lastState;
+                mResumedActivity = lastResumedActivity;
+                Slog.i(TAG, "Restarting because process died: " + next);
+                if (!next.hasBeenLaunched) {
+                    next.hasBeenLaunched = true;
+                } else {
+                    if (SHOW_APP_STARTING_PREVIEW && mMainStack) {
+                        mService.mWindowManager.setAppStartingWindow(
+                                next, next.packageName, next.theme,
+                                next.nonLocalizedLabel,
+                                next.labelRes, next.icon, null, true);
+                    }
+                }
+                startSpecificActivityLocked(next, true, false);
+                return true;
+            }
+
+            // From this point on, if something goes wrong there is no way
+            // to recover the activity.
+            try {
+                next.visible = true;
+                completeResumeLocked(next);
+            } catch (Exception e) {
+                // If any exception gets thrown, toss away this
+                // activity and try the next one.
+                Slog.w(TAG, "Exception thrown during resume of " + next, e);
+                requestFinishActivityLocked(next, Activity.RESULT_CANCELED, null,
+                        "resume-exception");
+                return true;
+            }
+
+            // Didn't need to use the icicle, and it is now out of date.
+            next.icicle = null;
+            next.haveState = false;
+            next.stopped = false;
+
+        } else {
+            // Whoops, need to restart this activity!
+            if (!next.hasBeenLaunched) {
+                next.hasBeenLaunched = true;
+            } else {
+                if (SHOW_APP_STARTING_PREVIEW) {
+                    mService.mWindowManager.setAppStartingWindow(
+                            next, next.packageName, next.theme,
+                            next.nonLocalizedLabel,
+                            next.labelRes, next.icon, null, true);
+                }
+                if (DEBUG_SWITCH) Slog.v(TAG, "Restarting: " + next);
+            }
+            startSpecificActivityLocked(next, true, true);
+        }
+
+        return true;
+    }
+
+    private final void startActivityLocked(ActivityRecord r, boolean newTask,
+            boolean doResume) {
+        final int NH = mHistory.size();
+
+        int addPos = -1;
+        
+        if (!newTask) {
+            // If starting in an existing task, find where that is...
+            ActivityRecord next = null;
+            boolean startIt = true;
+            for (int i = NH-1; i >= 0; i--) {
+                ActivityRecord p = (ActivityRecord)mHistory.get(i);
+                if (p.finishing) {
+                    continue;
+                }
+                if (p.task == r.task) {
+                    // Here it is!  Now, if this is not yet visible to the
+                    // user, then just add it without starting; it will
+                    // get started when the user navigates back to it.
+                    addPos = i+1;
+                    if (!startIt) {
+                        mHistory.add(addPos, r);
+                        r.inHistory = true;
+                        r.task.numActivities++;
+                        mService.mWindowManager.addAppToken(addPos, r, r.task.taskId,
+                                r.info.screenOrientation, r.fullscreen);
+                        if (VALIDATE_TOKENS) {
+                            mService.mWindowManager.validateAppTokens(mHistory);
+                        }
+                        return;
+                    }
+                    break;
+                }
+                if (p.fullscreen) {
+                    startIt = false;
+                }
+                next = p;
+            }
+        }
+
+        // Place a new activity at top of stack, so it is next to interact
+        // with the user.
+        if (addPos < 0) {
+            addPos = mHistory.size();
+        }
+        
+        // If we are not placing the new activity frontmost, we do not want
+        // to deliver the onUserLeaving callback to the actual frontmost
+        // activity
+        if (addPos < NH) {
+            mUserLeaving = false;
+            if (DEBUG_USER_LEAVING) Slog.v(TAG, "startActivity() behind front, mUserLeaving=false");
+        }
+        
+        // Slot the activity into the history stack and proceed
+        mHistory.add(addPos, r);
+        r.inHistory = true;
+        r.frontOfTask = newTask;
+        r.task.numActivities++;
+        if (NH > 0) {
+            // We want to show the starting preview window if we are
+            // switching to a new task, or the next activity's process is
+            // not currently running.
+            boolean showStartingIcon = newTask;
+            ProcessRecord proc = r.app;
+            if (proc == null) {
+                proc = mService.mProcessNames.get(r.processName, r.info.applicationInfo.uid);
+            }
+            if (proc == null || proc.thread == null) {
+                showStartingIcon = true;
+            }
+            if (DEBUG_TRANSITION) Slog.v(TAG,
+                    "Prepare open transition: starting " + r);
+            if ((r.intent.getFlags()&Intent.FLAG_ACTIVITY_NO_ANIMATION) != 0) {
+                mService.mWindowManager.prepareAppTransition(WindowManagerPolicy.TRANSIT_NONE);
+                mNoAnimActivities.add(r);
+            } else if ((r.intent.getFlags()&Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET) != 0) {
+                mService.mWindowManager.prepareAppTransition(
+                        WindowManagerPolicy.TRANSIT_TASK_OPEN);
+                mNoAnimActivities.remove(r);
+            } else {
+                mService.mWindowManager.prepareAppTransition(newTask
+                        ? WindowManagerPolicy.TRANSIT_TASK_OPEN
+                        : WindowManagerPolicy.TRANSIT_ACTIVITY_OPEN);
+                mNoAnimActivities.remove(r);
+            }
+            mService.mWindowManager.addAppToken(
+                    addPos, r, r.task.taskId, r.info.screenOrientation, r.fullscreen);
+            boolean doShow = true;
+            if (newTask) {
+                // Even though this activity is starting fresh, we still need
+                // to reset it to make sure we apply affinities to move any
+                // existing activities from other tasks in to it.
+                // If the caller has requested that the target task be
+                // reset, then do so.
+                if ((r.intent.getFlags()
+                        &Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED) != 0) {
+                    resetTaskIfNeededLocked(r, r);
+                    doShow = topRunningNonDelayedActivityLocked(null) == r;
+                }
+            }
+            if (SHOW_APP_STARTING_PREVIEW && doShow) {
+                // Figure out if we are transitioning from another activity that is
+                // "has the same starting icon" as the next one.  This allows the
+                // window manager to keep the previous window it had previously
+                // created, if it still had one.
+                ActivityRecord prev = mResumedActivity;
+                if (prev != null) {
+                    // We don't want to reuse the previous starting preview if:
+                    // (1) The current activity is in a different task.
+                    if (prev.task != r.task) prev = null;
+                    // (2) The current activity is already displayed.
+                    else if (prev.nowVisible) prev = null;
+                }
+                mService.mWindowManager.setAppStartingWindow(
+                        r, r.packageName, r.theme, r.nonLocalizedLabel,
+                        r.labelRes, r.icon, prev, showStartingIcon);
+            }
+        } else {
+            // If this is the first activity, don't do any fancy animations,
+            // because there is nothing for it to animate on top of.
+            mService.mWindowManager.addAppToken(addPos, r, r.task.taskId,
+                    r.info.screenOrientation, r.fullscreen);
+        }
+        if (VALIDATE_TOKENS) {
+            mService.mWindowManager.validateAppTokens(mHistory);
+        }
+
+        if (doResume) {
+            resumeTopActivityLocked(null);
+        }
+    }
+
+    /**
+     * Perform a reset of the given task, if needed as part of launching it.
+     * Returns the new HistoryRecord at the top of the task.
+     */
+    private final ActivityRecord resetTaskIfNeededLocked(ActivityRecord taskTop,
+            ActivityRecord newActivity) {
+        boolean forceReset = (newActivity.info.flags
+                &ActivityInfo.FLAG_CLEAR_TASK_ON_LAUNCH) != 0;
+        if (taskTop.task.getInactiveDuration() > ACTIVITY_INACTIVE_RESET_TIME) {
+            if ((newActivity.info.flags
+                    &ActivityInfo.FLAG_ALWAYS_RETAIN_TASK_STATE) == 0) {
+                forceReset = true;
+            }
+        }
+        
+        final TaskRecord task = taskTop.task;
+        
+        // We are going to move through the history list so that we can look
+        // at each activity 'target' with 'below' either the interesting
+        // activity immediately below it in the stack or null.
+        ActivityRecord target = null;
+        int targetI = 0;
+        int taskTopI = -1;
+        int replyChainEnd = -1;
+        int lastReparentPos = -1;
+        for (int i=mHistory.size()-1; i>=-1; i--) {
+            ActivityRecord below = i >= 0 ? (ActivityRecord)mHistory.get(i) : null;
+            
+            if (below != null && below.finishing) {
+                continue;
+            }
+            if (target == null) {
+                target = below;
+                targetI = i;
+                // If we were in the middle of a reply chain before this
+                // task, it doesn't appear like the root of the chain wants
+                // anything interesting, so drop it.
+                replyChainEnd = -1;
+                continue;
+            }
+        
+            final int flags = target.info.flags;
+            
+            final boolean finishOnTaskLaunch =
+                (flags&ActivityInfo.FLAG_FINISH_ON_TASK_LAUNCH) != 0;
+            final boolean allowTaskReparenting =
+                (flags&ActivityInfo.FLAG_ALLOW_TASK_REPARENTING) != 0;
+            
+            if (target.task == task) {
+                // We are inside of the task being reset...  we'll either
+                // finish this activity, push it out for another task,
+                // or leave it as-is.  We only do this
+                // for activities that are not the root of the task (since
+                // if we finish the root, we may no longer have the task!).
+                if (taskTopI < 0) {
+                    taskTopI = targetI;
+                }
+                if (below != null && below.task == task) {
+                    final boolean clearWhenTaskReset =
+                            (target.intent.getFlags()
+                                    &Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET) != 0;
+                    if (!finishOnTaskLaunch && !clearWhenTaskReset && target.resultTo != null) {
+                        // If this activity is sending a reply to a previous
+                        // activity, we can't do anything with it now until
+                        // we reach the start of the reply chain.
+                        // XXX note that we are assuming the result is always
+                        // to the previous activity, which is almost always
+                        // the case but we really shouldn't count on.
+                        if (replyChainEnd < 0) {
+                            replyChainEnd = targetI;
+                        }
+                    } else if (!finishOnTaskLaunch && !clearWhenTaskReset && allowTaskReparenting
+                            && target.taskAffinity != null
+                            && !target.taskAffinity.equals(task.affinity)) {
+                        // If this activity has an affinity for another
+                        // task, then we need to move it out of here.  We will
+                        // move it as far out of the way as possible, to the
+                        // bottom of the activity stack.  This also keeps it
+                        // correctly ordered with any activities we previously
+                        // moved.
+                        ActivityRecord p = (ActivityRecord)mHistory.get(0);
+                        if (target.taskAffinity != null
+                                && target.taskAffinity.equals(p.task.affinity)) {
+                            // If the activity currently at the bottom has the
+                            // same task affinity as the one we are moving,
+                            // then merge it into the same task.
+                            target.task = p.task;
+                            if (DEBUG_TASKS) Slog.v(TAG, "Start pushing activity " + target
+                                    + " out to bottom task " + p.task);
+                        } else {
+                            mService.mCurTask++;
+                            if (mService.mCurTask <= 0) {
+                                mService.mCurTask = 1;
+                            }
+                            target.task = new TaskRecord(mService.mCurTask, target.info, null,
+                                    (target.info.flags&ActivityInfo.FLAG_CLEAR_TASK_ON_LAUNCH) != 0);
+                            target.task.affinityIntent = target.intent;
+                            if (DEBUG_TASKS) Slog.v(TAG, "Start pushing activity " + target
+                                    + " out to new task " + target.task);
+                        }
+                        mService.mWindowManager.setAppGroupId(target, task.taskId);
+                        if (replyChainEnd < 0) {
+                            replyChainEnd = targetI;
+                        }
+                        int dstPos = 0;
+                        for (int srcPos=targetI; srcPos<=replyChainEnd; srcPos++) {
+                            p = (ActivityRecord)mHistory.get(srcPos);
+                            if (p.finishing) {
+                                continue;
+                            }
+                            if (DEBUG_TASKS) Slog.v(TAG, "Pushing next activity " + p
+                                    + " out to target's task " + target.task);
+                            task.numActivities--;
+                            p.task = target.task;
+                            target.task.numActivities++;
+                            mHistory.remove(srcPos);
+                            mHistory.add(dstPos, p);
+                            mService.mWindowManager.moveAppToken(dstPos, p);
+                            mService.mWindowManager.setAppGroupId(p, p.task.taskId);
+                            dstPos++;
+                            if (VALIDATE_TOKENS) {
+                                mService.mWindowManager.validateAppTokens(mHistory);
+                            }
+                            i++;
+                        }
+                        if (taskTop == p) {
+                            taskTop = below;
+                        }
+                        if (taskTopI == replyChainEnd) {
+                            taskTopI = -1;
+                        }
+                        replyChainEnd = -1;
+                        if (mMainStack) {
+                            mService.addRecentTaskLocked(target.task);
+                        }
+                    } else if (forceReset || finishOnTaskLaunch
+                            || clearWhenTaskReset) {
+                        // If the activity should just be removed -- either
+                        // because it asks for it, or the task should be
+                        // cleared -- then finish it and anything that is
+                        // part of its reply chain.
+                        if (clearWhenTaskReset) {
+                            // In this case, we want to finish this activity
+                            // and everything above it, so be sneaky and pretend
+                            // like these are all in the reply chain.
+                            replyChainEnd = targetI+1;
+                            while (replyChainEnd < mHistory.size() &&
+                                    ((ActivityRecord)mHistory.get(
+                                                replyChainEnd)).task == task) {
+                                replyChainEnd++;
+                            }
+                            replyChainEnd--;
+                        } else if (replyChainEnd < 0) {
+                            replyChainEnd = targetI;
+                        }
+                        ActivityRecord p = null;
+                        for (int srcPos=targetI; srcPos<=replyChainEnd; srcPos++) {
+                            p = (ActivityRecord)mHistory.get(srcPos);
+                            if (p.finishing) {
+                                continue;
+                            }
+                            if (finishActivityLocked(p, srcPos,
+                                    Activity.RESULT_CANCELED, null, "reset")) {
+                                replyChainEnd--;
+                                srcPos--;
+                            }
+                        }
+                        if (taskTop == p) {
+                            taskTop = below;
+                        }
+                        if (taskTopI == replyChainEnd) {
+                            taskTopI = -1;
+                        }
+                        replyChainEnd = -1;
+                    } else {
+                        // If we were in the middle of a chain, well the
+                        // activity that started it all doesn't want anything
+                        // special, so leave it all as-is.
+                        replyChainEnd = -1;
+                    }
+                } else {
+                    // Reached the bottom of the task -- any reply chain
+                    // should be left as-is.
+                    replyChainEnd = -1;
+                }
+                
+            } else if (target.resultTo != null) {
+                // If this activity is sending a reply to a previous
+                // activity, we can't do anything with it now until
+                // we reach the start of the reply chain.
+                // XXX note that we are assuming the result is always
+                // to the previous activity, which is almost always
+                // the case but we really shouldn't count on.
+                if (replyChainEnd < 0) {
+                    replyChainEnd = targetI;
+                }
+
+            } else if (taskTopI >= 0 && allowTaskReparenting
+                    && task.affinity != null
+                    && task.affinity.equals(target.taskAffinity)) {
+                // We are inside of another task...  if this activity has
+                // an affinity for our task, then either remove it if we are
+                // clearing or move it over to our task.  Note that
+                // we currently punt on the case where we are resetting a
+                // task that is not at the top but who has activities above
+                // with an affinity to it...  this is really not a normal
+                // case, and we will need to later pull that task to the front
+                // and usually at that point we will do the reset and pick
+                // up those remaining activities.  (This only happens if
+                // someone starts an activity in a new task from an activity
+                // in a task that is not currently on top.)
+                if (forceReset || finishOnTaskLaunch) {
+                    if (replyChainEnd < 0) {
+                        replyChainEnd = targetI;
+                    }
+                    ActivityRecord p = null;
+                    for (int srcPos=targetI; srcPos<=replyChainEnd; srcPos++) {
+                        p = (ActivityRecord)mHistory.get(srcPos);
+                        if (p.finishing) {
+                            continue;
+                        }
+                        if (finishActivityLocked(p, srcPos,
+                                Activity.RESULT_CANCELED, null, "reset")) {
+                            taskTopI--;
+                            lastReparentPos--;
+                            replyChainEnd--;
+                            srcPos--;
+                        }
+                    }
+                    replyChainEnd = -1;
+                } else {
+                    if (replyChainEnd < 0) {
+                        replyChainEnd = targetI;
+                    }
+                    for (int srcPos=replyChainEnd; srcPos>=targetI; srcPos--) {
+                        ActivityRecord p = (ActivityRecord)mHistory.get(srcPos);
+                        if (p.finishing) {
+                            continue;
+                        }
+                        if (lastReparentPos < 0) {
+                            lastReparentPos = taskTopI;
+                            taskTop = p;
+                        } else {
+                            lastReparentPos--;
+                        }
+                        mHistory.remove(srcPos);
+                        p.task.numActivities--;
+                        p.task = task;
+                        mHistory.add(lastReparentPos, p);
+                        if (DEBUG_TASKS) Slog.v(TAG, "Pulling activity " + p
+                                + " in to resetting task " + task);
+                        task.numActivities++;
+                        mService.mWindowManager.moveAppToken(lastReparentPos, p);
+                        mService.mWindowManager.setAppGroupId(p, p.task.taskId);
+                        if (VALIDATE_TOKENS) {
+                            mService.mWindowManager.validateAppTokens(mHistory);
+                        }
+                    }
+                    replyChainEnd = -1;
+                    
+                    // Now we've moved it in to place...  but what if this is
+                    // a singleTop activity and we have put it on top of another
+                    // instance of the same activity?  Then we drop the instance
+                    // below so it remains singleTop.
+                    if (target.info.launchMode == ActivityInfo.LAUNCH_SINGLE_TOP) {
+                        for (int j=lastReparentPos-1; j>=0; j--) {
+                            ActivityRecord p = (ActivityRecord)mHistory.get(j);
+                            if (p.finishing) {
+                                continue;
+                            }
+                            if (p.intent.getComponent().equals(target.intent.getComponent())) {
+                                if (finishActivityLocked(p, j,
+                                        Activity.RESULT_CANCELED, null, "replace")) {
+                                    taskTopI--;
+                                    lastReparentPos--;
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+            
+            target = below;
+            targetI = i;
+        }
+        
+        return taskTop;
+    }
+    
+    /**
+     * Perform clear operation as requested by
+     * {@link Intent#FLAG_ACTIVITY_CLEAR_TOP}: search from the top of the
+     * stack to the given task, then look for
+     * an instance of that activity in the stack and, if found, finish all
+     * activities on top of it and return the instance.
+     *
+     * @param newR Description of the new activity being started.
+     * @return Returns the old activity that should be continue to be used,
+     * or null if none was found.
+     */
+    private final ActivityRecord performClearTaskLocked(int taskId,
+            ActivityRecord newR, int launchFlags, boolean doClear) {
+        int i = mHistory.size();
+        
+        // First find the requested task.
+        while (i > 0) {
+            i--;
+            ActivityRecord r = (ActivityRecord)mHistory.get(i);
+            if (r.task.taskId == taskId) {
+                i++;
+                break;
+            }
+        }
+        
+        // Now clear it.
+        while (i > 0) {
+            i--;
+            ActivityRecord r = (ActivityRecord)mHistory.get(i);
+            if (r.finishing) {
+                continue;
+            }
+            if (r.task.taskId != taskId) {
+                return null;
+            }
+            if (r.realActivity.equals(newR.realActivity)) {
+                // Here it is!  Now finish everything in front...
+                ActivityRecord ret = r;
+                if (doClear) {
+                    while (i < (mHistory.size()-1)) {
+                        i++;
+                        r = (ActivityRecord)mHistory.get(i);
+                        if (r.finishing) {
+                            continue;
+                        }
+                        if (finishActivityLocked(r, i, Activity.RESULT_CANCELED,
+                                null, "clear")) {
+                            i--;
+                        }
+                    }
+                }
+                
+                // Finally, if this is a normal launch mode (that is, not
+                // expecting onNewIntent()), then we will finish the current
+                // instance of the activity so a new fresh one can be started.
+                if (ret.launchMode == ActivityInfo.LAUNCH_MULTIPLE
+                        && (launchFlags&Intent.FLAG_ACTIVITY_SINGLE_TOP) == 0) {
+                    if (!ret.finishing) {
+                        int index = indexOfTokenLocked(ret);
+                        if (index >= 0) {
+                            finishActivityLocked(ret, index, Activity.RESULT_CANCELED,
+                                    null, "clear");
+                        }
+                        return null;
+                    }
+                }
+                
+                return ret;
+            }
+        }
+
+        return null;
+    }
+
+    /**
+     * Find the activity in the history stack within the given task.  Returns
+     * the index within the history at which it's found, or < 0 if not found.
+     */
+    private final int findActivityInHistoryLocked(ActivityRecord r, int task) {
+        int i = mHistory.size();
+        while (i > 0) {
+            i--;
+            ActivityRecord candidate = (ActivityRecord)mHistory.get(i);
+            if (candidate.task.taskId != task) {
+                break;
+            }
+            if (candidate.realActivity.equals(r.realActivity)) {
+                return i;
+            }
+        }
+
+        return -1;
+    }
+
+    /**
+     * Reorder the history stack so that the activity at the given index is
+     * brought to the front.
+     */
+    private final ActivityRecord moveActivityToFrontLocked(int where) {
+        ActivityRecord newTop = (ActivityRecord)mHistory.remove(where);
+        int top = mHistory.size();
+        ActivityRecord oldTop = (ActivityRecord)mHistory.get(top-1);
+        mHistory.add(top, newTop);
+        oldTop.frontOfTask = false;
+        newTop.frontOfTask = true;
+        return newTop;
+    }
+
+    final int startActivityLocked(IApplicationThread caller,
+            Intent intent, String resolvedType,
+            Uri[] grantedUriPermissions,
+            int grantedMode, ActivityInfo aInfo, IBinder resultTo,
+            String resultWho, int requestCode,
+            int callingPid, int callingUid, boolean onlyIfNeeded,
+            boolean componentSpecified) {
+        Slog.i(TAG, "Starting activity: " + intent);
+
+        ActivityRecord sourceRecord = null;
+        ActivityRecord resultRecord = null;
+        if (resultTo != null) {
+            int index = indexOfTokenLocked(resultTo);
+            if (DEBUG_RESULTS) Slog.v(
+                TAG, "Sending result to " + resultTo + " (index " + index + ")");
+            if (index >= 0) {
+                sourceRecord = (ActivityRecord)mHistory.get(index);
+                if (requestCode >= 0 && !sourceRecord.finishing) {
+                    resultRecord = sourceRecord;
+                }
+            }
+        }
+
+        int launchFlags = intent.getFlags();
+
+        if ((launchFlags&Intent.FLAG_ACTIVITY_FORWARD_RESULT) != 0
+                && sourceRecord != null) {
+            // Transfer the result target from the source activity to the new
+            // one being started, including any failures.
+            if (requestCode >= 0) {
+                return START_FORWARD_AND_REQUEST_CONFLICT;
+            }
+            resultRecord = sourceRecord.resultTo;
+            resultWho = sourceRecord.resultWho;
+            requestCode = sourceRecord.requestCode;
+            sourceRecord.resultTo = null;
+            if (resultRecord != null) {
+                resultRecord.removeResultsLocked(
+                    sourceRecord, resultWho, requestCode);
+            }
+        }
+
+        int err = START_SUCCESS;
+
+        if (intent.getComponent() == null) {
+            // We couldn't find a class that can handle the given Intent.
+            // That's the end of that!
+            err = START_INTENT_NOT_RESOLVED;
+        }
+
+        if (err == START_SUCCESS && aInfo == null) {
+            // We couldn't find the specific class specified in the Intent.
+            // Also the end of the line.
+            err = START_CLASS_NOT_FOUND;
+        }
+
+        ProcessRecord callerApp = null;
+        if (err == START_SUCCESS && caller != null) {
+            callerApp = mService.getRecordForAppLocked(caller);
+            if (callerApp != null) {
+                callingPid = callerApp.pid;
+                callingUid = callerApp.info.uid;
+            } else {
+                Slog.w(TAG, "Unable to find app for caller " + caller
+                      + " (pid=" + callingPid + ") when starting: "
+                      + intent.toString());
+                err = START_PERMISSION_DENIED;
+            }
+        }
+
+        if (err != START_SUCCESS) {
+            if (resultRecord != null) {
+                sendActivityResultLocked(-1,
+                    resultRecord, resultWho, requestCode,
+                    Activity.RESULT_CANCELED, null);
+            }
+            return err;
+        }
+
+        final int perm = mService.checkComponentPermission(aInfo.permission, callingPid,
+                callingUid, aInfo.exported ? -1 : aInfo.applicationInfo.uid);
+        if (perm != PackageManager.PERMISSION_GRANTED) {
+            if (resultRecord != null) {
+                sendActivityResultLocked(-1,
+                    resultRecord, resultWho, requestCode,
+                    Activity.RESULT_CANCELED, null);
+            }
+            String msg = "Permission Denial: starting " + intent.toString()
+                    + " from " + callerApp + " (pid=" + callingPid
+                    + ", uid=" + callingUid + ")"
+                    + " requires " + aInfo.permission;
+            Slog.w(TAG, msg);
+            throw new SecurityException(msg);
+        }
+
+        if (mMainStack) {
+            if (mService.mController != null) {
+                boolean abort = false;
+                try {
+                    // The Intent we give to the watcher has the extra data
+                    // stripped off, since it can contain private information.
+                    Intent watchIntent = intent.cloneFilter();
+                    abort = !mService.mController.activityStarting(watchIntent,
+                            aInfo.applicationInfo.packageName);
+                } catch (RemoteException e) {
+                    mService.mController = null;
+                }
+    
+                if (abort) {
+                    if (resultRecord != null) {
+                        sendActivityResultLocked(-1,
+                            resultRecord, resultWho, requestCode,
+                            Activity.RESULT_CANCELED, null);
+                    }
+                    // We pretend to the caller that it was really started, but
+                    // they will just get a cancel result.
+                    return START_SUCCESS;
+                }
+            }
+        }
+        
+        ActivityRecord r = new ActivityRecord(mService, this, callerApp, callingUid,
+                intent, resolvedType, aInfo, mService.mConfiguration,
+                resultRecord, resultWho, requestCode, componentSpecified);
+
+        if (mMainStack) {
+            if (mResumedActivity == null
+                    || mResumedActivity.info.applicationInfo.uid != callingUid) {
+                if (!mService.checkAppSwitchAllowedLocked(callingPid, callingUid, "Activity start")) {
+                    PendingActivityLaunch pal = new PendingActivityLaunch();
+                    pal.r = r;
+                    pal.sourceRecord = sourceRecord;
+                    pal.grantedUriPermissions = grantedUriPermissions;
+                    pal.grantedMode = grantedMode;
+                    pal.onlyIfNeeded = onlyIfNeeded;
+                    mService.mPendingActivityLaunches.add(pal);
+                    return START_SWITCHES_CANCELED;
+                }
+            }
+        
+            if (mService.mDidAppSwitch) {
+                // This is the second allowed switch since we stopped switches,
+                // so now just generally allow switches.  Use case: user presses
+                // home (switches disabled, switch to home, mDidAppSwitch now true);
+                // user taps a home icon (coming from home so allowed, we hit here
+                // and now allow anyone to switch again).
+                mService.mAppSwitchesAllowedTime = 0;
+            } else {
+                mService.mDidAppSwitch = true;
+            }
+         
+            mService.doPendingActivityLaunchesLocked(false);
+        }
+        
+        return startActivityUncheckedLocked(r, sourceRecord,
+                grantedUriPermissions, grantedMode, onlyIfNeeded, true);
+    }
+  
+    final int startActivityUncheckedLocked(ActivityRecord r,
+            ActivityRecord sourceRecord, Uri[] grantedUriPermissions,
+            int grantedMode, boolean onlyIfNeeded, boolean doResume) {
+        final Intent intent = r.intent;
+        final int callingUid = r.launchedFromUid;
+        
+        int launchFlags = intent.getFlags();
+        
+        // We'll invoke onUserLeaving before onPause only if the launching
+        // activity did not explicitly state that this is an automated launch.
+        mUserLeaving = (launchFlags&Intent.FLAG_ACTIVITY_NO_USER_ACTION) == 0;
+        if (DEBUG_USER_LEAVING) Slog.v(TAG,
+                "startActivity() => mUserLeaving=" + mUserLeaving);
+        
+        // If the caller has asked not to resume at this point, we make note
+        // of this in the record so that we can skip it when trying to find
+        // the top running activity.
+        if (!doResume) {
+            r.delayedResume = true;
+        }
+        
+        ActivityRecord notTop = (launchFlags&Intent.FLAG_ACTIVITY_PREVIOUS_IS_TOP)
+                != 0 ? r : null;
+
+        // If the onlyIfNeeded flag is set, then we can do this if the activity
+        // being launched is the same as the one making the call...  or, as
+        // a special case, if we do not know the caller then we count the
+        // current top activity as the caller.
+        if (onlyIfNeeded) {
+            ActivityRecord checkedCaller = sourceRecord;
+            if (checkedCaller == null) {
+                checkedCaller = topRunningNonDelayedActivityLocked(notTop);
+            }
+            if (!checkedCaller.realActivity.equals(r.realActivity)) {
+                // Caller is not the same as launcher, so always needed.
+                onlyIfNeeded = false;
+            }
+        }
+
+        if (grantedUriPermissions != null && callingUid > 0) {
+            for (int i=0; i<grantedUriPermissions.length; i++) {
+                mService.grantUriPermissionLocked(callingUid, r.packageName,
+                        grantedUriPermissions[i], grantedMode, r);
+            }
+        }
+
+        mService.grantUriPermissionFromIntentLocked(callingUid, r.packageName,
+                intent, r);
+
+        if (sourceRecord == null) {
+            // This activity is not being started from another...  in this
+            // case we -always- start a new task.
+            if ((launchFlags&Intent.FLAG_ACTIVITY_NEW_TASK) == 0) {
+                Slog.w(TAG, "startActivity called from non-Activity context; forcing Intent.FLAG_ACTIVITY_NEW_TASK for: "
+                      + intent);
+                launchFlags |= Intent.FLAG_ACTIVITY_NEW_TASK;
+            }
+        } else if (sourceRecord.launchMode == ActivityInfo.LAUNCH_SINGLE_INSTANCE) {
+            // The original activity who is starting us is running as a single
+            // instance...  this new activity it is starting must go on its
+            // own task.
+            launchFlags |= Intent.FLAG_ACTIVITY_NEW_TASK;
+        } else if (r.launchMode == ActivityInfo.LAUNCH_SINGLE_INSTANCE
+                || r.launchMode == ActivityInfo.LAUNCH_SINGLE_TASK) {
+            // The activity being started is a single instance...  it always
+            // gets launched into its own task.
+            launchFlags |= Intent.FLAG_ACTIVITY_NEW_TASK;
+        }
+
+        if (r.resultTo != null && (launchFlags&Intent.FLAG_ACTIVITY_NEW_TASK) != 0) {
+            // For whatever reason this activity is being launched into a new
+            // task...  yet the caller has requested a result back.  Well, that
+            // is pretty messed up, so instead immediately send back a cancel
+            // and let the new task continue launched as normal without a
+            // dependency on its originator.
+            Slog.w(TAG, "Activity is launching as a new task, so cancelling activity result.");
+            sendActivityResultLocked(-1,
+                    r.resultTo, r.resultWho, r.requestCode,
+                Activity.RESULT_CANCELED, null);
+            r.resultTo = null;
+        }
+
+        boolean addingToTask = false;
+        if (((launchFlags&Intent.FLAG_ACTIVITY_NEW_TASK) != 0 &&
+                (launchFlags&Intent.FLAG_ACTIVITY_MULTIPLE_TASK) == 0)
+                || r.launchMode == ActivityInfo.LAUNCH_SINGLE_TASK
+                || r.launchMode == ActivityInfo.LAUNCH_SINGLE_INSTANCE) {
+            // If bring to front is requested, and no result is requested, and
+            // we can find a task that was started with this same
+            // component, then instead of launching bring that one to the front.
+            if (r.resultTo == null) {
+                // See if there is a task to bring to the front.  If this is
+                // a SINGLE_INSTANCE activity, there can be one and only one
+                // instance of it in the history, and it is always in its own
+                // unique task, so we do a special search.
+                ActivityRecord taskTop = r.launchMode != ActivityInfo.LAUNCH_SINGLE_INSTANCE
+                        ? findTaskLocked(intent, r.info)
+                        : findActivityLocked(intent, r.info);
+                if (taskTop != null) {
+                    if (taskTop.task.intent == null) {
+                        // This task was started because of movement of
+                        // the activity based on affinity...  now that we
+                        // are actually launching it, we can assign the
+                        // base intent.
+                        taskTop.task.setIntent(intent, r.info);
+                    }
+                    // If the target task is not in the front, then we need
+                    // to bring it to the front...  except...  well, with
+                    // SINGLE_TASK_LAUNCH it's not entirely clear.  We'd like
+                    // to have the same behavior as if a new instance was
+                    // being started, which means not bringing it to the front
+                    // if the caller is not itself in the front.
+                    ActivityRecord curTop = topRunningNonDelayedActivityLocked(notTop);
+                    if (curTop.task != taskTop.task) {
+                        r.intent.addFlags(Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT);
+                        boolean callerAtFront = sourceRecord == null
+                                || curTop.task == sourceRecord.task;
+                        if (callerAtFront) {
+                            // We really do want to push this one into the
+                            // user's face, right now.
+                            moveTaskToFrontLocked(taskTop.task, r);
+                        }
+                    }
+                    // If the caller has requested that the target task be
+                    // reset, then do so.
+                    if ((launchFlags&Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED) != 0) {
+                        taskTop = resetTaskIfNeededLocked(taskTop, r);
+                    }
+                    if (onlyIfNeeded) {
+                        // We don't need to start a new activity, and
+                        // the client said not to do anything if that
+                        // is the case, so this is it!  And for paranoia, make
+                        // sure we have correctly resumed the top activity.
+                        if (doResume) {
+                            resumeTopActivityLocked(null);
+                        }
+                        return START_RETURN_INTENT_TO_CALLER;
+                    }
+                    if ((launchFlags&Intent.FLAG_ACTIVITY_CLEAR_TOP) != 0
+                            || r.launchMode == ActivityInfo.LAUNCH_SINGLE_TASK
+                            || r.launchMode == ActivityInfo.LAUNCH_SINGLE_INSTANCE) {
+                        // In this situation we want to remove all activities
+                        // from the task up to the one being started.  In most
+                        // cases this means we are resetting the task to its
+                        // initial state.
+                        ActivityRecord top = performClearTaskLocked(
+                                taskTop.task.taskId, r, launchFlags, true);
+                        if (top != null) {
+                            if (top.frontOfTask) {
+                                // Activity aliases may mean we use different
+                                // intents for the top activity, so make sure
+                                // the task now has the identity of the new
+                                // intent.
+                                top.task.setIntent(r.intent, r.info);
+                            }
+                            logStartActivity(EventLogTags.AM_NEW_INTENT, r, top.task);
+                            top.deliverNewIntentLocked(r.intent);
+                        } else {
+                            // A special case: we need to
+                            // start the activity because it is not currently
+                            // running, and the caller has asked to clear the
+                            // current task to have this activity at the top.
+                            addingToTask = true;
+                            // Now pretend like this activity is being started
+                            // by the top of its task, so it is put in the
+                            // right place.
+                            sourceRecord = taskTop;
+                        }
+                    } else if (r.realActivity.equals(taskTop.task.realActivity)) {
+                        // In this case the top activity on the task is the
+                        // same as the one being launched, so we take that
+                        // as a request to bring the task to the foreground.
+                        // If the top activity in the task is the root
+                        // activity, deliver this new intent to it if it
+                        // desires.
+                        if ((launchFlags&Intent.FLAG_ACTIVITY_SINGLE_TOP) != 0
+                                && taskTop.realActivity.equals(r.realActivity)) {
+                            logStartActivity(EventLogTags.AM_NEW_INTENT, r, taskTop.task);
+                            if (taskTop.frontOfTask) {
+                                taskTop.task.setIntent(r.intent, r.info);
+                            }
+                            taskTop.deliverNewIntentLocked(r.intent);
+                        } else if (!r.intent.filterEquals(taskTop.task.intent)) {
+                            // In this case we are launching the root activity
+                            // of the task, but with a different intent.  We
+                            // should start a new instance on top.
+                            addingToTask = true;
+                            sourceRecord = taskTop;
+                        }
+                    } else if ((launchFlags&Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED) == 0) {
+                        // In this case an activity is being launched in to an
+                        // existing task, without resetting that task.  This
+                        // is typically the situation of launching an activity
+                        // from a notification or shortcut.  We want to place
+                        // the new activity on top of the current task.
+                        addingToTask = true;
+                        sourceRecord = taskTop;
+                    } else if (!taskTop.task.rootWasReset) {
+                        // In this case we are launching in to an existing task
+                        // that has not yet been started from its front door.
+                        // The current task has been brought to the front.
+                        // Ideally, we'd probably like to place this new task
+                        // at the bottom of its stack, but that's a little hard
+                        // to do with the current organization of the code so
+                        // for now we'll just drop it.
+                        taskTop.task.setIntent(r.intent, r.info);
+                    }
+                    if (!addingToTask) {
+                        // We didn't do anything...  but it was needed (a.k.a., client
+                        // don't use that intent!)  And for paranoia, make
+                        // sure we have correctly resumed the top activity.
+                        if (doResume) {
+                            resumeTopActivityLocked(null);
+                        }
+                        return START_TASK_TO_FRONT;
+                    }
+                }
+            }
+        }
+
+        //String uri = r.intent.toURI();
+        //Intent intent2 = new Intent(uri);
+        //Slog.i(TAG, "Given intent: " + r.intent);
+        //Slog.i(TAG, "URI is: " + uri);
+        //Slog.i(TAG, "To intent: " + intent2);
+
+        if (r.packageName != null) {
+            // If the activity being launched is the same as the one currently
+            // at the top, then we need to check if it should only be launched
+            // once.
+            ActivityRecord top = topRunningNonDelayedActivityLocked(notTop);
+            if (top != null && r.resultTo == null) {
+                if (top.realActivity.equals(r.realActivity)) {
+                    if (top.app != null && top.app.thread != null) {
+                        if ((launchFlags&Intent.FLAG_ACTIVITY_SINGLE_TOP) != 0
+                            || r.launchMode == ActivityInfo.LAUNCH_SINGLE_TOP
+                            || r.launchMode == ActivityInfo.LAUNCH_SINGLE_TASK) {
+                            logStartActivity(EventLogTags.AM_NEW_INTENT, top, top.task);
+                            // For paranoia, make sure we have correctly
+                            // resumed the top activity.
+                            if (doResume) {
+                                resumeTopActivityLocked(null);
+                            }
+                            if (onlyIfNeeded) {
+                                // We don't need to start a new activity, and
+                                // the client said not to do anything if that
+                                // is the case, so this is it!
+                                return START_RETURN_INTENT_TO_CALLER;
+                            }
+                            top.deliverNewIntentLocked(r.intent);
+                            return START_DELIVERED_TO_TOP;
+                        }
+                    }
+                }
+            }
+
+        } else {
+            if (r.resultTo != null) {
+                sendActivityResultLocked(-1,
+                        r.resultTo, r.resultWho, r.requestCode,
+                    Activity.RESULT_CANCELED, null);
+            }
+            return START_CLASS_NOT_FOUND;
+        }
+
+        boolean newTask = false;
+
+        // Should this be considered a new task?
+        if (r.resultTo == null && !addingToTask
+                && (launchFlags&Intent.FLAG_ACTIVITY_NEW_TASK) != 0) {
+            // todo: should do better management of integers.
+            mService.mCurTask++;
+            if (mService.mCurTask <= 0) {
+                mService.mCurTask = 1;
+            }
+            r.task = new TaskRecord(mService.mCurTask, r.info, intent,
+                    (r.info.flags&ActivityInfo.FLAG_CLEAR_TASK_ON_LAUNCH) != 0);
+            if (DEBUG_TASKS) Slog.v(TAG, "Starting new activity " + r
+                    + " in new task " + r.task);
+            newTask = true;
+            if (mMainStack) {
+                mService.addRecentTaskLocked(r.task);
+            }
+            
+        } else if (sourceRecord != null) {
+            if (!addingToTask &&
+                    (launchFlags&Intent.FLAG_ACTIVITY_CLEAR_TOP) != 0) {
+                // In this case, we are adding the activity to an existing
+                // task, but the caller has asked to clear that task if the
+                // activity is already running.
+                ActivityRecord top = performClearTaskLocked(
+                        sourceRecord.task.taskId, r, launchFlags, true);
+                if (top != null) {
+                    logStartActivity(EventLogTags.AM_NEW_INTENT, r, top.task);
+                    top.deliverNewIntentLocked(r.intent);
+                    // For paranoia, make sure we have correctly
+                    // resumed the top activity.
+                    if (doResume) {
+                        resumeTopActivityLocked(null);
+                    }
+                    return START_DELIVERED_TO_TOP;
+                }
+            } else if (!addingToTask &&
+                    (launchFlags&Intent.FLAG_ACTIVITY_REORDER_TO_FRONT) != 0) {
+                // In this case, we are launching an activity in our own task
+                // that may already be running somewhere in the history, and
+                // we want to shuffle it to the front of the stack if so.
+                int where = findActivityInHistoryLocked(r, sourceRecord.task.taskId);
+                if (where >= 0) {
+                    ActivityRecord top = moveActivityToFrontLocked(where);
+                    logStartActivity(EventLogTags.AM_NEW_INTENT, r, top.task);
+                    top.deliverNewIntentLocked(r.intent);
+                    if (doResume) {
+                        resumeTopActivityLocked(null);
+                    }
+                    return START_DELIVERED_TO_TOP;
+                }
+            }
+            // An existing activity is starting this new activity, so we want
+            // to keep the new one in the same task as the one that is starting
+            // it.
+            r.task = sourceRecord.task;
+            if (DEBUG_TASKS) Slog.v(TAG, "Starting new activity " + r
+                    + " in existing task " + r.task);
+
+        } else {
+            // This not being started from an existing activity, and not part
+            // of a new task...  just put it in the top task, though these days
+            // this case should never happen.
+            final int N = mHistory.size();
+            ActivityRecord prev =
+                N > 0 ? (ActivityRecord)mHistory.get(N-1) : null;
+            r.task = prev != null
+                ? prev.task
+                : new TaskRecord(mService.mCurTask, r.info, intent,
+                        (r.info.flags&ActivityInfo.FLAG_CLEAR_TASK_ON_LAUNCH) != 0);
+            if (DEBUG_TASKS) Slog.v(TAG, "Starting new activity " + r
+                    + " in new guessed " + r.task);
+        }
+        if (newTask) {
+            EventLog.writeEvent(EventLogTags.AM_CREATE_TASK, r.task.taskId);
+        }
+        logStartActivity(EventLogTags.AM_CREATE_ACTIVITY, r, r.task);
+        startActivityLocked(r, newTask, doResume);
+        return START_SUCCESS;
+    }
+
+    final int startActivityMayWait(IApplicationThread caller,
+            Intent intent, String resolvedType, Uri[] grantedUriPermissions,
+            int grantedMode, IBinder resultTo,
+            String resultWho, int requestCode, boolean onlyIfNeeded,
+            boolean debug, WaitResult outResult, Configuration config) {
+        // Refuse possible leaked file descriptors
+        if (intent != null && intent.hasFileDescriptors()) {
+            throw new IllegalArgumentException("File descriptors passed in Intent");
+        }
+
+        boolean componentSpecified = intent.getComponent() != null;
+        
+        // Don't modify the client's object!
+        intent = new Intent(intent);
+
+        // Collect information about the target of the Intent.
+        ActivityInfo aInfo;
+        try {
+            ResolveInfo rInfo =
+                AppGlobals.getPackageManager().resolveIntent(
+                        intent, resolvedType,
+                        PackageManager.MATCH_DEFAULT_ONLY
+                        | ActivityManagerService.STOCK_PM_FLAGS);
+            aInfo = rInfo != null ? rInfo.activityInfo : null;
+        } catch (RemoteException e) {
+            aInfo = null;
+        }
+
+        if (aInfo != null) {
+            // Store the found target back into the intent, because now that
+            // we have it we never want to do this again.  For example, if the
+            // user navigates back to this point in the history, we should
+            // always restart the exact same activity.
+            intent.setComponent(new ComponentName(
+                    aInfo.applicationInfo.packageName, aInfo.name));
+
+            // Don't debug things in the system process
+            if (debug) {
+                if (!aInfo.processName.equals("system")) {
+                    mService.setDebugApp(aInfo.processName, true, false);
+                }
+            }
+        }
+
+        synchronized (mService) {
+            int callingPid;
+            int callingUid;
+            if (caller == null) {
+                callingPid = Binder.getCallingPid();
+                callingUid = Binder.getCallingUid();
+            } else {
+                callingPid = callingUid = -1;
+            }
+            
+            mConfigWillChange = config != null
+                    && mService.mConfiguration.diff(config) != 0;
+            if (DEBUG_CONFIGURATION) Slog.v(TAG,
+                    "Starting activity when config will change = " + mConfigWillChange);
+            
+            final long origId = Binder.clearCallingIdentity();
+            
+            if (mMainStack && aInfo != null &&
+                    (aInfo.applicationInfo.flags&ApplicationInfo.FLAG_HEAVY_WEIGHT) != 0) {
+                // This may be a heavy-weight process!  Check to see if we already
+                // have another, different heavy-weight process running.
+                if (aInfo.processName.equals(aInfo.applicationInfo.packageName)) {
+                    if (mService.mHeavyWeightProcess != null &&
+                            (mService.mHeavyWeightProcess.info.uid != aInfo.applicationInfo.uid ||
+                            !mService.mHeavyWeightProcess.processName.equals(aInfo.processName))) {
+                        int realCallingPid = callingPid;
+                        int realCallingUid = callingUid;
+                        if (caller != null) {
+                            ProcessRecord callerApp = mService.getRecordForAppLocked(caller);
+                            if (callerApp != null) {
+                                realCallingPid = callerApp.pid;
+                                realCallingUid = callerApp.info.uid;
+                            } else {
+                                Slog.w(TAG, "Unable to find app for caller " + caller
+                                      + " (pid=" + realCallingPid + ") when starting: "
+                                      + intent.toString());
+                                return START_PERMISSION_DENIED;
+                            }
+                        }
+                        
+                        IIntentSender target = mService.getIntentSenderLocked(
+                                IActivityManager.INTENT_SENDER_ACTIVITY, "android",
+                                realCallingUid, null, null, 0, intent,
+                                resolvedType, PendingIntent.FLAG_CANCEL_CURRENT
+                                | PendingIntent.FLAG_ONE_SHOT);
+                        
+                        Intent newIntent = new Intent();
+                        if (requestCode >= 0) {
+                            // Caller is requesting a result.
+                            newIntent.putExtra(HeavyWeightSwitcherActivity.KEY_HAS_RESULT, true);
+                        }
+                        newIntent.putExtra(HeavyWeightSwitcherActivity.KEY_INTENT,
+                                new IntentSender(target));
+                        if (mService.mHeavyWeightProcess.activities.size() > 0) {
+                            ActivityRecord hist = mService.mHeavyWeightProcess.activities.get(0);
+                            newIntent.putExtra(HeavyWeightSwitcherActivity.KEY_CUR_APP,
+                                    hist.packageName);
+                            newIntent.putExtra(HeavyWeightSwitcherActivity.KEY_CUR_TASK,
+                                    hist.task.taskId);
+                        }
+                        newIntent.putExtra(HeavyWeightSwitcherActivity.KEY_NEW_APP,
+                                aInfo.packageName);
+                        newIntent.setFlags(intent.getFlags());
+                        newIntent.setClassName("android",
+                                HeavyWeightSwitcherActivity.class.getName());
+                        intent = newIntent;
+                        resolvedType = null;
+                        caller = null;
+                        callingUid = Binder.getCallingUid();
+                        callingPid = Binder.getCallingPid();
+                        componentSpecified = true;
+                        try {
+                            ResolveInfo rInfo =
+                                AppGlobals.getPackageManager().resolveIntent(
+                                        intent, null,
+                                        PackageManager.MATCH_DEFAULT_ONLY
+                                        | ActivityManagerService.STOCK_PM_FLAGS);
+                            aInfo = rInfo != null ? rInfo.activityInfo : null;
+                        } catch (RemoteException e) {
+                            aInfo = null;
+                        }
+                    }
+                }
+            }
+            
+            int res = startActivityLocked(caller, intent, resolvedType,
+                    grantedUriPermissions, grantedMode, aInfo,
+                    resultTo, resultWho, requestCode, callingPid, callingUid,
+                    onlyIfNeeded, componentSpecified);
+            
+            if (mConfigWillChange && mMainStack) {
+                // If the caller also wants to switch to a new configuration,
+                // do so now.  This allows a clean switch, as we are waiting
+                // for the current activity to pause (so we will not destroy
+                // it), and have not yet started the next activity.
+                mService.enforceCallingPermission(android.Manifest.permission.CHANGE_CONFIGURATION,
+                        "updateConfiguration()");
+                mConfigWillChange = false;
+                if (DEBUG_CONFIGURATION) Slog.v(TAG,
+                        "Updating to new configuration after starting activity.");
+                mService.updateConfigurationLocked(config, null);
+            }
+            
+            Binder.restoreCallingIdentity(origId);
+            
+            if (outResult != null) {
+                outResult.result = res;
+                if (res == IActivityManager.START_SUCCESS) {
+                    mWaitingActivityLaunched.add(outResult);
+                    do {
+                        try {
+                            wait();
+                        } catch (InterruptedException e) {
+                        }
+                    } while (!outResult.timeout && outResult.who == null);
+                } else if (res == IActivityManager.START_TASK_TO_FRONT) {
+                    ActivityRecord r = this.topRunningActivityLocked(null);
+                    if (r.nowVisible) {
+                        outResult.timeout = false;
+                        outResult.who = new ComponentName(r.info.packageName, r.info.name);
+                        outResult.totalTime = 0;
+                        outResult.thisTime = 0;
+                    } else {
+                        outResult.thisTime = SystemClock.uptimeMillis();
+                        mWaitingActivityVisible.add(outResult);
+                        do {
+                            try {
+                                wait();
+                            } catch (InterruptedException e) {
+                            }
+                        } while (!outResult.timeout && outResult.who == null);
+                    }
+                }
+            }
+            
+            return res;
+        }
+    }
+    
+    void reportActivityLaunchedLocked(boolean timeout, ActivityRecord r,
+            long thisTime, long totalTime) {
+        for (int i=mWaitingActivityLaunched.size()-1; i>=0; i--) {
+            WaitResult w = mWaitingActivityLaunched.get(i);
+            w.timeout = timeout;
+            if (r != null) {
+                w.who = new ComponentName(r.info.packageName, r.info.name);
+            }
+            w.thisTime = thisTime;
+            w.totalTime = totalTime;
+        }
+        mService.notifyAll();
+    }
+    
+    void reportActivityVisibleLocked(ActivityRecord r) {
+        for (int i=mWaitingActivityVisible.size()-1; i>=0; i--) {
+            WaitResult w = mWaitingActivityVisible.get(i);
+            w.timeout = false;
+            if (r != null) {
+                w.who = new ComponentName(r.info.packageName, r.info.name);
+            }
+            w.totalTime = SystemClock.uptimeMillis() - w.thisTime;
+            w.thisTime = w.totalTime;
+        }
+        mService.notifyAll();
+    }
+
+    void sendActivityResultLocked(int callingUid, ActivityRecord r,
+            String resultWho, int requestCode, int resultCode, Intent data) {
+
+        if (callingUid > 0) {
+            mService.grantUriPermissionFromIntentLocked(callingUid, r.packageName,
+                    data, r);
+        }
+
+        if (DEBUG_RESULTS) Slog.v(TAG, "Send activity result to " + r
+                + " : who=" + resultWho + " req=" + requestCode
+                + " res=" + resultCode + " data=" + data);
+        if (mResumedActivity == r && r.app != null && r.app.thread != null) {
+            try {
+                ArrayList<ResultInfo> list = new ArrayList<ResultInfo>();
+                list.add(new ResultInfo(resultWho, requestCode,
+                        resultCode, data));
+                r.app.thread.scheduleSendResult(r, list);
+                return;
+            } catch (Exception e) {
+                Slog.w(TAG, "Exception thrown sending result to " + r, e);
+            }
+        }
+
+        r.addResultLocked(null, resultWho, requestCode, resultCode, data);
+    }
+
+    private 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) {
+            if (!r.finishing) {
+                requestFinishActivityLocked(r, Activity.RESULT_CANCELED, null,
+                        "no-history");
+            }
+        } else if (r.app != null && r.app.thread != null) {
+            if (mMainStack) {
+                if (mService.mFocusedActivity == r) {
+                    mService.setFocusedActivityLocked(topRunningActivityLocked(null));
+                }
+            }
+            r.resumeKeyDispatchingLocked();
+            try {
+                r.stopped = false;
+                r.state = ActivityState.STOPPING;
+                if (DEBUG_VISBILITY) Slog.v(
+                        TAG, "Stopping visible=" + r.visible + " for " + r);
+                if (!r.visible) {
+                    mService.mWindowManager.setAppVisibility(r, false);
+                }
+                r.app.thread.scheduleStopActivity(r, r.visible, r.configChangeFlags);
+            } catch (Exception e) {
+                // Maybe just ignore exceptions here...  if the process
+                // has crashed, our death notification will clean things
+                // up.
+                Slog.w(TAG, "Exception thrown during pause", e);
+                // Just in case, assume it to be stopped.
+                r.stopped = true;
+                r.state = ActivityState.STOPPED;
+                if (r.configDestroy) {
+                    destroyActivityLocked(r, true);
+                }
+            }
+        }
+    }
+    
+    final ArrayList<ActivityRecord> processStoppingActivitiesLocked(
+            boolean remove) {
+        int N = mStoppingActivities.size();
+        if (N <= 0) return null;
+
+        ArrayList<ActivityRecord> stops = null;
+
+        final boolean nowVisible = mResumedActivity != null
+                && mResumedActivity.nowVisible
+                && !mResumedActivity.waitingVisible;
+        for (int i=0; i<N; i++) {
+            ActivityRecord s = mStoppingActivities.get(i);
+            if (localLOGV) Slog.v(TAG, "Stopping " + s + ": nowVisible="
+                    + nowVisible + " waitingVisible=" + s.waitingVisible
+                    + " finishing=" + s.finishing);
+            if (s.waitingVisible && nowVisible) {
+                mWaitingVisibleActivities.remove(s);
+                s.waitingVisible = false;
+                if (s.finishing) {
+                    // If this activity is finishing, it is sitting on top of
+                    // everyone else but we now know it is no longer needed...
+                    // so get rid of it.  Otherwise, we need to go through the
+                    // normal flow and hide it once we determine that it is
+                    // hidden by the activities in front of it.
+                    if (localLOGV) Slog.v(TAG, "Before stopping, can hide: " + s);
+                    mService.mWindowManager.setAppVisibility(s, false);
+                }
+            }
+            if (!s.waitingVisible && remove) {
+                if (localLOGV) Slog.v(TAG, "Ready to stop: " + s);
+                if (stops == null) {
+                    stops = new ArrayList<ActivityRecord>();
+                }
+                stops.add(s);
+                mStoppingActivities.remove(i);
+                N--;
+                i--;
+            }
+        }
+
+        return stops;
+    }
+
+    final void activityIdleInternal(IBinder token, boolean fromTimeout,
+            Configuration config) {
+        if (localLOGV) Slog.v(TAG, "Activity idle: " + token);
+
+        ArrayList<ActivityRecord> stops = null;
+        ArrayList<ActivityRecord> finishes = null;
+        ArrayList<ActivityRecord> thumbnails = null;
+        int NS = 0;
+        int NF = 0;
+        int NT = 0;
+        IApplicationThread sendThumbnail = null;
+        boolean booting = false;
+        boolean enableScreen = false;
+
+        synchronized (mService) {
+            if (token != null) {
+                mHandler.removeMessages(IDLE_TIMEOUT_MSG, token);
+            }
+
+            // Get the activity record.
+            int index = indexOfTokenLocked(token);
+            if (index >= 0) {
+                ActivityRecord r = (ActivityRecord)mHistory.get(index);
+
+                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;
+                }
+                
+                // No longer need to keep the device awake.
+                if (mResumedActivity == r && 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;
+                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);
+                ensureActivitiesVisibleLocked(null, 0);
+
+                //Slog.i(TAG, "IDLE: mBooted=" + mBooted + ", fromTimeout=" + fromTimeout);
+                if (mMainStack) {
+                    if (!mService.mBooted && !fromTimeout) {
+                        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();
+            }
+            if ((NT=mService.mCancelledThumbnails.size()) > 0) {
+                thumbnails = new ArrayList<ActivityRecord>(mService.mCancelledThumbnails);
+                mService.mCancelledThumbnails.clear();
+            }
+
+            if (mMainStack) {
+                booting = mService.mBooting;
+                mService.mBooting = false;
+            }
+        }
+
+        int i;
+
+        // Send thumbnail if requested.
+        if (sendThumbnail != null) {
+            try {
+                sendThumbnail.requestThumbnail(token);
+            } catch (Exception e) {
+                Slog.w(TAG, "Exception thrown when requesting thumbnail", e);
+                mService.sendPendingThumbnail(null, token, null, null, true);
+            }
+        }
+
+        // Stop any activities that are scheduled to do so but have been
+        // waiting for the next one to start.
+        for (i=0; i<NS; i++) {
+            ActivityRecord r = (ActivityRecord)stops.get(i);
+            synchronized (mService) {
+                if (r.finishing) {
+                    finishCurrentActivityLocked(r, FINISH_IMMEDIATELY);
+                } else {
+                    stopActivityLocked(r);
+                }
+            }
+        }
+
+        // Finish any activities that are scheduled to do so but have been
+        // waiting for the next one to start.
+        for (i=0; i<NF; i++) {
+            ActivityRecord r = (ActivityRecord)finishes.get(i);
+            synchronized (mService) {
+                destroyActivityLocked(r, true);
+            }
+        }
+
+        // Report back to any thumbnail receivers.
+        for (i=0; i<NT; i++) {
+            ActivityRecord r = (ActivityRecord)thumbnails.get(i);
+            mService.sendPendingThumbnail(r, null, null, null, true);
+        }
+
+        if (booting) {
+            mService.finishBooting();
+        }
+
+        mService.trimApplications();
+        //dump();
+        //mWindowManager.dump();
+
+        if (enableScreen) {
+            mService.enableScreenAfterBoot();
+        }
+    }
+
+    /**
+     * @return Returns true if the activity is being finished, false if for
+     * some reason it is being left as-is.
+     */
+    final boolean requestFinishActivityLocked(IBinder token, int resultCode,
+            Intent resultData, String reason) {
+        if (DEBUG_RESULTS) Slog.v(
+            TAG, "Finishing activity: token=" + token
+            + ", result=" + resultCode + ", data=" + resultData);
+
+        int index = indexOfTokenLocked(token);
+        if (index < 0) {
+            return false;
+        }
+        ActivityRecord r = (ActivityRecord)mHistory.get(index);
+
+        // Is this the last activity left?
+        boolean lastActivity = true;
+        for (int i=mHistory.size()-1; i>=0; i--) {
+            ActivityRecord p = (ActivityRecord)mHistory.get(i);
+            if (!p.finishing && p != r) {
+                lastActivity = false;
+                break;
+            }
+        }
+        
+        // If this is the last activity, but it is the home activity, then
+        // just don't finish it.
+        if (lastActivity) {
+            if (r.intent.hasCategory(Intent.CATEGORY_HOME)) {
+                return false;
+            }
+        }
+        
+        finishActivityLocked(r, index, resultCode, resultData, reason);
+        return true;
+    }
+
+    /**
+     * @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 index,
+            int resultCode, Intent resultData, String reason) {
+        if (r.finishing) {
+            Slog.w(TAG, "Duplicate finish request for " + r);
+            return false;
+        }
+
+        r.finishing = true;
+        EventLog.writeEvent(EventLogTags.AM_FINISH_ACTIVITY,
+                System.identityHashCode(r),
+                r.task.taskId, r.shortComponentName, reason);
+        r.task.numActivities--;
+        if (index < (mHistory.size()-1)) {
+            ActivityRecord next = (ActivityRecord)mHistory.get(index+1);
+            if (next.task == r.task) {
+                if (r.frontOfTask) {
+                    // The next activity is now the front of the task.
+                    next.frontOfTask = true;
+                }
+                if ((r.intent.getFlags()&Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET) != 0) {
+                    // If the caller asked that this activity (and all above it)
+                    // be cleared when the task is reset, don't lose that information,
+                    // but propagate it up to the next activity.
+                    next.intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET);
+                }
+            }
+        }
+
+        r.pauseKeyDispatchingLocked();
+        if (mMainStack) {
+            if (mService.mFocusedActivity == r) {
+                mService.setFocusedActivityLocked(topRunningActivityLocked(null));
+            }
+        }
+
+        // send the result
+        ActivityRecord resultTo = r.resultTo;
+        if (resultTo != null) {
+            if (DEBUG_RESULTS) Slog.v(TAG, "Adding result to " + resultTo
+                    + " who=" + r.resultWho + " req=" + r.requestCode
+                    + " res=" + resultCode + " data=" + resultData);
+            if (r.info.applicationInfo.uid > 0) {
+                mService.grantUriPermissionFromIntentLocked(r.info.applicationInfo.uid,
+                        r.packageName, resultData, r);
+            }
+            resultTo.addResultLocked(r, r.resultWho, r.requestCode, resultCode,
+                                     resultData);
+            r.resultTo = null;
+        }
+        else if (DEBUG_RESULTS) Slog.v(TAG, "No result destination from " + r);
+
+        // Make sure this HistoryRecord is not holding on to other resources,
+        // because clients have remote IPC references to this object so we
+        // can't assume that will go away and want to avoid circular IPC refs.
+        r.results = null;
+        r.pendingResults = null;
+        r.newIntents = null;
+        r.icicle = null;
+        
+        if (mService.mPendingThumbnails.size() > 0) {
+            // 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.
+            mService.mCancelledThumbnails.add(r);
+        }
+
+        if (mResumedActivity == r) {
+            boolean endTask = index <= 0
+                    || ((ActivityRecord)mHistory.get(index-1)).task != r.task;
+            if (DEBUG_TRANSITION) Slog.v(TAG,
+                    "Prepare close transition: finishing " + r);
+            mService.mWindowManager.prepareAppTransition(endTask
+                    ? WindowManagerPolicy.TRANSIT_TASK_CLOSE
+                    : WindowManagerPolicy.TRANSIT_ACTIVITY_CLOSE);
+    
+            // Tell window manager to prepare for this one to be removed.
+            mService.mWindowManager.setAppVisibility(r, false);
+                
+            if (mPausingActivity == null) {
+                if (DEBUG_PAUSE) Slog.v(TAG, "Finish needs to pause: " + r);
+                if (DEBUG_USER_LEAVING) Slog.v(TAG, "finish() => pause with userLeaving=false");
+                startPausingLocked(false, false);
+            }
+
+        } else if (r.state != ActivityState.PAUSING) {
+            // If the activity is PAUSING, we will complete the finish once
+            // it is done pausing; else we can just directly finish it here.
+            if (DEBUG_PAUSE) Slog.v(TAG, "Finish not pausing: " + r);
+            return finishCurrentActivityLocked(r, index,
+                    FINISH_AFTER_PAUSE) == null;
+        } else {
+            if (DEBUG_PAUSE) Slog.v(TAG, "Finish waiting for pause of: " + r);
+        }
+
+        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;
+
+    private final ActivityRecord finishCurrentActivityLocked(ActivityRecord r,
+            int mode) {
+        final int index = indexOfTokenLocked(r);
+        if (index < 0) {
+            return null;
+        }
+
+        return finishCurrentActivityLocked(r, index, mode);
+    }
+
+    private final ActivityRecord finishCurrentActivityLocked(ActivityRecord r,
+            int index, int mode) {
+        // 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.
+        if (mode == FINISH_AFTER_VISIBLE && r.nowVisible) {
+            if (!mStoppingActivities.contains(r)) {
+                mStoppingActivities.add(r);
+                if (mStoppingActivities.size() > 3) {
+                    // If we already have a few activities waiting to stop,
+                    // then give up on things going idle and start clearing
+                    // them out.
+                    Message msg = Message.obtain();
+                    msg.what = IDLE_NOW_MSG;
+                    mHandler.sendMessage(msg);
+                }
+            }
+            r.state = ActivityState.STOPPING;
+            mService.updateOomAdjLocked();
+            return r;
+        }
+
+        // make sure the record is cleaned out of other places.
+        mStoppingActivities.remove(r);
+        mWaitingVisibleActivities.remove(r);
+        if (mResumedActivity == r) {
+            mResumedActivity = null;
+        }
+        final ActivityState prevState = r.state;
+        r.state = ActivityState.FINISHING;
+
+        if (mode == FINISH_IMMEDIATELY
+                || prevState == ActivityState.STOPPED
+                || prevState == ActivityState.INITIALIZING) {
+            // If this activity is already stopped, we can just finish
+            // it right now.
+            return destroyActivityLocked(r, true) ? null : r;
+        } else {
+            // 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);
+            resumeTopActivityLocked(null);
+        }
+        return r;
+    }
+
+    /**
+     * Perform the common clean-up of an activity record.  This is called both
+     * as part of destroyActivityLocked() (when destroying the client-side
+     * representation) and cleaning things up as a result of its hosting
+     * processing going away, in which case there is no remaining client-side
+     * state to destroy so only the cleanup here is needed.
+     */
+    final void cleanUpActivityLocked(ActivityRecord r, boolean cleanServices) {
+        if (mResumedActivity == r) {
+            mResumedActivity = null;
+        }
+        if (mService.mFocusedActivity == r) {
+            mService.mFocusedActivity = null;
+        }
+
+        r.configDestroy = false;
+        r.frozenBeforeDestroy = false;
+
+        // 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);
+        mWaitingVisibleActivities.remove(r);
+        
+        // Remove any pending results.
+        if (r.finishing && r.pendingResults != null) {
+            for (WeakReference<PendingIntentRecord> apr : r.pendingResults) {
+                PendingIntentRecord rec = apr.get();
+                if (rec != null) {
+                    mService.cancelIntentSenderLocked(rec, false);
+                }
+            }
+            r.pendingResults = null;
+        }
+
+        if (cleanServices) {
+            cleanUpActivityServicesLocked(r);            
+        }
+
+        if (mService.mPendingThumbnails.size() > 0) {
+            // 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.
+            mService.mCancelledThumbnails.add(r);
+        }
+
+        // Get rid of any pending idle timeouts.
+        mHandler.removeMessages(PAUSE_TIMEOUT_MSG, r);
+        mHandler.removeMessages(IDLE_TIMEOUT_MSG, r);
+    }
+
+    private final void removeActivityFromHistoryLocked(ActivityRecord r) {
+        if (r.state != ActivityState.DESTROYED) {
+            mHistory.remove(r);
+            r.inHistory = false;
+            r.state = ActivityState.DESTROYED;
+            mService.mWindowManager.removeAppToken(r);
+            if (VALIDATE_TOKENS) {
+                mService.mWindowManager.validateAppTokens(mHistory);
+            }
+            cleanUpActivityServicesLocked(r);
+            r.removeUriPermissionsLocked();
+        }
+    }
+    
+    /**
+     * Perform clean-up of service connections in an activity record.
+     */
+    final void cleanUpActivityServicesLocked(ActivityRecord r) {
+        // Throw away any services that have been bound by this activity.
+        if (r.connections != null) {
+            Iterator<ConnectionRecord> it = r.connections.iterator();
+            while (it.hasNext()) {
+                ConnectionRecord c = it.next();
+                mService.removeConnectionLocked(c, null, r);
+            }
+            r.connections = null;
+        }
+    }
+    
+    /**
+     * Destroy the current CLIENT SIDE instance of an activity.  This may be
+     * called both when actually finishing an activity, or when performing
+     * a configuration switch where we destroy the current client-side object
+     * but then create a new client-side object for this same HistoryRecord.
+     */
+    final boolean destroyActivityLocked(ActivityRecord r,
+            boolean removeFromApp) {
+        if (DEBUG_SWITCH) Slog.v(
+            TAG, "Removing activity: token=" + r
+              + ", app=" + (r.app != null ? r.app.processName : "(null)"));
+        EventLog.writeEvent(EventLogTags.AM_DESTROY_ACTIVITY,
+                System.identityHashCode(r),
+                r.task.taskId, r.shortComponentName);
+
+        boolean removedFromHistory = false;
+        
+        cleanUpActivityLocked(r, false);
+
+        final boolean hadApp = r.app != null;
+        
+        if (hadApp) {
+            if (removeFromApp) {
+                int idx = r.app.activities.indexOf(r);
+                if (idx >= 0) {
+                    r.app.activities.remove(idx);
+                }
+                if (mService.mHeavyWeightProcess == r.app && r.app.activities.size() <= 0) {
+                    mService.mHeavyWeightProcess = null;
+                    mService.mHandler.sendEmptyMessage(
+                            ActivityManagerService.CANCEL_HEAVY_NOTIFICATION_MSG);
+                }
+                if (r.persistent) {
+                    mService.decPersistentCountLocked(r.app);
+                }
+                if (r.app.activities.size() == 0) {
+                    // No longer have activities, so update location in
+                    // LRU list.
+                    mService.updateLruProcessLocked(r.app, true, false);
+                }
+            }
+
+            boolean skipDestroy = false;
+            
+            try {
+                if (DEBUG_SWITCH) Slog.i(TAG, "Destroying: " + r);
+                r.app.thread.scheduleDestroyActivity(r, r.finishing,
+                        r.configChangeFlags);
+            } catch (Exception e) {
+                // We can just ignore exceptions here...  if the process
+                // has crashed, our death notification will clean things
+                // up.
+                //Slog.w(TAG, "Exception thrown during finish", e);
+                if (r.finishing) {
+                    removeActivityFromHistoryLocked(r);
+                    removedFromHistory = true;
+                    skipDestroy = true;
+                }
+            }
+
+            r.app = null;
+            r.nowVisible = false;
+            
+            if (r.finishing && !skipDestroy) {
+                r.state = ActivityState.DESTROYING;
+                Message msg = mHandler.obtainMessage(DESTROY_TIMEOUT_MSG);
+                msg.obj = r;
+                mHandler.sendMessageDelayed(msg, DESTROY_TIMEOUT);
+            } else {
+                r.state = ActivityState.DESTROYED;
+            }
+        } else {
+            // remove this record from the history.
+            if (r.finishing) {
+                removeActivityFromHistoryLocked(r);
+                removedFromHistory = true;
+            } else {
+                r.state = ActivityState.DESTROYED;
+            }
+        }
+
+        r.configChangeFlags = 0;
+        
+        if (!mLRUActivities.remove(r) && hadApp) {
+            Slog.w(TAG, "Activity " + r + " being finished, but not in LRU list");
+        }
+        
+        return removedFromHistory;
+    }
+
+    final void activityDestroyed(IBinder token) {
+        synchronized (mService) {
+            mHandler.removeMessages(DESTROY_TIMEOUT_MSG, token);
+            
+            int index = indexOfTokenLocked(token);
+            if (index >= 0) {
+                ActivityRecord r = (ActivityRecord)mHistory.get(index);
+                if (r.state == ActivityState.DESTROYING) {
+                    final long origId = Binder.clearCallingIdentity();
+                    removeActivityFromHistoryLocked(r);
+                    Binder.restoreCallingIdentity(origId);
+                }
+            }
+        }
+    }
+    
+    private static void removeHistoryRecordsForAppLocked(ArrayList list, ProcessRecord app) {
+        int i = list.size();
+        if (localLOGV) Slog.v(
+            TAG, "Removing app " + app + " from list " + list
+            + " with " + i + " entries");
+        while (i > 0) {
+            i--;
+            ActivityRecord r = (ActivityRecord)list.get(i);
+            if (localLOGV) Slog.v(
+                TAG, "Record #" + i + " " + r + ": app=" + r.app);
+            if (r.app == app) {
+                if (localLOGV) Slog.v(TAG, "Removing this entry!");
+                list.remove(i);
+            }
+        }
+    }
+
+    void removeHistoryRecordsForAppLocked(ProcessRecord app) {
+        removeHistoryRecordsForAppLocked(mLRUActivities, app);
+        removeHistoryRecordsForAppLocked(mStoppingActivities, app);
+        removeHistoryRecordsForAppLocked(mWaitingVisibleActivities, app);
+        removeHistoryRecordsForAppLocked(mFinishingActivities, app);
+    }
+    
+    final void moveTaskToFrontLocked(TaskRecord tr, ActivityRecord reason) {
+        if (DEBUG_SWITCH) Slog.v(TAG, "moveTaskToFront: " + tr);
+
+        final int task = tr.taskId;
+        int top = mHistory.size()-1;
+
+        if (top < 0 || ((ActivityRecord)mHistory.get(top)).task.taskId == task) {
+            // nothing to do!
+            return;
+        }
+
+        ArrayList moved = new ArrayList();
+
+        // Applying the affinities may have removed entries from the history,
+        // so get the size again.
+        top = mHistory.size()-1;
+        int pos = top;
+
+        // Shift all activities with this task up to the top
+        // of the stack, keeping them in the same internal order.
+        while (pos >= 0) {
+            ActivityRecord r = (ActivityRecord)mHistory.get(pos);
+            if (localLOGV) Slog.v(
+                TAG, "At " + pos + " ckp " + r.task + ": " + r);
+            boolean first = true;
+            if (r.task.taskId == task) {
+                if (localLOGV) Slog.v(TAG, "Removing and adding at " + top);
+                mHistory.remove(pos);
+                mHistory.add(top, r);
+                moved.add(0, r);
+                top--;
+                if (first && mMainStack) {
+                    mService.addRecentTaskLocked(r.task);
+                    first = false;
+                }
+            }
+            pos--;
+        }
+
+        if (DEBUG_TRANSITION) Slog.v(TAG,
+                "Prepare to front transition: task=" + tr);
+        if (reason != null &&
+                (reason.intent.getFlags()&Intent.FLAG_ACTIVITY_NO_ANIMATION) != 0) {
+            mService.mWindowManager.prepareAppTransition(WindowManagerPolicy.TRANSIT_NONE);
+            ActivityRecord r = topRunningActivityLocked(null);
+            if (r != null) {
+                mNoAnimActivities.add(r);
+            }
+        } else {
+            mService.mWindowManager.prepareAppTransition(WindowManagerPolicy.TRANSIT_TASK_TO_FRONT);
+        }
+        
+        mService.mWindowManager.moveAppTokensToTop(moved);
+        if (VALIDATE_TOKENS) {
+            mService.mWindowManager.validateAppTokens(mHistory);
+        }
+
+        finishTaskMoveLocked(task);
+        EventLog.writeEvent(EventLogTags.AM_TASK_TO_FRONT, task);
+    }
+
+    private final void finishTaskMoveLocked(int task) {
+        resumeTopActivityLocked(null);
+    }
+
+    /**
+     * Worker method for rearranging history stack.  Implements the function of moving all 
+     * activities for a specific task (gathering them if disjoint) into a single group at the 
+     * bottom of the stack.
+     * 
+     * If a watcher is installed, the action is preflighted and the watcher has an opportunity
+     * to premeptively cancel the move.
+     * 
+     * @param task The taskId to collect and move to the bottom.
+     * @return Returns true if the move completed, false if not.
+     */
+    final boolean moveTaskToBackLocked(int task, ActivityRecord reason) {
+        Slog.i(TAG, "moveTaskToBack: " + task);
+        
+        // If we have a watcher, preflight the move before committing to it.  First check
+        // for *other* available tasks, but if none are available, then try again allowing the
+        // current task to be selected.
+        if (mMainStack && mService.mController != null) {
+            ActivityRecord next = topRunningActivityLocked(null, task);
+            if (next == null) {
+                next = topRunningActivityLocked(null, 0);
+            }
+            if (next != null) {
+                // ask watcher if this is allowed
+                boolean moveOK = true;
+                try {
+                    moveOK = mService.mController.activityResuming(next.packageName);
+                } catch (RemoteException e) {
+                    mService.mController = null;
+                }
+                if (!moveOK) {
+                    return false;
+                }
+            }
+        }
+
+        ArrayList moved = new ArrayList();
+
+        if (DEBUG_TRANSITION) Slog.v(TAG,
+                "Prepare to back transition: task=" + task);
+        
+        final int N = mHistory.size();
+        int bottom = 0;
+        int pos = 0;
+
+        // Shift all activities with this task down to the bottom
+        // of the stack, keeping them in the same internal order.
+        while (pos < N) {
+            ActivityRecord r = (ActivityRecord)mHistory.get(pos);
+            if (localLOGV) Slog.v(
+                TAG, "At " + pos + " ckp " + r.task + ": " + r);
+            if (r.task.taskId == task) {
+                if (localLOGV) Slog.v(TAG, "Removing and adding at " + (N-1));
+                mHistory.remove(pos);
+                mHistory.add(bottom, r);
+                moved.add(r);
+                bottom++;
+            }
+            pos++;
+        }
+
+        if (reason != null &&
+                (reason.intent.getFlags()&Intent.FLAG_ACTIVITY_NO_ANIMATION) != 0) {
+            mService.mWindowManager.prepareAppTransition(WindowManagerPolicy.TRANSIT_NONE);
+            ActivityRecord r = topRunningActivityLocked(null);
+            if (r != null) {
+                mNoAnimActivities.add(r);
+            }
+        } else {
+            mService.mWindowManager.prepareAppTransition(WindowManagerPolicy.TRANSIT_TASK_TO_BACK);
+        }
+        mService.mWindowManager.moveAppTokensToBottom(moved);
+        if (VALIDATE_TOKENS) {
+            mService.mWindowManager.validateAppTokens(mHistory);
+        }
+
+        finishTaskMoveLocked(task);
+        return true;
+    }
+    
+    private final void logStartActivity(int tag, ActivityRecord r,
+            TaskRecord task) {
+        EventLog.writeEvent(tag,
+                System.identityHashCode(r), task.taskId,
+                r.shortComponentName, r.intent.getAction(),
+                r.intent.getType(), r.intent.getDataString(),
+                r.intent.getFlags());
+    }
+
+    /**
+     * Make sure the given activity matches the current configuration.  Returns
+     * false if the activity had to be destroyed.  Returns true if the
+     * configuration is the same, or the activity will remain running as-is
+     * for whatever reason.  Ensures the HistoryRecord is updated with the
+     * correct configuration and all other bookkeeping is handled.
+     */
+    final boolean ensureActivityConfigurationLocked(ActivityRecord r,
+            int globalChanges) {
+        if (mConfigWillChange) {
+            if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG,
+                    "Skipping config check (will change): " + r);
+            return true;
+        }
+        
+        if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG,
+                "Ensuring correct configuration: " + r);
+        
+        // Short circuit: if the two configurations are the exact same
+        // object (the common case), then there is nothing to do.
+        Configuration newConfig = mService.mConfiguration;
+        if (r.configuration == newConfig) {
+            if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG,
+                    "Configuration unchanged in " + r);
+            return true;
+        }
+        
+        // We don't worry about activities that are finishing.
+        if (r.finishing) {
+            if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG,
+                    "Configuration doesn't matter in finishing " + r);
+            r.stopFreezingScreenLocked(false);
+            return true;
+        }
+        
+        // Okay we now are going to make this activity have the new config.
+        // But then we need to figure out how it needs to deal with that.
+        Configuration oldConfig = r.configuration;
+        r.configuration = newConfig;
+        
+        // If the activity isn't currently running, just leave the new
+        // configuration and it will pick that up next time it starts.
+        if (r.app == null || r.app.thread == null) {
+            if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG,
+                    "Configuration doesn't matter not running " + r);
+            r.stopFreezingScreenLocked(false);
+            return true;
+        }
+        
+        // If the activity isn't persistent, there is a chance we will
+        // need to restart it.
+        if (!r.persistent) {
+
+            // Figure out what has changed between the two configurations.
+            int changes = oldConfig.diff(newConfig);
+            if (DEBUG_SWITCH || DEBUG_CONFIGURATION) {
+                Slog.v(TAG, "Checking to restart " + r.info.name + ": changed=0x"
+                        + Integer.toHexString(changes) + ", handles=0x"
+                        + Integer.toHexString(r.info.configChanges)
+                        + ", newConfig=" + newConfig);
+            }
+            if ((changes&(~r.info.configChanges)) != 0) {
+                // Aha, the activity isn't handling the change, so DIE DIE DIE.
+                r.configChangeFlags |= changes;
+                r.startFreezingScreenLocked(r.app, globalChanges);
+                if (r.app == null || r.app.thread == null) {
+                    if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG,
+                            "Switch is destroying non-running " + r);
+                    destroyActivityLocked(r, true);
+                } else if (r.state == ActivityState.PAUSING) {
+                    // A little annoying: we are waiting for this activity to
+                    // finish pausing.  Let's not do anything now, but just
+                    // flag that it needs to be restarted when done pausing.
+                    if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG,
+                            "Switch is skipping already pausing " + r);
+                    r.configDestroy = true;
+                    return true;
+                } else if (r.state == ActivityState.RESUMED) {
+                    // Try to optimize this case: the configuration is changing
+                    // and we need to restart the top, resumed activity.
+                    // Instead of doing the normal handshaking, just say
+                    // "restart!".
+                    if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG,
+                            "Switch is restarting resumed " + r);
+                    relaunchActivityLocked(r, r.configChangeFlags, true);
+                    r.configChangeFlags = 0;
+                } else {
+                    if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG,
+                            "Switch is restarting non-resumed " + r);
+                    relaunchActivityLocked(r, r.configChangeFlags, false);
+                    r.configChangeFlags = 0;
+                }
+                
+                // All done...  tell the caller we weren't able to keep this
+                // activity around.
+                return false;
+            }
+        }
+        
+        // Default case: the activity can handle this new configuration, so
+        // hand it over.  Note that we don't need to give it the new
+        // configuration, since we always send configuration changes to all
+        // process when they happen so it can just use whatever configuration
+        // it last got.
+        if (r.app != null && r.app.thread != null) {
+            try {
+                if (DEBUG_CONFIGURATION) Slog.v(TAG, "Sending new config to " + r);
+                r.app.thread.scheduleActivityConfigurationChanged(r);
+            } catch (RemoteException e) {
+                // If process died, whatever.
+            }
+        }
+        r.stopFreezingScreenLocked(false);
+        
+        return true;
+    }
+
+    private final boolean relaunchActivityLocked(ActivityRecord r,
+            int changes, boolean andResume) {
+        List<ResultInfo> results = null;
+        List<Intent> newIntents = null;
+        if (andResume) {
+            results = r.results;
+            newIntents = r.newIntents;
+        }
+        if (DEBUG_SWITCH) Slog.v(TAG, "Relaunching: " + r
+                + " with results=" + results + " newIntents=" + newIntents
+                + " andResume=" + andResume);
+        EventLog.writeEvent(andResume ? EventLogTags.AM_RELAUNCH_RESUME_ACTIVITY
+                : EventLogTags.AM_RELAUNCH_ACTIVITY, System.identityHashCode(r),
+                r.task.taskId, r.shortComponentName);
+        
+        r.startFreezingScreenLocked(r.app, 0);
+        
+        try {
+            if (DEBUG_SWITCH) Slog.i(TAG, "Switch is restarting resumed " + r);
+            r.app.thread.scheduleRelaunchActivity(r, results, newIntents,
+                    changes, !andResume, mService.mConfiguration);
+            // Note: don't need to call pauseIfSleepingLocked() here, because
+            // the caller will only pass in 'andResume' if this activity is
+            // currently resumed, which implies we aren't sleeping.
+        } catch (RemoteException e) {
+            return false;
+        }
+
+        if (andResume) {
+            r.results = null;
+            r.newIntents = null;
+            if (mMainStack) {
+                mService.reportResumedActivityLocked(r);
+            }
+        }
+
+        return true;
+    }
+}
diff --git a/services/java/com/android/server/am/PendingIntentRecord.java b/services/java/com/android/server/am/PendingIntentRecord.java
index e7e9130..7a85eb8 100644
--- a/services/java/com/android/server/am/PendingIntentRecord.java
+++ b/services/java/com/android/server/am/PendingIntentRecord.java
@@ -218,7 +218,7 @@
                         }
                         break;
                     case IActivityManager.INTENT_SENDER_ACTIVITY_RESULT:
-                        owner.sendActivityResultLocked(-1, key.activity,
+                        key.activity.stack.sendActivityResultLocked(-1, key.activity,
                                 key.who, key.requestCode, code, finalIntent);
                         break;
                     case IActivityManager.INTENT_SENDER_BROADCAST:
diff --git a/wifi/java/android/net/wifi/IWifiManager.aidl b/wifi/java/android/net/wifi/IWifiManager.aidl
index 5fd44b1..6e0bc9d 100644
--- a/wifi/java/android/net/wifi/IWifiManager.aidl
+++ b/wifi/java/android/net/wifi/IWifiManager.aidl
@@ -83,5 +83,7 @@
     int getWifiApEnabledState();
 
     WifiConfiguration getWifiApConfiguration();
+
+    void setWifiApConfiguration(in WifiConfiguration wifiConfig);
 }
 
diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java
index 970d5fc..4a22b68 100644
--- a/wifi/java/android/net/wifi/WifiManager.java
+++ b/wifi/java/android/net/wifi/WifiManager.java
@@ -824,6 +824,21 @@
     }
 
     /**
+     * Sets the Wi-Fi AP Configuration.
+     * @return {@code true} if the operation succeeded, {@code false} otherwise
+     *
+     * @hide Dont open yet
+     */
+    public boolean setWifiApConfiguration(WifiConfiguration wifiConfig) {
+        try {
+            mService.setWifiApConfiguration(wifiConfig);
+            return true;
+        } catch (RemoteException e) {
+            return false;
+        }
+    }
+
+    /**
      * Allows an application to keep the Wi-Fi radio awake.
      * Normally the Wi-Fi radio may turn off when the user has not used the device in a while.
      * Acquiring a WifiLock will keep the radio on until the lock is released.  Multiple