Merge "move ndk headers to frameworks/native"
diff --git a/api/current.txt b/api/current.txt
index 4398ea3..8747e9a 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -2744,6 +2744,7 @@
     method public static boolean isUserAMonkey();
     method public void killBackgroundProcesses(java.lang.String);
     method public void moveTaskToFront(int, int);
+    method public void moveTaskToFront(int, int, android.os.Bundle);
     method public deprecated void restartPackage(java.lang.String);
     field public static final int MOVE_TASK_NO_USER_ACTION = 2; // 0x2
     field public static final int MOVE_TASK_WITH_HOME = 1; // 0x1
@@ -2867,9 +2868,14 @@
   public class ActivityOptions {
     method public void join(android.app.ActivityOptions);
     method public static android.app.ActivityOptions makeCustomAnimation(android.content.Context, int, int);
+    method public static android.app.ActivityOptions makeThumbnailScaleUpAnimation(android.view.View, android.graphics.Bitmap, int, int, android.app.ActivityOptions.OnAnimationStartedListener);
     method public android.os.Bundle toBundle();
   }
 
+  public static abstract interface ActivityOptions.OnAnimationStartedListener {
+    method public abstract void onAnimationStarted();
+  }
+
   public class AlarmManager {
     method public void cancel(android.app.PendingIntent);
     method public void set(int, long, android.app.PendingIntent);
@@ -7358,6 +7364,7 @@
     method public static int releaseMemory();
     method public long replace(java.lang.String, java.lang.String, android.content.ContentValues);
     method public long replaceOrThrow(java.lang.String, java.lang.String, android.content.ContentValues) throws android.database.SQLException;
+    method public void setForeignKeyConstraintsEnabled(boolean);
     method public void setLocale(java.util.Locale);
     method public deprecated void setLockingEnabled(boolean);
     method public void setMaxSqlCacheSize(int);
@@ -7437,6 +7444,7 @@
     method public java.lang.String getDatabaseName();
     method public android.database.sqlite.SQLiteDatabase getReadableDatabase();
     method public android.database.sqlite.SQLiteDatabase getWritableDatabase();
+    method public void onConfigure(android.database.sqlite.SQLiteDatabase);
     method public abstract void onCreate(android.database.sqlite.SQLiteDatabase);
     method public void onDowngrade(android.database.sqlite.SQLiteDatabase, int, int);
     method public void onOpen(android.database.sqlite.SQLiteDatabase);
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index ea32745..b277efb 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -3639,7 +3639,7 @@
      */
     public void startActivityFromChild(Activity child, Intent intent,
             int requestCode) {
-        startActivityFromChild(child, intent, requestCode);
+        startActivityFromChild(child, intent, requestCode, null);
     }
 
     /**
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index d056b17..531a695 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -31,6 +31,7 @@
 import android.graphics.Bitmap;
 import android.graphics.Point;
 import android.os.Binder;
+import android.os.Bundle;
 import android.os.Debug;
 import android.os.Handler;
 import android.os.Parcel;
@@ -816,6 +817,19 @@
     public static final int MOVE_TASK_NO_USER_ACTION = 0x00000002;
 
     /**
+     * Equivalent to calling {@link #moveTaskToFront(int, int, Bundle)}
+     * with a null options argument.
+     *
+     * @param taskId The identifier of the task to be moved, as found in
+     * {@link RunningTaskInfo} or {@link RecentTaskInfo}.
+     * @param flags Additional operational flags, 0 or more of
+     * {@link #MOVE_TASK_WITH_HOME}.
+     */
+    public void moveTaskToFront(int taskId, int flags) {
+        moveTaskToFront(taskId, flags, null);
+    }
+
+    /**
      * Ask that the task associated with a given task ID be moved to the
      * front of the stack, so it is now visible to the user.  Requires that
      * the caller hold permission {@link android.Manifest.permission#REORDER_TASKS}
@@ -825,10 +839,13 @@
      * {@link RunningTaskInfo} or {@link RecentTaskInfo}.
      * @param flags Additional operational flags, 0 or more of
      * {@link #MOVE_TASK_WITH_HOME}.
+     * @param options Additional options for the operation, either null or
+     * as per {@link Context#startActivity(Intent, android.os.Bundle)
+     * Context.startActivity(Intent, Bundle)}.
      */
-    public void moveTaskToFront(int taskId, int flags) {
+    public void moveTaskToFront(int taskId, int flags, Bundle options) {
         try {
-            ActivityManagerNative.getDefault().moveTaskToFront(taskId, flags);
+            ActivityManagerNative.getDefault().moveTaskToFront(taskId, flags, options);
         } catch (RemoteException e) {
             // System dead, we will be dead too soon!
         }
diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java
index a3cc352..c402329 100644
--- a/core/java/android/app/ActivityManagerNative.java
+++ b/core/java/android/app/ActivityManagerNative.java
@@ -510,7 +510,9 @@
             data.enforceInterface(IActivityManager.descriptor);
             int task = data.readInt();
             int fl = data.readInt();
-            moveTaskToFront(task, fl);
+            Bundle options = data.readInt() != 0
+                    ? Bundle.CREATOR.createFromParcel(data) : null;
+            moveTaskToFront(task, fl, options);
             reply.writeNoException();
             return true;
         }
@@ -1055,6 +1057,15 @@
             return true;
         }
 
+        case KILL_PROCESSES_BELOW_FOREGROUND_TRANSACTION: {
+            data.enforceInterface(IActivityManager.descriptor);
+            String reason = data.readString();
+            boolean res = killProcessesBelowForeground(reason);
+            reply.writeNoException();
+            reply.writeInt(res ? 1 : 0);
+            return true;
+        }
+
         case START_RUNNING_TRANSACTION: {
             data.enforceInterface(IActivityManager.descriptor);
             String pkg = data.readString();
@@ -2134,13 +2145,19 @@
         reply.recycle();
         return list;
     }
-    public void moveTaskToFront(int task, int flags) throws RemoteException
+    public void moveTaskToFront(int task, int flags, Bundle options) throws RemoteException
     {
         Parcel data = Parcel.obtain();
         Parcel reply = Parcel.obtain();
         data.writeInterfaceToken(IActivityManager.descriptor);
         data.writeInt(task);
         data.writeInt(flags);
+        if (options != null) {
+            data.writeInt(1);
+            options.writeToParcel(data, 0);
+        } else {
+            data.writeInt(0);
+        }
         mRemote.transact(MOVE_TASK_TO_FRONT_TRANSACTION, data, reply, 0);
         reply.readException();
         data.recycle();
@@ -2902,6 +2919,18 @@
         reply.recycle();
         return res;
     }
+    @Override
+    public boolean killProcessesBelowForeground(String reason) throws RemoteException {
+        Parcel data = Parcel.obtain();
+        Parcel reply = Parcel.obtain();
+        data.writeInterfaceToken(IActivityManager.descriptor);
+        data.writeString(reason);
+        mRemote.transact(KILL_PROCESSES_BELOW_FOREGROUND_TRANSACTION, data, reply, 0);
+        boolean res = reply.readInt() != 0;
+        data.recycle();
+        reply.recycle();
+        return res;
+    }
     public void startRunning(String pkg, String cls, String action,
             String indata) throws RemoteException {
         Parcel data = Parcel.obtain();
diff --git a/core/java/android/app/ActivityOptions.java b/core/java/android/app/ActivityOptions.java
index 03bc338..c637df0 100644
--- a/core/java/android/app/ActivityOptions.java
+++ b/core/java/android/app/ActivityOptions.java
@@ -17,7 +17,13 @@
 package android.app;
 
 import android.content.Context;
+import android.graphics.Bitmap;
 import android.os.Bundle;
+import android.os.Handler;
+import android.os.IRemoteCallback;
+import android.os.Message;
+import android.os.RemoteException;
+import android.view.View;
 
 /**
  * Helper class for building an options Bundle that can be used with
@@ -32,6 +38,12 @@
     public static final String KEY_PACKAGE_NAME = "android:packageName";
 
     /**
+     * Type of animation that arguments specify.
+     * @hide
+     */
+    public static final String KEY_ANIM_TYPE = "android:animType";
+
+    /**
      * Custom enter animation resource ID.
      * @hide
      */
@@ -43,10 +55,45 @@
      */
     public static final String KEY_ANIM_EXIT_RES_ID = "android:animExitRes";
 
+    /**
+     * Bitmap for thumbnail animation.
+     * @hide
+     */
+    public static final String KEY_ANIM_THUMBNAIL = "android:animThumbnail";
+
+    /**
+     * Start X position of thumbnail animation.
+     * @hide
+     */
+    public static final String KEY_ANIM_START_X = "android:animStartX";
+
+    /**
+     * Start Y position of thumbnail animation.
+     * @hide
+     */
+    public static final String KEY_ANIM_START_Y = "android:animStartY";
+
+    /**
+     * Callback for when animation is started.
+     * @hide
+     */
+    public static final String KEY_ANIM_START_LISTENER = "android:animStartListener";
+
+    /** @hide */
+    public static final int ANIM_NONE = 0;
+    /** @hide */
+    public static final int ANIM_CUSTOM = 1;
+    /** @hide */
+    public static final int ANIM_THUMBNAIL = 2;
+
     private String mPackageName;
-    private boolean mIsCustomAnimation;
+    private int mAnimationType = ANIM_NONE;
     private int mCustomEnterResId;
     private int mCustomExitResId;
+    private Bitmap mThumbnail;
+    private int mStartX;
+    private int mStartY;
+    private IRemoteCallback mAnimationStartedListener;
 
     /**
      * Create an ActivityOptions specifying a custom animation to run when
@@ -65,22 +112,79 @@
             int enterResId, int exitResId) {
         ActivityOptions opts = new ActivityOptions();
         opts.mPackageName = context.getPackageName();
-        opts.mIsCustomAnimation = true;
+        opts.mAnimationType = ANIM_CUSTOM;
         opts.mCustomEnterResId = enterResId;
         opts.mCustomExitResId = exitResId;
         return opts;
     }
 
+    /**
+     * Callback for use with {@link ActivityOptions#makeThumbnailScaleUpAnimation}
+     * to find out when the given animation has started running.
+     */
+    public interface OnAnimationStartedListener {
+        void onAnimationStarted();
+    }
+
+    /**
+     * Create an ActivityOptions specifying an animation where a thumbnail
+     * is scaled from a given position to the new activity window that is
+     * being started.
+     *
+     * @param source The View that this thumbnail is animating from.  This
+     * defines the coordinate space for <var>startX</var> and <var>startY</var>.
+     * @param thumbnail The bitmap that will be shown as the initial thumbnail
+     * of the animation.
+     * @param startX The x starting location of the bitmap, in screen coordiantes.
+     * @param startY The y starting location of the bitmap, in screen coordinates.
+     * @param listener Optional OnAnimationStartedListener to find out when the
+     * requested animation has started running.  If for some reason the animation
+     * is not executed, the callback will happen immediately.
+     * @return Returns a new ActivityOptions object that you can use to
+     * supply these options as the options Bundle when starting an activity.
+     */
+    public static ActivityOptions makeThumbnailScaleUpAnimation(View source,
+            Bitmap thumbnail, int startX, int startY, OnAnimationStartedListener listener) {
+        ActivityOptions opts = new ActivityOptions();
+        opts.mPackageName = source.getContext().getPackageName();
+        opts.mAnimationType = ANIM_THUMBNAIL;
+        opts.mThumbnail = thumbnail;
+        int[] pts = new int[2];
+        source.getLocationOnScreen(pts);
+        opts.mStartX = pts[0] + startX;
+        opts.mStartY = pts[1] + startY;
+        if (listener != null) {
+            final Handler h = source.getHandler();
+            final OnAnimationStartedListener finalListener = listener;
+            opts.mAnimationStartedListener = new IRemoteCallback.Stub() {
+                @Override public void sendResult(Bundle data) throws RemoteException {
+                    h.post(new Runnable() {
+                        @Override public void run() {
+                            finalListener.onAnimationStarted();
+                        }
+                    });
+                }
+            };
+        }
+        return opts;
+    }
+
     private ActivityOptions() {
     }
 
     /** @hide */
     public ActivityOptions(Bundle opts) {
         mPackageName = opts.getString(KEY_PACKAGE_NAME);
-        if (opts.containsKey(KEY_ANIM_ENTER_RES_ID)) {
-            mIsCustomAnimation = true;
+        mAnimationType = opts.getInt(KEY_ANIM_TYPE);
+        if (mAnimationType == ANIM_CUSTOM) {
             mCustomEnterResId = opts.getInt(KEY_ANIM_ENTER_RES_ID, 0);
             mCustomExitResId = opts.getInt(KEY_ANIM_EXIT_RES_ID, 0);
+        } else if (mAnimationType == ANIM_THUMBNAIL) {
+            mThumbnail = (Bitmap)opts.getParcelable(KEY_ANIM_THUMBNAIL);
+            mStartX = opts.getInt(KEY_ANIM_START_X, 0);
+            mStartY = opts.getInt(KEY_ANIM_START_Y, 0);
+            mAnimationStartedListener = IRemoteCallback.Stub.asInterface(
+                    opts.getIBinder(KEY_ANIM_START_LISTENER));
         }
     }
 
@@ -90,8 +194,8 @@
     }
 
     /** @hide */
-    public boolean isCustomAnimation() {
-        return mIsCustomAnimation;
+    public int getAnimationType() {
+        return mAnimationType;
     }
 
     /** @hide */
@@ -104,6 +208,43 @@
         return mCustomExitResId;
     }
 
+    /** @hide */
+    public Bitmap getThumbnail() {
+        return mThumbnail;
+    }
+
+    /** @hide */
+    public int getStartX() {
+        return mStartX;
+    }
+
+    /** @hide */
+    public int getStartY() {
+        return mStartY;
+    }
+
+    /** @hide */
+    public IRemoteCallback getOnAnimationStartListener() {
+        return mAnimationStartedListener;
+    }
+
+    /** @hide */
+    public void abort() {
+        if (mAnimationStartedListener != null) {
+            try {
+                mAnimationStartedListener.sendResult(null);
+            } catch (RemoteException e) {
+            }
+        }
+    }
+
+    /** @hide */
+    public static void abort(Bundle options) {
+        if (options != null) {
+            (new ActivityOptions(options)).abort();
+        }
+    }
+
     /**
      * Join the values in <var>otherOptions</var> in to this one.  Any values
      * defined in <var>otherOptions</var> replace those in the base options.
@@ -112,10 +253,27 @@
         if (otherOptions.mPackageName != null) {
             mPackageName = otherOptions.mPackageName;
         }
-        if (otherOptions.mIsCustomAnimation) {
-            mIsCustomAnimation = true;
-            mCustomEnterResId = otherOptions.mCustomEnterResId;
-            mCustomExitResId = otherOptions.mCustomExitResId;
+        switch (otherOptions.mAnimationType) {
+            case ANIM_CUSTOM:
+                mAnimationType = otherOptions.mAnimationType;
+                mCustomEnterResId = otherOptions.mCustomEnterResId;
+                mCustomExitResId = otherOptions.mCustomExitResId;
+                mThumbnail = null;
+                mAnimationStartedListener = null;
+                break;
+            case ANIM_THUMBNAIL:
+                mAnimationType = otherOptions.mAnimationType;
+                mThumbnail = otherOptions.mThumbnail;
+                mStartX = otherOptions.mStartX;
+                mStartY = otherOptions.mStartY;
+                if (otherOptions.mAnimationStartedListener != null) {
+                    try {
+                        otherOptions.mAnimationStartedListener.sendResult(null);
+                    } catch (RemoteException e) {
+                    }
+                }
+                mAnimationStartedListener = otherOptions.mAnimationStartedListener;
+                break;
         }
     }
 
@@ -132,9 +290,19 @@
         if (mPackageName != null) {
             b.putString(KEY_PACKAGE_NAME, mPackageName);
         }
-        if (mIsCustomAnimation) {
-            b.putInt(KEY_ANIM_ENTER_RES_ID, mCustomEnterResId);
-            b.putInt(KEY_ANIM_EXIT_RES_ID, mCustomExitResId);
+        switch (mAnimationType) {
+            case ANIM_CUSTOM:
+                b.putInt(KEY_ANIM_TYPE, mAnimationType);
+                b.putInt(KEY_ANIM_ENTER_RES_ID, mCustomEnterResId);
+                b.putInt(KEY_ANIM_EXIT_RES_ID, mCustomExitResId);
+                break;
+            case ANIM_THUMBNAIL:
+                b.putInt(KEY_ANIM_TYPE, mAnimationType);
+                b.putParcelable(KEY_ANIM_THUMBNAIL, mThumbnail);
+                b.putInt(KEY_ANIM_START_X, mStartX);
+                b.putInt(KEY_ANIM_START_Y, mStartY);
+                b.putIBinder(KEY_ANIM_START_LISTENER, mAnimationStartedListener
+                        != null ? mAnimationStartedListener.asBinder() : null);
         }
         return b;
     }
diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java
index 31066b5..1d994d8 100644
--- a/core/java/android/app/IActivityManager.java
+++ b/core/java/android/app/IActivityManager.java
@@ -105,7 +105,7 @@
     public List getServices(int maxNum, int flags) throws RemoteException;
     public List<ActivityManager.ProcessErrorStateInfo> getProcessesInErrorState()
             throws RemoteException;
-    public void moveTaskToFront(int task, int flags) throws RemoteException;
+    public void moveTaskToFront(int task, int flags, Bundle options) throws RemoteException;
     public void moveTaskToBack(int task) throws RemoteException;
     public boolean moveActivityTaskToBack(IBinder token, boolean nonRoot) throws RemoteException;
     public void moveTaskBackwards(int task) throws RemoteException;
@@ -216,9 +216,10 @@
     public void enterSafeMode() throws RemoteException;
     
     public void noteWakeupAlarm(IIntentSender sender) throws RemoteException;
-    
+
     public boolean killPids(int[] pids, String reason, boolean secure) throws RemoteException;
-    
+    public boolean killProcessesBelowForeground(String reason) throws RemoteException;
+
     // Special low-level communication with activity manager.
     public void startRunning(String pkg, String cls, String action,
             String data) throws RemoteException;
@@ -573,4 +574,5 @@
     int GET_CONTENT_PROVIDER_EXTERNAL_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+140;
     int REMOVE_CONTENT_PROVIDER_EXTERNAL_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+141;
     int GET_MY_MEMORY_STATE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+142;
+    int KILL_PROCESSES_BELOW_FOREGROUND_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+143;
 }
diff --git a/core/java/android/database/sqlite/SQLiteConnection.java b/core/java/android/database/sqlite/SQLiteConnection.java
index e999316..254f652 100644
--- a/core/java/android/database/sqlite/SQLiteConnection.java
+++ b/core/java/android/database/sqlite/SQLiteConnection.java
@@ -211,6 +211,7 @@
                 SQLiteDebug.DEBUG_SQL_STATEMENTS, SQLiteDebug.DEBUG_SQL_TIME);
 
         setPageSize();
+        setForeignKeyModeFromConfiguration();
         setWalModeFromConfiguration();
         setJournalSizeLimit();
         setAutoCheckpointInterval();
@@ -267,6 +268,16 @@
         }
     }
 
+    private void setForeignKeyModeFromConfiguration() {
+        if (!mIsReadOnlyConnection) {
+            final long newValue = mConfiguration.foreignKeyConstraintsEnabled ? 1 : 0;
+            long value = executeForLong("PRAGMA foreign_keys", null, null);
+            if (value != newValue) {
+                execute("PRAGMA foreign_keys=" + newValue, null, null);
+            }
+        }
+    }
+
     private void setWalModeFromConfiguration() {
         if (!mConfiguration.isInMemoryDb() && !mIsReadOnlyConnection) {
             if ((mConfiguration.openFlags & SQLiteDatabase.ENABLE_WRITE_AHEAD_LOGGING) != 0) {
@@ -389,6 +400,8 @@
         }
 
         // Remember what changed.
+        boolean foreignKeyModeChanged = configuration.foreignKeyConstraintsEnabled
+                != mConfiguration.foreignKeyConstraintsEnabled;
         boolean walModeChanged = ((configuration.openFlags ^ mConfiguration.openFlags)
                 & SQLiteDatabase.ENABLE_WRITE_AHEAD_LOGGING) != 0;
         boolean localeChanged = !configuration.locale.equals(mConfiguration.locale);
@@ -399,6 +412,11 @@
         // Update prepared statement cache size.
         mPreparedStatementCache.resize(configuration.maxSqlCacheSize);
 
+        // Update foreign key mode.
+        if (foreignKeyModeChanged) {
+            setForeignKeyModeFromConfiguration();
+        }
+
         // Update WAL.
         if (walModeChanged) {
             setWalModeFromConfiguration();
diff --git a/core/java/android/database/sqlite/SQLiteConnectionPool.java b/core/java/android/database/sqlite/SQLiteConnectionPool.java
index 0538ce4..5c8e38bf 100644
--- a/core/java/android/database/sqlite/SQLiteConnectionPool.java
+++ b/core/java/android/database/sqlite/SQLiteConnectionPool.java
@@ -277,6 +277,20 @@
                 assert mAvailableNonPrimaryConnections.isEmpty();
             }
 
+            boolean foreignKeyModeChanged = configuration.foreignKeyConstraintsEnabled
+                    != mConfiguration.foreignKeyConstraintsEnabled;
+            if (foreignKeyModeChanged) {
+                // Foreign key constraints can only be changed if there are no transactions
+                // in progress.  To make this clear, we throw an exception if there are
+                // any acquired connections.
+                if (!mAcquiredConnections.isEmpty()) {
+                    throw new IllegalStateException("Foreign Key Constraints cannot "
+                            + "be enabled or disabled while there are transactions in "
+                            + "progress.  Finish all transactions and release all active "
+                            + "database connections first.");
+                }
+            }
+
             if (mConfiguration.openFlags != configuration.openFlags) {
                 // If we are changing open flags and WAL mode at the same time, then
                 // we have no choice but to close the primary connection beforehand
diff --git a/core/java/android/database/sqlite/SQLiteDatabase.java b/core/java/android/database/sqlite/SQLiteDatabase.java
index 049a615..7bd0c8d 100644
--- a/core/java/android/database/sqlite/SQLiteDatabase.java
+++ b/core/java/android/database/sqlite/SQLiteDatabase.java
@@ -1793,6 +1793,53 @@
     }
 
     /**
+     * Sets whether foreign key constraints are enabled for the database.
+     * <p>
+     * By default, foreign key constraints are not enforced by the database.
+     * This method allows an application to enable foreign key constraints.
+     * It must be called each time the database is opened to ensure that foreign
+     * key constraints are enabled for the session.
+     * </p><p>
+     * A good time to call this method is right after calling {@link #openOrCreateDatabase}
+     * or in the {@link SQLiteOpenHelper#onConfigure} callback.
+     * </p><p>
+     * When foreign key constraints are disabled, the database does not check whether
+     * changes to the database will violate foreign key constraints.  Likewise, when
+     * foreign key constraints are disabled, the database will not execute cascade
+     * delete or update triggers.  As a result, it is possible for the database
+     * state to become inconsistent.  To perform a database integrity check,
+     * call {@link #isDatabaseIntegrityOk}.
+     * </p><p>
+     * This method must not be called while a transaction is in progress.
+     * </p><p>
+     * See also <a href="http://sqlite.org/foreignkeys.html">SQLite Foreign Key Constraints</a>
+     * for more details about foreign key constraint support.
+     * </p>
+     *
+     * @param enable True to enable foreign key constraints, false to disable them.
+     *
+     * @throws IllegalStateException if the are transactions is in progress
+     * when this method is called.
+     */
+    public void setForeignKeyConstraintsEnabled(boolean enable) {
+        synchronized (mLock) {
+            throwIfNotOpenLocked();
+
+            if (mConfigurationLocked.foreignKeyConstraintsEnabled == enable) {
+                return;
+            }
+
+            mConfigurationLocked.foreignKeyConstraintsEnabled = enable;
+            try {
+                mConnectionPoolLocked.reconfigure(mConfigurationLocked);
+            } catch (RuntimeException ex) {
+                mConfigurationLocked.foreignKeyConstraintsEnabled = !enable;
+                throw ex;
+            }
+        }
+    }
+
+    /**
      * This method enables parallel execution of queries from multiple threads on the
      * same database.  It does this by opening multiple connections to the database
      * and using a different database connection for each query.  The database
diff --git a/core/java/android/database/sqlite/SQLiteDatabaseConfiguration.java b/core/java/android/database/sqlite/SQLiteDatabaseConfiguration.java
index 123c2c6..549ab90 100644
--- a/core/java/android/database/sqlite/SQLiteDatabaseConfiguration.java
+++ b/core/java/android/database/sqlite/SQLiteDatabaseConfiguration.java
@@ -77,6 +77,13 @@
     public Locale locale;
 
     /**
+     * True if foreign key constraints are enabled.
+     *
+     * Default is false.
+     */
+    public boolean foreignKeyConstraintsEnabled;
+
+    /**
      * The custom functions to register.
      */
     public final ArrayList<SQLiteCustomFunction> customFunctions =
@@ -136,6 +143,7 @@
         openFlags = other.openFlags;
         maxSqlCacheSize = other.maxSqlCacheSize;
         locale = other.locale;
+        foreignKeyConstraintsEnabled = other.foreignKeyConstraintsEnabled;
         customFunctions.clear();
         customFunctions.addAll(other.customFunctions);
     }
diff --git a/core/java/android/database/sqlite/SQLiteOpenHelper.java b/core/java/android/database/sqlite/SQLiteOpenHelper.java
index fe37b8f..431eca2 100644
--- a/core/java/android/database/sqlite/SQLiteOpenHelper.java
+++ b/core/java/android/database/sqlite/SQLiteOpenHelper.java
@@ -237,6 +237,8 @@
                 }
             }
 
+            onConfigure(db);
+
             final int version = db.getVersion();
             if (version != mNewVersion) {
                 if (db.isReadOnly()) {
@@ -261,6 +263,7 @@
                     db.endTransaction();
                 }
             }
+
             onOpen(db);
 
             if (db.isReadOnly()) {
@@ -290,6 +293,25 @@
     }
 
     /**
+     * Called when the database connection is being configured, to enable features
+     * such as write-ahead logging or foreign key support.
+     * <p>
+     * This method is called before {@link #onCreate}, {@link #onUpgrade},
+     * {@link #onDowngrade}, or {@link #onOpen} are called.  It should not modify
+     * the database except to configure the database connection as required.
+     * </p><p>
+     * This method should only call methods that configure the parameters of the
+     * database connection, such as {@link SQLiteDatabase#enableWriteAheadLogging}
+     * {@link SQLiteDatabase#setForeignKeyConstraintsEnabled},
+     * {@link SQLiteDatabase#setLocale}, {@link SQLiteDatabase#setMaximumSize},
+     * or executing PRAGMA statements.
+     * </p>
+     *
+     * @param db The database.
+     */
+    public void onConfigure(SQLiteDatabase db) {}
+
+    /**
      * Called when the database is created for the first time. This is where the
      * creation of tables and the initial population of the tables should happen.
      *
@@ -302,11 +324,16 @@
      * should use this method to drop tables, add tables, or do anything else it
      * needs to upgrade to the new schema version.
      *
-     * <p>The SQLite ALTER TABLE documentation can be found
+     * <p>
+     * The SQLite ALTER TABLE documentation can be found
      * <a href="http://sqlite.org/lang_altertable.html">here</a>. If you add new columns
      * you can use ALTER TABLE to insert them into a live table. If you rename or remove columns
      * you can use ALTER TABLE to rename the old table, then create the new table and then
      * populate the new table with the contents of the old table.
+     * </p><p>
+     * This method executes within a transaction.  If an exception is thrown, all changes
+     * will automatically be rolled back.
+     * </p>
      *
      * @param db The database.
      * @param oldVersion The old database version.
@@ -316,11 +343,16 @@
 
     /**
      * Called when the database needs to be downgraded. This is strictly similar to
-     * onUpgrade() method, but is called whenever current version is newer than requested one.
+     * {@link #onUpgrade} method, but is called whenever current version is newer than requested one.
      * However, this method is not abstract, so it is not mandatory for a customer to
      * implement it. If not overridden, default implementation will reject downgrade and
      * throws SQLiteException
      *
+     * <p>
+     * This method executes within a transaction.  If an exception is thrown, all changes
+     * will automatically be rolled back.
+     * </p>
+     *
      * @param db The database.
      * @param oldVersion The old database version.
      * @param newVersion The new database version.
@@ -334,6 +366,12 @@
      * Called when the database has been opened.  The implementation
      * should check {@link SQLiteDatabase#isReadOnly} before updating the
      * database.
+     * <p>
+     * This method is called after the database connection has been configured
+     * and after the database schema has been created, upgraded or downgraded as necessary.
+     * If the database connection must be configured in some way before the schema
+     * is created, upgraded, or downgraded, do it in {@link #onConfigure} instead.
+     * </p>
      *
      * @param db The database.
      */
diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl
index c54d09e..14cd48f 100644
--- a/core/java/android/view/IWindowManager.aidl
+++ b/core/java/android/view/IWindowManager.aidl
@@ -89,6 +89,8 @@
     void prepareAppTransition(int transit, boolean alwaysKeepCurrent);
     int getPendingAppTransition();
     void overridePendingAppTransition(String packageName, int enterAnim, int exitAnim);
+    void overridePendingAppTransitionThumb(in Bitmap srcThumb, int startX, int startY,
+            IRemoteCallback startedCallback);
     void executeAppTransition();
     void setAppStartingWindow(IBinder token, String pkg, int theme,
             in CompatibilityInfo compatInfo, CharSequence nonLocalizedLabel, int labelRes,
diff --git a/core/java/android/view/animation/Animation.java b/core/java/android/view/animation/Animation.java
index dc8c71b7..d92ebcd 100644
--- a/core/java/android/view/animation/Animation.java
+++ b/core/java/android/view/animation/Animation.java
@@ -875,7 +875,7 @@
      * otherwise.
      *
      * @param currentTime Where we are in the animation. This is wall clock time.
-     * @param outTransformation A tranformation object that is provided by the
+     * @param outTransformation A transformation object that is provided by the
      *        caller and will be filled in by the animation.
      * @param scale Scaling factor to apply to any inputs to the transform operation, such
      *        pivot points being rotated or scaled around.
diff --git a/core/java/android/view/animation/Transformation.java b/core/java/android/view/animation/Transformation.java
index cf210c8..e8c1d23 100644
--- a/core/java/android/view/animation/Transformation.java
+++ b/core/java/android/view/animation/Transformation.java
@@ -112,6 +112,16 @@
     }
     
     /**
+     * Like {@link #compose(Transformation)} but does this.postConcat(t) of
+     * the transformation matrix.
+     * @hide
+     */
+    public void postCompose(Transformation t) {
+        mAlpha *= t.getAlpha();
+        mMatrix.postConcat(t.getMatrix());
+    }
+
+    /**
      * @return The 3x3 Matrix representing the trnasformation to apply to the
      * coordinates of the object being animated
      */
diff --git a/core/java/com/android/internal/app/HeavyWeightSwitcherActivity.java b/core/java/com/android/internal/app/HeavyWeightSwitcherActivity.java
index ef7e651..3d46cdd 100644
--- a/core/java/com/android/internal/app/HeavyWeightSwitcherActivity.java
+++ b/core/java/com/android/internal/app/HeavyWeightSwitcherActivity.java
@@ -123,7 +123,7 @@
     private OnClickListener mSwitchOldListener = new OnClickListener() {
         public void onClick(View v) {
             try {
-                ActivityManagerNative.getDefault().moveTaskToFront(mCurTask, 0);
+                ActivityManagerNative.getDefault().moveTaskToFront(mCurTask, 0, null);
             } catch (RemoteException e) {
             }
             finish();
diff --git a/graphics/java/android/renderscript/Allocation.java b/graphics/java/android/renderscript/Allocation.java
index adeeaca..a76a628 100644
--- a/graphics/java/android/renderscript/Allocation.java
+++ b/graphics/java/android/renderscript/Allocation.java
@@ -188,6 +188,40 @@
         return getID();
     }
 
+
+   /**
+     * Get the element of the type of the Allocation.
+     *
+     * @hide
+     * @return Element
+     *
+     */
+    public Element getElement() {
+        return mType.getElement();
+    }
+
+    /**
+     * Get the usage flags of the Allocation.
+     *
+     * @hide
+     * @return usage
+     *
+     */
+    public int getUsage() {
+        return mUsage;
+    }
+
+    /**
+     * Get the size of the Allocation in bytes.
+     *
+     * @hide
+     * @return sizeInBytes
+     *
+     */
+    public int getSizeBytes() {
+        return mType.getCount() * mType.getElement().getSizeBytes();
+    }
+
     private void updateCacheInfo(Type t) {
         mCurrentDimX = t.getX();
         mCurrentDimY = t.getY();
@@ -294,10 +328,21 @@
         }
     }
 
+    /**
+     * Get the type of the Allocation.
+     *
+     * @return Type
+     *
+     */
     public Type getType() {
         return mType;
     }
 
+    /**
+     * Propagate changes from one usage of the allocation to the
+     * remaining usages of the allocation.
+     *
+     */
     public void syncAll(int srcLocation) {
         switch (srcLocation) {
         case USAGE_SCRIPT:
@@ -343,6 +388,11 @@
         mRS.nAllocationIoReceive(getID());
     }
 
+    /**
+     * Copy an array of RS objects to the allocation.
+     *
+     * @param d Source array.
+     */
     public void copyFrom(BaseObj[] d) {
         mRS.validate();
         validateIsObject();
diff --git a/packages/SystemUI/src/com/android/systemui/recent/RecentsPanelView.java b/packages/SystemUI/src/com/android/systemui/recent/RecentsPanelView.java
index 66cb32c..564b07b 100644
--- a/packages/SystemUI/src/com/android/systemui/recent/RecentsPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/recent/RecentsPanelView.java
@@ -19,6 +19,7 @@
 import android.animation.Animator;
 import android.animation.LayoutTransition;
 import android.app.ActivityManager;
+import android.app.ActivityOptions;
 import android.content.Context;
 import android.content.Intent;
 import android.content.res.Configuration;
@@ -615,10 +616,11 @@
         if (!mFirstScreenful && tasks.size() == 0) {
             return;
         }
-        mNumItemsWaitingForThumbnailsAndIcons =
-                mFirstScreenful ? tasks.size() : mRecentTaskDescriptions.size();
+        mNumItemsWaitingForThumbnailsAndIcons = mFirstScreenful 
+                ? tasks.size() : mRecentTaskDescriptions == null 
+                        ? 0 : mRecentTaskDescriptions.size();
         if (mRecentTaskDescriptions == null) {
-            mRecentTaskDescriptions = new ArrayList(tasks);
+            mRecentTaskDescriptions = new ArrayList<TaskDescription>(tasks);
         } else {
             mRecentTaskDescriptions.addAll(tasks);
         }
@@ -656,22 +658,33 @@
     }
 
     public void handleOnClick(View view) {
-        TaskDescription ad = ((ViewHolder) view.getTag()).taskDescription;
+        ViewHolder holder = (ViewHolder)view.getTag();
+        TaskDescription ad = holder.taskDescription;
         final Context context = view.getContext();
         final ActivityManager am = (ActivityManager)
                 context.getSystemService(Context.ACTIVITY_SERVICE);
+        holder.thumbnailViewImage.setDrawingCacheEnabled(true);
+        Bitmap bm = holder.thumbnailViewImage.getDrawingCache();
+        ActivityOptions opts = ActivityOptions.makeThumbnailScaleUpAnimation(
+                holder.thumbnailViewImage, bm, 0, 0,
+                new ActivityOptions.OnAnimationStartedListener() {
+                    @Override public void onAnimationStarted() {
+                        hide(true);
+                    }
+                });
         if (ad.taskId >= 0) {
             // This is an active task; it should just go to the foreground.
-            am.moveTaskToFront(ad.taskId, ActivityManager.MOVE_TASK_WITH_HOME);
+            am.moveTaskToFront(ad.taskId, ActivityManager.MOVE_TASK_WITH_HOME,
+                    opts.toBundle());
         } else {
             Intent intent = ad.intent;
             intent.addFlags(Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY
                     | Intent.FLAG_ACTIVITY_TASK_ON_HOME
                     | Intent.FLAG_ACTIVITY_NEW_TASK);
             if (DEBUG) Log.v(TAG, "Starting activity " + intent);
-            context.startActivity(intent);
+            context.startActivity(intent, opts.toBundle());
         }
-        hide(true);
+        holder.thumbnailViewImage.setDrawingCacheEnabled(false);
     }
 
     public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index b422678..60749b3 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -34,6 +34,7 @@
 import android.app.Activity;
 import android.app.ActivityManager;
 import android.app.ActivityManagerNative;
+import android.app.ActivityOptions;
 import android.app.ActivityThread;
 import android.app.AlertDialog;
 import android.app.AppGlobals;
@@ -2353,10 +2354,12 @@
         synchronized (this) {
             ActivityRecord r = mMainStack.isInStackLocked(callingActivity);
             if (r == null) {
+                ActivityOptions.abort(options);
                 return false;
             }
             if (r.app == null || r.app.thread == null) {
                 // The caller is not running...  d'oh!
+                ActivityOptions.abort(options);
                 return false;
             }
             intent = new Intent(intent);
@@ -2393,6 +2396,7 @@
 
             if (aInfo == null) {
                 // Nobody who is next!
+                ActivityOptions.abort(options);
                 return false;
             }
 
@@ -2422,8 +2426,6 @@
             }
 
             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 = mMainStack.startActivityLocked(r.app.thread, intent,
                     r.resolvedType, aInfo, resultTo != null ? resultTo.appToken : null,
                     resultWho, requestCode, -1, r.launchedFromUid, 0,
@@ -3653,7 +3655,7 @@
                 }
                 lastTask = r.task;
                 if (r.stack.finishActivityLocked(r, i, Activity.RESULT_CANCELED,
-                        null, "force-stop")) {
+                        null, "force-stop", true)) {
                     i--;
                 }
             }
@@ -5686,13 +5688,14 @@
     /**
      * TODO: Add mController hook
      */
-    public void moveTaskToFront(int task, int flags) {
+    public void moveTaskToFront(int task, int flags, Bundle options) {
         enforceCallingPermission(android.Manifest.permission.REORDER_TASKS,
                 "moveTaskToFront()");
 
         synchronized(this) {
             if (!checkAppSwitchAllowedLocked(Binder.getCallingPid(),
                     Binder.getCallingUid(), "Task to front")) {
+                ActivityOptions.abort(options);
                 return;
             }
             final long origId = Binder.clearCallingIdentity();
@@ -5707,7 +5710,7 @@
                         // we'll just move the home task to the top first.
                         mMainStack.moveHomeToFrontLocked();
                     }
-                    mMainStack.moveTaskToFrontLocked(tr, null);
+                    mMainStack.moveTaskToFrontLocked(tr, null, options);
                     return;
                 }
                 for (int i=mMainStack.mHistory.size()-1; i>=0; i--) {
@@ -5721,13 +5724,14 @@
                             // we'll just move the home task to the top first.
                             mMainStack.moveHomeToFrontLocked();
                         }
-                        mMainStack.moveTaskToFrontLocked(hr.task, null);
+                        mMainStack.moveTaskToFrontLocked(hr.task, null, options);
                         return;
                     }
                 }
             } finally {
                 Binder.restoreCallingIdentity(origId);
             }
+            ActivityOptions.abort(options);
         }
     }
 
@@ -6993,7 +6997,43 @@
         }
         return killed;
     }
-    
+
+    @Override
+    public boolean killProcessesBelowForeground(String reason) {
+        if (Binder.getCallingUid() != Process.SYSTEM_UID) {
+            throw new SecurityException("killProcessesBelowForeground() only available to system");
+        }
+
+        return killProcessesBelowAdj(ProcessList.FOREGROUND_APP_ADJ, reason);
+    }
+
+    private boolean killProcessesBelowAdj(int belowAdj, String reason) {
+        if (Binder.getCallingUid() != Process.SYSTEM_UID) {
+            throw new SecurityException("killProcessesBelowAdj() only available to system");
+        }
+
+        boolean killed = false;
+        synchronized (mPidsSelfLocked) {
+            final int size = mPidsSelfLocked.size();
+            for (int i = 0; i < size; i++) {
+                final int pid = mPidsSelfLocked.keyAt(i);
+                final ProcessRecord proc = mPidsSelfLocked.valueAt(i);
+                if (proc == null) continue;
+
+                final int adj = proc.setAdj;
+                if (adj > belowAdj && !proc.killedBackground) {
+                    Slog.w(TAG, "Killing " + proc + " (adj " + adj + "): " + reason);
+                    EventLog.writeEvent(
+                            EventLogTags.AM_KILL, proc.pid, proc.processName, adj, reason);
+                    killed = true;
+                    proc.killedBackground = true;
+                    Process.killProcessQuiet(pid);
+                }
+            }
+        }
+        return killed;
+    }
+
     public final void startRunning(String pkg, String cls, String action,
             String data) {
         synchronized(this) {
diff --git a/services/java/com/android/server/am/ActivityRecord.java b/services/java/com/android/server/am/ActivityRecord.java
index 53cb2b0..d60ff2b 100644
--- a/services/java/com/android/server/am/ActivityRecord.java
+++ b/services/java/com/android/server/am/ActivityRecord.java
@@ -26,7 +26,6 @@
 import android.content.pm.ActivityInfo;
 import android.content.pm.ApplicationInfo;
 import android.content.res.CompatibilityInfo;
-import android.content.pm.PackageManager;
 import android.content.res.Configuration;
 import android.graphics.Bitmap;
 import android.os.Build;
@@ -542,24 +541,38 @@
 
     void updateOptionsLocked(Bundle options) {
         if (options != null) {
+            if (pendingOptions != null) {
+                pendingOptions.abort();
+            }
             pendingOptions = new ActivityOptions(options);
         }
     }
 
     void applyOptionsLocked() {
         if (pendingOptions != null) {
-            if (pendingOptions.isCustomAnimation()) {
-                service.mWindowManager.overridePendingAppTransition(
-                        pendingOptions.getPackageName(),
-                        pendingOptions.getCustomEnterResId(),
-                        pendingOptions.getCustomExitResId());
+            switch (pendingOptions.getAnimationType()) {
+                case ActivityOptions.ANIM_CUSTOM:
+                    service.mWindowManager.overridePendingAppTransition(
+                            pendingOptions.getPackageName(),
+                            pendingOptions.getCustomEnterResId(),
+                            pendingOptions.getCustomExitResId());
+                    break;
+                case ActivityOptions.ANIM_THUMBNAIL:
+                    service.mWindowManager.overridePendingAppTransitionThumb(
+                            pendingOptions.getThumbnail(),
+                            pendingOptions.getStartX(), pendingOptions.getStartY(),
+                            pendingOptions.getOnAnimationStartListener());
+                    break;
             }
             pendingOptions = null;
         }
     }
 
     void clearOptionsLocked() {
-        pendingOptions = null;
+        if (pendingOptions != null) {
+            pendingOptions.abort();
+            pendingOptions = null;
+        }
     }
 
     void removeUriPermissionsLocked() {
diff --git a/services/java/com/android/server/am/ActivityStack.java b/services/java/com/android/server/am/ActivityStack.java
index 48b4f4ff..a01ed25 100644
--- a/services/java/com/android/server/am/ActivityStack.java
+++ b/services/java/com/android/server/am/ActivityStack.java
@@ -1695,6 +1695,7 @@
                         if (VALIDATE_TOKENS) {
                             validateAppTokensLocked();
                         }
+                        ActivityOptions.abort(options);
                         return;
                     }
                     break;
@@ -1797,6 +1798,7 @@
             // because there is nothing for it to animate on top of.
             mService.mWindowManager.addAppToken(addPos, r.appToken, r.task.taskId,
                     r.info.screenOrientation, r.fullscreen);
+            ActivityOptions.abort(options);
         }
         if (VALIDATE_TOKENS) {
             validateAppTokensLocked();
@@ -2344,6 +2346,7 @@
             // Transfer the result target from the source activity to the new
             // one being started, including any failures.
             if (requestCode >= 0) {
+                ActivityOptions.abort(options);
                 return ActivityManager.START_FORWARD_AND_REQUEST_CONFLICT;
             }
             resultRecord = sourceRecord.resultTo;
@@ -2375,6 +2378,7 @@
                     Activity.RESULT_CANCELED, null);
             }
             mDismissKeyguardOnNextActivity = false;
+            ActivityOptions.abort(options);
             return err;
         }
 
@@ -2425,6 +2429,7 @@
                     // We pretend to the caller that it was really started, but
                     // they will just get a cancel result.
                     mDismissKeyguardOnNextActivity = false;
+                    ActivityOptions.abort(options);
                     return ActivityManager.START_SUCCESS;
                 }
             }
@@ -2447,6 +2452,7 @@
                     pal.startFlags = startFlags;
                     mService.mPendingActivityLaunches.add(pal);
                     mDismissKeyguardOnNextActivity = false;
+                    ActivityOptions.abort(options);
                     return ActivityManager.START_SWITCHES_CANCELED;
                 }
             }
@@ -2601,8 +2607,7 @@
                             // We really do want to push this one into the
                             // user's face, right now.
                             moveHomeToFrontFromLaunchLocked(launchFlags);
-                            r.updateOptionsLocked(options);
-                            moveTaskToFrontLocked(taskTop.task, r);
+                            moveTaskToFrontLocked(taskTop.task, r, options);
                         }
                     }
                     // If the caller has requested that the target task be
@@ -2618,6 +2623,7 @@
                         if (doResume) {
                             resumeTopActivityLocked(null);
                         }
+                        ActivityOptions.abort(options);
                         return ActivityManager.START_RETURN_INTENT_TO_CALLER;
                     }
                     if ((launchFlags &
@@ -2705,6 +2711,7 @@
                         if (doResume) {
                             resumeTopActivityLocked(null);
                         }
+                        ActivityOptions.abort(options);
                         return ActivityManager.START_TASK_TO_FRONT;
                     }
                 }
@@ -2734,6 +2741,7 @@
                             if (doResume) {
                                 resumeTopActivityLocked(null);
                             }
+                            ActivityOptions.abort(options);
                             if ((startFlags&ActivityManager.START_FLAG_ONLY_IF_NEEDED) != 0) {
                                 // We don't need to start a new activity, and
                                 // the client said not to do anything if that
@@ -2753,6 +2761,7 @@
                         r.resultTo, r.resultWho, r.requestCode,
                     Activity.RESULT_CANCELED, null);
             }
+            ActivityOptions.abort(options);
             return ActivityManager.START_CLASS_NOT_FOUND;
         }
 
@@ -2794,6 +2803,7 @@
                     if (doResume) {
                         resumeTopActivityLocked(null);
                     }
+                    ActivityOptions.abort(options);
                     return ActivityManager.START_DELIVERED_TO_TOP;
                 }
             } else if (!addingToTask &&
@@ -2948,6 +2958,7 @@
                                 Slog.w(TAG, "Unable to find app for caller " + caller
                                       + " (pid=" + realCallingPid + ") when starting: "
                                       + intent.toString());
+                                ActivityOptions.abort(options);
                                 return ActivityManager.START_PERMISSION_DENIED;
                             }
                         }
@@ -3480,6 +3491,15 @@
      */
     final boolean finishActivityLocked(ActivityRecord r, int index,
             int resultCode, Intent resultData, String reason) {
+        return finishActivityLocked(r, index, resultCode, resultData, reason, false);
+    }
+
+    /**
+     * @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, boolean immediate) {
         if (r.finishing) {
             Slog.w(TAG, "Duplicate finish request for " + r);
             return false;
@@ -3521,7 +3541,10 @@
             mService.mCancelledThumbnails.add(r);
         }
 
-        if (mResumedActivity == r) {
+        if (immediate) {
+            return finishCurrentActivityLocked(r, index,
+                    FINISH_IMMEDIATELY) == null;
+        } else if (mResumedActivity == r) {
             boolean endTask = index <= 0
                     || (mHistory.get(index-1)).task != r.task;
             if (DEBUG_TRANSITION) Slog.v(TAG,
@@ -3887,12 +3910,12 @@
             }
         }
         if (homeTask != null) {
-            moveTaskToFrontLocked(homeTask, null);
+            moveTaskToFrontLocked(homeTask, null, null);
         }
     }
 
 
-    final void moveTaskToFrontLocked(TaskRecord tr, ActivityRecord reason) {
+    final void moveTaskToFrontLocked(TaskRecord tr, ActivityRecord reason, Bundle options) {
         if (DEBUG_SWITCH) Slog.v(TAG, "moveTaskToFront: " + tr);
 
         final int task = tr.taskId;
@@ -3900,6 +3923,7 @@
 
         if (top < 0 || (mHistory.get(top)).task.taskId == task) {
             // nothing to do!
+            ActivityOptions.abort(options);
             return;
         }
 
@@ -3941,7 +3965,16 @@
             if (r != null) {
                 mNoAnimActivities.add(r);
             }
+            ActivityOptions.abort(options);
         } else {
+            if (options != null) {
+                ActivityRecord r = topRunningActivityLocked(null);
+                if (r != null && r.state != ActivityState.RESUMED) {
+                    r.updateOptionsLocked(options);
+                } else {
+                    ActivityOptions.abort(options);
+                }
+            }
             mService.mWindowManager.prepareAppTransition(
                     WindowManagerPolicy.TRANSIT_TASK_TO_FRONT, false);
         }
diff --git a/services/java/com/android/server/pm/PackageManagerService.java b/services/java/com/android/server/pm/PackageManagerService.java
index 95666c0..067bf28 100644
--- a/services/java/com/android/server/pm/PackageManagerService.java
+++ b/services/java/com/android/server/pm/PackageManagerService.java
@@ -1587,7 +1587,16 @@
             if (p != null) {
                 final PackageSetting ps = (PackageSetting)p.mExtras;
                 final SharedUserSetting suid = ps.sharedUser;
-                return suid != null ? suid.gids : ps.gids;
+                int[] gids = suid != null ? suid.gids : ps.gids;
+
+                // include GIDs for any unenforced permissions
+                if (!isPermissionEnforcedLocked(READ_EXTERNAL_STORAGE)) {
+                    final BasePermission basePerm = mSettings.mPermissions.get(
+                            READ_EXTERNAL_STORAGE);
+                    gids = appendInts(gids, basePerm.gids);
+                }
+
+                return gids;
             }
         }
         // stupid thing to indicate an error.
@@ -8890,6 +8899,19 @@
                 if (mSettings.mReadExternalStorageEnforcement != enforcement) {
                     mSettings.mReadExternalStorageEnforcement = enforcement;
                     mSettings.writeLPr();
+
+                    // kill any non-foreground processes so we restart them and
+                    // grant/revoke the GID.
+                    final IActivityManager am = ActivityManagerNative.getDefault();
+                    if (am != null) {
+                        final long token = Binder.clearCallingIdentity();
+                        try {
+                            am.killProcessesBelowForeground("setPermissionEnforcement");
+                        } catch (RemoteException e) {
+                        } finally {
+                            Binder.restoreCallingIdentity(token);
+                        }
+                    }
                 }
             }
         } else {
diff --git a/services/java/com/android/server/wm/AppWindowToken.java b/services/java/com/android/server/wm/AppWindowToken.java
index 0e110be..c29ef3f 100644
--- a/services/java/com/android/server/wm/AppWindowToken.java
+++ b/services/java/com/android/server/wm/AppWindowToken.java
@@ -21,10 +21,12 @@
 import com.android.server.wm.WindowManagerService.H;
 
 import android.content.pm.ActivityInfo;
+import android.graphics.Matrix;
 import android.os.Message;
 import android.os.RemoteException;
 import android.util.Slog;
 import android.view.IApplicationToken;
+import android.view.Surface;
 import android.view.View;
 import android.view.WindowManager;
 import android.view.WindowManagerPolicy;
@@ -90,6 +92,7 @@
 
     boolean animating;
     Animation animation;
+    boolean animInitialized;
     boolean hasTransformation;
     final Transformation transformation = new Transformation();
 
@@ -105,6 +108,15 @@
     boolean startingMoved;
     boolean firstWindowDrawn;
 
+    // Special surface for thumbnail animation.
+    Surface thumbnail;
+    int thumbnailTransactionSeq;
+    int thumbnailX;
+    int thumbnailY;
+    int thumbnailLayer;
+    Animation thumbnailAnimation;
+    final Transformation thumbnailTransformation = new Transformation();
+
     // Input application handle used by the input dispatcher.
     final InputApplicationHandle mInputApplicationHandle;
 
@@ -116,11 +128,12 @@
         mInputApplicationHandle = new InputApplicationHandle(this);
     }
 
-    public void setAnimation(Animation anim) {
+    public void setAnimation(Animation anim, boolean initialized) {
         if (WindowManagerService.localLOGV) Slog.v(
             WindowManagerService.TAG, "Setting animation in " + this + ": " + anim);
         animation = anim;
         animating = false;
+        animInitialized = initialized;
         anim.restrictDuration(WindowManagerService.MAX_ANIMATION_DURATION);
         anim.scaleCurrentDuration(service.mTransitionAnimationScale);
         int zorder = anim.getZAdjustment();
@@ -146,6 +159,7 @@
             if (WindowManagerService.localLOGV) Slog.v(
                 WindowManagerService.TAG, "Setting dummy animation in " + this);
             animation = WindowManagerService.sDummyAnimation;
+            animInitialized = false;
         }
     }
 
@@ -153,15 +167,28 @@
         if (animation != null) {
             animation = null;
             animating = true;
+            animInitialized = false;
+        }
+        clearThumbnail();
+    }
+
+    public void clearThumbnail() {
+        if (thumbnail != null) {
+            thumbnail.destroy();
+            thumbnail = null;
         }
     }
 
     void updateLayers() {
         final int N = allAppWindows.size();
         final int adj = animLayerAdjustment;
+        thumbnailLayer = -1;
         for (int i=0; i<N; i++) {
             WindowState w = allAppWindows.get(i);
             w.mAnimLayer = w.mLayer + adj;
+            if (w.mAnimLayer > thumbnailLayer) {
+                thumbnailLayer = w.mAnimLayer;
+            }
             if (WindowManagerService.DEBUG_LAYERS) Slog.v(WindowManagerService.TAG, "Updating layer " + w + ": "
                     + w.mAnimLayer);
             if (w == service.mInputMethodTarget && !service.mInputMethodTargetWaitingAnim) {
@@ -203,6 +230,38 @@
         return isAnimating;
     }
 
+    private void stepThumbnailAnimation(long currentTime) {
+        thumbnailTransformation.clear();
+        thumbnailAnimation.getTransformation(currentTime, thumbnailTransformation);
+        thumbnailTransformation.getMatrix().preTranslate(thumbnailX, thumbnailY);
+        final boolean screenAnimation = service.mAnimator.mScreenRotationAnimation != null
+                && service.mAnimator.mScreenRotationAnimation.isAnimating();
+        if (screenAnimation) {
+            thumbnailTransformation.postCompose(
+                    service.mAnimator.mScreenRotationAnimation.getEnterTransformation());
+        }
+        // cache often used attributes locally
+        final float tmpFloats[] = service.mTmpFloats;
+        thumbnailTransformation.getMatrix().getValues(tmpFloats);
+        if (WindowManagerService.SHOW_TRANSACTIONS) service.logSurface(thumbnail,
+                "thumbnail", "POS " + tmpFloats[Matrix.MTRANS_X]
+                + ", " + tmpFloats[Matrix.MTRANS_Y], null);
+        thumbnail.setPosition(tmpFloats[Matrix.MTRANS_X], tmpFloats[Matrix.MTRANS_Y]);
+        if (WindowManagerService.SHOW_TRANSACTIONS) service.logSurface(thumbnail,
+                "thumbnail", "alpha=" + thumbnailTransformation.getAlpha()
+                + " layer=" + thumbnailLayer
+                + " matrix=[" + tmpFloats[Matrix.MSCALE_X]
+                + "," + tmpFloats[Matrix.MSKEW_Y]
+                + "][" + tmpFloats[Matrix.MSKEW_X]
+                + "," + tmpFloats[Matrix.MSCALE_Y] + "]", null);
+        thumbnail.setAlpha(thumbnailTransformation.getAlpha());
+        // The thumbnail is layered below the window immediately above this
+        // token's anim layer.
+        thumbnail.setLayer(thumbnailLayer + WindowManagerService.WINDOW_LAYER_MULTIPLIER
+                - WindowManagerService.LAYER_OFFSET_THUMBNAIL);
+        thumbnail.setMatrix(tmpFloats[Matrix.MSCALE_X], tmpFloats[Matrix.MSKEW_Y],
+                tmpFloats[Matrix.MSKEW_X], tmpFloats[Matrix.MSCALE_Y]);
+    }
 
     private boolean stepAnimation(long currentTime) {
         if (animation == null) {
@@ -215,6 +274,7 @@
             ": more=" + more + ", xform=" + transformation);
         if (!more) {
             animation = null;
+            clearThumbnail();
             if (WindowManagerService.DEBUG_ANIM) Slog.v(
                 WindowManagerService.TAG, "Finished animation in " + this +
                 " @ " + currentTime);
@@ -243,12 +303,22 @@
                         " @ " + currentTime + ": dw=" + dw + " dh=" + dh
                         + " scale=" + service.mTransitionAnimationScale
                         + " allDrawn=" + allDrawn + " animating=" + animating);
-                    animation.initialize(dw, dh, dw, dh);
+                    if (!animInitialized) {
+                        animation.initialize(dw, dh, dw, dh);
+                    }
                     animation.setStartTime(currentTime);
                     animating = true;
+                    if (thumbnail != null) {
+                        thumbnail.show();
+                        thumbnailAnimation.setStartTime(currentTime);
+                    }
                 }
                 if (stepAnimation(currentTime)) {
-                    // we're done!
+                    // animation isn't over, step any thumbnail and that's
+                    // it for now.
+                    if (thumbnail != null) {
+                        stepThumbnailAnimation(currentTime);
+                    }
                     return true;
                 }
             }
@@ -440,6 +510,15 @@
                     pw.print(" startingDisplayed="); pw.print(startingDisplayed);
                     pw.print(" startingMoved"); pw.println(startingMoved);
         }
+        if (thumbnail != null) {
+            pw.print(prefix); pw.print("thumbnail="); pw.print(thumbnail);
+                    pw.print(" x="); pw.print(thumbnailX);
+                    pw.print(" y="); pw.print(thumbnailY);
+                    pw.print(" layer="); pw.println(thumbnailLayer);
+            pw.print(prefix); pw.print("thumbnailAnimation="); pw.println(thumbnailAnimation);
+            pw.print(prefix); pw.print("thumbnailTransformation=");
+                    pw.println(thumbnailTransformation.toShortString());
+        }
     }
 
     @Override
diff --git a/services/java/com/android/server/wm/WindowAnimator.java b/services/java/com/android/server/wm/WindowAnimator.java
index b3dbee1..7aa6716 100644
--- a/services/java/com/android/server/wm/WindowAnimator.java
+++ b/services/java/com/android/server/wm/WindowAnimator.java
@@ -23,7 +23,7 @@
  * on behalf of WindowManagerService.
  */
 public class WindowAnimator {
-    private static final String TAG = "WindowAnimations";
+    private static final String TAG = "WindowAnimator";
 
     final WindowManagerService mService;
     final Context mContext;
@@ -67,8 +67,24 @@
         final int NAT = mService.mAppTokens.size();
         for (i=0; i<NAT; i++) {
             final AppWindowToken appToken = mService.mAppTokens.get(i);
+            final boolean wasAnimating = appToken.animation != null;
             if (appToken.stepAnimationLocked(mCurrentTime, mInnerDw, mInnerDh)) {
                 mAnimating = true;
+            } else if (wasAnimating) {
+                // stopped animating, do one more pass through the layout
+                mPendingLayoutChanges |= WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
+            }
+        }
+        
+        final int NEAT = mService.mExitingAppTokens.size();
+        for (i=0; i<NEAT; i++) {
+            final AppWindowToken appToken = mService.mExitingAppTokens.get(i);
+            final boolean wasAnimating = appToken.animation != null;
+            if (appToken.stepAnimationLocked(mCurrentTime, mInnerDw, mInnerDh)) {
+                mAnimating = true;
+            } else if (wasAnimating) {
+                // stopped animating, do one more pass through the layout
+                mPendingLayoutChanges |= WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
             }
         }
 
@@ -273,6 +289,15 @@
                 w.performShowLocked();
                 mPendingLayoutChanges |= WindowManagerPolicy.FINISH_LAYOUT_REDO_ANIM;
             }
+            if (atoken != null && atoken.thumbnail != null) {
+                if (atoken.thumbnailTransactionSeq != mTransactionSequence) {
+                    atoken.thumbnailTransactionSeq = mTransactionSequence;
+                    atoken.thumbnailLayer = 0;
+                }
+                if (atoken.thumbnailLayer < w.mAnimLayer) {
+                    atoken.thumbnailLayer = w.mAnimLayer;
+                }
+            }
         } // end forall windows
     }
 
@@ -517,6 +542,7 @@
     }
 
     void animate() {
+        mPendingLayoutChanges = 0;
         mCurrentTime = SystemClock.uptimeMillis();
 
         // Update animations of all applications, including those
diff --git a/services/java/com/android/server/wm/WindowManagerService.java b/services/java/com/android/server/wm/WindowManagerService.java
index 26367d2..a978b35 100644
--- a/services/java/com/android/server/wm/WindowManagerService.java
+++ b/services/java/com/android/server/wm/WindowManagerService.java
@@ -117,8 +117,12 @@
 import android.view.WindowManagerPolicy;
 import android.view.WindowManager.LayoutParams;
 import android.view.WindowManagerPolicy.FakeWindow;
+import android.view.animation.AlphaAnimation;
 import android.view.animation.Animation;
+import android.view.animation.AnimationSet;
 import android.view.animation.AnimationUtils;
+import android.view.animation.Interpolator;
+import android.view.animation.ScaleAnimation;
 import android.view.animation.Transformation;
 
 import java.io.BufferedWriter;
@@ -194,6 +198,18 @@
     static final int LAYER_OFFSET_DIM = 1;
 
     /**
+     * Blur surface layer is immediately below dim layer.
+     */
+    static final int LAYER_OFFSET_BLUR = 2;
+
+    /**
+     * Animation thumbnail is as far as possible below the window above
+     * the thumbnail (or in other words as far as possible above the window
+     * below it).
+     */
+    static final int LAYER_OFFSET_THUMBNAIL = WINDOW_LAYER_MULTIPLIER-1;
+
+    /**
      * Layer at which to put the rotation freeze snapshot.
      */
     static final int FREEZE_LAYER = (TYPE_LAYER_MULTIPLIER * 200) + 1;
@@ -479,8 +495,12 @@
     // made visible or hidden at the next transition.
     int mNextAppTransition = WindowManagerPolicy.TRANSIT_UNSET;
     String mNextAppTransitionPackage;
+    Bitmap mNextAppTransitionThumbnail;
+    IRemoteCallback mNextAppTransitionCallback;
     int mNextAppTransitionEnter;
     int mNextAppTransitionExit;
+    int mNextAppTransitionStartX;
+    int mNextAppTransitionStartY;
     boolean mAppTransitionReady = false;
     boolean mAppTransitionRunning = false;
     boolean mAppTransitionTimeout = false;
@@ -588,6 +608,9 @@
     }
     LayoutAndSurfaceFields mInnerFields = new LayoutAndSurfaceFields();
 
+    /** Only do a maximum of 6 repeated layouts. After that quit */
+    private int mLayoutRepeatCount;
+
     private final class AnimationRunnable implements Runnable {
         @Override
         public void run() {
@@ -1897,7 +1920,7 @@
             rawChanged = true;
         }
 
-        if (rawChanged && (wallpaperWin.getAttrs().privateFlags &
+        if (rawChanged && (wallpaperWin.mAttrs.privateFlags &
                     WindowManager.LayoutParams.PRIVATE_FLAG_WANTS_OFFSET_NOTIFICATIONS) != 0) {
             try {
                 if (DEBUG_WALLPAPER) Slog.v(TAG, "Report new wp offset "
@@ -2296,7 +2319,7 @@
             if (wasVisible) {
 
                 int transit = WindowManagerPolicy.TRANSIT_EXIT;
-                if (win.getAttrs().type == TYPE_APPLICATION_STARTING) {
+                if (win.mAttrs.type == TYPE_APPLICATION_STARTING) {
                     transit = WindowManagerPolicy.TRANSIT_PREVIEW_DONE;
                 }
                 // Try starting an animation.
@@ -2441,6 +2464,15 @@
             Slog.i(TAG, str);
         }
     }
+
+    static void logSurface(Surface s, String title, String msg, RuntimeException where) {
+        String str = "  SURFACE " + s + ": " + msg + " / " + title;
+        if (where != null) {
+            Slog.i(TAG, str, where);
+        } else {
+            Slog.i(TAG, str);
+        }
+    }
     
     void setTransparentRegionWindow(Session session, IWindow client, Region region) {
         long origId = Binder.clearCallingIdentity();
@@ -2761,7 +2793,7 @@
                         // Try starting an animation; if there isn't one, we
                         // can destroy the surface right away.
                         int transit = WindowManagerPolicy.TRANSIT_EXIT;
-                        if (win.getAttrs().type == TYPE_APPLICATION_STARTING) {
+                        if (win.mAttrs.type == TYPE_APPLICATION_STARTING) {
                             transit = WindowManagerPolicy.TRANSIT_PREVIEW_DONE;
                         }
                         if (!win.mSurfacePendingDestroy && win.isWinVisibleLw() &&
@@ -3081,6 +3113,63 @@
         return null;
     }
 
+    private Animation createThumbnailAnimationLocked(int transit,
+            boolean enter, boolean thumb) {
+        Animation a;
+        final float thumbWidth = mNextAppTransitionThumbnail.getWidth();
+        final float thumbHeight = mNextAppTransitionThumbnail.getHeight();
+        // Pick the desired duration.  If this is an inter-activity transition,
+        // it  is the standard duration for that.  Otherwise we use the longer
+        // task transition duration.
+        int duration;
+        switch (transit) {
+            case WindowManagerPolicy.TRANSIT_ACTIVITY_OPEN:
+            case WindowManagerPolicy.TRANSIT_ACTIVITY_CLOSE:
+                duration = mContext.getResources().getInteger(
+                        com.android.internal.R.integer.config_shortAnimTime);
+                break;
+            default:
+                duration = 500;
+                break;
+            
+        }
+        if (thumb) {
+            // Animation for zooming thumbnail from its initial size to
+            // filling the screen.
+            Animation scale = new ScaleAnimation(
+                    1, mAppDisplayWidth/thumbWidth,
+                    1, mAppDisplayHeight/thumbHeight,
+                    mNextAppTransitionStartX + thumbWidth/2,
+                    mNextAppTransitionStartY + thumbHeight/2);
+            AnimationSet set = new AnimationSet(true);
+            Animation alpha = new AlphaAnimation(1, 0);
+            scale.setDuration(duration);
+            set.addAnimation(scale);
+            alpha.setDuration(duration);
+            set.addAnimation(alpha);
+            a = set;
+        } else if (enter) {
+            // Entering app zooms out from the center of the thumbnail.
+            a = new ScaleAnimation(
+                    thumbWidth/mAppDisplayWidth, 1,
+                    thumbHeight/mAppDisplayHeight, 1,
+                    mNextAppTransitionStartX + thumbWidth/2,
+                    mNextAppTransitionStartY + thumbHeight/2);
+            a.setDuration(duration);
+        } else {
+            // Exiting app just holds in place.
+            a = new AlphaAnimation(1, 1);
+            a.setDuration(duration);
+        }
+        a.setFillAfter(true);
+        final Interpolator interpolator = AnimationUtils.loadInterpolator(mContext,
+                com.android.internal.R.interpolator.decelerate_quint);
+        a.setInterpolator(interpolator);
+        a.initialize(mAppDisplayWidth, mAppDisplayHeight,
+                mAppDisplayWidth, mAppDisplayHeight);
+        return a;
+    }
+
     private boolean applyAnimationLocked(AppWindowToken wtoken,
             WindowManager.LayoutParams lp, int transit, boolean enter) {
         // Only apply an animation if the display isn't frozen.  If it is
@@ -3089,7 +3178,11 @@
         // is running.
         if (okToDisplay()) {
             Animation a;
-            if (mNextAppTransitionPackage != null) {
+            boolean initialized = false;
+            if (mNextAppTransitionThumbnail != null) {
+                a = createThumbnailAnimationLocked(transit, enter, false);
+                initialized = true;
+            } else if (mNextAppTransitionPackage != null) {
                 a = loadAnimation(mNextAppTransitionPackage, enter ?
                         mNextAppTransitionEnter : mNextAppTransitionExit);
             } else {
@@ -3161,7 +3254,7 @@
                     }
                     Slog.v(TAG, "Loaded animation " + a + " for " + wtoken, e);
                 }
-                wtoken.setAnimation(a);
+                wtoken.setAnimation(a, initialized);
             }
         } else {
             wtoken.clearAnimation();
@@ -3689,11 +3782,23 @@
             int enterAnim, int exitAnim) {
         if (mNextAppTransition != WindowManagerPolicy.TRANSIT_UNSET) {
             mNextAppTransitionPackage = packageName;
+            mNextAppTransitionThumbnail = null;
             mNextAppTransitionEnter = enterAnim;
             mNextAppTransitionExit = exitAnim;
         }
     }
 
+    public void overridePendingAppTransitionThumb(Bitmap srcThumb, int startX,
+            int startY, IRemoteCallback startedCallback) {
+        if (mNextAppTransition != WindowManagerPolicy.TRANSIT_UNSET) {
+            mNextAppTransitionPackage = null;
+            mNextAppTransitionThumbnail = srcThumb;
+            mNextAppTransitionStartX = startX;
+            mNextAppTransitionStartY = startY;
+            mNextAppTransitionCallback = startedCallback;
+        }
+    }
+
     public void executeAppTransition() {
         if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS,
                 "executeAppTransition()")) {
@@ -3837,6 +3942,19 @@
                         mH.sendMessageAtFrontOfQueue(m);
                         return;
                     }
+                    if (ttoken.thumbnail != null) {
+                        // The old token is animating with a thumbnail, transfer
+                        // that to the new token.
+                        if (wtoken.thumbnail != null) {
+                            wtoken.thumbnail.destroy();
+                        }
+                        wtoken.thumbnail = ttoken.thumbnail;
+                        wtoken.thumbnailX = ttoken.thumbnailX;
+                        wtoken.thumbnailY = ttoken.thumbnailY;
+                        wtoken.thumbnailLayer = ttoken.thumbnailLayer;
+                        wtoken.thumbnailAnimation = ttoken.thumbnailAnimation;
+                        ttoken.thumbnail = null;
+                    }
                 }
             }
 
@@ -4232,6 +4350,7 @@
                     // Make sure there is no animation running on this token,
                     // so any windows associated with it will be removed as
                     // soon as their animations are complete
+                    wtoken.clearAnimation();
                     wtoken.animation = null;
                     wtoken.animating = false;
                 }
@@ -7459,10 +7578,25 @@
 
             } else {
                 mInLayout = false;
-                if (mLayoutNeeded) {
-                    requestTraversalLocked();
-                }
             }
+
+            if (mLayoutNeeded) {
+                if (++mLayoutRepeatCount < 6) {
+                    requestTraversalLocked();
+                } else {
+                    Slog.e(TAG, "Performed 6 layouts in a row. Skipping");
+                    mLayoutRepeatCount = 0;
+                }
+            } else {
+                mLayoutRepeatCount = 0;
+            }
+            
+            if (mAnimator.mAnimating) {
+                // Do this even if requestTraversalLocked was called above so we get a frame drawn
+                // at the proper time as well as the one drawn early.
+                scheduleAnimationLocked();
+            }
+
             if (mWindowsChanged && !mWindowChangeListeners.isEmpty()) {
                 mH.removeMessages(H.REPORT_WINDOWS_CHANGE);
                 mH.sendMessage(mH.obtainMessage(H.REPORT_WINDOWS_CHANGE));
@@ -7784,11 +7918,15 @@
                 animLp = null;
             }
 
+            AppWindowToken topOpeningApp = null;
+            int topOpeningLayer = 0;
+
             NN = mOpeningApps.size();
             for (i=0; i<NN; i++) {
                 AppWindowToken wtoken = mOpeningApps.get(i);
                 if (DEBUG_APP_TRANSITIONS) Slog.v(TAG,
                         "Now opening app" + wtoken);
+                wtoken.clearThumbnail();
                 wtoken.reportedVisible = false;
                 wtoken.inPendingTransaction = false;
                 wtoken.animation = null;
@@ -7797,12 +7935,26 @@
                 wtoken.updateReportedVisibilityLocked();
                 wtoken.waitingToShow = false;
                 mAnimator.mAnimating |= wtoken.showAllWindowsLocked();
+                if (animLp != null) {
+                    int layer = -1;
+                    for (int j=0; j<wtoken.windows.size(); j++) {
+                        WindowState win = wtoken.windows.get(j);
+                        if (win.mAnimLayer > layer) {
+                            layer = win.mAnimLayer;
+                        }
+                    }
+                    if (topOpeningApp == null || layer > topOpeningLayer) {
+                        topOpeningApp = wtoken;
+                        topOpeningLayer = layer;
+                    }
+                }
             }
             NN = mClosingApps.size();
             for (i=0; i<NN; i++) {
                 AppWindowToken wtoken = mClosingApps.get(i);
                 if (DEBUG_APP_TRANSITIONS) Slog.v(TAG,
                         "Now closing app" + wtoken);
+                wtoken.clearThumbnail();
                 wtoken.inPendingTransaction = false;
                 wtoken.animation = null;
                 setTokenVisibilityLocked(wtoken, animLp, false,
@@ -7815,7 +7967,47 @@
                 wtoken.allDrawn = true;
             }
 
+            if (mNextAppTransitionThumbnail != null && topOpeningApp != null
+                    && topOpeningApp.animation != null) {
+                // This thumbnail animation is very special, we need to have
+                // an extra surface with the thumbnail included with the animation.
+                Rect dirty = new Rect(0, 0, mNextAppTransitionThumbnail.getWidth(),
+                        mNextAppTransitionThumbnail.getHeight());
+                try {
+                    Surface surface = new Surface(mFxSession, Process.myPid(),
+                            "thumbnail anim", 0, dirty.width(), dirty.height(),
+                            PixelFormat.TRANSLUCENT, Surface.HIDDEN);
+                    topOpeningApp.thumbnail = surface;
+                    if (SHOW_TRANSACTIONS) Slog.i(TAG, "  THUMBNAIL "
+                            + surface + ": CREATE");
+                    Surface drawSurface = new Surface();
+                    drawSurface.copyFrom(surface);
+                    Canvas c = drawSurface.lockCanvas(dirty);
+                    c.drawBitmap(mNextAppTransitionThumbnail, 0, 0, null);
+                    drawSurface.unlockCanvasAndPost(c);
+                    drawSurface.release();
+                    topOpeningApp.thumbnailLayer = topOpeningLayer;
+                    Animation anim = createThumbnailAnimationLocked(transit, true, true);
+                    topOpeningApp.thumbnailAnimation = anim;
+                    anim.restrictDuration(MAX_ANIMATION_DURATION);
+                    anim.scaleCurrentDuration(mTransitionAnimationScale);
+                    topOpeningApp.thumbnailX = mNextAppTransitionStartX;
+                    topOpeningApp.thumbnailY = mNextAppTransitionStartY;
+                } catch (Surface.OutOfResourcesException e) {
+                    Slog.e(TAG, "Can't allocate thumbnail surface w=" + dirty.width()
+                            + " h=" + dirty.height(), e);
+                    topOpeningApp.clearThumbnail();
+                }
+            }
+
             mNextAppTransitionPackage = null;
+            mNextAppTransitionThumbnail = null;
+            if (mNextAppTransitionCallback != null) {
+                try {
+                    mNextAppTransitionCallback.sendResult(null);
+                } catch (RemoteException e) {
+                }
+            }
 
             mOpeningApps.clear();
             mClosingApps.clear();
@@ -8192,6 +8384,7 @@
                         mLayoutNeeded = true;
                     }
                 }
+
                 if ((mPendingLayoutChanges & WindowManagerPolicy.FINISH_LAYOUT_REDO_CONFIG) != 0) {
                     if (DEBUG_LAYOUT) Slog.v(TAG, "Computing new config from layout");
                     if (updateOrientationFromAppTokensLocked(true)) {
@@ -8199,6 +8392,7 @@
                         mH.sendEmptyMessage(H.SEND_NEW_CONFIGURATION);
                     }
                 }
+
                 if ((mPendingLayoutChanges & WindowManagerPolicy.FINISH_LAYOUT_REDO_LAYOUT) != 0) {
                     mLayoutNeeded = true;
                 }
@@ -8400,6 +8594,7 @@
                 // Make sure there is no animation running on this token,
                 // so any windows associated with it will be removed as
                 // soon as their animations are complete
+                token.clearAnimation();
                 token.animation = null;
                 token.animating = false;
                 if (DEBUG_ADD_REMOVE || DEBUG_TOKEN_MOVEMENT) Slog.v(TAG,
@@ -8409,8 +8604,6 @@
             }
         }
 
-        boolean needRelayout = false;
-
         if (!mAnimator.mAnimating && mAppTransitionRunning) {
             // We have finished the animation of an app transition.  To do
             // this, we have delayed a lot of operations like showing and
@@ -8419,7 +8612,7 @@
             // be out of sync with it.  So here we will just rebuild the
             // entire app window list.  Fun!
             mAppTransitionRunning = false;
-            needRelayout = true;
+            mLayoutNeeded = true;
             rebuildAppWindowListLocked();
             assignLayersLocked();
             // Clear information about apps that were moving.
@@ -8430,19 +8623,10 @@
             mH.sendEmptyMessage(H.REPORT_LOSING_FOCUS);
         }
         if (wallpaperDestroyed) {
-            needRelayout = adjustWallpaperWindowsLocked() != 0;
+            mLayoutNeeded |= adjustWallpaperWindowsLocked() != 0;
         }
-        if ((mPendingLayoutChanges & (
-                WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER |
-                ADJUST_WALLPAPER_LAYERS_CHANGED |
-                WindowManagerPolicy.FINISH_LAYOUT_REDO_CONFIG |
-                WindowManagerPolicy.FINISH_LAYOUT_REDO_LAYOUT)) != 0) {
-            needRelayout = true;
-        }
-        if (needRelayout) {
-            requestTraversalLocked();
-        } else if (mAnimator.mAnimating) {
-            scheduleAnimationLocked();
+        if (mPendingLayoutChanges != 0) {
+            mLayoutNeeded = true;
         }
 
         // Finally update all input windows now that the window changes have stabilized.
@@ -8485,7 +8669,7 @@
             }
         }
 
-        if (mInnerFields.mOrientationChangeComplete && !needRelayout &&
+        if (mInnerFields.mOrientationChangeComplete && !mLayoutNeeded &&
                 !mInnerFields.mUpdateRotation) {
             checkDrawnWindowsLocked();
         }
@@ -8839,6 +9023,7 @@
         if (mNextAppTransition != WindowManagerPolicy.TRANSIT_UNSET) {
             mNextAppTransition = WindowManagerPolicy.TRANSIT_UNSET;
             mNextAppTransitionPackage = null;
+            mNextAppTransitionThumbnail = null;
             mAppTransitionReady = true;
         }
 
@@ -9399,6 +9584,12 @@
                     pw.print(" mNextAppTransitionExit=0x");
                     pw.print(Integer.toHexString(mNextAppTransitionExit));
             }
+            if (mNextAppTransitionThumbnail != null) {
+                pw.print("  mNextAppTransitionThumbnail=");
+                    pw.print(mNextAppTransitionThumbnail);
+                    pw.print(" mNextAppTransitionStartX="); pw.print(mNextAppTransitionStartX);
+                    pw.print(" mNextAppTransitionStartY="); pw.println(mNextAppTransitionStartY);
+            }
             pw.print("  mStartingIconInTransition="); pw.print(mStartingIconInTransition);
                     pw.print(", mSkipAppTransitionAnimation="); pw.println(mSkipAppTransitionAnimation);
         }
diff --git a/tests/permission/src/com/android/framework/permission/tests/ActivityManagerPermissionTests.java b/tests/permission/src/com/android/framework/permission/tests/ActivityManagerPermissionTests.java
index 4cacbc4..cceed16 100644
--- a/tests/permission/src/com/android/framework/permission/tests/ActivityManagerPermissionTests.java
+++ b/tests/permission/src/com/android/framework/permission/tests/ActivityManagerPermissionTests.java
@@ -39,7 +39,7 @@
     @SmallTest
 	public void testREORDER_TASKS() {
         try {
-            mAm.moveTaskToFront(0, 0);
+            mAm.moveTaskToFront(0, 0, null);
             fail("IActivityManager.moveTaskToFront did not throw SecurityException as"
                     + " expected");
         } catch (SecurityException e) {
diff --git a/tools/layoutlib/bridge/src/android/graphics/BitmapFactory_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/BitmapFactory_Delegate.java
index 945b3cd7..5256b58 100644
--- a/tools/layoutlib/bridge/src/android/graphics/BitmapFactory_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/graphics/BitmapFactory_Delegate.java
@@ -85,15 +85,18 @@
     // ------ Native Delegates ------
 
     @LayoutlibDelegate
-    /*package*/ static void nativeSetDefaultConfig(int nativeConfig) {
-        // pass
+    /*package*/ static Bitmap nativeDecodeStream(InputStream is, byte[] storage,
+            Rect padding, Options opts) {
+        return nativeDecodeStream(is, storage, padding, opts, false, 1.f);
     }
 
     @LayoutlibDelegate
-    /*package*/ static Bitmap nativeDecodeStream(InputStream is, byte[] storage,
-            Rect padding, Options opts) {
+    /*package*/ static  Bitmap nativeDecodeStream(InputStream is, byte[] storage,
+            Rect padding, Options opts, boolean applyScale, float scale) {
         Bitmap bm = null;
 
+        //TODO support rescaling
+
         Density density = Density.MEDIUM;
         if (opts != null) {
             density = Density.getEnum(opts.inDensity);
@@ -147,6 +150,13 @@
     }
 
     @LayoutlibDelegate
+    /*package*/ static Bitmap nativeDecodeAsset(int asset, Rect padding, Options opts,
+            boolean applyScale, float scale) {
+        opts.inBitmap = null;
+        return null;
+    }
+
+    @LayoutlibDelegate
     /*package*/ static Bitmap nativeDecodeByteArray(byte[] data, int offset,
             int length, Options opts) {
         opts.inBitmap = null;
diff --git a/tools/layoutlib/bridge/src/android/view/ViewRootImpl_Delegate.java b/tools/layoutlib/bridge/src/android/view/ViewRootImpl_Delegate.java
new file mode 100644
index 0000000..14b84ef
--- /dev/null
+++ b/tools/layoutlib/bridge/src/android/view/ViewRootImpl_Delegate.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2012 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 android.view;
+
+import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
+
+/**
+ * Delegate used to provide new implementation of a select few methods of {@link ViewRootImpl}
+ *
+ * Through the layoutlib_create tool, the original  methods of ViewRootImpl have been replaced
+ * by calls to methods of the same name in this delegate class.
+ *
+ */
+public class ViewRootImpl_Delegate {
+
+    @LayoutlibDelegate
+    /*package*/ static boolean isInTouchMode() {
+        return false; // this allows displaying selection.
+    }
+}
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindowManager.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindowManager.java
index bef2c95..8b1d41a 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindowManager.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindowManager.java
@@ -340,6 +340,12 @@
     }
 
     @Override
+    public void overridePendingAppTransitionThumb(Bitmap srcThumb, int startX, int startY,
+            IRemoteCallback startedCallback) throws RemoteException {
+        // TODO Auto-generated method stub
+    }
+
+    @Override
     public void pauseKeyDispatching(IBinder arg0) throws RemoteException {
         // TODO Auto-generated method stub
 
diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java
index 170cd6a..79e02c8 100644
--- a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java
+++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java
@@ -114,6 +114,7 @@
         "android.view.LayoutInflater#rInflate",
         "android.view.LayoutInflater#parseInclude",
         "android.view.View#isInEditMode",
+        "android.view.ViewRootImpl#isInTouchMode",
         "android.view.inputmethod.InputMethodManager#getInstance",
         "android.util.Log#println_native",
         "com.android.internal.util.XmlUtils#convertValueToInt",