Merge "Fix permission documentation."
diff --git a/api/current.txt b/api/current.txt
index 4cdd30b..233938b5 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -3724,6 +3724,7 @@
     method public android.app.Notification.Builder setSmallIcon(int, int);
     method public android.app.Notification.Builder setSound(android.net.Uri);
     method public android.app.Notification.Builder setSound(android.net.Uri, int);
+    method public android.app.Notification.Builder setSubText(java.lang.CharSequence);
     method public android.app.Notification.Builder setTicker(java.lang.CharSequence);
     method public android.app.Notification.Builder setTicker(java.lang.CharSequence, android.widget.RemoteViews);
     method public android.app.Notification.Builder setUsesIntruderAlert(boolean);
@@ -12734,12 +12735,16 @@
     method public void requestPeers(android.net.wifi.p2p.WifiP2pManager.Channel, android.net.wifi.p2p.WifiP2pManager.PeerListListener);
     field public static final int BUSY = 2; // 0x2
     field public static final int ERROR = 0; // 0x0
+    field public static final java.lang.String EXTRA_DISCOVERY_STATE = "discoveryState";
     field public static final java.lang.String EXTRA_NETWORK_INFO = "networkInfo";
     field public static final java.lang.String EXTRA_WIFI_P2P_DEVICE = "wifiP2pDevice";
     field public static final java.lang.String EXTRA_WIFI_P2P_INFO = "wifiP2pInfo";
     field public static final java.lang.String EXTRA_WIFI_STATE = "wifi_p2p_state";
     field public static final int P2P_UNSUPPORTED = 1; // 0x1
     field public static final java.lang.String WIFI_P2P_CONNECTION_CHANGED_ACTION = "android.net.wifi.p2p.CONNECTION_STATE_CHANGE";
+    field public static final java.lang.String WIFI_P2P_DISCOVERY_CHANGED_ACTION = "android.net.wifi.p2p.DISCOVERY_STATE_CHANGE";
+    field public static final int WIFI_P2P_DISCOVERY_STARTED = 2; // 0x2
+    field public static final int WIFI_P2P_DISCOVERY_STOPPED = 1; // 0x1
     field public static final java.lang.String WIFI_P2P_PEERS_CHANGED_ACTION = "android.net.wifi.p2p.PEERS_CHANGED";
     field public static final java.lang.String WIFI_P2P_STATE_CHANGED_ACTION = "android.net.wifi.p2p.STATE_CHANGED";
     field public static final int WIFI_P2P_STATE_DISABLED = 1; // 0x1
@@ -18916,6 +18921,7 @@
     method public void onCancel();
     method public void onClose();
     method public abstract void onCreate();
+    method public android.view.textservice.SentenceSuggestionsInfo[] onGetSentenceSuggestionsMultiple(android.view.textservice.TextInfo[], int);
     method public abstract android.view.textservice.SuggestionsInfo onGetSuggestions(android.view.textservice.TextInfo, int);
     method public android.view.textservice.SuggestionsInfo[] onGetSuggestionsMultiple(android.view.textservice.TextInfo[], int, boolean);
   }
@@ -25147,6 +25153,18 @@
 
 package android.view.textservice {
 
+  public final class SentenceSuggestionsInfo implements android.os.Parcelable {
+    ctor public SentenceSuggestionsInfo(android.view.textservice.SuggestionsInfo[], int[], int[]);
+    ctor public SentenceSuggestionsInfo(android.os.Parcel);
+    method public int describeContents();
+    method public int getLengthAt(int);
+    method public int getOffsetAt(int);
+    method public int getSuggestionsCount();
+    method public android.view.textservice.SuggestionsInfo getSuggestionsInfoAt(int);
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator CREATOR;
+  }
+
   public final class SpellCheckerInfo implements android.os.Parcelable {
     method public int describeContents();
     method public android.content.ComponentName getComponent();
@@ -25165,6 +25183,7 @@
   public class SpellCheckerSession {
     method public void cancel();
     method public void close();
+    method public void getSentenceSuggestions(android.view.textservice.TextInfo[], int);
     method public android.view.textservice.SpellCheckerInfo getSpellChecker();
     method public void getSuggestions(android.view.textservice.TextInfo, int);
     method public void getSuggestions(android.view.textservice.TextInfo[], int, boolean);
diff --git a/core/java/android/animation/ValueAnimator.java b/core/java/android/animation/ValueAnimator.java
index e2b8ce4..fade20c 100755
--- a/core/java/android/animation/ValueAnimator.java
+++ b/core/java/android/animation/ValueAnimator.java
@@ -647,7 +647,7 @@
             // onAnimate to process the next frame of the animations.
             if (!mAnimationScheduled
                     && (!mAnimations.isEmpty() || !mDelayedAnims.isEmpty())) {
-                mChoreographer.postAnimationCallback(this, null);
+                mChoreographer.postCallback(Choreographer.CALLBACK_ANIMATION, this, null);
                 mAnimationScheduled = true;
             }
         }
diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java
index c402329..5917cbf 100644
--- a/core/java/android/app/ActivityManagerNative.java
+++ b/core/java/android/app/ActivityManagerNative.java
@@ -25,6 +25,7 @@
 import android.content.pm.ApplicationInfo;
 import android.content.pm.ConfigurationInfo;
 import android.content.pm.IPackageDataObserver;
+import android.content.pm.UserInfo;
 import android.content.res.Configuration;
 import android.graphics.Bitmap;
 import android.net.Uri;
@@ -1494,7 +1495,15 @@
             reply.writeInt(result ? 1 : 0);
             return true;
         }
-        
+
+        case GET_CURRENT_USER_TRANSACTION: {
+            data.enforceInterface(IActivityManager.descriptor);
+            UserInfo userInfo = getCurrentUser();
+            reply.writeNoException();
+            userInfo.writeToParcel(reply, 0);
+            return true;
+        }
+
         case REMOVE_SUB_TASK_TRANSACTION:
         {
             data.enforceInterface(IActivityManager.descriptor);
@@ -3530,7 +3539,19 @@
         data.recycle();
         return result;
     }
-    
+
+    public UserInfo getCurrentUser() throws RemoteException {
+        Parcel data = Parcel.obtain();
+        Parcel reply = Parcel.obtain();
+        data.writeInterfaceToken(IActivityManager.descriptor);
+        mRemote.transact(SWITCH_USER_TRANSACTION, data, reply, 0);
+        reply.readException();
+        UserInfo userInfo = UserInfo.CREATOR.createFromParcel(reply);
+        reply.recycle();
+        data.recycle();
+        return userInfo;
+    }
+
     public boolean removeSubTask(int taskId, int subTaskIndex) throws RemoteException {
         Parcel data = Parcel.obtain();
         Parcel reply = Parcel.obtain();
diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java
index 1d994d8..2b1eb43 100644
--- a/core/java/android/app/IActivityManager.java
+++ b/core/java/android/app/IActivityManager.java
@@ -28,6 +28,7 @@
 import android.content.pm.ConfigurationInfo;
 import android.content.pm.IPackageDataObserver;
 import android.content.pm.ProviderInfo;
+import android.content.pm.UserInfo;
 import android.content.res.Configuration;
 import android.graphics.Bitmap;
 import android.net.Uri;
@@ -318,10 +319,11 @@
     public boolean getPackageAskScreenCompat(String packageName) throws RemoteException;
     public void setPackageAskScreenCompat(String packageName, boolean ask)
             throws RemoteException;
-    
+
     // Multi-user APIs
     public boolean switchUser(int userid) throws RemoteException;
-    
+    public UserInfo getCurrentUser() throws RemoteException;
+
     public boolean removeSubTask(int taskId, int subTaskIndex) throws RemoteException;
 
     public boolean removeTask(int taskId, int flags) throws RemoteException;
@@ -575,4 +577,5 @@
     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;
+    int GET_CURRENT_USER_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+144;
 }
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index bbb6a4e..1356801 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -187,7 +187,6 @@
      */
     public RemoteViews contentView;
 
-
     /**
      * The view that will represent this notification in the pop-up "intruder alert" dialog.
      * @hide
@@ -195,6 +194,14 @@
     public RemoteViews intruderView;
 
     /**
+     * A larger version of {@link #contentView}, giving the Notification an
+     * opportunity to show more detail. The system UI may choose to show this
+     * instead of the normal content view at its discretion.
+     * @hide
+     */
+    public RemoteViews bigContentView;
+
+    /**
      * The bitmap that may escape the bounds of the panel and bar.
      */
     public Bitmap largeIcon;
@@ -584,6 +591,9 @@
         if (parcel.readInt() != 0) {
             intruderView = RemoteViews.CREATOR.createFromParcel(parcel);
         }
+        if (parcel.readInt() != 0) {
+            bigContentView = RemoteViews.CREATOR.createFromParcel(parcel);
+        }
     }
 
     @Override
@@ -650,6 +660,9 @@
         if (this.intruderView != null) {
             that.intruderView = this.intruderView.clone();
         }
+        if (this.bigContentView != null) {
+            that.bigContentView = this.bigContentView.clone();
+        }
 
         return that;
     }
@@ -747,6 +760,13 @@
         } else {
             parcel.writeInt(0);
         }
+
+        if (bigContentView != null) {
+            parcel.writeInt(1);
+            bigContentView.writeToParcel(parcel, 0);
+        } else {
+            parcel.writeInt(0);
+        }
     }
 
     /**
@@ -896,6 +916,7 @@
         private CharSequence mContentTitle;
         private CharSequence mContentText;
         private CharSequence mContentInfo;
+        private CharSequence mSubText;
         private PendingIntent mContentIntent;
         private RemoteViews mContentView;
         private PendingIntent mDeleteIntent;
@@ -1013,6 +1034,15 @@
         }
 
         /**
+         * Set the third line of text in the platform notification template. 
+         * Don't use if you're also using {@link #setProgress(int, int, boolean)}; they occupy the same location in the standard template.
+         */
+        public Builder setSubText(CharSequence text) {
+            mSubText = text;
+            return this;
+        }
+
+        /**
          * Set the large number at the right-hand side of the notification.  This is
          * equivalent to setContentInfo, although it might show the number in a different
          * font size for readability.
@@ -1025,7 +1055,6 @@
         /**
          * A small piece of additional information pertaining to this notification.
          *
-
          * The platform template will draw this on the last line of the notification, at the far
          * right (to the right of a smallIcon if it has been placed there).
          */
@@ -1037,7 +1066,6 @@
         /**
          * Set the progress this notification represents.
          *
-
          * The platform template will represent this using a {@link ProgressBar}.
          */
         public Builder setProgress(int max, int progress, boolean indeterminate) {
@@ -1050,7 +1078,6 @@
         /**
          * Supply a custom RemoteViews to use instead of the platform template.
          *
-
          * @see Notification#contentView
          */
         public Builder setContent(RemoteViews views) {
@@ -1061,17 +1088,12 @@
         /**
          * Supply a {@link PendingIntent} to be sent when the notification is clicked.
          *
-
          * As of {@link android.os.Build.VERSION_CODES#HONEYCOMB}, if this field is unset and you
          * have specified a custom RemoteViews with {@link #setContent(RemoteViews)}, you can use
          * {@link RemoteViews#setOnClickPendingIntent RemoteViews.setOnClickPendingIntent(int,PendingIntent)}
-
          * to assign PendingIntents to individual views in that custom layout (i.e., to create
-
-         * clickable buttons inside the
-         * notification view).
+         * clickable buttons inside the notification view).
          *
-
          * @see Notification#contentIntent Notification.contentIntent
          */
         public Builder setContentIntent(PendingIntent intent) {
@@ -1082,7 +1104,6 @@
         /**
          * Supply a {@link PendingIntent} to send when the notification is cleared explicitly by the user.
          *
-
          * @see Notification#deleteIntent
          */
         public Builder setDeleteIntent(PendingIntent intent) {
@@ -1115,7 +1136,6 @@
          * Set the "ticker" text which is displayed in the status bar when the notification first
          * arrives.
          *
-
          * @see Notification#tickerText
          */
         public Builder setTicker(CharSequence tickerText) {
@@ -1355,20 +1375,28 @@
             }
         }
 
-        private RemoteViews makeRemoteViews(int resId) {
+        private RemoteViews applyStandardTemplate(int resId) {
             RemoteViews contentView = new RemoteViews(mContext.getPackageName(), resId);
             boolean hasLine3 = false;
+            boolean hasLine2 = false;
+            int smallIconImageViewId = R.id.icon;
+            if (mLargeIcon != null) {
+                contentView.setImageViewBitmap(R.id.icon, mLargeIcon);
+                smallIconImageViewId = R.id.right_icon;
+            }
             if (mSmallIcon != 0) {
-                contentView.setImageViewResource(R.id.icon, mSmallIcon);
-                contentView.setViewVisibility(R.id.icon, View.VISIBLE);
+                contentView.setImageViewResource(smallIconImageViewId, mSmallIcon);
+                contentView.setViewVisibility(smallIconImageViewId, View.VISIBLE);
             } else {
-                contentView.setViewVisibility(R.id.icon, View.GONE);
+                contentView.setViewVisibility(smallIconImageViewId, View.GONE);
             }
             if (mContentTitle != null) {
                 contentView.setTextViewText(R.id.title, mContentTitle);
             }
             if (mContentText != null) {
-                contentView.setTextViewText(R.id.text, mContentText);
+                contentView.setTextViewText(
+                        (mSubText != null) ? R.id.text2 : R.id.text, 
+                        mContentText);
                 hasLine3 = true;
             }
             if (mContentInfo != null) {
@@ -1390,12 +1418,19 @@
             } else {
                 contentView.setViewVisibility(R.id.info, View.GONE);
             }
-            if (mProgressMax != 0 || mProgressIndeterminate) {
-                contentView.setProgressBar(
-                        R.id.progress, mProgressMax, mProgress, mProgressIndeterminate);
-                contentView.setViewVisibility(R.id.progress, View.VISIBLE);
+
+            if (mSubText != null) {
+                contentView.setTextViewText(R.id.text, mSubText);
+                contentView.setViewVisibility(R.id.text2, View.VISIBLE);
             } else {
-                contentView.setViewVisibility(R.id.progress, View.GONE);
+                contentView.setViewVisibility(R.id.text2, View.GONE);
+                if (mProgressMax != 0 || mProgressIndeterminate) {
+                    contentView.setProgressBar(
+                            R.id.progress, mProgressMax, mProgress, mProgressIndeterminate);
+                    contentView.setViewVisibility(R.id.progress, View.VISIBLE);
+                } else {
+                    contentView.setViewVisibility(R.id.progress, View.GONE);
+                }
             }
             if (mWhen != 0) {
                 contentView.setLong(R.id.time, "setTime", mWhen);
@@ -1408,9 +1443,7 @@
             if (mContentView != null) {
                 return mContentView;
             } else {
-                    return makeRemoteViews(mLargeIcon == null
-                            ? R.layout.status_bar_latest_event_content
-                        : R.layout.status_bar_latest_event_content_large_icon);
+                return applyStandardTemplate(R.layout.status_bar_latest_event_content); // no more special large_icon flavor
             }
         }
 
@@ -1419,7 +1452,7 @@
                 return mTickerView;
             } else {
                 if (mContentView == null) {
-                    return makeRemoteViews(mLargeIcon == null
+                    return applyStandardTemplate(mLargeIcon == null
                             ? R.layout.status_bar_latest_event_ticker
                             : R.layout.status_bar_latest_event_ticker_large_icon);
                 } else {
@@ -1516,4 +1549,100 @@
             return n;
         }
     }
+
+    /**
+     * @hide because this API is still very rough
+     * 
+     * This is a "rebuilder": It consumes a Builder object and modifies its output.
+     * 
+     * This represents the "big picture" style notification, with a large Bitmap atop the usual notification.
+     *
+     * Usage:
+     * <pre class="prettyprint">
+     * Notification noti = new Notification.BigPictureStyle(
+     *      new Notification.Builder()
+     *         .setContentTitle(&quot;New mail from &quot; + sender.toString())
+     *         .setContentText(subject)
+     *         .setSmallIcon(R.drawable.new_mail)
+     *         .setLargeIcon(aBitmap))
+     *      .bigPicture(aBigBitmap)
+     *      .build();
+     * </pre>
+     */
+    public static class BigPictureStyle {
+        private Builder mBuilder;
+        private Bitmap mPicture;
+
+        public BigPictureStyle(Builder builder) {
+            mBuilder = builder;
+        }
+
+        public BigPictureStyle bigPicture(Bitmap b) {
+            mPicture = b;
+            return this;
+        }
+
+        private RemoteViews makeBigContentView() {
+            RemoteViews contentView = mBuilder.applyStandardTemplate(R.layout.notification_template_big_picture);
+
+            contentView.setImageViewBitmap(R.id.big_picture, mPicture);
+
+            return contentView;
+        }
+
+        public Notification build() {
+            Notification wip = mBuilder.getNotification();
+            wip.bigContentView = makeBigContentView();
+            return wip;
+        }
+    }
+
+    /**
+     * @hide because this API is still very rough
+     * 
+     * This is a "rebuilder": It consumes a Builder object and modifies its output.
+     * 
+     * This represents the "big text" style notification, with more area for the main content text to be read in its entirety.
+     *
+     * Usage:
+     * <pre class="prettyprint">
+     * Notification noti = new Notification.BigPictureStyle(
+     *      new Notification.Builder()
+     *         .setContentTitle(&quot;New mail from &quot; + sender.toString())
+     *         .setContentText(subject)
+     *         .setSmallIcon(R.drawable.new_mail)
+     *         .setLargeIcon(aBitmap))
+     *      .bigText(aVeryLongString)
+     *      .build();
+     * </pre>
+     */
+    public static class BigTextStyle {
+        private Builder mBuilder;
+        private CharSequence mBigText;
+
+        public BigTextStyle(Builder builder) {
+            mBuilder = builder;
+        }
+
+        public BigTextStyle bigText(CharSequence cs) {
+            mBigText = cs;
+            return this;
+        }
+
+        private RemoteViews makeBigContentView() {
+            RemoteViews contentView = mBuilder.applyStandardTemplate(R.layout.status_bar_latest_event_content);
+
+            contentView.setTextViewText(R.id.big_text, mBigText);
+            contentView.setViewVisibility(R.id.big_text, View.VISIBLE);
+            contentView.setTextViewText(R.id.text, ""); // XXX: what do do with this spot?
+
+            return contentView;
+        }
+
+        public Notification build() {
+            Notification wip = mBuilder.getNotification();
+            wip.bigContentView = makeBigContentView();
+            return wip;
+        }
+    }
 }
diff --git a/core/java/android/nfc/INfcAdapter.aidl b/core/java/android/nfc/INfcAdapter.aidl
index 10da9ef..ddd00a4 100644
--- a/core/java/android/nfc/INfcAdapter.aidl
+++ b/core/java/android/nfc/INfcAdapter.aidl
@@ -42,7 +42,7 @@
 
     void setForegroundDispatch(in PendingIntent intent,
             in IntentFilter[] filters, in TechListParcel techLists);
-    void setForegroundNdefPush(in NdefMessage msg, in INdefPushCallback callback);
+    void setNdefPushCallback(in INdefPushCallback callback);
 
     void dispatch(in Tag tag);
 
diff --git a/core/java/android/nfc/NfcActivityManager.java b/core/java/android/nfc/NfcActivityManager.java
index 5fe58e9..2c73056 100644
--- a/core/java/android/nfc/NfcActivityManager.java
+++ b/core/java/android/nfc/NfcActivityManager.java
@@ -17,210 +17,303 @@
 package android.nfc;
 
 import android.app.Activity;
+import android.app.Application;
+import android.os.Bundle;
 import android.os.RemoteException;
 import android.util.Log;
 
-import java.util.WeakHashMap;
+import java.util.ArrayList;
+import java.util.LinkedList;
+import java.util.List;
 
 /**
  * Manages NFC API's that are coupled to the life-cycle of an Activity.
  *
- * <p>Uses a fragment to hook into onPause() and onResume() of the host
- * activities.
- *
- * <p>Ideally all of this management would be done in the NFC Service,
- * but right now it is much easier to do it in the application process.
+ * <p>Uses {@link Application#registerActivityLifecycleCallbacks} to hook
+ * into activity life-cycle events such as onPause() and onResume().
  *
  * @hide
  */
-public final class NfcActivityManager extends INdefPushCallback.Stub {
+public final class NfcActivityManager extends INdefPushCallback.Stub
+        implements Application.ActivityLifecycleCallbacks {
     static final String TAG = NfcAdapter.TAG;
     static final Boolean DBG = false;
 
     final NfcAdapter mAdapter;
-    final WeakHashMap<Activity, NfcActivityState> mNfcState;  // contents protected by this
-    final NfcEvent mDefaultEvent;  // can re-use one NfcEvent because it just contains adapter
+    final NfcEvent mDefaultEvent;  // cached NfcEvent (its currently always the same)
+
+    // All objects in the lists are protected by this
+    final List<NfcApplicationState> mApps;  // Application(s) that have NFC state. Usually one
+    final List<NfcActivityState> mActivities;  // Activities that have NFC state
+
+    /**
+     * NFC State associated with an {@link Application}.
+     */
+    class NfcApplicationState {
+        int refCount = 0;
+        final Application app;
+        public NfcApplicationState(Application app) {
+            this.app = app;
+        }
+        public void register() {
+            refCount++;
+            if (refCount == 1) {
+                this.app.registerActivityLifecycleCallbacks(NfcActivityManager.this);
+            }
+        }
+        public void unregister() {
+            refCount--;
+            if (refCount == 0) {
+                this.app.unregisterActivityLifecycleCallbacks(NfcActivityManager.this);
+            } else if (refCount < 0) {
+                Log.e(TAG, "-ve refcount for " + app);
+            }
+        }
+    }
+
+    NfcApplicationState findAppState(Application app) {
+        for (NfcApplicationState appState : mApps) {
+            if (appState.app == app) {
+                return appState;
+            }
+        }
+        return null;
+    }
+
+    void registerApplication(Application app) {
+        NfcApplicationState appState = findAppState(app);
+        if (appState == null) {
+            appState = new NfcApplicationState(app);
+            mApps.add(appState);
+        }
+        appState.register();
+    }
+
+    void unregisterApplication(Application app) {
+        NfcApplicationState appState = findAppState(app);
+        if (appState == null) {
+            Log.e(TAG, "app was not registered " + app);
+            return;
+        }
+        appState.unregister();
+    }
 
     /**
      * NFC state associated with an {@link Activity}
      */
     class NfcActivityState {
-        boolean resumed = false;  // is the activity resumed
-        NdefMessage ndefMessage;
-        NfcAdapter.CreateNdefMessageCallback ndefMessageCallback;
-        NfcAdapter.OnNdefPushCompleteCallback onNdefPushCompleteCallback;
+        boolean resumed = false;
+        Activity activity;
+        NdefMessage ndefMessage = null;  // static NDEF message
+        NfcAdapter.CreateNdefMessageCallback ndefMessageCallback = null;
+        NfcAdapter.OnNdefPushCompleteCallback onNdefPushCompleteCallback = null;
+        public NfcActivityState(Activity activity) {
+            if (activity.getWindow().isDestroyed()) {
+                throw new IllegalStateException("activity is already destroyed");
+            }
+            this.activity = activity;
+            registerApplication(activity.getApplication());
+        }
+        public void destroy() {
+            unregisterApplication(activity.getApplication());
+            resumed = false;
+            activity = null;
+            ndefMessage = null;
+            ndefMessageCallback = null;
+            onNdefPushCompleteCallback = null;
+        }
         @Override
         public String toString() {
-            StringBuilder s = new StringBuilder("[").append(resumed).append(" ");
+            StringBuilder s = new StringBuilder("[").append(" ");
             s.append(ndefMessage).append(" ").append(ndefMessageCallback).append(" ");
             s.append(onNdefPushCompleteCallback).append("]");
             return s.toString();
         }
     }
 
-    public NfcActivityManager(NfcAdapter adapter) {
-        mAdapter = adapter;
-        mNfcState = new WeakHashMap<Activity, NfcActivityState>();
-        mDefaultEvent = new NfcEvent(mAdapter);
+    /** find activity state from mActivities */
+    synchronized NfcActivityState findActivityState(Activity activity) {
+        for (NfcActivityState state : mActivities) {
+            if (state.activity == activity) {
+                return state;
+            }
+        }
+        return null;
     }
 
-    /**
-     * onResume hook from fragment attached to activity
-     */
-    public synchronized void onResume(Activity activity) {
-        NfcActivityState state = mNfcState.get(activity);
-        if (DBG) Log.d(TAG, "onResume() for " + activity + " " + state);
-        if (state != null) {
-            state.resumed = true;
-            updateNfcService(state);
-        }
-    }
-
-    /**
-     * onPause hook from fragment attached to activity
-     */
-    public synchronized void onPause(Activity activity) {
-        NfcActivityState state = mNfcState.get(activity);
-        if (DBG) Log.d(TAG, "onPause() for " + activity + " " + state);
-        if (state != null) {
-            state.resumed = false;
-            updateNfcService(state);
-        }
-    }
-
-    /**
-     * onDestroy hook from fragment attached to activity
-     */
-    public void onDestroy(Activity activity) {
-        mNfcState.remove(activity);
-    }
-
-    public synchronized void setNdefPushMessage(Activity activity, NdefMessage message) {
-        NfcActivityState state = getOrCreateState(activity, message != null);
-        if (state == null || state.ndefMessage == message) {
-            return;  // nothing more to do;
-        }
-        state.ndefMessage = message;
-        if (message == null) {
-            maybeRemoveState(activity, state);
-        }
-        if (state.resumed) {
-            updateNfcService(state);
-        }
-    }
-
-    public synchronized void setNdefPushMessageCallback(Activity activity,
-            NfcAdapter.CreateNdefMessageCallback callback) {
-        NfcActivityState state = getOrCreateState(activity, callback != null);
-        if (state == null || state.ndefMessageCallback == callback) {
-            return;  // nothing more to do;
-        }
-        state.ndefMessageCallback = callback;
-        if (callback == null) {
-            maybeRemoveState(activity, state);
-        }
-        if (state.resumed) {
-            updateNfcService(state);
-        }
-    }
-
-    public synchronized void setOnNdefPushCompleteCallback(Activity activity,
-            NfcAdapter.OnNdefPushCompleteCallback callback) {
-        NfcActivityState state = getOrCreateState(activity, callback != null);
-        if (state == null || state.onNdefPushCompleteCallback == callback) {
-            return;  // nothing more to do;
-        }
-        state.onNdefPushCompleteCallback = callback;
-        if (callback == null) {
-            maybeRemoveState(activity, state);
-        }
-        if (state.resumed) {
-            updateNfcService(state);
-        }
-    }
-
-    /**
-     * Get the NfcActivityState for the specified Activity.
-     * If create is true, then create it if it doesn't already exist,
-     * and ensure the NFC fragment is attached to the activity.
-     */
-    synchronized NfcActivityState getOrCreateState(Activity activity, boolean create) {
-        if (DBG) Log.d(TAG, "getOrCreateState " + activity + " " + create);
-        NfcActivityState state = mNfcState.get(activity);
-        if (state == null && create) {
-            state = new NfcActivityState();
-            mNfcState.put(activity, state);
-            NfcFragment.attach(activity);
+    /** find or create activity state from mActivities */
+    synchronized NfcActivityState getActivityState(Activity activity) {
+        NfcActivityState state = findActivityState(activity);
+        if (state == null) {
+            state = new NfcActivityState(activity);
+            mActivities.add(state);
         }
         return state;
     }
 
-    /**
-     * If the NfcActivityState is empty then remove it, and
-     * detach it from the Activity.
-     */
-    synchronized void maybeRemoveState(Activity activity, NfcActivityState state) {
-        if (state.ndefMessage == null && state.ndefMessageCallback == null &&
-                state.onNdefPushCompleteCallback == null) {
-            NfcFragment.remove(activity);
-            mNfcState.remove(activity);
+    synchronized NfcActivityState findResumedActivityState() {
+        for (NfcActivityState state : mActivities) {
+            if (state.resumed) {
+                return state;
+            }
+        }
+        return null;
+    }
+
+    synchronized void destroyActivityState(Activity activity) {
+        NfcActivityState activityState = findActivityState(activity);
+        if (activityState != null) {
+            activityState.destroy();
+            mActivities.remove(activityState);
+        }
+    }
+
+    public NfcActivityManager(NfcAdapter adapter) {
+        mAdapter = adapter;
+        mActivities = new LinkedList<NfcActivityState>();
+        mApps = new ArrayList<NfcApplicationState>(1);  // Android VM usually has 1 app
+        mDefaultEvent = new NfcEvent(mAdapter);
+    }
+
+    public void setNdefPushMessage(Activity activity, NdefMessage message) {
+        boolean isResumed;
+        synchronized (NfcActivityManager.this) {
+            NfcActivityState state = getActivityState(activity);
+            state.ndefMessage = message;
+            isResumed = state.resumed;
+        }
+        if (isResumed) {
+            requestNfcServiceCallback(true);
+        }
+    }
+
+    public void setNdefPushMessageCallback(Activity activity,
+            NfcAdapter.CreateNdefMessageCallback callback) {
+        boolean isResumed;
+        synchronized (NfcActivityManager.this) {
+            NfcActivityState state = getActivityState(activity);
+            state.ndefMessageCallback = callback;
+            isResumed = state.resumed;
+        }
+        if (isResumed) {
+            requestNfcServiceCallback(true);
+        }
+    }
+
+    public void setOnNdefPushCompleteCallback(Activity activity,
+            NfcAdapter.OnNdefPushCompleteCallback callback) {
+        boolean isResumed;
+        synchronized (NfcActivityManager.this) {
+            NfcActivityState state = getActivityState(activity);
+            state.onNdefPushCompleteCallback = callback;
+            isResumed = state.resumed;
+        }
+        if (isResumed) {
+            requestNfcServiceCallback(true);
         }
     }
 
     /**
-     * Register NfcActivityState with the NFC service.
+     * Request or unrequest NFC service callbacks for NDEF push.
+     * Makes IPC call - do not hold lock.
+     * TODO: Do not do IPC on every onPause/onResume
      */
-    synchronized void updateNfcService(NfcActivityState state) {
-        boolean serviceCallbackNeeded = state.ndefMessageCallback != null ||
-                state.onNdefPushCompleteCallback != null;
-
+    void requestNfcServiceCallback(boolean request) {
         try {
-            NfcAdapter.sService.setForegroundNdefPush(state.resumed ? state.ndefMessage : null,
-                    state.resumed && serviceCallbackNeeded ? this : null);
+            NfcAdapter.sService.setNdefPushCallback(request ? this : null);
         } catch (RemoteException e) {
             mAdapter.attemptDeadServiceRecovery(e);
         }
     }
 
-    /**
-     * Callback from NFC service
-     */
+    /** Callback from NFC service, usually on binder thread */
     @Override
     public NdefMessage createMessage() {
-        NfcAdapter.CreateNdefMessageCallback callback = null;
+        NfcAdapter.CreateNdefMessageCallback callback;
+        NdefMessage message;
         synchronized (NfcActivityManager.this) {
-            for (NfcActivityState state : mNfcState.values()) {
-                if (state.resumed) {
-                    callback = state.ndefMessageCallback;
-                }
-            }
+            NfcActivityState state = findResumedActivityState();
+            if (state == null) return null;
+
+            callback = state.ndefMessageCallback;
+            message = state.ndefMessage;
         }
 
-        // drop lock before making callback
+        // Make callback without lock
         if (callback != null) {
             return callback.createNdefMessage(mDefaultEvent);
+        } else {
+            return message;
         }
-        return null;
     }
 
-    /**
-     * Callback from NFC service
-     */
+    /** Callback from NFC service, usually on binder thread */
     @Override
     public void onNdefPushComplete() {
-        NfcAdapter.OnNdefPushCompleteCallback callback = null;
+        NfcAdapter.OnNdefPushCompleteCallback callback;
         synchronized (NfcActivityManager.this) {
-            for (NfcActivityState state : mNfcState.values()) {
-                if (state.resumed) {
-                    callback = state.onNdefPushCompleteCallback;
-                }
-            }
+            NfcActivityState state = findResumedActivityState();
+            if (state == null) return;
+
+            callback = state.onNdefPushCompleteCallback;
         }
 
-        // drop lock before making callback
+        // Make callback without lock
         if (callback != null) {
             callback.onNdefPushComplete(mDefaultEvent);
         }
     }
 
+    /** Callback from Activity life-cycle, on main thread */
+    @Override
+    public void onActivityCreated(Activity activity, Bundle savedInstanceState) { /* NO-OP */ }
+
+    /** Callback from Activity life-cycle, on main thread */
+    @Override
+    public void onActivityStarted(Activity activity) { /* NO-OP */ }
+
+    /** Callback from Activity life-cycle, on main thread */
+    @Override
+    public void onActivityResumed(Activity activity) {
+        synchronized (NfcActivityManager.this) {
+            NfcActivityState state = findActivityState(activity);
+            if (DBG) Log.d(TAG, "onResume() for " + activity + " " + state);
+            if (state == null) return;
+            state.resumed = true;
+        }
+        requestNfcServiceCallback(true);
+    }
+
+    /** Callback from Activity life-cycle, on main thread */
+    @Override
+    public void onActivityPaused(Activity activity) {
+        synchronized (NfcActivityManager.this) {
+            NfcActivityState state = findActivityState(activity);
+            if (DBG) Log.d(TAG, "onPause() for " + activity + " " + state);
+            if (state == null) return;
+            state.resumed = false;
+        }
+        requestNfcServiceCallback(false);
+    }
+
+    /** Callback from Activity life-cycle, on main thread */
+    @Override
+    public void onActivityStopped(Activity activity) { /* NO-OP */ }
+
+    /** Callback from Activity life-cycle, on main thread */
+    @Override
+    public void onActivitySaveInstanceState(Activity activity, Bundle outState) { /* NO-OP */ }
+
+    /** Callback from Activity life-cycle, on main thread */
+    @Override
+    public void onActivityDestroyed(Activity activity) {
+        synchronized (NfcActivityManager.this) {
+            NfcActivityState state = findActivityState(activity);
+            if (DBG) Log.d(TAG, "onDestroy() for " + activity + " " + state);
+            if (state != null) {
+                // release all associated references
+                destroyActivityState(activity);
+            }
+        }
+    }
 }
diff --git a/core/java/android/nfc/NfcAdapter.java b/core/java/android/nfc/NfcAdapter.java
index 23f96e3..b7a7bd5 100644
--- a/core/java/android/nfc/NfcAdapter.java
+++ b/core/java/android/nfc/NfcAdapter.java
@@ -556,109 +556,230 @@
     }
 
     /**
-     * Set the {@link NdefMessage} to push over NFC during the specified activities.
+     * Set a static {@link NdefMessage} to send using Android Beam (TM).
      *
-     * <p>This method may be called at any time, but the NDEF message is
-     * only made available for NDEF push when one of the specified activities
-     * is in resumed (foreground) state.
+     * <p>This method may be called at any time before {@link Activity#onDestroy},
+     * but the NDEF message is only made available for NDEF push when the
+     * specified activity(s) are in resumed (foreground) state. The recommended
+     * approach is to call this method during your Activity's
+     * {@link Activity#onCreate} - see sample
+     * code below. This method does not immediately perform any I/O or blocking work,
+     * so is safe to call on your main thread.
      *
      * <p>Only one NDEF message can be pushed by the currently resumed activity.
      * If both {@link #setNdefPushMessage} and
-     * {@link #setNdefPushMessageCallback} are set then
+     * {@link #setNdefPushMessageCallback} are set, then
      * the callback will take priority.
      *
-     * <p>Pass a null NDEF message to disable foreground NDEF push in the
-     * specified activities.
+     * <p>If neither {@link #setNdefPushMessage} or
+     * {@link #setNdefPushMessageCallback} have been called for your activity, then
+     * the Android OS may choose to send a default NDEF message on your behalf,
+     * such as a URI for your application.
      *
-     * <p>At least one activity must be specified, and usually only one is necessary.
+     * <p>If {@link #setNdefPushMessage} is called with a null NDEF message,
+     * and/or {@link #setNdefPushMessageCallback} is called with a null callback,
+     * then NDEF push will be completely disabled for the specified activity(s).
+     * This also disables any default NDEF message the Android OS would have
+     * otherwise sent on your behalf.
+     *
+     * <p>The API allows for multiple activities to be specified at a time,
+     * but it is strongly recommended to just register one at a time,
+     * and to do so during the activity's {@link Activity#onCreate}. For example:
+     * <pre>
+     * protected void onCreate(Bundle savedInstanceState) {
+     *     super.onCreate(savedInstanceState);
+     *     NfcAdapter nfcAdapter = NfcAdapter.getDefaultAdapter(this);
+     *     if (nfcAdapter == null) return;  // NFC not available on this device
+     *     nfcAdapter.setNdefPushMessage(ndefMessage, this);
+     * }
+     * </pre>
+     * And that is it. Only one call per activity is necessary. The Android
+     * OS will automatically release its references to the NDEF message and the
+     * Activity object when it is destroyed if you follow this pattern.
+     *
+     * <p>If your Activity wants to dynamically generate an NDEF message,
+     * then set a callback using {@link #setNdefPushMessageCallback} instead
+     * of a static message.
+     *
+     * <p class="note">Do not pass in an Activity that has already been through
+     * {@link Activity#onDestroy}. This is guaranteed if you call this API
+     * during {@link Activity#onCreate}.
      *
      * <p class="note">Requires the {@link android.Manifest.permission#NFC} permission.
      *
      * @param message NDEF message to push over NFC, or null to disable
-     * @param activity an activity in which NDEF push should be enabled to share the provided
-     *                 NDEF message
-     * @param activities optional additional activities that should also enable NDEF push with
-     *                   the provided NDEF message
+     * @param activity activity for which the NDEF message will be pushed
+     * @param activities optional additional activities, however we strongly recommend
+     *        to only register one at a time, and to do so in that activity's
+     *        {@link Activity#onCreate}
      */
     public void setNdefPushMessage(NdefMessage message, Activity activity,
             Activity ... activities) {
-        if (activity == null) {
-            throw new NullPointerException("activity cannot be null");
-        }
-        mNfcActivityManager.setNdefPushMessage(activity, message);
-        for (Activity a : activities) {
-            if (a == null) {
-                throw new NullPointerException("activities cannot contain null");
+        int targetSdkVersion = getSdkVersion();
+        try {
+            if (activity == null) {
+                throw new NullPointerException("activity cannot be null");
             }
-            mNfcActivityManager.setNdefPushMessage(a, message);
+            mNfcActivityManager.setNdefPushMessage(activity, message);
+            for (Activity a : activities) {
+                if (a == null) {
+                    throw new NullPointerException("activities cannot contain null");
+                }
+                mNfcActivityManager.setNdefPushMessage(a, message);
+            }
+        } catch (IllegalStateException e) {
+            if (targetSdkVersion < android.os.Build.VERSION_CODES.JELLY_BEAN) {
+                // Less strict on old applications - just log the error
+                Log.e(TAG, "Cannot call API with Activity that has already " +
+                        "been destroyed", e);
+            } else {
+                // Prevent new applications from making this mistake, re-throw
+                throw(e);
+            }
         }
     }
 
     /**
-     * Set the callback to create a {@link NdefMessage} to push over NFC.
+     * Set a callback that dynamically generates NDEF messages to send using Android Beam (TM).
      *
-     * <p>This method may be called at any time, but this callback is
-     * only made if one of the specified activities
-     * is in resumed (foreground) state.
+     * <p>This method may be called at any time before {@link Activity#onDestroy},
+     * but the NDEF message callback can only occur when the
+     * specified activity(s) are in resumed (foreground) state. The recommended
+     * approach is to call this method during your Activity's
+     * {@link Activity#onCreate} - see sample
+     * code below. This method does not immediately perform any I/O or blocking work,
+     * so is safe to call on your main thread.
      *
      * <p>Only one NDEF message can be pushed by the currently resumed activity.
      * If both {@link #setNdefPushMessage} and
-     * {@link #setNdefPushMessageCallback} are set then
+     * {@link #setNdefPushMessageCallback} are set, then
      * the callback will take priority.
      *
-     * <p>Pass a null callback to disable the callback in the
-     * specified activities.
+     * <p>If neither {@link #setNdefPushMessage} or
+     * {@link #setNdefPushMessageCallback} have been called for your activity, then
+     * the Android OS may choose to send a default NDEF message on your behalf,
+     * such as a URI for your application.
      *
-     * <p>At least one activity must be specified, and usually only one is necessary.
+     * <p>If {@link #setNdefPushMessage} is called with a null NDEF message,
+     * and/or {@link #setNdefPushMessageCallback} is called with a null callback,
+     * then NDEF push will be completely disabled for the specified activity(s).
+     * This also disables any default NDEF message the Android OS would have
+     * otherwise sent on your behalf.
+     *
+     * <p>The API allows for multiple activities to be specified at a time,
+     * but it is strongly recommended to just register one at a time,
+     * and to do so during the activity's {@link Activity#onCreate}. For example:
+     * <pre>
+     * protected void onCreate(Bundle savedInstanceState) {
+     *     super.onCreate(savedInstanceState);
+     *     NfcAdapter nfcAdapter = NfcAdapter.getDefaultAdapter(this);
+     *     if (nfcAdapter == null) return;  // NFC not available on this device
+     *     nfcAdapter.setNdefPushMessageCallback(callback, this);
+     * }
+     * </pre>
+     * And that is it. Only one call per activity is necessary. The Android
+     * OS will automatically release its references to the callback and the
+     * Activity object when it is destroyed if you follow this pattern.
+     *
+     * <p class="note">Do not pass in an Activity that has already been through
+     * {@link Activity#onDestroy}. This is guaranteed if you call this API
+     * during {@link Activity#onCreate}.
      *
      * <p class="note">Requires the {@link android.Manifest.permission#NFC} permission.
      *
      * @param callback callback, or null to disable
-     * @param activity an activity in which NDEF push should be enabled to share an NDEF message
-     *                 that's retrieved from the provided callback
-     * @param activities optional additional activities that should also enable NDEF push using
-     *                   the provided callback
+     * @param activity activity for which the NDEF message will be pushed
+     * @param activities optional additional activities, however we strongly recommend
+     *        to only register one at a time, and to do so in that activity's
+     *        {@link Activity#onCreate}
      */
     public void setNdefPushMessageCallback(CreateNdefMessageCallback callback, Activity activity,
             Activity ... activities) {
-        if (activity == null) {
-            throw new NullPointerException("activity cannot be null");
-        }
-        mNfcActivityManager.setNdefPushMessageCallback(activity, callback);
-        for (Activity a : activities) {
-            if (a == null) {
-                throw new NullPointerException("activities cannot contain null");
+        int targetSdkVersion = getSdkVersion();
+        try {
+            if (activity == null) {
+                throw new NullPointerException("activity cannot be null");
             }
-            mNfcActivityManager.setNdefPushMessageCallback(a, callback);
+            mNfcActivityManager.setNdefPushMessageCallback(activity, callback);
+            for (Activity a : activities) {
+                if (a == null) {
+                    throw new NullPointerException("activities cannot contain null");
+                }
+                mNfcActivityManager.setNdefPushMessageCallback(a, callback);
+            }
+        } catch (IllegalStateException e) {
+            if (targetSdkVersion < android.os.Build.VERSION_CODES.JELLY_BEAN) {
+                // Less strict on old applications - just log the error
+                Log.e(TAG, "Cannot call API with Activity that has already " +
+                        "been destroyed", e);
+            } else {
+                // Prevent new applications from making this mistake, re-throw
+                throw(e);
+            }
         }
     }
 
     /**
-     * Set the callback on a successful NDEF push over NFC.
+     * Set a callback on successful Android Beam (TM).
      *
-     * <p>This method may be called at any time, but NDEF push and this callback
-     * can only occur when one of the specified activities is in resumed
-     * (foreground) state.
+     * <p>This method may be called at any time before {@link Activity#onDestroy},
+     * but the callback can only occur when the
+     * specified activity(s) are in resumed (foreground) state. The recommended
+     * approach is to call this method during your Activity's
+     * {@link Activity#onCreate} - see sample
+     * code below. This method does not immediately perform any I/O or blocking work,
+     * so is safe to call on your main thread.
      *
-     * <p>One or more activities must be specified.
+     * <p>The API allows for multiple activities to be specified at a time,
+     * but it is strongly recommended to just register one at a time,
+     * and to do so during the activity's {@link Activity#onCreate}. For example:
+     * <pre>
+     * protected void onCreate(Bundle savedInstanceState) {
+     *     super.onCreate(savedInstanceState);
+     *     NfcAdapter nfcAdapter = NfcAdapter.getDefaultAdapter(this);
+     *     if (nfcAdapter == null) return;  // NFC not available on this device
+     *     nfcAdapter.setOnNdefPushCompleteCallback(callback, this);
+     * }
+     * </pre>
+     * And that is it. Only one call per activity is necessary. The Android
+     * OS will automatically release its references to the callback and the
+     * Activity object when it is destroyed if you follow this pattern.
+     *
+     * <p class="note">Do not pass in an Activity that has already been through
+     * {@link Activity#onDestroy}. This is guaranteed if you call this API
+     * during {@link Activity#onCreate}.
      *
      * <p class="note">Requires the {@link android.Manifest.permission#NFC} permission.
      *
      * @param callback callback, or null to disable
-     * @param activity an activity to enable the callback (at least one is required)
-     * @param activities zero or more additional activities to enable to callback
+     * @param activity activity for which the NDEF message will be pushed
+     * @param activities optional additional activities, however we strongly recommend
+     *        to only register one at a time, and to do so in that activity's
+     *        {@link Activity#onCreate}
      */
     public void setOnNdefPushCompleteCallback(OnNdefPushCompleteCallback callback,
             Activity activity, Activity ... activities) {
-        if (activity == null) {
-            throw new NullPointerException("activity cannot be null");
-        }
-        mNfcActivityManager.setOnNdefPushCompleteCallback(activity, callback);
-        for (Activity a : activities) {
-            if (a == null) {
-                throw new NullPointerException("activities cannot contain null");
+        int targetSdkVersion = getSdkVersion();
+        try {
+            if (activity == null) {
+                throw new NullPointerException("activity cannot be null");
             }
-            mNfcActivityManager.setOnNdefPushCompleteCallback(a, callback);
+            mNfcActivityManager.setOnNdefPushCompleteCallback(activity, callback);
+            for (Activity a : activities) {
+                if (a == null) {
+                    throw new NullPointerException("activities cannot contain null");
+                }
+                mNfcActivityManager.setOnNdefPushCompleteCallback(a, callback);
+            }
+        } catch (IllegalStateException e) {
+            if (targetSdkVersion < android.os.Build.VERSION_CODES.JELLY_BEAN) {
+                // Less strict on old applications - just log the error
+                Log.e(TAG, "Cannot call API with Activity that has already " +
+                        "been destroyed", e);
+            } else {
+                // Prevent new applications from making this mistake, re-throw
+                throw(e);
+            }
         }
     }
 
@@ -932,4 +1053,12 @@
             throw new IllegalStateException("API cannot be called while activity is paused");
         }
     }
+
+    int getSdkVersion() {
+        if (mContext == null) {
+            return android.os.Build.VERSION_CODES.GINGERBREAD; // best guess
+        } else {
+            return mContext.getApplicationInfo().targetSdkVersion;
+        }
+    }
 }
diff --git a/core/java/android/nfc/NfcFragment.java b/core/java/android/nfc/NfcFragment.java
deleted file mode 100644
index d6b15ad..0000000
--- a/core/java/android/nfc/NfcFragment.java
+++ /dev/null
@@ -1,96 +0,0 @@
-/*
- * Copyright (C) 2011 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.nfc;
-
-import android.app.Activity;
-import android.app.Fragment;
-import android.app.FragmentManager;
-
-/**
- * Used by {@link NfcActivityManager} to attach to activity life-cycle.
- * @hide
- */
-public final class NfcFragment extends Fragment {
-    static final String FRAGMENT_TAG = "android.nfc.NfcFragment";
-
-    // only used on UI thread
-    static boolean sIsInitialized = false;
-    static NfcActivityManager sNfcActivityManager;
-
-    /**
-     * Attach NfcFragment to an activity (if not already attached).
-     */
-    public static void attach(Activity activity) {
-        FragmentManager manager = activity.getFragmentManager();
-        if (manager.findFragmentByTag(FRAGMENT_TAG) == null) {
-            manager.beginTransaction().add(new NfcFragment(), FRAGMENT_TAG).commit();
-        }
-    }
-
-    /**
-     * Remove NfcFragment from activity.
-     */
-    public static void remove(Activity activity) {
-        FragmentManager manager = activity.getFragmentManager();
-        Fragment fragment = manager.findFragmentByTag(FRAGMENT_TAG);
-        if (fragment != null) {
-            // We allow state loss at this point, because the state is only
-            // lost when activity is being paused *AND* subsequently destroyed.
-            // In that case, the app will setup foreground dispatch again anyway.
-            manager.beginTransaction().remove(fragment).commitAllowingStateLoss();
-        }
-    }
-
-    @Override
-    public void onAttach(Activity activity) {
-        super.onAttach(activity);
-        if (!sIsInitialized) {
-            sIsInitialized = true;
-            NfcAdapter adapter = NfcAdapter.getDefaultAdapter(
-                    activity.getApplicationContext());
-            if (adapter != null) {
-                sNfcActivityManager = adapter.mNfcActivityManager;
-            }
-        }
-    }
-
-    @Override
-    public void onResume() {
-        super.onResume();
-        if (sNfcActivityManager != null) {
-            sNfcActivityManager.onResume(getActivity());
-        }
-    }
-
-    @Override
-    public void onPause() {
-        super.onPause();
-        if (sNfcActivityManager != null) {
-            sNfcActivityManager.onPause(getActivity());
-        }
-    }
-
-    @Override
-    public void onDestroy() {
-        super.onDestroy();
-        if (sNfcActivityManager != null) {
-            sNfcActivityManager.onDestroy(getActivity());
-        }
-    }
-
-
-}
diff --git a/core/java/android/service/textservice/SpellCheckerService.java b/core/java/android/service/textservice/SpellCheckerService.java
index cac449d..53ce32d 100644
--- a/core/java/android/service/textservice/SpellCheckerService.java
+++ b/core/java/android/service/textservice/SpellCheckerService.java
@@ -112,7 +112,7 @@
          * So, this is not called on the main thread,
          * but will be called in series on another thread.
          * @param textInfo the text metadata
-         * @param suggestionsLimit the number of limit of suggestions returned
+         * @param suggestionsLimit the maximum number of suggestions to be returned
          * @return SuggestionsInfo which contains suggestions for textInfo
          */
         public abstract SuggestionsInfo onGetSuggestions(TextInfo textInfo, int suggestionsLimit);
@@ -123,9 +123,10 @@
          * So, this is not called on the main thread,
          * but will be called in series on another thread.
          * @param textInfos an array of the text metadata
-         * @param suggestionsLimit the number of limit of suggestions returned
+         * @param suggestionsLimit the maximum number of suggestions to be returned
          * @param sequentialWords true if textInfos can be treated as sequential words.
-         * @return an array of SuggestionsInfo of onGetSuggestions
+         * @return an array of {@link SentenceSuggestionsInfo} returned by
+         * {@link SpellCheckerService.Session#onGetSuggestions(TextInfo, int)}
          */
         public SuggestionsInfo[] onGetSuggestionsMultiple(TextInfo[] textInfos,
                 int suggestionsLimit, boolean sequentialWords) {
@@ -140,11 +141,18 @@
         }
 
         /**
-         * @hide
-         * The default implementation returns an array of SentenceSuggestionsInfo by simply calling
-         * onGetSuggestions().
+         * Get sentence suggestions for specified texts in an array of TextInfo.
+         * The default implementation returns an array of SentenceSuggestionsInfo by simply
+         * calling onGetSuggestions.
+         * This function will run on the incoming IPC thread.
+         * So, this is not called on the main thread,
+         * but will be called in series on another thread.
          * When you override this method, make sure that suggestionsLimit is applied to suggestions
          * that share the same start position and length.
+         * @param textInfos an array of the text metadata
+         * @param suggestionsLimit the maximum number of suggestions to be returned
+         * @return an array of {@link SentenceSuggestionsInfo} returned by
+         * {@link SpellCheckerService.Session#onGetSuggestions(TextInfo, int)}
          */
         public SentenceSuggestionsInfo[] onGetSentenceSuggestionsMultiple(TextInfo[] textInfos,
                 int suggestionsLimit) {
diff --git a/core/java/android/text/DynamicLayout.java b/core/java/android/text/DynamicLayout.java
index 5f2d642..ff5a467 100644
--- a/core/java/android/text/DynamicLayout.java
+++ b/core/java/android/text/DynamicLayout.java
@@ -378,12 +378,16 @@
      * An index is associated to each block (which will be used by display lists),
      * this class simply invalidates the index of blocks overlapping a modification.
      *
+     * This method is package private and not private so that it can be tested.
+     *
      * @param startLine the first line of the range of modified lines
      * @param endLine the last line of the range, possibly equal to startLine, lower
      * than getLineCount()
      * @param newLineCount the number of lines that will replace the range, possibly 0
+     *
+     * @hide
      */
-    private void updateBlocks(int startLine, int endLine, int newLineCount) {
+    void updateBlocks(int startLine, int endLine, int newLineCount) {
         int firstBlock = -1;
         int lastBlock = -1;
         for (int i = 0; i < mNumberOfBlocks; i++) {
@@ -466,6 +470,18 @@
     }
 
     /**
+     * This package private method is used for test purposes only
+     * @hide
+     */
+    void setBlocksDataForTest(int[] blockEnds, int[] blockIndices, int numberOfBlocks) {
+        mBlockEnds = new int[blockEnds.length];
+        mBlockIndices = new int[blockIndices.length];
+        System.arraycopy(blockEnds, 0, mBlockEnds, 0, blockEnds.length);
+        System.arraycopy(blockIndices, 0, mBlockIndices, 0, blockIndices.length);
+        mNumberOfBlocks = numberOfBlocks;
+    }
+
+    /**
      * @hide
      */
     public int[] getBlockEnds() {
diff --git a/core/java/android/view/Choreographer.java b/core/java/android/view/Choreographer.java
index d217cab..1cb15a6 100644
--- a/core/java/android/view/Choreographer.java
+++ b/core/java/android/view/Choreographer.java
@@ -65,24 +65,22 @@
         }
     };
 
-    // System property to enable/disable vsync for animations and drawing.
-    // Enabled by default.
+    // Enable/disable vsync for animations and drawing.
     private static final boolean USE_VSYNC = SystemProperties.getBoolean(
             "debug.choreographer.vsync", true);
 
-    // System property to enable/disable the use of the vsync / animation timer
-    // for drawing rather than drawing immediately.
-    // Temporarily disabled by default because postponing performTraversals() violates
-    // assumptions about traversals happening in-order relative to other posted messages.
-    // Bug: 5721047
-    private static final boolean USE_ANIMATION_TIMER_FOR_DRAW = SystemProperties.getBoolean(
-            "debug.choreographer.animdraw", false);
+    // Enable/disable allowing traversals to proceed immediately if no drawing occurred
+    // during the previous frame.  When true, the Choreographer can degrade more gracefully
+    // if drawing takes longer than a frame, but it may potentially block in eglSwapBuffers()
+    // if there are two dirty buffers enqueued.
+    // When false, we always schedule traversals on strict vsync boundaries.
+    private static final boolean USE_PIPELINING = SystemProperties.getBoolean(
+            "debug.choreographer.pipeline", false);
 
-    private static final int MSG_DO_ANIMATION = 0;
-    private static final int MSG_DO_DRAW = 1;
-    private static final int MSG_DO_SCHEDULE_VSYNC = 2;
-    private static final int MSG_DO_SCHEDULE_ANIMATION = 3;
-    private static final int MSG_DO_SCHEDULE_DRAW = 4;
+    private static final int MSG_DO_FRAME = 0;
+    private static final int MSG_DO_SCHEDULE_VSYNC = 1;
+    private static final int MSG_DO_SCHEDULE_CALLBACK = 2;
+    private static final int MSG_DO_TRAVERSAL = 3;
 
     private final Object mLock = new Object();
 
@@ -92,20 +90,41 @@
 
     private Callback mCallbackPool;
 
-    private final CallbackQueue mAnimationCallbackQueue = new CallbackQueue();
-    private final CallbackQueue mDrawCallbackQueue = new CallbackQueue();
+    private final CallbackQueue[] mCallbackQueues;
 
-    private boolean mAnimationScheduled;
-    private boolean mDrawScheduled;
-    private long mLastAnimationTime;
-    private long mLastDrawTime;
+    private boolean mFrameScheduled;
+    private long mLastFrameTime;
+    private boolean mDrewLastFrame;
+    private boolean mTraversalScheduled;
+
+    /**
+     * Callback type: Input callback.  Runs first.
+     */
+    public static final int CALLBACK_INPUT = 0;
+
+    /**
+     * Callback type: Animation callback.  Runs before traversals.
+     */
+    public static final int CALLBACK_ANIMATION = 1;
+
+    /**
+     * Callback type: Traversal callback.  Handles layout and draw.  Runs last
+     * after all other asynchronous messages have been handled.
+     */
+    public static final int CALLBACK_TRAVERSAL = 2;
+
+    private static final int CALLBACK_LAST = CALLBACK_TRAVERSAL;
 
     private Choreographer(Looper looper) {
         mLooper = looper;
         mHandler = new FrameHandler(looper);
         mDisplayEventReceiver = USE_VSYNC ? new FrameDisplayEventReceiver(looper) : null;
-        mLastAnimationTime = Long.MIN_VALUE;
-        mLastDrawTime = Long.MIN_VALUE;
+        mLastFrameTime = Long.MIN_VALUE;
+
+        mCallbackQueues = new CallbackQueue[CALLBACK_LAST + 1];
+        for (int i = 0; i <= CALLBACK_LAST; i++) {
+            mCallbackQueues[i] = new CallbackQueue();
+        }
     }
 
     /**
@@ -177,156 +196,142 @@
     }
 
     /**
-     * Posts a callback to run on the next animation cycle.
+     * Posts a callback to run on the next frame.
      * The callback only runs once and then is automatically removed.
      *
-     * @param action The callback action to run during the next animation cycle.
+     * @param callbackType The callback type.
+     * @param action The callback action to run during the next frame.
      * @param token The callback token, or null if none.
      *
-     * @see #removeAnimationCallback
+     * @see #removeCallbacks
      */
-    public void postAnimationCallback(Runnable action, Object token) {
-        postAnimationCallbackDelayed(action, token, 0);
+    public void postCallback(int callbackType, Runnable action, Object token) {
+        postCallbackDelayed(callbackType, action, token, 0);
     }
 
     /**
-     * Posts a callback to run on the next animation cycle following the specified delay.
+     * Posts a callback to run on the next frame following the specified delay.
      * The callback only runs once and then is automatically removed.
      *
-     * @param action The callback action to run during the next animation cycle after
-     * the specified delay.
+     * @param callbackType The callback type.
+     * @param action The callback action to run during the next frame after the specified delay.
      * @param token The callback token, or null if none.
      * @param delayMillis The delay time in milliseconds.
      *
-     * @see #removeAnimationCallback
+     * @see #removeCallback
      */
-    public void postAnimationCallbackDelayed(Runnable action, Object token, long delayMillis) {
+    public void postCallbackDelayed(int callbackType,
+            Runnable action, Object token, long delayMillis) {
         if (action == null) {
             throw new IllegalArgumentException("action must not be null");
         }
+        if (callbackType < 0 || callbackType > CALLBACK_LAST) {
+            throw new IllegalArgumentException("callbackType is invalid");
+        }
 
         if (DEBUG) {
-            Log.d(TAG, "PostAnimationCallback: " + action + ", token=" + token
+            Log.d(TAG, "PostCallback: type=" + callbackType
+                    + ", action=" + action + ", token=" + token
                     + ", delayMillis=" + delayMillis);
         }
 
         synchronized (mLock) {
+            if (USE_PIPELINING && callbackType == CALLBACK_INPUT) {
+                Message msg = Message.obtain(mHandler, action);
+                msg.setAsynchronous(true);
+                mHandler.sendMessage(msg);
+                return;
+            }
+
             final long now = SystemClock.uptimeMillis();
             final long dueTime = now + delayMillis;
-            mAnimationCallbackQueue.addCallbackLocked(dueTime, action, token);
+            mCallbackQueues[callbackType].addCallbackLocked(dueTime, action, token);
 
             if (dueTime <= now) {
-                scheduleAnimationLocked(now);
+                if (USE_PIPELINING && callbackType == CALLBACK_TRAVERSAL) {
+                    if (!mDrewLastFrame) {
+                        if (DEBUG) {
+                            Log.d(TAG, "Scheduling traversal immediately.");
+                        }
+                        if (!mTraversalScheduled) {
+                            mTraversalScheduled = true;
+                            Message msg = mHandler.obtainMessage(MSG_DO_TRAVERSAL);
+                            msg.setAsynchronous(true);
+                            mHandler.sendMessageAtTime(msg, dueTime);
+                        }
+                        return;
+                    }
+                    if (DEBUG) {
+                        Log.d(TAG, "Scheduling traversal on next frame.");
+                    }
+                }
+                scheduleFrameLocked(now);
             } else {
-                Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_ANIMATION, action);
+                Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_CALLBACK, action);
+                msg.arg1 = callbackType;
+                msg.setAsynchronous(true);
                 mHandler.sendMessageAtTime(msg, dueTime);
             }
         }
     }
 
     /**
-     * Removes animation callbacks that have the specified action and token.
+     * Removes callbacks that have the specified action and token.
      *
+     * @param callbackType The callback type.
      * @param action The action property of the callbacks to remove, or null to remove
      * callbacks with any action.
      * @param token The token property of the callbacks to remove, or null to remove
      * callbacks with any token.
      *
-     * @see #postAnimationCallback
-     * @see #postAnimationCallbackDelayed
+     * @see #postCallback
+     * @see #postCallbackDelayed
      */
-    public void removeAnimationCallbacks(Runnable action, Object token) {
+    public void removeCallbacks(int callbackType, Runnable action, Object token) {
+        if (callbackType < 0 || callbackType > CALLBACK_LAST) {
+            throw new IllegalArgumentException("callbackType is invalid");
+        }
+
         if (DEBUG) {
-            Log.d(TAG, "RemoveAnimationCallbacks: " + action + ", token=" + token);
+            Log.d(TAG, "RemoveCallbacks: type=" + callbackType
+                    + ", action=" + action + ", token=" + token);
         }
 
         synchronized (mLock) {
-            mAnimationCallbackQueue.removeCallbacksLocked(action, token);
+            mCallbackQueues[callbackType].removeCallbacksLocked(action, token);
             if (action != null && token == null) {
-                mHandler.removeMessages(MSG_DO_SCHEDULE_ANIMATION, action);
+                mHandler.removeMessages(MSG_DO_SCHEDULE_CALLBACK, action);
             }
         }
     }
 
     /**
-     * Posts a callback to run on the next draw cycle.
-     * The callback only runs once and then is automatically removed.
+     * Tells the choreographer that the application has actually drawn to a surface.
      *
-     * @param action The callback action to run during the next draw cycle.
-     * @param token The callback token, or null if none.
-     *
-     * @see #removeDrawCallback
+     * It uses this information to determine whether to draw immediately or to
+     * post a draw to the next vsync because it might otherwise block.
      */
-    public void postDrawCallback(Runnable action, Object token) {
-        postDrawCallbackDelayed(action, token, 0);
-    }
-
-    /**
-     * Posts a callback to run on the next draw cycle following the specified delay.
-     * The callback only runs once and then is automatically removed.
-     *
-     * @param action The callback action to run during the next animation cycle after
-     * the specified delay.
-     * @param token The callback token, or null if none.
-     * @param delayMillis The delay time in milliseconds.
-     *
-     * @see #removeDrawCallback
-     */
-    public void postDrawCallbackDelayed(Runnable action, Object token, long delayMillis) {
-        if (action == null) {
-            throw new IllegalArgumentException("action must not be null");
-        }
-
+    public void notifyDrawOccurred() {
         if (DEBUG) {
-            Log.d(TAG, "PostDrawCallback: " + action + ", token=" + token
-                    + ", delayMillis=" + delayMillis);
+            Log.d(TAG, "Draw occurred.");
         }
 
-        synchronized (mLock) {
-            final long now = SystemClock.uptimeMillis();
-            final long dueTime = now + delayMillis;
-            mDrawCallbackQueue.addCallbackLocked(dueTime, action, token);
-            scheduleDrawLocked(now);
-
-            if (dueTime <= now) {
-                scheduleDrawLocked(now);
-            } else {
-                Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_DRAW, action);
-                mHandler.sendMessageAtTime(msg, dueTime);
+        if (USE_PIPELINING) {
+            synchronized (mLock) {
+                if (!mDrewLastFrame) {
+                    mDrewLastFrame = true;
+                    scheduleFrameLocked(SystemClock.uptimeMillis());
+                }
             }
         }
     }
 
-    /**
-     * Removes draw callbacks that have the specified action and token.
-     *
-     * @param action The action property of the callbacks to remove, or null to remove
-     * callbacks with any action.
-     * @param token The token property of the callbacks to remove, or null to remove
-     * callbacks with any token.
-     *
-     * @see #postDrawCallback
-     * @see #postDrawCallbackDelayed
-     */
-    public void removeDrawCallbacks(Runnable action, Object token) {
-        if (DEBUG) {
-            Log.d(TAG, "RemoveDrawCallbacks: " + action + ", token=" + token);
-        }
-
-        synchronized (mLock) {
-            mDrawCallbackQueue.removeCallbacksLocked(action, token);
-            if (action != null && token == null) {
-                mHandler.removeMessages(MSG_DO_SCHEDULE_DRAW, action);
-            }
-        }
-    }
-
-    private void scheduleAnimationLocked(long now) {
-        if (!mAnimationScheduled) {
-            mAnimationScheduled = true;
+    private void scheduleFrameLocked(long now) {
+        if (!mFrameScheduled) {
+            mFrameScheduled = true;
             if (USE_VSYNC) {
                 if (DEBUG) {
-                    Log.d(TAG, "Scheduling vsync for animation.");
+                    Log.d(TAG, "Scheduling next frame on vsync.");
                 }
 
                 // If running on the Looper thread, then schedule the vsync immediately,
@@ -340,125 +345,94 @@
                     mHandler.sendMessageAtFrontOfQueue(msg);
                 }
             } else {
-                final long nextAnimationTime = Math.max(mLastAnimationTime + sFrameDelay, now);
+                final long nextFrameTime = Math.max(mLastFrameTime + sFrameDelay, now);
                 if (DEBUG) {
-                    Log.d(TAG, "Scheduling animation in " + (nextAnimationTime - now) + " ms.");
+                    Log.d(TAG, "Scheduling next frame in " + (nextFrameTime - now) + " ms.");
                 }
-                Message msg = mHandler.obtainMessage(MSG_DO_ANIMATION);
+                Message msg = mHandler.obtainMessage(MSG_DO_FRAME);
                 msg.setAsynchronous(true);
-                mHandler.sendMessageAtTime(msg, nextAnimationTime);
+                mHandler.sendMessageAtTime(msg, nextFrameTime);
             }
         }
     }
 
-    private void scheduleDrawLocked(long now) {
-        if (!mDrawScheduled) {
-            mDrawScheduled = true;
-            if (USE_ANIMATION_TIMER_FOR_DRAW) {
-                scheduleAnimationLocked(now);
-            } else {
-                if (DEBUG) {
-                    Log.d(TAG, "Scheduling draw immediately.");
-                }
-                Message msg = mHandler.obtainMessage(MSG_DO_DRAW);
-                msg.setAsynchronous(true);
-                mHandler.sendMessageAtTime(msg, now);
+    void doFrame(int frame) {
+        synchronized (mLock) {
+            if (!mFrameScheduled) {
+                return; // no work to do
             }
+            mFrameScheduled = false;
+            mLastFrameTime = SystemClock.uptimeMillis();
+            mDrewLastFrame = false;
+        }
+
+        doCallbacks(Choreographer.CALLBACK_INPUT);
+        doCallbacks(Choreographer.CALLBACK_ANIMATION);
+        doCallbacks(Choreographer.CALLBACK_TRAVERSAL);
+
+        if (DEBUG) {
+            Log.d(TAG, "Frame " + frame + ": Finished, took "
+                    + (SystemClock.uptimeMillis() - mLastFrameTime) + " ms.");
         }
     }
 
-    void doAnimation() {
-        doAnimationInner();
-
-        if (USE_ANIMATION_TIMER_FOR_DRAW) {
-            doDraw();
-        }
-    }
-
-    void doAnimationInner() {
+    void doCallbacks(int callbackType) {
         final long start;
         Callback callbacks;
         synchronized (mLock) {
-            if (!mAnimationScheduled) {
-                return; // no work to do
-            }
-            mAnimationScheduled = false;
-
             start = SystemClock.uptimeMillis();
-            if (DEBUG) {
-                Log.d(TAG, "Performing animation: " + Math.max(0, start - mLastAnimationTime)
-                        + " ms have elapsed since previous animation.");
-            }
-            mLastAnimationTime = start;
+            callbacks = mCallbackQueues[callbackType].extractDueCallbacksLocked(start);
 
-            callbacks = mAnimationCallbackQueue.extractDueCallbacksLocked(start);
+            if (USE_PIPELINING && callbackType == CALLBACK_TRAVERSAL && mTraversalScheduled) {
+                mTraversalScheduled = false;
+                mHandler.removeMessages(MSG_DO_TRAVERSAL);
+            }
         }
 
         if (callbacks != null) {
-            runCallbacks(callbacks);
+            for (Callback c = callbacks; c != null; c = c.next) {
+                if (DEBUG) {
+                    Log.d(TAG, "RunCallback: type=" + callbackType
+                            + ", action=" + c.action + ", token=" + c.token
+                            + ", latencyMillis=" + (SystemClock.uptimeMillis() - c.dueTime));
+                }
+                c.action.run();
+            }
+
             synchronized (mLock) {
-                recycleCallbacksLocked(callbacks);
+                do {
+                    final Callback next = callbacks.next;
+                    recycleCallbackLocked(callbacks);
+                    callbacks = next;
+                } while (callbacks != null);
             }
         }
-
-        if (DEBUG) {
-            Log.d(TAG, "Animation took " + (SystemClock.uptimeMillis() - start) + " ms.");
-        }
-    }
-
-    void doDraw() {
-        final long start;
-        Callback callbacks;
-        synchronized (mLock) {
-            if (!mDrawScheduled) {
-                return; // no work to do
-            }
-            mDrawScheduled = false;
-
-            start = SystemClock.uptimeMillis();
-            if (DEBUG) {
-                Log.d(TAG, "Performing draw: " + Math.max(0, start - mLastDrawTime)
-                        + " ms have elapsed since previous draw.");
-            }
-            mLastDrawTime = start;
-
-            callbacks = mDrawCallbackQueue.extractDueCallbacksLocked(start);
-        }
-
-        if (callbacks != null) {
-            runCallbacks(callbacks);
-            synchronized (mLock) {
-                recycleCallbacksLocked(callbacks);
-            }
-        }
-
-        if (DEBUG) {
-            Log.d(TAG, "Draw took " + (SystemClock.uptimeMillis() - start) + " ms.");
-        }
     }
 
     void doScheduleVsync() {
         synchronized (mLock) {
-            if (mAnimationScheduled) {
+            if (mFrameScheduled) {
                 scheduleVsyncLocked();
             }
         }
     }
 
-    void doScheduleAnimation() {
+    void doScheduleCallback(int callbackType) {
         synchronized (mLock) {
-            final long now = SystemClock.uptimeMillis();
-            if (mAnimationCallbackQueue.hasDueCallbacksLocked(now)) {
-                scheduleAnimationLocked(now);
+            if (!mFrameScheduled) {
+                final long now = SystemClock.uptimeMillis();
+                if (mCallbackQueues[callbackType].hasDueCallbacksLocked(now)) {
+                    scheduleFrameLocked(now);
+                }
             }
         }
     }
 
-    void doScheduleDraw() {
+    void doTraversal() {
         synchronized (mLock) {
-            final long now = SystemClock.uptimeMillis();
-            if (mDrawCallbackQueue.hasDueCallbacksLocked(now)) {
-                scheduleDrawLocked(now);
+            if (mTraversalScheduled) {
+                mTraversalScheduled = false;
+                doCallbacks(CALLBACK_TRAVERSAL);
             }
         }
     }
@@ -471,25 +445,6 @@
         return Looper.myLooper() == mLooper;
     }
 
-    private void runCallbacks(Callback head) {
-        while (head != null) {
-            if (DEBUG) {
-                Log.d(TAG, "RunCallback: " + head.action + ", token=" + head.token
-                        + ", waitMillis=" + (SystemClock.uptimeMillis() - head.dueTime));
-            }
-            head.action.run();
-            head = head.next;
-        }
-    }
-
-    private void recycleCallbacksLocked(Callback head) {
-        while (head != null) {
-            final Callback next = head.next;
-            recycleCallbackLocked(head);
-            head = next;
-        }
-    }
-
     private Callback obtainCallbackLocked(long dueTime, Runnable action, Object token) {
         Callback callback = mCallbackPool;
         if (callback == null) {
@@ -519,20 +474,17 @@
         @Override
         public void handleMessage(Message msg) {
             switch (msg.what) {
-                case MSG_DO_ANIMATION:
-                    doAnimation();
-                    break;
-                case MSG_DO_DRAW:
-                    doDraw();
+                case MSG_DO_FRAME:
+                    doFrame(0);
                     break;
                 case MSG_DO_SCHEDULE_VSYNC:
                     doScheduleVsync();
                     break;
-                case MSG_DO_SCHEDULE_ANIMATION:
-                    doScheduleAnimation();
+                case MSG_DO_SCHEDULE_CALLBACK:
+                    doScheduleCallback(msg.arg1);
                     break;
-                case MSG_DO_SCHEDULE_DRAW:
-                    doScheduleDraw();
+                case MSG_DO_TRAVERSAL:
+                    doTraversal();
                     break;
             }
         }
@@ -545,7 +497,7 @@
 
         @Override
         public void onVsync(long timestampNanos, int frame) {
-            doAnimation();
+            doFrame(frame);
         }
     }
 
diff --git a/core/java/android/view/FocusFinder.java b/core/java/android/view/FocusFinder.java
index 9639faf..3529b8e 100644
--- a/core/java/android/view/FocusFinder.java
+++ b/core/java/android/view/FocusFinder.java
@@ -54,7 +54,7 @@
     /**
      * Find the next view to take focus in root's descendants, starting from the view
      * that currently is focused.
-     * @param root Contains focused
+     * @param root Contains focused. Cannot be null.
      * @param focused Has focus now.
      * @param direction Direction to look.
      * @return The next focusable view, or null if none exists.
@@ -82,7 +82,7 @@
                     setFocusBottomRight(root);
                     break;
                 case View.FOCUS_FORWARD:
-                    if (focused != null && focused.isLayoutRtl()) {
+                    if (root.isLayoutRtl()) {
                         setFocusTopLeft(root);
                     } else {
                         setFocusBottomRight(root);
@@ -94,7 +94,7 @@
                     setFocusTopLeft(root);
                     break;
                 case View.FOCUS_BACKWARD:
-                    if (focused != null && focused.isLayoutRtl()) {
+                    if (root.isLayoutRtl()) {
                         setFocusBottomRight(root);
                     } else {
                         setFocusTopLeft(root);
@@ -121,7 +121,7 @@
     /**
      * Find the next view to take focus in root's descendants, searching from
      * a particular rectangle in root's coordinates.
-     * @param root Contains focusedRect.
+     * @param root Contains focusedRect. Cannot be null.
      * @param focusedRect The starting point of the search.
      * @param direction Direction to look.
      * @return The next focusable view, or null if none exists.
@@ -155,10 +155,10 @@
             final int count = focusables.size();
             switch (direction) {
                 case View.FOCUS_FORWARD:
-                    return getForwardFocusable(focused, focusables, count);
+                    return getForwardFocusable(root, focused, focusables, count);
 
                 case View.FOCUS_BACKWARD:
-                    return getBackwardFocusable(focused, focusables, count);
+                    return getBackwardFocusable(root, focused, focusables, count);
             }
             return null;
         }
@@ -201,13 +201,14 @@
         return closest;
     }
 
-    private View getForwardFocusable(View focused, ArrayList<View> focusables, int count) {
-        return (focused != null && focused.isLayoutRtl()) ?
+    private static View getForwardFocusable(ViewGroup root, View focused,
+                                            ArrayList<View> focusables, int count) {
+        return (root.isLayoutRtl()) ?
                 getPreviousFocusable(focused, focusables, count) :
                 getNextFocusable(focused, focusables, count);
     }
 
-    private View getNextFocusable(View focused, ArrayList<View> focusables, int count) {
+    private static View getNextFocusable(View focused, ArrayList<View> focusables, int count) {
         if (focused != null) {
             int position = focusables.lastIndexOf(focused);
             if (position >= 0 && position + 1 < count) {
@@ -217,13 +218,14 @@
         return focusables.get(0);
     }
 
-    private View getBackwardFocusable(View focused, ArrayList<View> focusables, int count) {
-        return (focused != null && focused.isLayoutRtl()) ?
+    private static View getBackwardFocusable(ViewGroup root, View focused,
+                                             ArrayList<View> focusables, int count) {
+        return (root.isLayoutRtl()) ?
                 getNextFocusable(focused, focusables, count) :
                 getPreviousFocusable(focused, focusables, count);
     }
 
-    private View getPreviousFocusable(View focused, ArrayList<View> focusables, int count) {
+    private static View getPreviousFocusable(View focused, ArrayList<View> focusables, int count) {
         if (focused != null) {
             int position = focusables.indexOf(focused);
             if (position > 0) {
@@ -353,7 +355,7 @@
 
 
     /**
-     * Do the "beams" w.r.t the given direcition's axis of rect1 and rect2 overlap?
+     * Do the "beams" w.r.t the given direction's axis of rect1 and rect2 overlap?
      * @param direction the direction (up, down, left, right)
      * @param rect1 The first rectangle
      * @param rect2 The second rectangle
@@ -441,7 +443,7 @@
 
     /**
      * Find the distance on the minor axis w.r.t the direction to the nearest
-     * edge of the destination rectange.
+     * edge of the destination rectangle.
      * @param direction the direction (up, down, left, right)
      * @param source The source rect.
      * @param dest The destination rect.
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 2deeba6..6c964b0 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -1459,7 +1459,7 @@
      * apps.
      * @hide
      */
-    public static final boolean USE_DISPLAY_LIST_PROPERTIES = true;
+    public static final boolean USE_DISPLAY_LIST_PROPERTIES = false;
 
     /**
      * Map used to store views' tags.
@@ -8983,7 +8983,8 @@
     public void postOnAnimation(Runnable action) {
         final AttachInfo attachInfo = mAttachInfo;
         if (attachInfo != null) {
-            attachInfo.mViewRootImpl.mChoreographer.postAnimationCallback(action, null);
+            attachInfo.mViewRootImpl.mChoreographer.postCallback(
+                    Choreographer.CALLBACK_ANIMATION, action, null);
         } else {
             // Assume that post will succeed later
             ViewRootImpl.getRunQueue().post(action);
@@ -9007,8 +9008,8 @@
     public void postOnAnimationDelayed(Runnable action, long delayMillis) {
         final AttachInfo attachInfo = mAttachInfo;
         if (attachInfo != null) {
-            attachInfo.mViewRootImpl.mChoreographer.postAnimationCallbackDelayed(
-                    action, null, delayMillis);
+            attachInfo.mViewRootImpl.mChoreographer.postCallbackDelayed(
+                    Choreographer.CALLBACK_ANIMATION, action, null, delayMillis);
         } else {
             // Assume that post will succeed later
             ViewRootImpl.getRunQueue().postDelayed(action, delayMillis);
@@ -9033,7 +9034,8 @@
             final AttachInfo attachInfo = mAttachInfo;
             if (attachInfo != null) {
                 attachInfo.mHandler.removeCallbacks(action);
-                attachInfo.mViewRootImpl.mChoreographer.removeAnimationCallbacks(action, null);
+                attachInfo.mViewRootImpl.mChoreographer.removeCallbacks(
+                        Choreographer.CALLBACK_ANIMATION, action, null);
             } else {
                 // Assume that post will succeed later
                 ViewRootImpl.getRunQueue().removeCallbacks(action);
@@ -12239,8 +12241,9 @@
         if (verifyDrawable(who) && what != null) {
             final long delay = when - SystemClock.uptimeMillis();
             if (mAttachInfo != null) {
-                mAttachInfo.mViewRootImpl.mChoreographer.postAnimationCallbackDelayed(
-                        what, who, Choreographer.subtractFrameDelay(delay));
+                mAttachInfo.mViewRootImpl.mChoreographer.postCallbackDelayed(
+                        Choreographer.CALLBACK_ANIMATION, what, who,
+                        Choreographer.subtractFrameDelay(delay));
             } else {
                 ViewRootImpl.getRunQueue().postDelayed(what, delay);
             }
@@ -12256,7 +12259,8 @@
     public void unscheduleDrawable(Drawable who, Runnable what) {
         if (verifyDrawable(who) && what != null) {
             if (mAttachInfo != null) {
-                mAttachInfo.mViewRootImpl.mChoreographer.removeAnimationCallbacks(what, who);
+                mAttachInfo.mViewRootImpl.mChoreographer.removeCallbacks(
+                        Choreographer.CALLBACK_ANIMATION, what, who);
             } else {
                 ViewRootImpl.getRunQueue().removeCallbacks(what);
             }
@@ -12274,7 +12278,8 @@
      */
     public void unscheduleDrawable(Drawable who) {
         if (mAttachInfo != null && who != null) {
-            mAttachInfo.mViewRootImpl.mChoreographer.removeAnimationCallbacks(null, who);
+            mAttachInfo.mViewRootImpl.mChoreographer.removeCallbacks(
+                    Choreographer.CALLBACK_ANIMATION, null, who);
         }
     }
 
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index befc1c6..4d589d7 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -877,50 +877,6 @@
     public void bringChildToFront(View child) {
     }
 
-    public void scheduleTraversals() {
-        if (!mTraversalScheduled) {
-            mTraversalScheduled = true;
-            mTraversalBarrier = mHandler.getLooper().postSyncBarrier();
-            scheduleFrame();
-        }
-    }
-
-    public void unscheduleTraversals() {
-        if (mTraversalScheduled) {
-            mTraversalScheduled = false;
-            mHandler.getLooper().removeSyncBarrier(mTraversalBarrier);
-        }
-    }
-
-    void scheduleFrame() {
-        if (!mFrameScheduled) {
-            mFrameScheduled = true;
-            mChoreographer.postDrawCallback(mFrameRunnable, null);
-        }
-    }
-
-    void unscheduleFrame() {
-        unscheduleTraversals();
-
-        if (mFrameScheduled) {
-            mFrameScheduled = false;
-            mChoreographer.removeDrawCallbacks(mFrameRunnable, null);
-        }
-    }
-
-    void doFrame() {
-        if (mInputEventReceiver != null) {
-            mInputEventReceiver.consumeBatchedInputEvents();
-        }
-        doProcessInputEvents();
-
-        if (mTraversalScheduled) {
-            mTraversalScheduled = false;
-            mHandler.getLooper().removeSyncBarrier(mTraversalBarrier);
-            doTraversal();
-        }
-    }
-
     int getHostVisibility() {
         return mAppVisible ? mView.getVisibility() : View.GONE;
     }
@@ -954,41 +910,67 @@
         }
     }
 
-    private void doTraversal() {
-        if (mProfile) {
-            Debug.startMethodTracing("ViewAncestor");
+    void scheduleTraversals() {
+        if (!mTraversalScheduled) {
+            mTraversalScheduled = true;
+            mTraversalBarrier = mHandler.getLooper().postSyncBarrier();
+            mChoreographer.postCallback(
+                    Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
         }
+    }
 
-        final long traversalStartTime;
-        if (ViewDebug.DEBUG_LATENCY) {
-            traversalStartTime = System.nanoTime();
-            if (mLastTraversalFinishedTimeNanos != 0) {
-                Log.d(ViewDebug.DEBUG_LATENCY_TAG, "Starting performTraversals(); it has been "
-                        + ((traversalStartTime - mLastTraversalFinishedTimeNanos) * 0.000001f)
-                        + "ms since the last traversals finished.");
-            } else {
-                Log.d(ViewDebug.DEBUG_LATENCY_TAG, "Starting performTraversals().");
+    void unscheduleTraversals() {
+        if (mTraversalScheduled) {
+            mTraversalScheduled = false;
+            mHandler.getLooper().removeSyncBarrier(mTraversalBarrier);
+            mChoreographer.removeCallbacks(
+                    Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
+        }
+    }
+
+    void doTraversal() {
+        if (mTraversalScheduled) {
+            mTraversalScheduled = false;
+            mHandler.getLooper().removeSyncBarrier(mTraversalBarrier);
+
+            doConsumeBatchedInput(false);
+            doProcessInputEvents();
+
+            if (mProfile) {
+                Debug.startMethodTracing("ViewAncestor");
             }
-        }
 
-        Trace.traceBegin(Trace.TRACE_TAG_VIEW, "performTraversals");
-        try {
-            performTraversals();
-        } finally {
-            Trace.traceEnd(Trace.TRACE_TAG_VIEW);
-        }
+            final long traversalStartTime;
+            if (ViewDebug.DEBUG_LATENCY) {
+                traversalStartTime = System.nanoTime();
+                if (mLastTraversalFinishedTimeNanos != 0) {
+                    Log.d(ViewDebug.DEBUG_LATENCY_TAG, "Starting performTraversals(); it has been "
+                            + ((traversalStartTime - mLastTraversalFinishedTimeNanos) * 0.000001f)
+                            + "ms since the last traversals finished.");
+                } else {
+                    Log.d(ViewDebug.DEBUG_LATENCY_TAG, "Starting performTraversals().");
+                }
+            }
 
-        if (ViewDebug.DEBUG_LATENCY) {
-            long now = System.nanoTime();
-            Log.d(ViewDebug.DEBUG_LATENCY_TAG, "performTraversals() took "
-                    + ((now - traversalStartTime) * 0.000001f)
-                    + "ms.");
-            mLastTraversalFinishedTimeNanos = now;
-        }
+            Trace.traceBegin(Trace.TRACE_TAG_VIEW, "performTraversals");
+            try {
+                performTraversals();
+            } finally {
+                Trace.traceEnd(Trace.TRACE_TAG_VIEW);
+            }
 
-        if (mProfile) {
-            Debug.stopMethodTracing();
-            mProfile = false;
+            if (ViewDebug.DEBUG_LATENCY) {
+                long now = System.nanoTime();
+                Log.d(ViewDebug.DEBUG_LATENCY_TAG, "performTraversals() took "
+                        + ((now - traversalStartTime) * 0.000001f)
+                        + "ms.");
+                mLastTraversalFinishedTimeNanos = now;
+            }
+
+            if (mProfile) {
+                Debug.stopMethodTracing();
+                mProfile = false;
+            }
         }
     }
 
@@ -1937,6 +1919,7 @@
 
         final boolean fullRedrawNeeded = mFullRedrawNeeded;
         mFullRedrawNeeded = false;
+        mChoreographer.notifyDrawOccurred();
 
         Trace.traceBegin(Trace.TRACE_TAG_VIEW, "draw");
         try {
@@ -2460,7 +2443,7 @@
             mInputChannel = null;
         }
 
-        unscheduleFrame();
+        unscheduleTraversals();
     }
 
     void updateConfiguration(Configuration config, boolean force) {
@@ -3846,6 +3829,7 @@
         Message msg = mHandler.obtainMessage(MSG_IME_FINISHED_EVENT);
         msg.arg1 = seq;
         msg.arg2 = handled ? 1 : 0;
+        msg.setAsynchronous(true);
         mHandler.sendMessage(msg);
     }
 
@@ -3971,11 +3955,13 @@
     private void scheduleProcessInputEvents() {
         if (!mProcessInputEventsScheduled) {
             mProcessInputEventsScheduled = true;
-            mHandler.sendEmptyMessage(MSG_PROCESS_INPUT_EVENTS);
+            Message msg = mHandler.obtainMessage(MSG_PROCESS_INPUT_EVENTS);
+            msg.setAsynchronous(true);
+            mHandler.sendMessage(msg);
         }
     }
 
-    private void doProcessInputEvents() {
+    void doProcessInputEvents() {
         while (mCurrentInputEvent == null && mFirstPendingInputEvent != null) {
             QueuedInputEvent q = mFirstPendingInputEvent;
             mFirstPendingInputEvent = q.mNext;
@@ -4047,15 +4033,46 @@
         }
     }
 
-    final class FrameRunnable implements Runnable {
-        @Override
-        public void run() {
-            mFrameScheduled = false;
-            doFrame();
+    void scheduleConsumeBatchedInput() {
+        if (!mConsumeBatchedInputScheduled) {
+            mConsumeBatchedInputScheduled = true;
+            mChoreographer.postCallback(Choreographer.CALLBACK_INPUT,
+                    mConsumedBatchedInputRunnable, null);
         }
     }
-    final FrameRunnable mFrameRunnable = new FrameRunnable();
-    boolean mFrameScheduled;
+
+    void unscheduleConsumeBatchedInput() {
+        if (mConsumeBatchedInputScheduled) {
+            mConsumeBatchedInputScheduled = false;
+            mChoreographer.removeCallbacks(Choreographer.CALLBACK_INPUT,
+                    mConsumedBatchedInputRunnable, null);
+        }
+    }
+
+    void doConsumeBatchedInput(boolean callback) {
+        if (mConsumeBatchedInputScheduled) {
+            mConsumeBatchedInputScheduled = false;
+            if (!callback) {
+                mChoreographer.removeCallbacks(Choreographer.CALLBACK_INPUT,
+                        mConsumedBatchedInputRunnable, null);
+            }
+        }
+
+        // Always consume batched input events even if not scheduled, because there
+        // might be new input there waiting for us that we have no noticed yet because
+        // the Looper has not had a chance to run again.
+        if (mInputEventReceiver != null) {
+            mInputEventReceiver.consumeBatchedInputEvents();
+        }
+    }
+
+    final class TraversalRunnable implements Runnable {
+        @Override
+        public void run() {
+            doTraversal();
+        }
+    }
+    final TraversalRunnable mTraversalRunnable = new TraversalRunnable();
 
     final class WindowInputEventReceiver extends InputEventReceiver {
         public WindowInputEventReceiver(InputChannel inputChannel, Looper looper) {
@@ -4069,11 +4086,28 @@
 
         @Override
         public void onBatchedInputEventPending() {
-            scheduleFrame();
+            scheduleConsumeBatchedInput();
+        }
+
+        @Override
+        public void dispose() {
+            unscheduleConsumeBatchedInput();
+            super.dispose();
         }
     }
     WindowInputEventReceiver mInputEventReceiver;
 
+    final class ConsumeBatchedInputRunnable implements Runnable {
+        @Override
+        public void run() {
+            doConsumeBatchedInput(true);
+            doProcessInputEvents();
+        }
+    }
+    final ConsumeBatchedInputRunnable mConsumedBatchedInputRunnable =
+            new ConsumeBatchedInputRunnable();
+    boolean mConsumeBatchedInputScheduled;
+
     final class InvalidateOnAnimationRunnable implements Runnable {
         private boolean mPosted;
         private ArrayList<View> mViews = new ArrayList<View>();
@@ -4109,7 +4143,7 @@
                 }
 
                 if (mPosted && mViews.isEmpty() && mViewRects.isEmpty()) {
-                    mChoreographer.removeAnimationCallbacks(this, null);
+                    mChoreographer.removeCallbacks(Choreographer.CALLBACK_ANIMATION, this, null);
                     mPosted = false;
                 }
             }
@@ -4150,7 +4184,7 @@
 
         private void postIfNeededLocked() {
             if (!mPosted) {
-                mChoreographer.postAnimationCallback(this, null);
+                mChoreographer.postCallback(Choreographer.CALLBACK_ANIMATION, this, null);
                 mPosted = true;
             }
         }
diff --git a/core/java/android/view/textservice/SentenceSuggestionsInfo.java b/core/java/android/view/textservice/SentenceSuggestionsInfo.java
index cb9e496..afd62eb 100644
--- a/core/java/android/view/textservice/SentenceSuggestionsInfo.java
+++ b/core/java/android/view/textservice/SentenceSuggestionsInfo.java
@@ -22,8 +22,13 @@
 import java.util.Arrays;
 
 /**
- * @hide
- * This class contains a metadata of sentence level suggestions from the text service
+ * This class contains a metadata of suggestions returned from a text service
+ * (e.g. {@link android.service.textservice.SpellCheckerService}).
+ * The text service uses this class to return the suggestions
+ * for a sentence. See {@link SuggestionsInfo} which is used for suggestions for a word.
+ * This class extends the functionality of {@link SuggestionsInfo} as far as this class enables
+ * you to put multiple {@link SuggestionsInfo}s on a sentence with the offsets and the lengths
+ * of all {@link SuggestionsInfo}s.
  */
 public final class SentenceSuggestionsInfo implements Parcelable {
 
@@ -82,14 +87,15 @@
     }
 
     /**
-     * @hide
+     * @return the count of {@link SuggestionsInfo}s this instance holds.
      */
     public int getSuggestionsCount() {
         return mSuggestionsInfos.length;
     }
 
     /**
-     * @hide
+     * @param i the id of {@link SuggestionsInfo}s this instance holds.
+     * @return a {@link SuggestionsInfo} at the specified id
      */
     public SuggestionsInfo getSuggestionsInfoAt(int i) {
         if (i >= 0 && i < mSuggestionsInfos.length) {
@@ -99,7 +105,8 @@
     }
 
     /**
-     * @hide
+     * @param i the id of {@link SuggestionsInfo}s this instance holds
+     * @return the offset of the specified {@link SuggestionsInfo}
      */
     public int getOffsetAt(int i) {
         if (i >= 0 && i < mOffsets.length) {
@@ -109,7 +116,8 @@
     }
 
     /**
-     * @hide
+     * @param i the id of {@link SuggestionsInfo}s this instance holds
+     * @return the length of the specified {@link SuggestionsInfo}
      */
     public int getLengthAt(int i) {
         if (i >= 0 && i < mLengths.length) {
diff --git a/core/java/android/view/textservice/SpellCheckerSession.java b/core/java/android/view/textservice/SpellCheckerSession.java
index 6ff3b9b..35940ba 100644
--- a/core/java/android/view/textservice/SpellCheckerSession.java
+++ b/core/java/android/view/textservice/SpellCheckerSession.java
@@ -178,17 +178,19 @@
     }
 
     /**
-     * @hide
+     * Get suggestions from the specified sentences
+     * @param textInfos an array of text metadata for a spell checker
+     * @param suggestionsLimit the maximum number of suggestions that will be returned
      */
-    public void getSentenceSuggestions(TextInfo[] textInfo, int suggestionsLimit) {
+    public void getSentenceSuggestions(TextInfo[] textInfos, int suggestionsLimit) {
         mSpellCheckerSessionListenerImpl.getSentenceSuggestionsMultiple(
-                textInfo, suggestionsLimit);
+                textInfos, suggestionsLimit);
     }
 
     /**
      * Get candidate strings for a substring of the specified text.
      * @param textInfo text metadata for a spell checker
-     * @param suggestionsLimit the number of limit of suggestions returned
+     * @param suggestionsLimit the maximum number of suggestions that will be returned
      */
     public void getSuggestions(TextInfo textInfo, int suggestionsLimit) {
         getSuggestions(new TextInfo[] {textInfo}, suggestionsLimit, false);
@@ -197,7 +199,7 @@
     /**
      * A batch process of getSuggestions
      * @param textInfos an array of text metadata for a spell checker
-     * @param suggestionsLimit the number of limit of suggestions returned
+     * @param suggestionsLimit the maximum number of suggestions that will be returned
      * @param sequentialWords true if textInfos can be treated as sequential words.
      */
     public void getSuggestions(
@@ -434,12 +436,19 @@
      */
     public interface SpellCheckerSessionListener {
         /**
-         * Callback for "getSuggestions"
-         * @param results an array of results of getSuggestions
+         * Callback for {@link SpellCheckerSession#getSuggestions(TextInfo[], int, boolean)}
+         * @param results an array of {@link SuggestionsInfo}s.
+         * These results are suggestions for {@link TextInfo}s queried by
+         * {@link SpellCheckerSession#getSuggestions(TextInfo[], int, boolean)}.
          */
         public void onGetSuggestions(SuggestionsInfo[] results);
+        // TODO: Remove @hide as soon as the sample spell checker client gets fixed.
         /**
          * @hide
+         * Callback for {@link SpellCheckerSession#getSentenceSuggestions(TextInfo[], int)}
+         * @param results an array of {@link SentenceSuggestionsInfo}s.
+         * These results are suggestions for {@link TextInfo}s
+         * queried by {@link SpellCheckerSession#getSentenceSuggestions(TextInfo[], int)}.
          */
         public void onGetSentenceSuggestions(SentenceSuggestionsInfo[] results);
     }
diff --git a/core/java/android/webkit/GeolocationPermissions.java b/core/java/android/webkit/GeolocationPermissions.java
index d7b6adb..93eb082 100755
--- a/core/java/android/webkit/GeolocationPermissions.java
+++ b/core/java/android/webkit/GeolocationPermissions.java
@@ -18,14 +18,12 @@
 
 import android.os.Handler;
 import android.os.Message;
-import android.util.Log;
+
 import java.util.HashMap;
-import java.util.HashSet;
 import java.util.Map;
 import java.util.Set;
 import java.util.Vector;
 
-
 /**
  * This class is used to manage permissions for the WebView's Geolocation
  * JavaScript API.
@@ -47,7 +45,7 @@
  * Geolocation permissions at any time.
  */
 // This class is the Java counterpart of the WebKit C++ GeolocationPermissions
-// class. It simply marshalls calls from the UI thread to the WebKit thread.
+// class. It simply marshals calls from the UI thread to the WebKit thread.
 //
 // Within WebKit, Geolocation permissions may be applied either temporarily
 // (for the duration of the page) or permanently. This class deals only with
@@ -70,9 +68,6 @@
         public void invoke(String origin, boolean allow, boolean retain);
     };
 
-    // Log tag
-    private static final String TAG = "geolocationPermissions";
-
     // Global instance
     private static GeolocationPermissions sInstance;
 
diff --git a/core/java/android/webkit/WebStorage.java b/core/java/android/webkit/WebStorage.java
index c079404..2300c2e 100644
--- a/core/java/android/webkit/WebStorage.java
+++ b/core/java/android/webkit/WebStorage.java
@@ -18,13 +18,10 @@
 
 import android.os.Handler;
 import android.os.Message;
-import android.util.Log;
 
 import java.util.Collection;
-import java.util.Map;
 import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Iterator;
+import java.util.Map;
 import java.util.Set;
 
 /**
@@ -44,9 +41,6 @@
         public void updateQuota(long newQuota);
     };
 
-    // Log tag
-    private static final String TAG = "webstorage";
-
     // Global instance of a WebStorage
     private static WebStorage sWebStorage;
 
diff --git a/core/java/android/webkit/WebViewClassic.java b/core/java/android/webkit/WebViewClassic.java
index 7ddff8e..ba1c7e3 100644
--- a/core/java/android/webkit/WebViewClassic.java
+++ b/core/java/android/webkit/WebViewClassic.java
@@ -70,8 +70,6 @@
 import android.util.EventLog;
 import android.util.Log;
 import android.view.Display;
-import android.view.GestureDetector;
-import android.view.GestureDetector.SimpleOnGestureListener;
 import android.view.Gravity;
 import android.view.HapticFeedbackConstants;
 import android.view.HardwareCanvas;
@@ -386,6 +384,7 @@
         private boolean mIsAutoFillable;
         private boolean mIsAutoCompleteEnabled;
         private String mName;
+        private int mBatchLevel;
 
         public WebViewInputConnection() {
             super(mWebView, true);
@@ -404,6 +403,24 @@
             }
         }
 
+        @Override
+        public boolean beginBatchEdit() {
+            if (mBatchLevel == 0) {
+                beginTextBatch();
+            }
+            mBatchLevel++;
+            return false;
+        }
+
+        @Override
+        public boolean endBatchEdit() {
+            mBatchLevel--;
+            if (mBatchLevel == 0) {
+                commitTextBatch();
+            }
+            return false;
+        }
+
         public boolean getIsAutoFillable() {
             return mIsAutoFillable;
         }
@@ -801,51 +818,6 @@
         }
     }
 
-    private class TextScrollListener extends SimpleOnGestureListener {
-        @Override
-        public boolean onFling(MotionEvent e1, MotionEvent e2,
-                float velocityX, float velocityY) {
-            int maxScrollX = mEditTextContent.width() -
-                    mEditTextBounds.width();
-            int maxScrollY = mEditTextContent.height() -
-                    mEditTextBounds.height();
-
-            int contentVelocityX = viewToContentDimension((int)-velocityX);
-            int contentVelocityY = viewToContentDimension((int)-velocityY);
-            mEditTextScroller.fling(-mEditTextContent.left,
-                    -mEditTextContent.top,
-                    contentVelocityX, contentVelocityY,
-                    0, maxScrollX, 0, maxScrollY);
-            return true;
-        }
-
-        @Override
-        public boolean onScroll(MotionEvent e1, MotionEvent e2,
-                float distanceX, float distanceY) {
-            // Scrollable edit text. Scroll it.
-            int newScrollX = deltaToTextScroll(
-                    -mEditTextContent.left, mEditTextContent.width(),
-                    mEditTextBounds.width(),
-                    (int) distanceX);
-            int newScrollY = deltaToTextScroll(
-                    -mEditTextContent.top, mEditTextContent.height(),
-                    mEditTextBounds.height(),
-                    (int) distanceY);
-            scrollEditText(newScrollX, newScrollY);
-            return true;
-        }
-
-        private int deltaToTextScroll(int oldScroll, int contentSize,
-                int boundsSize, int delta) {
-            int newScroll = oldScroll +
-                    viewToContentDimension(delta);
-            int maxScroll = contentSize - boundsSize;
-            newScroll = Math.min(maxScroll, newScroll);
-            newScroll = Math.max(0, newScroll);
-            return newScroll;
-        }
-    }
-
     // The listener to capture global layout change event.
     private InnerGlobalLayoutListener mGlobalLayoutListener = null;
 
@@ -874,11 +846,12 @@
     private int mFieldPointer;
     private PastePopupWindow mPasteWindow;
     private AutoCompletePopup mAutoCompletePopup;
-    private GestureDetector mGestureDetector;
     Rect mEditTextBounds = new Rect();
     Rect mEditTextContent = new Rect();
     int mEditTextLayerId;
     boolean mIsEditingText = false;
+    ArrayList<Message> mBatchedTextChanges = new ArrayList<Message>();
+    boolean mIsBatchingTextChanges = false;
 
     private static class OnTrimMemoryListener implements ComponentCallbacks2 {
         private static OnTrimMemoryListener sInstance = null;
@@ -1016,6 +989,7 @@
     private static final int TOUCH_DONE_MODE = 7;
     private static final int TOUCH_PINCH_DRAG = 8;
     private static final int TOUCH_DRAG_LAYER_MODE = 9;
+    private static final int TOUCH_DRAG_TEXT_MODE = 10;
 
     // Whether to forward the touch events to WebCore
     // Can only be set by WebKit via JNI.
@@ -1493,7 +1467,6 @@
         }
 
         mAutoFillData = new WebViewCore.AutoFillData();
-        mGestureDetector = new GestureDetector(mContext, new TextScrollListener());
         mEditTextScroller = new Scroller(context);
     }
 
@@ -3383,6 +3356,10 @@
             boolean clampedY) {
         // Special-case layer scrolling so that we do not trigger normal scroll
         // updating.
+        if (mTouchMode == TOUCH_DRAG_TEXT_MODE) {
+            scrollEditText(scrollX, scrollY);
+            return;
+        }
         if (mTouchMode == TOUCH_DRAG_LAYER_MODE) {
             scrollLayerTo(scrollX, scrollY);
             return;
@@ -3855,6 +3832,12 @@
                     rangeY = mScrollingLayerRect.bottom;
                     // No overscrolling for layers.
                     overflingDistance = 0;
+                } else if (mTouchMode == TOUCH_DRAG_TEXT_MODE) {
+                    oldX = getTextScrollX();
+                    oldY = getTextScrollY();
+                    rangeX = getMaxTextScrollX();
+                    rangeY = getMaxTextScrollY();
+                    overflingDistance = 0;
                 }
 
                 mWebViewPrivate.overScrollBy(x - oldX, y - oldY, oldX, oldY,
@@ -3865,12 +3848,14 @@
                     mOverScrollGlow.absorbGlow(x, y, oldX, oldY, rangeX, rangeY);
                 }
             } else {
-                if (mTouchMode != TOUCH_DRAG_LAYER_MODE) {
-                    setScrollXRaw(x);
-                    setScrollYRaw(y);
-                } else {
+                if (mTouchMode == TOUCH_DRAG_LAYER_MODE) {
                     // Update the layer position instead of WebView.
                     scrollLayerTo(x, y);
+                } else if (mTouchMode == TOUCH_DRAG_TEXT_MODE) {
+                    scrollEditText(x, y);
+                } else {
+                    setScrollXRaw(x);
+                    setScrollYRaw(y);
                 }
                 abortAnimation();
                 nativeSetIsScrolling(false);
@@ -3892,7 +3877,7 @@
     private void scrollLayerTo(int x, int y) {
         int dx = mScrollingLayerRect.left - x;
         int dy = mScrollingLayerRect.top - y;
-        if (dx == 0 && y == 0) {
+        if (dx == 0 && dy == 0) {
             return;
         }
         if (mSelectingText) {
@@ -5095,8 +5080,8 @@
         // send complex characters to webkit for use by JS and plugins
         if (keyCode == KeyEvent.KEYCODE_UNKNOWN && event.getCharacters() != null) {
             // pass the key to DOM
-            mWebViewCore.sendMessage(EventHub.KEY_DOWN, event);
-            mWebViewCore.sendMessage(EventHub.KEY_UP, event);
+            sendBatchableInputMessage(EventHub.KEY_DOWN, 0, 0, event);
+            sendBatchableInputMessage(EventHub.KEY_UP, 0, 0, event);
             // return true as DOM handles the key
             return true;
         }
@@ -5162,7 +5147,7 @@
                 // if an accessibility script is injected we delegate to it the key handling.
                 // this script is a screen reader which is a fully fledged solution for blind
                 // users to navigate in and interact with web pages.
-                mWebViewCore.sendMessage(EventHub.KEY_DOWN, event);
+                sendBatchableInputMessage(EventHub.KEY_DOWN, 0, 0, event);
                 return true;
             } else {
                 // Clean up if accessibility was disabled after loading the current URL.
@@ -5289,7 +5274,7 @@
                 // if an accessibility script is injected we delegate to it the key handling.
                 // this script is a screen reader which is a fully fledged solution for blind
                 // users to navigate in and interact with web pages.
-                mWebViewCore.sendMessage(EventHub.KEY_UP, event);
+                sendBatchableInputMessage(EventHub.KEY_UP, 0, 0, event);
                 return true;
             } else {
                 // Clean up if accessibility was disabled after loading the current URL.
@@ -6156,7 +6141,6 @@
                 startTouch(x, y, eventTime);
                 if (mIsEditingText) {
                     mTouchInEditText = mEditTextBounds.contains(contentX, contentY);
-                    mGestureDetector.onTouchEvent(ev);
                 }
                 break;
             }
@@ -6189,13 +6173,6 @@
                         invalidate();
                     }
                     break;
-                } else if (mConfirmMove && mTouchInEditText) {
-                    ViewParent parent = mWebView.getParent();
-                    if (parent != null) {
-                        parent.requestDisallowInterceptTouchEvent(true);
-                    }
-                    mGestureDetector.onTouchEvent(ev);
-                    break;
                 }
 
                 // pass the touch events from UI thread to WebCore thread
@@ -6243,7 +6220,8 @@
                 }
 
                 if (mTouchMode != TOUCH_DRAG_MODE &&
-                        mTouchMode != TOUCH_DRAG_LAYER_MODE) {
+                        mTouchMode != TOUCH_DRAG_LAYER_MODE &&
+                        mTouchMode != TOUCH_DRAG_TEXT_MODE) {
 
                     if (!mConfirmMove) {
                         break;
@@ -6326,9 +6304,6 @@
                             deltaX = 0;
                         }
                     }
-                    mLastTouchX = x;
-                    mLastTouchY = y;
-
                     if (deltaX * deltaX + deltaY * deltaY > mTouchSlopSquare) {
                         mHeldMotionless = MOTIONLESS_FALSE;
                         nativeSetIsScrolling(true);
@@ -6339,13 +6314,24 @@
                     }
 
                     mLastTouchTime = eventTime;
+                    boolean allDrag = doDrag(deltaX, deltaY);
+                    if (allDrag) {
+                        mLastTouchX = x;
+                        mLastTouchY = y;
+                    } else {
+                        int contentDeltaX = (int)Math.floor(deltaX * mZoomManager.getInvScale());
+                        int roundedDeltaX = contentToViewDimension(contentDeltaX);
+                        int contentDeltaY = (int)Math.floor(deltaY * mZoomManager.getInvScale());
+                        int roundedDeltaY = contentToViewDimension(contentDeltaY);
+                        mLastTouchX -= roundedDeltaX;
+                        mLastTouchY -= roundedDeltaY;
+                    }
                 }
 
-                doDrag(deltaX, deltaY);
-
                 // Turn off scrollbars when dragging a layer.
                 if (keepScrollBarsVisible &&
-                        mTouchMode != TOUCH_DRAG_LAYER_MODE) {
+                        mTouchMode != TOUCH_DRAG_LAYER_MODE &&
+                        mTouchMode != TOUCH_DRAG_TEXT_MODE) {
                     if (mHeldMotionless != MOTIONLESS_TRUE) {
                         mHeldMotionless = MOTIONLESS_TRUE;
                         invalidate();
@@ -6366,11 +6352,6 @@
                 break;
             }
             case MotionEvent.ACTION_UP: {
-                mGestureDetector.onTouchEvent(ev);
-                if (mTouchInEditText && mConfirmMove) {
-                    stopTouch();
-                    break; // We've been scrolling the edit text.
-                }
                 if (!mConfirmMove && mIsEditingText && mSelectionStarted &&
                         mIsCaretSelection) {
                     showPasteWindow();
@@ -6484,6 +6465,7 @@
                         }
                     case TOUCH_DRAG_MODE:
                     case TOUCH_DRAG_LAYER_MODE:
+                    case TOUCH_DRAG_TEXT_MODE:
                         mPrivateHandler.removeMessages(DRAG_HELD_MOTIONLESS);
                         mPrivateHandler.removeMessages(AWAKEN_SCROLL_BARS);
                         // if the user waits a while w/o moving before the
@@ -6680,20 +6662,31 @@
         }
     }
 
-    private void doDrag(int deltaX, int deltaY) {
+    private boolean doDrag(int deltaX, int deltaY) {
+        boolean allDrag = true;
         if ((deltaX | deltaY) != 0) {
             int oldX = getScrollX();
             int oldY = getScrollY();
             int rangeX = computeMaxScrollX();
             int rangeY = computeMaxScrollY();
-            // Check for the original scrolling layer in case we change
-            // directions.  mTouchMode might be TOUCH_DRAG_MODE if we have
-            // reached the edge of a layer but mScrollingLayer will be non-zero
-            // if we initiated the drag on a layer.
-            if (mCurrentScrollingLayerId != 0) {
-                final int contentX = viewToContentDimension(deltaX);
-                final int contentY = viewToContentDimension(deltaY);
+            final int contentX = (int)Math.floor(deltaX * mZoomManager.getInvScale());
+            final int contentY = (int)Math.floor(deltaY * mZoomManager.getInvScale());
 
+            // Assume page scrolling and change below if we're wrong
+            mTouchMode = TOUCH_DRAG_MODE;
+
+            // Check special scrolling before going to main page scrolling.
+            if (mIsEditingText && mTouchInEditText && canTextScroll(deltaX, deltaY)) {
+                // Edit text scrolling
+                oldX = getTextScrollX();
+                rangeX = getMaxTextScrollX();
+                deltaX = contentX;
+                oldY = getTextScrollY();
+                rangeY = getMaxTextScrollY();
+                deltaY = contentY;
+                mTouchMode = TOUCH_DRAG_TEXT_MODE;
+                allDrag = false;
+            } else if (mCurrentScrollingLayerId != 0) {
                 // Check the scrolling bounds to see if we will actually do any
                 // scrolling.  The rectangle is in document coordinates.
                 final int maxX = mScrollingLayerRect.right;
@@ -6713,12 +6706,7 @@
                     oldY = mScrollingLayerRect.top;
                     rangeX = maxX;
                     rangeY = maxY;
-                } else {
-                    // Scroll the main page if we are not going to scroll the
-                    // layer.  This does not reset mScrollingLayer in case the
-                    // user changes directions and the layer can scroll the
-                    // other way.
-                    mTouchMode = TOUCH_DRAG_MODE;
+                    allDrag = false;
                 }
             }
 
@@ -6734,11 +6722,13 @@
             }
         }
         mZoomManager.keepZoomPickerVisible();
+        return allDrag;
     }
 
     private void stopTouch() {
         if (mScroller.isFinished() && !mSelectingText
-                && (mTouchMode == TOUCH_DRAG_MODE || mTouchMode == TOUCH_DRAG_LAYER_MODE)) {
+                && (mTouchMode == TOUCH_DRAG_MODE
+                || mTouchMode == TOUCH_DRAG_LAYER_MODE)) {
             WebViewCore.resumePriority();
             WebViewCore.resumeUpdatePicture(mWebViewCore);
             nativeSetIsScrolling(false);
@@ -7132,6 +7122,13 @@
             maxY = mScrollingLayerRect.bottom;
             // No overscrolling for layers.
             overscrollDistance = overflingDistance = 0;
+        } else if (mTouchMode == TOUCH_DRAG_TEXT_MODE) {
+            scrollX = getTextScrollX();
+            scrollY = getTextScrollY();
+            maxX = getMaxTextScrollX();
+            maxY = getMaxTextScrollY();
+            // No overscrolling for edit text.
+            overscrollDistance = overflingDistance = 0;
         }
 
         if (mSnapScrollMode != SNAP_NONE) {
@@ -7211,7 +7208,7 @@
         final int time = mScroller.getDuration();
 
         // Suppress scrollbars for layer scrolling.
-        if (mTouchMode != TOUCH_DRAG_LAYER_MODE) {
+        if (mTouchMode != TOUCH_DRAG_LAYER_MODE && mTouchMode != TOUCH_DRAG_TEXT_MODE) {
             mWebViewPrivate.awakenScrollBars(time);
         }
 
@@ -7543,7 +7540,7 @@
         arg.mNewEnd = newEnd;
         mTextGeneration++;
         arg.mTextGeneration = mTextGeneration;
-        mWebViewCore.sendMessage(EventHub.REPLACE_TEXT, oldStart, oldEnd, arg);
+        sendBatchableInputMessage(EventHub.REPLACE_TEXT, oldStart, oldEnd, arg);
     }
 
     /* package */ void passToJavaScript(String currentText, KeyEvent event) {
@@ -7569,6 +7566,36 @@
         return mWebViewCore;
     }
 
+    private boolean canTextScroll(int directionX, int directionY) {
+        int scrollX = getTextScrollX();
+        int scrollY = getTextScrollY();
+        int maxScrollX = getMaxTextScrollX();
+        int maxScrollY = getMaxTextScrollY();
+        boolean canScrollX = (directionX > 0)
+                ? (scrollX < maxScrollX)
+                : (scrollX > 0);
+        boolean canScrollY = (directionY > 0)
+                ? (scrollY < maxScrollY)
+                : (scrollY > 0);
+        return canScrollX || canScrollY;
+    }
+
+    private int getTextScrollX() {
+        return -mEditTextContent.left;
+    }
+
+    private int getTextScrollY() {
+        return -mEditTextContent.top;
+    }
+
+    private int getMaxTextScrollX() {
+        return Math.max(0, mEditTextContent.width() - mEditTextBounds.width());
+    }
+
+    private int getMaxTextScrollY() {
+        return Math.max(0, mEditTextContent.height() - mEditTextBounds.height());
+    }
+
     /**
      * Used only by TouchEventQueue to store pending touch events.
      */
@@ -8512,7 +8539,7 @@
                     break;
 
                 case KEY_PRESS:
-                    mWebViewCore.sendMessage(EventHub.KEY_PRESS, msg.arg1);
+                    sendBatchableInputMessage(EventHub.KEY_PRESS, msg.arg1, 0, null);
                     break;
 
                 case RELOCATE_AUTO_COMPLETE_POPUP:
@@ -8888,8 +8915,7 @@
 
     private void scrollEditText(int scrollX, int scrollY) {
         // Scrollable edit text. Scroll it.
-        float maxScrollX = (float)(mEditTextContent.width() -
-                mEditTextBounds.width());
+        float maxScrollX = getMaxTextScrollX();
         float scrollPercentX = ((float)scrollX)/maxScrollX;
         mEditTextContent.offsetTo(-scrollX, -scrollY);
         mWebViewCore.sendMessageAtFrontOfQueue(EventHub.SCROLL_TEXT_INPUT, 0,
@@ -8898,6 +8924,31 @@
                 TEXT_SCROLL_ANIMATION_DELAY_MS);
     }
 
+    private void beginTextBatch() {
+        mIsBatchingTextChanges = true;
+    }
+
+    private void commitTextBatch() {
+        if (mWebViewCore != null) {
+            mWebViewCore.sendMessages(mBatchedTextChanges);
+        }
+        mBatchedTextChanges.clear();
+        mIsBatchingTextChanges = false;
+    }
+
+    private void sendBatchableInputMessage(int what, int arg1, int arg2,
+            Object obj) {
+        if (mWebViewCore == null) {
+            return;
+        }
+        Message message = Message.obtain(null, what, arg1, arg2, obj);
+        if (mIsBatchingTextChanges) {
+            mBatchedTextChanges.add(message);
+        } else {
+            mWebViewCore.sendMessage(message);
+        }
+    }
+
     // Class used to use a dropdown for a <select> element
     private class InvokeListBox implements Runnable {
         // Whether the listbox allows multiple selection.
@@ -9296,7 +9347,7 @@
                 mWebView.playSoundEffect(sound);
             }
         }
-        mWebViewCore.sendMessage(eventHubAction, direction, event);
+        sendBatchableInputMessage(eventHubAction, direction, 0, event);
     }
 
     /**
diff --git a/core/java/android/webkit/WebViewCore.java b/core/java/android/webkit/WebViewCore.java
index afb2992..3eba6d7 100644
--- a/core/java/android/webkit/WebViewCore.java
+++ b/core/java/android/webkit/WebViewCore.java
@@ -1914,6 +1914,14 @@
         mEventHub.sendMessage(msg);
     }
 
+    void sendMessages(ArrayList<Message> messages) {
+        synchronized (mEventHub) {
+            for (int i = 0; i < messages.size(); i++) {
+                mEventHub.sendMessage(messages.get(i));
+            }
+        }
+    }
+
     void sendMessage(int what) {
         mEventHub.sendMessage(Message.obtain(null, what));
     }
diff --git a/core/java/com/android/internal/statusbar/IStatusBar.aidl b/core/java/com/android/internal/statusbar/IStatusBar.aidl
index aca1fa2..6e36fdb 100644
--- a/core/java/com/android/internal/statusbar/IStatusBar.aidl
+++ b/core/java/com/android/internal/statusbar/IStatusBar.aidl
@@ -35,5 +35,7 @@
     void setImeWindowStatus(in IBinder token, int vis, int backDisposition);
     void setHardKeyboardStatus(boolean available, boolean enabled);
     void toggleRecentApps();
+    void preloadRecentApps();
+    void cancelPreloadRecentApps();
 }
 
diff --git a/core/java/com/android/internal/statusbar/IStatusBarService.aidl b/core/java/com/android/internal/statusbar/IStatusBarService.aidl
index ecebfc0..118e541 100644
--- a/core/java/com/android/internal/statusbar/IStatusBarService.aidl
+++ b/core/java/com/android/internal/statusbar/IStatusBarService.aidl
@@ -47,4 +47,6 @@
     void setSystemUiVisibility(int vis);
     void setHardKeyboardEnabled(boolean enabled);
     void toggleRecentApps();
+    void preloadRecentApps();
+    void cancelPreloadRecentApps();
 }
diff --git a/core/java/com/android/internal/widget/PointerLocationView.java b/core/java/com/android/internal/widget/PointerLocationView.java
index 9a0ce3a..0524c25 100644
--- a/core/java/com/android/internal/widget/PointerLocationView.java
+++ b/core/java/com/android/internal/widget/PointerLocationView.java
@@ -188,178 +188,176 @@
 
     @Override
     protected void onDraw(Canvas canvas) {
-        synchronized (mPointers) {
-            final int w = getWidth();
-            final int itemW = w/7;
-            final int base = -mTextMetrics.ascent+1;
-            final int bottom = mHeaderBottom;
-            
-            final int NP = mPointers.size();
-            
-            // Labels
-            if (mActivePointerId >= 0) {
-                final PointerState ps = mPointers.get(mActivePointerId);
-                
-                canvas.drawRect(0, 0, itemW-1, bottom,mTextBackgroundPaint);
-                canvas.drawText(mText.clear()
-                        .append("P: ").append(mCurNumPointers)
-                        .append(" / ").append(mMaxNumPointers)
-                        .toString(), 1, base, mTextPaint);
+        final int w = getWidth();
+        final int itemW = w/7;
+        final int base = -mTextMetrics.ascent+1;
+        final int bottom = mHeaderBottom;
 
-                final int N = ps.mTraceCount;
-                if ((mCurDown && ps.mCurDown) || N == 0) {
-                    canvas.drawRect(itemW, 0, (itemW * 2) - 1, bottom, mTextBackgroundPaint);
-                    canvas.drawText(mText.clear()
-                            .append("X: ").append(ps.mCoords.x, 1)
-                            .toString(), 1 + itemW, base, mTextPaint);
-                    canvas.drawRect(itemW * 2, 0, (itemW * 3) - 1, bottom, mTextBackgroundPaint);
-                    canvas.drawText(mText.clear()
-                            .append("Y: ").append(ps.mCoords.y, 1)
-                            .toString(), 1 + itemW * 2, base, mTextPaint);
-                } else {
-                    float dx = ps.mTraceX[N - 1] - ps.mTraceX[0];
-                    float dy = ps.mTraceY[N - 1] - ps.mTraceY[0];
-                    canvas.drawRect(itemW, 0, (itemW * 2) - 1, bottom,
-                            Math.abs(dx) < mVC.getScaledTouchSlop()
-                            ? mTextBackgroundPaint : mTextLevelPaint);
-                    canvas.drawText(mText.clear()
-                            .append("dX: ").append(dx, 1)
-                            .toString(), 1 + itemW, base, mTextPaint);
-                    canvas.drawRect(itemW * 2, 0, (itemW * 3) - 1, bottom,
-                            Math.abs(dy) < mVC.getScaledTouchSlop()
-                            ? mTextBackgroundPaint : mTextLevelPaint);
-                    canvas.drawText(mText.clear()
-                            .append("dY: ").append(dy, 1)
-                            .toString(), 1 + itemW * 2, base, mTextPaint);
-                }
-                
-                canvas.drawRect(itemW * 3, 0, (itemW * 4) - 1, bottom, mTextBackgroundPaint);
+        final int NP = mPointers.size();
+
+        // Labels
+        if (mActivePointerId >= 0) {
+            final PointerState ps = mPointers.get(mActivePointerId);
+            
+            canvas.drawRect(0, 0, itemW-1, bottom,mTextBackgroundPaint);
+            canvas.drawText(mText.clear()
+                    .append("P: ").append(mCurNumPointers)
+                    .append(" / ").append(mMaxNumPointers)
+                    .toString(), 1, base, mTextPaint);
+
+            final int N = ps.mTraceCount;
+            if ((mCurDown && ps.mCurDown) || N == 0) {
+                canvas.drawRect(itemW, 0, (itemW * 2) - 1, bottom, mTextBackgroundPaint);
                 canvas.drawText(mText.clear()
-                        .append("Xv: ").append(ps.mXVelocity, 3)
-                        .toString(), 1 + itemW * 3, base, mTextPaint);
-                
-                canvas.drawRect(itemW * 4, 0, (itemW * 5) - 1, bottom, mTextBackgroundPaint);
+                        .append("X: ").append(ps.mCoords.x, 1)
+                        .toString(), 1 + itemW, base, mTextPaint);
+                canvas.drawRect(itemW * 2, 0, (itemW * 3) - 1, bottom, mTextBackgroundPaint);
                 canvas.drawText(mText.clear()
-                        .append("Yv: ").append(ps.mYVelocity, 3)
-                        .toString(), 1 + itemW * 4, base, mTextPaint);
-                
-                canvas.drawRect(itemW * 5, 0, (itemW * 6) - 1, bottom, mTextBackgroundPaint);
-                canvas.drawRect(itemW * 5, 0, (itemW * 5) + (ps.mCoords.pressure * itemW) - 1,
-                        bottom, mTextLevelPaint);
+                        .append("Y: ").append(ps.mCoords.y, 1)
+                        .toString(), 1 + itemW * 2, base, mTextPaint);
+            } else {
+                float dx = ps.mTraceX[N - 1] - ps.mTraceX[0];
+                float dy = ps.mTraceY[N - 1] - ps.mTraceY[0];
+                canvas.drawRect(itemW, 0, (itemW * 2) - 1, bottom,
+                        Math.abs(dx) < mVC.getScaledTouchSlop()
+                        ? mTextBackgroundPaint : mTextLevelPaint);
                 canvas.drawText(mText.clear()
-                        .append("Prs: ").append(ps.mCoords.pressure, 2)
-                        .toString(), 1 + itemW * 5, base, mTextPaint);
-                
-                canvas.drawRect(itemW * 6, 0, w, bottom, mTextBackgroundPaint);
-                canvas.drawRect(itemW * 6, 0, (itemW * 6) + (ps.mCoords.size * itemW) - 1,
-                        bottom, mTextLevelPaint);
+                        .append("dX: ").append(dx, 1)
+                        .toString(), 1 + itemW, base, mTextPaint);
+                canvas.drawRect(itemW * 2, 0, (itemW * 3) - 1, bottom,
+                        Math.abs(dy) < mVC.getScaledTouchSlop()
+                        ? mTextBackgroundPaint : mTextLevelPaint);
                 canvas.drawText(mText.clear()
-                        .append("Size: ").append(ps.mCoords.size, 2)
-                        .toString(), 1 + itemW * 6, base, mTextPaint);
+                        .append("dY: ").append(dy, 1)
+                        .toString(), 1 + itemW * 2, base, mTextPaint);
             }
-            
-            // Pointer trace.
-            for (int p = 0; p < NP; p++) {
-                final PointerState ps = mPointers.get(p);
-                
-                // Draw path.
-                final int N = ps.mTraceCount;
-                float lastX = 0, lastY = 0;
-                boolean haveLast = false;
-                boolean drawn = false;
-                mPaint.setARGB(255, 128, 255, 255);
-                for (int i=0; i < N; i++) {
-                    float x = ps.mTraceX[i];
-                    float y = ps.mTraceY[i];
-                    if (Float.isNaN(x)) {
-                        haveLast = false;
-                        continue;
-                    }
-                    if (haveLast) {
-                        canvas.drawLine(lastX, lastY, x, y, mPathPaint);
-                        canvas.drawPoint(lastX, lastY, mPaint);
-                        drawn = true;
-                    }
-                    lastX = x;
-                    lastY = y;
-                    haveLast = true;
-                }
-                
-                if (drawn) {
-                    // Draw movement estimate curve.
-                    mPaint.setARGB(128, 128, 0, 128);
-                    float lx = ps.mEstimator.estimateX(-ESTIMATE_PAST_POINTS * ESTIMATE_INTERVAL);
-                    float ly = ps.mEstimator.estimateY(-ESTIMATE_PAST_POINTS * ESTIMATE_INTERVAL);
-                    for (int i = -ESTIMATE_PAST_POINTS + 1; i <= ESTIMATE_FUTURE_POINTS; i++) {
-                        float x = ps.mEstimator.estimateX(i * ESTIMATE_INTERVAL);
-                        float y = ps.mEstimator.estimateY(i * ESTIMATE_INTERVAL);
-                        canvas.drawLine(lx, ly, x, y, mPaint);
-                        lx = x;
-                        ly = y;
-                    }
 
-                    // Draw velocity vector.
-                    mPaint.setARGB(255, 255, 64, 128);
-                    float xVel = ps.mXVelocity * (1000 / 60);
-                    float yVel = ps.mYVelocity * (1000 / 60);
-                    canvas.drawLine(lastX, lastY, lastX + xVel, lastY + yVel, mPaint);
-                }
-                
-                if (mCurDown && ps.mCurDown) {
-                    // Draw crosshairs.
-                    canvas.drawLine(0, ps.mCoords.y, getWidth(), ps.mCoords.y, mTargetPaint);
-                    canvas.drawLine(ps.mCoords.x, 0, ps.mCoords.x, getHeight(), mTargetPaint);
-                    
-                    // Draw current point.
-                    int pressureLevel = (int)(ps.mCoords.pressure * 255);
-                    mPaint.setARGB(255, pressureLevel, 255, 255 - pressureLevel);
-                    canvas.drawPoint(ps.mCoords.x, ps.mCoords.y, mPaint);
-                    
-                    // Draw current touch ellipse.
-                    mPaint.setARGB(255, pressureLevel, 255 - pressureLevel, 128);
-                    drawOval(canvas, ps.mCoords.x, ps.mCoords.y, ps.mCoords.touchMajor,
-                            ps.mCoords.touchMinor, ps.mCoords.orientation, mPaint);
-                    
-                    // Draw current tool ellipse.
-                    mPaint.setARGB(255, pressureLevel, 128, 255 - pressureLevel);
-                    drawOval(canvas, ps.mCoords.x, ps.mCoords.y, ps.mCoords.toolMajor,
-                            ps.mCoords.toolMinor, ps.mCoords.orientation, mPaint);
+            canvas.drawRect(itemW * 3, 0, (itemW * 4) - 1, bottom, mTextBackgroundPaint);
+            canvas.drawText(mText.clear()
+                    .append("Xv: ").append(ps.mXVelocity, 3)
+                    .toString(), 1 + itemW * 3, base, mTextPaint);
 
-                    // Draw the orientation arrow.
-                    float arrowSize = ps.mCoords.toolMajor * 0.7f;
-                    if (arrowSize < 20) {
-                        arrowSize = 20;
-                    }
-                    mPaint.setARGB(255, pressureLevel, 255, 0);
-                    float orientationVectorX = (float) (Math.sin(ps.mCoords.orientation)
-                            * arrowSize);
-                    float orientationVectorY = (float) (-Math.cos(ps.mCoords.orientation)
-                            * arrowSize);
-                    if (ps.mToolType == MotionEvent.TOOL_TYPE_STYLUS
-                            || ps.mToolType == MotionEvent.TOOL_TYPE_ERASER) {
-                        // Show full circle orientation.
-                        canvas.drawLine(ps.mCoords.x, ps.mCoords.y,
-                                ps.mCoords.x + orientationVectorX,
-                                ps.mCoords.y + orientationVectorY,
-                                mPaint);
-                    } else {
-                        // Show half circle orientation.
-                        canvas.drawLine(
-                                ps.mCoords.x - orientationVectorX,
-                                ps.mCoords.y - orientationVectorY,
-                                ps.mCoords.x + orientationVectorX,
-                                ps.mCoords.y + orientationVectorY,
-                                mPaint);
-                    }
+            canvas.drawRect(itemW * 4, 0, (itemW * 5) - 1, bottom, mTextBackgroundPaint);
+            canvas.drawText(mText.clear()
+                    .append("Yv: ").append(ps.mYVelocity, 3)
+                    .toString(), 1 + itemW * 4, base, mTextPaint);
 
-                    // Draw the tilt point along the orientation arrow.
-                    float tiltScale = (float) Math.sin(
-                            ps.mCoords.getAxisValue(MotionEvent.AXIS_TILT));
-                    canvas.drawCircle(
-                            ps.mCoords.x + orientationVectorX * tiltScale,
-                            ps.mCoords.y + orientationVectorY * tiltScale,
-                            3.0f, mPaint);
+            canvas.drawRect(itemW * 5, 0, (itemW * 6) - 1, bottom, mTextBackgroundPaint);
+            canvas.drawRect(itemW * 5, 0, (itemW * 5) + (ps.mCoords.pressure * itemW) - 1,
+                    bottom, mTextLevelPaint);
+            canvas.drawText(mText.clear()
+                    .append("Prs: ").append(ps.mCoords.pressure, 2)
+                    .toString(), 1 + itemW * 5, base, mTextPaint);
+
+            canvas.drawRect(itemW * 6, 0, w, bottom, mTextBackgroundPaint);
+            canvas.drawRect(itemW * 6, 0, (itemW * 6) + (ps.mCoords.size * itemW) - 1,
+                    bottom, mTextLevelPaint);
+            canvas.drawText(mText.clear()
+                    .append("Size: ").append(ps.mCoords.size, 2)
+                    .toString(), 1 + itemW * 6, base, mTextPaint);
+        }
+
+        // Pointer trace.
+        for (int p = 0; p < NP; p++) {
+            final PointerState ps = mPointers.get(p);
+
+            // Draw path.
+            final int N = ps.mTraceCount;
+            float lastX = 0, lastY = 0;
+            boolean haveLast = false;
+            boolean drawn = false;
+            mPaint.setARGB(255, 128, 255, 255);
+            for (int i=0; i < N; i++) {
+                float x = ps.mTraceX[i];
+                float y = ps.mTraceY[i];
+                if (Float.isNaN(x)) {
+                    haveLast = false;
+                    continue;
                 }
+                if (haveLast) {
+                    canvas.drawLine(lastX, lastY, x, y, mPathPaint);
+                    canvas.drawPoint(lastX, lastY, mPaint);
+                    drawn = true;
+                }
+                lastX = x;
+                lastY = y;
+                haveLast = true;
+            }
+
+            if (drawn) {
+                // Draw movement estimate curve.
+                mPaint.setARGB(128, 128, 0, 128);
+                float lx = ps.mEstimator.estimateX(-ESTIMATE_PAST_POINTS * ESTIMATE_INTERVAL);
+                float ly = ps.mEstimator.estimateY(-ESTIMATE_PAST_POINTS * ESTIMATE_INTERVAL);
+                for (int i = -ESTIMATE_PAST_POINTS + 1; i <= ESTIMATE_FUTURE_POINTS; i++) {
+                    float x = ps.mEstimator.estimateX(i * ESTIMATE_INTERVAL);
+                    float y = ps.mEstimator.estimateY(i * ESTIMATE_INTERVAL);
+                    canvas.drawLine(lx, ly, x, y, mPaint);
+                    lx = x;
+                    ly = y;
+                }
+
+                // Draw velocity vector.
+                mPaint.setARGB(255, 255, 64, 128);
+                float xVel = ps.mXVelocity * (1000 / 60);
+                float yVel = ps.mYVelocity * (1000 / 60);
+                canvas.drawLine(lastX, lastY, lastX + xVel, lastY + yVel, mPaint);
+            }
+
+            if (mCurDown && ps.mCurDown) {
+                // Draw crosshairs.
+                canvas.drawLine(0, ps.mCoords.y, getWidth(), ps.mCoords.y, mTargetPaint);
+                canvas.drawLine(ps.mCoords.x, 0, ps.mCoords.x, getHeight(), mTargetPaint);
+
+                // Draw current point.
+                int pressureLevel = (int)(ps.mCoords.pressure * 255);
+                mPaint.setARGB(255, pressureLevel, 255, 255 - pressureLevel);
+                canvas.drawPoint(ps.mCoords.x, ps.mCoords.y, mPaint);
+
+                // Draw current touch ellipse.
+                mPaint.setARGB(255, pressureLevel, 255 - pressureLevel, 128);
+                drawOval(canvas, ps.mCoords.x, ps.mCoords.y, ps.mCoords.touchMajor,
+                        ps.mCoords.touchMinor, ps.mCoords.orientation, mPaint);
+
+                // Draw current tool ellipse.
+                mPaint.setARGB(255, pressureLevel, 128, 255 - pressureLevel);
+                drawOval(canvas, ps.mCoords.x, ps.mCoords.y, ps.mCoords.toolMajor,
+                        ps.mCoords.toolMinor, ps.mCoords.orientation, mPaint);
+
+                // Draw the orientation arrow.
+                float arrowSize = ps.mCoords.toolMajor * 0.7f;
+                if (arrowSize < 20) {
+                    arrowSize = 20;
+                }
+                mPaint.setARGB(255, pressureLevel, 255, 0);
+                float orientationVectorX = (float) (Math.sin(ps.mCoords.orientation)
+                        * arrowSize);
+                float orientationVectorY = (float) (-Math.cos(ps.mCoords.orientation)
+                        * arrowSize);
+                if (ps.mToolType == MotionEvent.TOOL_TYPE_STYLUS
+                        || ps.mToolType == MotionEvent.TOOL_TYPE_ERASER) {
+                    // Show full circle orientation.
+                    canvas.drawLine(ps.mCoords.x, ps.mCoords.y,
+                            ps.mCoords.x + orientationVectorX,
+                            ps.mCoords.y + orientationVectorY,
+                            mPaint);
+                } else {
+                    // Show half circle orientation.
+                    canvas.drawLine(
+                            ps.mCoords.x - orientationVectorX,
+                            ps.mCoords.y - orientationVectorY,
+                            ps.mCoords.x + orientationVectorX,
+                            ps.mCoords.y + orientationVectorY,
+                            mPaint);
+                }
+
+                // Draw the tilt point along the orientation arrow.
+                float tiltScale = (float) Math.sin(
+                        ps.mCoords.getAxisValue(MotionEvent.AXIS_TILT));
+                canvas.drawCircle(
+                        ps.mCoords.x + orientationVectorX * tiltScale,
+                        ps.mCoords.y + orientationVectorY * tiltScale,
+                        3.0f, mPaint);
             }
         }
     }
@@ -461,111 +459,109 @@
     }
 
     public void addPointerEvent(MotionEvent event) {
-        synchronized (mPointers) {
-            final int action = event.getAction();
-            int NP = mPointers.size();
+        final int action = event.getAction();
+        int NP = mPointers.size();
 
-            if (action == MotionEvent.ACTION_DOWN
-                    || (action & MotionEvent.ACTION_MASK) == MotionEvent.ACTION_POINTER_DOWN) {
-                final int index = (action & MotionEvent.ACTION_POINTER_INDEX_MASK)
-                        >> MotionEvent.ACTION_POINTER_INDEX_SHIFT; // will be 0 for down
-                if (action == MotionEvent.ACTION_DOWN) {
-                    for (int p=0; p<NP; p++) {
-                        final PointerState ps = mPointers.get(p);
-                        ps.clearTrace();
-                        ps.mCurDown = false;
-                    }
-                    mCurDown = true;
-                    mCurNumPointers = 0;
-                    mMaxNumPointers = 0;
-                    mVelocity.clear();
+        if (action == MotionEvent.ACTION_DOWN
+                || (action & MotionEvent.ACTION_MASK) == MotionEvent.ACTION_POINTER_DOWN) {
+            final int index = (action & MotionEvent.ACTION_POINTER_INDEX_MASK)
+                    >> MotionEvent.ACTION_POINTER_INDEX_SHIFT; // will be 0 for down
+            if (action == MotionEvent.ACTION_DOWN) {
+                for (int p=0; p<NP; p++) {
+                    final PointerState ps = mPointers.get(p);
+                    ps.clearTrace();
+                    ps.mCurDown = false;
                 }
-
-                mCurNumPointers += 1;
-                if (mMaxNumPointers < mCurNumPointers) {
-                    mMaxNumPointers = mCurNumPointers;
-                }
-
-                final int id = event.getPointerId(index);
-                while (NP <= id) {
-                    PointerState ps = new PointerState();
-                    mPointers.add(ps);
-                    NP++;
-                }
-                
-                if (mActivePointerId < 0 ||
-                        !mPointers.get(mActivePointerId).mCurDown) {
-                    mActivePointerId = id;
-                }
-                
-                final PointerState ps = mPointers.get(id);
-                ps.mCurDown = true;
+                mCurDown = true;
+                mCurNumPointers = 0;
+                mMaxNumPointers = 0;
+                mVelocity.clear();
             }
 
-            final int NI = event.getPointerCount();
-
-            mVelocity.addMovement(event);
-            mVelocity.computeCurrentVelocity(1);
-
-            final int N = event.getHistorySize();
-            for (int historyPos = 0; historyPos < N; historyPos++) {
-                for (int i = 0; i < NI; i++) {
-                    final int id = event.getPointerId(i);
-                    final PointerState ps = mCurDown ? mPointers.get(id) : null;
-                    final PointerCoords coords = ps != null ? ps.mCoords : mTempCoords;
-                    event.getHistoricalPointerCoords(i, historyPos, coords);
-                    if (mPrintCoords) {
-                        logCoords("Pointer", action, i, coords, id,
-                                event.getToolType(i), event.getButtonState());
-                    }
-                    if (ps != null) {
-                        ps.addTrace(coords.x, coords.y);
-                    }
-                }
+            mCurNumPointers += 1;
+            if (mMaxNumPointers < mCurNumPointers) {
+                mMaxNumPointers = mCurNumPointers;
             }
+
+            final int id = event.getPointerId(index);
+            while (NP <= id) {
+                PointerState ps = new PointerState();
+                mPointers.add(ps);
+                NP++;
+            }
+
+            if (mActivePointerId < 0 ||
+                    !mPointers.get(mActivePointerId).mCurDown) {
+                mActivePointerId = id;
+            }
+
+            final PointerState ps = mPointers.get(id);
+            ps.mCurDown = true;
+        }
+
+        final int NI = event.getPointerCount();
+
+        mVelocity.addMovement(event);
+        mVelocity.computeCurrentVelocity(1);
+
+        final int N = event.getHistorySize();
+        for (int historyPos = 0; historyPos < N; historyPos++) {
             for (int i = 0; i < NI; i++) {
                 final int id = event.getPointerId(i);
                 final PointerState ps = mCurDown ? mPointers.get(id) : null;
                 final PointerCoords coords = ps != null ? ps.mCoords : mTempCoords;
-                event.getPointerCoords(i, coords);
+                event.getHistoricalPointerCoords(i, historyPos, coords);
                 if (mPrintCoords) {
                     logCoords("Pointer", action, i, coords, id,
                             event.getToolType(i), event.getButtonState());
                 }
                 if (ps != null) {
                     ps.addTrace(coords.x, coords.y);
-                    ps.mXVelocity = mVelocity.getXVelocity(id);
-                    ps.mYVelocity = mVelocity.getYVelocity(id);
-                    mVelocity.getEstimator(id, -1, -1, ps.mEstimator);
-                    ps.mToolType = event.getToolType(i);
                 }
             }
+        }
+        for (int i = 0; i < NI; i++) {
+            final int id = event.getPointerId(i);
+            final PointerState ps = mCurDown ? mPointers.get(id) : null;
+            final PointerCoords coords = ps != null ? ps.mCoords : mTempCoords;
+            event.getPointerCoords(i, coords);
+            if (mPrintCoords) {
+                logCoords("Pointer", action, i, coords, id,
+                        event.getToolType(i), event.getButtonState());
+            }
+            if (ps != null) {
+                ps.addTrace(coords.x, coords.y);
+                ps.mXVelocity = mVelocity.getXVelocity(id);
+                ps.mYVelocity = mVelocity.getYVelocity(id);
+                mVelocity.getEstimator(id, -1, -1, ps.mEstimator);
+                ps.mToolType = event.getToolType(i);
+            }
+        }
+
+        if (action == MotionEvent.ACTION_UP
+                || action == MotionEvent.ACTION_CANCEL
+                || (action & MotionEvent.ACTION_MASK) == MotionEvent.ACTION_POINTER_UP) {
+            final int index = (action & MotionEvent.ACTION_POINTER_INDEX_MASK)
+                    >> MotionEvent.ACTION_POINTER_INDEX_SHIFT; // will be 0 for UP
+
+            final int id = event.getPointerId(index);
+            final PointerState ps = mPointers.get(id);
+            ps.mCurDown = false;
 
             if (action == MotionEvent.ACTION_UP
-                    || action == MotionEvent.ACTION_CANCEL
-                    || (action & MotionEvent.ACTION_MASK) == MotionEvent.ACTION_POINTER_UP) {
-                final int index = (action & MotionEvent.ACTION_POINTER_INDEX_MASK)
-                        >> MotionEvent.ACTION_POINTER_INDEX_SHIFT; // will be 0 for UP
-                
-                final int id = event.getPointerId(index);
-                final PointerState ps = mPointers.get(id);
-                ps.mCurDown = false;
-                
-                if (action == MotionEvent.ACTION_UP
-                        || action == MotionEvent.ACTION_CANCEL) {
-                    mCurDown = false;
-                    mCurNumPointers = 0;
-                } else {
-                    mCurNumPointers -= 1;
-                    if (mActivePointerId == id) {
-                        mActivePointerId = event.getPointerId(index == 0 ? 1 : 0);
-                    }
-                    ps.addTrace(Float.NaN, Float.NaN);
+                    || action == MotionEvent.ACTION_CANCEL) {
+                mCurDown = false;
+                mCurNumPointers = 0;
+            } else {
+                mCurNumPointers -= 1;
+                if (mActivePointerId == id) {
+                    mActivePointerId = event.getPointerId(index == 0 ? 1 : 0);
                 }
+                ps.addTrace(Float.NaN, Float.NaN);
             }
-
-            postInvalidate();
         }
+
+        invalidate();
     }
     
     @Override
diff --git a/core/jni/android_view_DisplayEventReceiver.cpp b/core/jni/android_view_DisplayEventReceiver.cpp
index 8a1c4a9..72c171c 100644
--- a/core/jni/android_view_DisplayEventReceiver.cpp
+++ b/core/jni/android_view_DisplayEventReceiver.cpp
@@ -49,7 +49,6 @@
 
     status_t initialize();
     status_t scheduleVsync();
-    static int handleReceiveCallback(int receiveFd, int events, void* data);
 
 protected:
     virtual ~NativeDisplayEventReceiver();
@@ -59,6 +58,9 @@
     sp<Looper> mLooper;
     DisplayEventReceiver mReceiver;
     bool mWaitingForVsync;
+
+    static int handleReceiveCallback(int receiveFd, int events, void* data);
+    bool readLastVsyncMessage(nsecs_t* outTimestamp, uint32_t* outCount);
 };
 
 
@@ -100,16 +102,9 @@
         ALOGV("receiver %p ~ Scheduling vsync.", this);
 
         // Drain all pending events.
-        DisplayEventReceiver::Event buf[EVENT_BUFFER_SIZE];
-        ssize_t n;
-        while ((n = mReceiver.getEvents(buf, EVENT_BUFFER_SIZE)) > 0) {
-            ALOGV("receiver %p ~ Drained %d events.", this, int(n));
-        }
-
-        if (n < 0) {
-            ALOGW("Failed to drain events from display event receiver, status=%d", status_t(n));
-            return status_t(n);
-        }
+        nsecs_t vsyncTimestamp;
+        uint32_t vsyncCount;
+        readLastVsyncMessage(&vsyncTimestamp, &vsyncCount);
 
         status_t status = mReceiver.requestNextVsync();
         if (status) {
@@ -138,23 +133,9 @@
     }
 
     // Drain all pending events, keep the last vsync.
-    nsecs_t vsyncTimestamp = -1;
-    uint32_t vsyncCount = 0;
-
-    DisplayEventReceiver::Event buf[EVENT_BUFFER_SIZE];
-    ssize_t n;
-    while ((n = r->mReceiver.getEvents(buf, EVENT_BUFFER_SIZE)) > 0) {
-        ALOGV("receiver %p ~ Read %d events.", data, int(n));
-        while (n-- > 0) {
-            if (buf[n].header.type == DisplayEventReceiver::DISPLAY_EVENT_VSYNC) {
-                vsyncTimestamp = buf[n].header.timestamp;
-                vsyncCount = buf[n].vsync.count;
-                break; // stop at last vsync in the buffer
-            }
-        }
-    }
-
-    if (vsyncTimestamp < 0) {
+    nsecs_t vsyncTimestamp;
+    uint32_t vsyncCount;
+    if (!r->readLastVsyncMessage(&vsyncTimestamp, &vsyncCount)) {
         ALOGV("receiver %p ~ Woke up but there was no vsync pulse!", data);
         return 1; // keep the callback, did not obtain a vsync pulse
     }
@@ -179,6 +160,26 @@
     return 1; // keep the callback
 }
 
+bool NativeDisplayEventReceiver::readLastVsyncMessage(
+        nsecs_t* outTimestamp, uint32_t* outCount) {
+    DisplayEventReceiver::Event buf[EVENT_BUFFER_SIZE];
+    ssize_t n;
+    while ((n = mReceiver.getEvents(buf, EVENT_BUFFER_SIZE)) > 0) {
+        ALOGV("receiver %p ~ Read %d events.", this, int(n));
+        while (n-- > 0) {
+            if (buf[n].header.type == DisplayEventReceiver::DISPLAY_EVENT_VSYNC) {
+                *outTimestamp = buf[n].header.timestamp;
+                *outCount = buf[n].vsync.count;
+                return true; // stop at last vsync in the buffer
+            }
+        }
+    }
+    if (n < 0) {
+        ALOGW("Failed to get events from display event receiver, status=%d", status_t(n));
+    }
+    return false;
+}
+
 
 static jint nativeInit(JNIEnv* env, jclass clazz, jobject receiverObj,
         jobject messageQueueObj) {
diff --git a/core/res/res/layout/notification_template_big_picture.xml b/core/res/res/layout/notification_template_big_picture.xml
new file mode 100644
index 0000000..6eb934e
--- /dev/null
+++ b/core/res/res/layout/notification_template_big_picture.xml
@@ -0,0 +1,17 @@
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/status_bar_latest_event_content"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    >
+    <ImageView
+        android:id="@+id/big_picture"
+        android:layout_width="match_parent"
+        android:layout_height="192dp"
+        android:scaleType="centerCrop"
+        />
+    <include layout="@layout/status_bar_latest_event_content" 
+        android:layout_width="match_parent"
+        android:layout_height="@dimen/notification_large_icon_height"
+        android:layout_marginTop="192dp"
+        />
+</FrameLayout>
\ No newline at end of file
diff --git a/core/res/res/layout/status_bar_latest_event_content.xml b/core/res/res/layout/status_bar_latest_event_content.xml
index ec1bc81..57c149f 100644
--- a/core/res/res/layout/status_bar_latest_event_content.xml
+++ b/core/res/res/layout/status_bar_latest_event_content.xml
@@ -1,7 +1,7 @@
 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
     android:id="@+id/status_bar_latest_event_content"
     android:layout_width="match_parent"
-    android:layout_height="match_parent"
+    android:layout_height="wrap_content"
     >
     <ImageView android:id="@+id/icon"
         android:layout_width="@dimen/notification_large_icon_width"
diff --git a/core/res/res/layout/status_bar_latest_event_content_large_icon.xml b/core/res/res/layout/status_bar_latest_event_content_large_icon.xml
index a2253b7..5f38e6a 100644
--- a/core/res/res/layout/status_bar_latest_event_content_large_icon.xml
+++ b/core/res/res/layout/status_bar_latest_event_content_large_icon.xml
@@ -6,6 +6,8 @@
     android:orientation="vertical"
     android:paddingLeft="12dp"
     android:paddingRight="12dp"
+    android:paddingTop="4dp"
+    android:paddingBottom="4dp"
     >
     <LinearLayout
         android:id="@+id/line1"
@@ -44,6 +46,13 @@
         android:ellipsize="marquee"
         android:visibility="gone"
         />
+    <TextView android:id="@+id/big_text"
+        android:textAppearance="@style/TextAppearance.StatusBar.EventContent"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:singleLine="false"
+        android:visibility="gone"
+        />
     <LinearLayout
         android:id="@+id/line3"
         android:layout_width="match_parent"
@@ -70,7 +79,7 @@
             android:gravity="center"
             android:paddingLeft="8dp"
             />
-        <ImageView android:id="@+id/icon"
+        <ImageView android:id="@+id/right_icon"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
             android:layout_gravity="center"
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index e1bc33b..7b4f50b 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -198,6 +198,8 @@
   <java-symbol type="id" name="action0" />
   <java-symbol type="id" name="action1" />
   <java-symbol type="id" name="action2" />
+  <java-symbol type="id" name="big_picture" />
+  <java-symbol type="id" name="big_text" />
 
   <java-symbol type="attr" name="actionModeShareDrawable" />
   <java-symbol type="attr" name="alertDialogCenterButtons" />
@@ -848,6 +850,8 @@
   <java-symbol type="string" name="wifi_watchdog_network_disabled" />
   <java-symbol type="string" name="wifi_watchdog_network_disabled_detailed" />
   <java-symbol type="string" name="yesterday" />
+  <java-symbol type="string" name="imei" />
+  <java-symbol type="string" name="meid" />
 
   <java-symbol type="plurals" name="abbrev_in_num_days" />
   <java-symbol type="plurals" name="abbrev_in_num_hours" />
@@ -1070,6 +1074,7 @@
   <java-symbol type="layout" name="zoom_controls" />
   <java-symbol type="layout" name="zoom_magnify" />
   <java-symbol type="layout" name="notification_intruder_content" />
+  <java-symbol type="layout" name="notification_template_big_picture" />
 
   <java-symbol type="anim" name="slide_in_child_bottom" />
   <java-symbol type="anim" name="slide_in_right" />
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 7799f74..6c9576f 100755
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -97,6 +97,12 @@
          the SIM card. -->
     <string name="needPuk">Your SIM card is PUK-locked. Type the PUK code to unlock it.</string>
     <string name="needPuk2">Type PUK2 to unblock SIM card.</string>
+    <!-- Title for the dialog used to display the user's IMEI number [CHAR LIMIT=10] -->
+    <string name="imei">IMEI</string>
+
+    <!-- Title for the dialog used to display the user's MEID number on CDMA network
+         [CHAR LIMIT=10] -->
+    <string name="meid">MEID</string>
 
     <!-- Displayed as the title for a success/failure report enabling/disabling caller ID. -->
     <string name="ClipMmi">Incoming Caller ID</string>
diff --git a/core/tests/coretests/src/android/text/DynamicLayoutBlocksTest.java b/core/tests/coretests/src/android/text/DynamicLayoutBlocksTest.java
new file mode 100644
index 0000000..73da84f
--- /dev/null
+++ b/core/tests/coretests/src/android/text/DynamicLayoutBlocksTest.java
@@ -0,0 +1,327 @@
+/*
+ * 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.text;
+
+import static android.text.Layout.Alignment.*;
+
+import android.text.DynamicLayout;
+
+import junit.framework.TestCase;
+
+/**
+ * Tests DynamciLayout updateBlocks method.
+ *
+ * Requires disabling access checks in the vm since this calls package-private APIs.
+ *
+ * @Suppress
+ */
+public class DynamicLayoutBlocksTest extends TestCase {
+    private DynamicLayout dl = new DynamicLayout("", new TextPaint(), 0, ALIGN_NORMAL, 0, 0, false);
+    private static final int ___ = DynamicLayout.INVALID_BLOCK_INDEX;
+
+    private int[] initialBlockEnds;
+    private int[] initialBlockIndices;
+
+    private void defineInitialState(int[] ends, int[] indices) {
+        initialBlockEnds = ends;
+        initialBlockIndices = indices;
+        assertEquals(initialBlockEnds.length, initialBlockIndices.length);
+    }
+
+    public void printBlocks(String message) {
+        System.out.print(message);
+        for (int i = 0; i < dl.getNumberOfBlocks(); i++) {
+            System.out.print("  " + Integer.toString(dl.getBlockEnds()[i]));
+        }
+        System.out.println();
+    }
+
+    public void checkInvariants() {
+        assertTrue(dl.getNumberOfBlocks() > 0);
+        assertTrue(dl.getNumberOfBlocks() <= dl.getBlockEnds().length);
+        assertEquals(dl.getBlockEnds().length, dl.getBlockIndices().length);
+
+        for (int i = 1; i < dl.getNumberOfBlocks(); i++) {
+            assertTrue(dl.getBlockEnds()[i] > dl.getBlockEnds()[i-1]);
+        }
+    }
+
+    private void update(int startLine, int endLine, int newLineCount) {
+        dl.setBlocksDataForTest(initialBlockEnds, initialBlockIndices, initialBlockEnds.length);
+        checkInvariants();
+        dl.updateBlocks(startLine, endLine, newLineCount);
+    }
+
+    private void assertState(int[] sizes, int[] indices) {
+        checkInvariants();
+
+        assertEquals(sizes.length, dl.getNumberOfBlocks());
+        assertEquals(indices.length, dl.getNumberOfBlocks());
+
+        int[] ends = new int[sizes.length];
+        for (int i = 0; i < ends.length; i++) {
+            ends[i] = i == 0 ? (sizes[0] == 0 ? 0 : sizes[0] - 1) : ends[i - 1] + sizes[i];
+        }
+
+        for (int i = 0; i < dl.getNumberOfBlocks(); i++) {
+            assertEquals(ends[i], dl.getBlockEnds()[i]);
+            assertEquals(indices[i], dl.getBlockIndices()[i]);
+        }
+    }
+
+    private void assertState(int[] sizes) {
+        int[] ids = new int[sizes.length];
+        for (int i = 0; i < sizes.length; i++) {
+            ids[i] = DynamicLayout.INVALID_BLOCK_INDEX;
+        }
+        assertState(sizes, ids);
+    }
+
+    public void testFrom0() {
+        defineInitialState( new int[] { 0 }, new int[] { 123 });
+
+        update(0, 0, 0);
+        assertState( new int[] { 0 } );
+
+        update(0, 0, 1);
+        assertState( new int[] { 0 } );
+
+        update(0, 0, 10);
+        assertState( new int[] { 10 } );
+    }
+
+    public void testFrom1ReplaceByEmpty() {
+        defineInitialState( new int[] { 100 }, new int[] { 123 });
+
+        update(0, 0, 0);
+        assertState( new int[] { 100 } );
+
+        update(0, 10, 0);
+        assertState( new int[] { 90 } );
+
+        update(0, 100, 0);
+        assertState( new int[] { 0 } );
+
+        update(20, 30, 0);
+        assertState( new int[] { 20, 70 } );
+
+        update(20, 20, 0);
+        assertState( new int[] { 20, 80 } );
+
+        update(40, 100, 0);
+        assertState( new int[] { 40 } );
+
+        update(100, 100, 0);
+        assertState( new int[] { 100 } );
+    }
+
+    public void testFrom1ReplaceFromFirstLine() {
+        defineInitialState( new int[] { 100 }, new int[] { 123 });
+
+        update(0, 0, 1);
+        assertState( new int[] { 0, 100 } );
+
+        update(0, 0, 10);
+        assertState( new int[] { 10, 100 } );
+
+        update(0, 30, 31);
+        assertState( new int[] { 31, 70 } );
+
+        update(0, 100, 20);
+        assertState( new int[] { 20 } );
+    }
+
+    public void testFrom1ReplaceFromCenter() {
+        defineInitialState( new int[] { 100 }, new int[] { 123 });
+
+        update(20, 20, 1);
+        assertState( new int[] { 20, 1, 80 } );
+
+        update(20, 20, 10);
+        assertState( new int[] { 20, 10, 80 } );
+
+        update(20, 30, 50);
+        assertState( new int[] { 20, 50, 70 } );
+
+        update(20, 100, 50);
+        assertState( new int[] { 20, 50 } );
+    }
+
+    public void testFrom1ReplaceFromEnd() {
+        defineInitialState( new int[] { 100 }, new int[] { 123 });
+
+        update(100, 100, 0);
+        assertState( new int[] { 100 } );
+
+        update(100, 100, 1);
+        assertState( new int[] { 100, 1 } );
+
+        update(100, 100, 10);
+        assertState( new int[] { 100, 10 } );
+    }
+
+    public void testFrom2ReplaceFromFirstLine() {
+        defineInitialState( new int[] { 10, 20 }, new int[] { 123, 456 });
+
+        update(0, 4, 50);
+        assertState( new int[] { 50, 10-4, 20-10 }, new int[] { ___, ___, 456 } );
+
+        update(0, 10, 50);
+        assertState( new int[] { 50, 20-10 }, new int[] { ___, 456 } );
+
+        update(0, 15, 50);
+        assertState( new int[] { 50, 20-15 }, new int[] { ___, ___ } );
+
+        update(0, 20, 50);
+        assertState( new int[] { 50 }, new int[] { ___ } );
+    }
+
+    public void testFrom2ReplaceFromFirstBlock() {
+        defineInitialState( new int[] { 10, 20 }, new int[] { 123, 456 });
+
+        update(3, 7, 50);
+        assertState( new int[] { 3, 50, 10-7, 20-10 }, new int[] { ___, ___, ___, 456 } );
+
+        update(3, 10, 50);
+        assertState( new int[] { 3, 50, 20-10 }, new int[] { ___, ___, 456 } );
+
+        update(3, 14, 50);
+        assertState( new int[] { 3, 50, 20-14 }, new int[] { ___, ___, ___ } );
+
+        update(3, 20, 50);
+        assertState( new int[] { 3, 50 }, new int[] { ___, ___ } );
+    }
+
+    public void testFrom2ReplaceFromBottomBoundary() {
+        defineInitialState( new int[] { 10, 20 }, new int[] { 123, 456 });
+
+        update(10, 10, 50);
+        assertState( new int[] { 10, 50, 20-10 }, new int[] { ___, ___, 456 } );
+
+        update(10, 14, 50);
+        assertState( new int[] { 10, 50, 20-14 }, new int[] { ___, ___, ___ } );
+
+        update(10, 20, 50);
+        assertState( new int[] { 10, 50 }, new int[] { ___, ___ } );
+    }
+
+    public void testFrom2ReplaceFromTopBoundary() {
+        defineInitialState( new int[] { 10, 20 }, new int[] { 123, 456 });
+
+        update(11, 11, 50);
+        assertState( new int[] { 11, 50, 20-11 }, new int[] { 123, ___, ___ } );
+
+        update(11, 14, 50);
+        assertState( new int[] { 11, 50, 20-14 }, new int[] { 123, ___, ___ } );
+
+        update(11, 20, 50);
+        assertState( new int[] { 11, 50 }, new int[] { 123, ___ } );
+    }
+
+    public void testFrom2ReplaceFromSecondBlock() {
+        defineInitialState( new int[] { 10, 20 }, new int[] { 123, 456 });
+
+        update(14, 14, 50);
+        assertState( new int[] { 11, 14-11, 50, 20-14 }, new int[] { 123, ___, ___, ___ } );
+
+        update(14, 17, 50);
+        assertState( new int[] { 11, 14-11, 50, 20-17 }, new int[] { 123, ___, ___, ___ } );
+
+        update(14, 20, 50);
+        assertState( new int[] { 11, 14-11, 50 }, new int[] { 123, ___, ___ } );
+    }
+
+    public void testFrom2RemoveFromFirst() {
+        defineInitialState( new int[] { 10, 20 }, new int[] { 123, 456 });
+
+        update(0, 4, 0);
+        assertState( new int[] { 10-4, 20-10 }, new int[] { ___, 456 } );
+
+        update(0, 10, 0);
+        assertState( new int[] { 20-10 }, new int[] { 456 } );
+
+        update(0, 14, 0);
+        assertState( new int[] { 20-14 }, new int[] { ___ } );
+
+        update(0, 20, 0);
+        assertState( new int[] { 0 }, new int[] { ___ } );
+    }
+
+    public void testFrom2RemoveFromFirstBlock() {
+        defineInitialState( new int[] { 10, 20 }, new int[] { 123, 456 });
+
+        update(4, 7, 0);
+        assertState( new int[] { 4, 10-7, 20-10 }, new int[] { ___, ___, 456 } );
+
+        update(4, 10, 0);
+        assertState( new int[] { 4, 20-10 }, new int[] { ___, 456 } );
+
+        update(4, 14, 0);
+        assertState( new int[] { 4, 20-14 }, new int[] { ___, ___ } );
+
+        update(4, 20, 0);
+        assertState( new int[] { 4 }, new int[] { ___ } );
+    }
+
+    public void testFrom2RemoveFromSecondBlock() {
+        defineInitialState( new int[] { 10, 20 }, new int[] { 123, 456 });
+
+        update(14, 17, 0);
+        assertState( new int[] { 11, 14-11, 20-17 }, new int[] { 123, ___, ___ } );
+
+        update(14, 20, 0);
+        assertState( new int[] { 11, 14-11 }, new int[] { 123, ___ } );
+    }
+
+    public void testFrom3ReplaceFromFirstBlock() {
+        defineInitialState( new int[] { 10, 30, 60 }, new int[] { 123, 456, 789 });
+
+        update(3, 7, 50);
+        assertState( new int[] { 3, 50, 10-7, 30-10, 60-30 }, new int[] { ___, ___, ___, 456, 789 } );
+
+        update(3, 10, 50);
+        assertState( new int[] { 3, 50, 30-10, 60-30 }, new int[] { ___, ___, 456, 789 } );
+
+        update(3, 17, 50);
+        assertState( new int[] { 3, 50, 30-17, 60-30 }, new int[] { ___, ___, ___, 789 } );
+
+        update(3, 30, 50);
+        assertState( new int[] { 3, 50, 60-30 }, new int[] { ___, ___, 789 } );
+
+        update(3, 40, 50);
+        assertState( new int[] { 3, 50, 60-40 }, new int[] { ___, ___, ___ } );
+
+        update(3, 60, 50);
+        assertState( new int[] { 3, 50 }, new int[] { ___, ___ } );
+    }
+
+    public void testFrom3ReplaceFromSecondBlock() {
+        defineInitialState( new int[] { 10, 30, 60 }, new int[] { 123, 456, 789 });
+
+        update(13, 17, 50);
+        assertState( new int[] { 11, 2, 50, 30-17, 60-30 }, new int[] { 123, ___, ___, ___, 789 } );
+
+        update(13, 30, 50);
+        assertState( new int[] { 11, 2, 50, 60-30 }, new int[] { 123, ___, ___, 789 } );
+
+        update(13, 40, 50);
+        assertState( new int[] { 11, 2, 50, 60-40 }, new int[] { 123, ___, ___, ___ } );
+
+        update(13, 60, 50);
+        assertState( new int[] { 11, 2, 50 }, new int[] { 123, ___, ___ } );
+    }
+}
diff --git a/docs/html/resources/articles/images/spellcheck_client_flow.png b/docs/html/resources/articles/images/spellcheck_client_flow.png
new file mode 100644
index 0000000..4e097aa
--- /dev/null
+++ b/docs/html/resources/articles/images/spellcheck_client_flow.png
Binary files differ
diff --git a/docs/html/resources/articles/images/spellcheck_lifecycle.png b/docs/html/resources/articles/images/spellcheck_lifecycle.png
new file mode 100644
index 0000000..0b10824
--- /dev/null
+++ b/docs/html/resources/articles/images/spellcheck_lifecycle.png
Binary files differ
diff --git a/docs/html/resources/articles/images/textview_spellcheck_screenshot_1.png b/docs/html/resources/articles/images/textview_spellcheck_screenshot_1.png
new file mode 100644
index 0000000..deb47c4
--- /dev/null
+++ b/docs/html/resources/articles/images/textview_spellcheck_screenshot_1.png
Binary files differ
diff --git a/docs/html/resources/articles/images/textview_spellcheck_screenshot_2.png b/docs/html/resources/articles/images/textview_spellcheck_screenshot_2.png
new file mode 100644
index 0000000..e3af4c5
--- /dev/null
+++ b/docs/html/resources/articles/images/textview_spellcheck_screenshot_2.png
Binary files differ
diff --git a/docs/html/resources/articles/index.jd b/docs/html/resources/articles/index.jd
index 220a4ed..2947e4a 100644
--- a/docs/html/resources/articles/index.jd
+++ b/docs/html/resources/articles/index.jd
@@ -47,7 +47,16 @@
   <dt><a href="{@docRoot}resources/articles/glsurfaceview.html">Introducing GLSurfaceView</a></dt>
   <dd>This article provides an overview of GLSurfaceView, a class that makes it easy to implement 2D or 3D OpenGL rendering inside of an Android application.</dd>
 </dl>
-
+<dl>
+    <dt>
+        <a href="{@docRoot}resources/articles/spell-checker-framework.jd">
+        Using the Spell Checker Framework</a>
+    </dt>
+    <dd>
+        This article describes how to use the Spell Checker Framework to check spelling in
+        various ways in your application.
+    </dd>
+</dl>
 <dl>
   <dt><a href="{@docRoot}resources/articles/layout-tricks-reuse.html">Layout Tricks: Creating Reusable UI Components</a></dt>
   <dd>Learn how to combine multiple standard UI widgets into a single high-level component, which can be reused throughout your application.</dd>
@@ -149,7 +158,7 @@
 </dl>
 
 <dl>
-  <dt><a href="{@docRoot}resources/articles/window-bg-speed.html">Window Backgrounds & UI Speed</a></dt>
+  <dt><a href="{@docRoot}resources/articles/window-bg-speed.html">Window Backgrounds &amp; UI Speed</a></dt>
   <dd>Some Android applications need to squeeze every bit of performance out of the UI toolkit and there are many ways to do so. In this article, you will discover how to speed up the drawing and the perceived startup time of your activities. Both of these techniques rely on a single feature, the window's background drawable.</dd>
 </dl>
 
diff --git a/docs/html/resources/articles/spell-checker-framework.jd b/docs/html/resources/articles/spell-checker-framework.jd
new file mode 100644
index 0000000..8d57b4e
--- /dev/null
+++ b/docs/html/resources/articles/spell-checker-framework.jd
@@ -0,0 +1,236 @@
+page.title=Using the Spell Checker Framework
+parent.title=Articles
+parent.link=../browser.html?tag=article
+@jd:body
+<div id="qv-wrapper">
+<div id="qv">
+<h2>In This Document</h2>
+<ol>
+    <li>
+        <a href="#SpellCheckLifeCycle">Spell Check Lifecycle</a> 
+    </li>
+    <li>
+        <a href="#SpellCheckImplementation">Implementing a Spell Checker Service</a>
+    </li>
+    <li>
+        <a href="#SpellCheckClient">Implementing a Spell Checker Client</a>
+    </li>
+</ol>
+  <h2>See also</h2>
+  <ol>
+    <li>
+        <a href="{@docRoot}resources/samples/SpellChecker/SampleSpellCheckerService/index.html">
+        Spell Checker Service</a> sample app
+    </li>
+    <li>
+        <a href="{@docRoot}resources/samples/SpellChecker/HelloSpellChecker/index.html">
+        Spell Checker Client</a> sample app
+    </li>
+  </ol>
+</div>
+</div>
+
+<p>
+    The Android platform offers a spell checker framework that lets you implement   
+    and access spell checking in your application. The framework is one of the 
+    Text Service APIs offered by the Android platform.
+</p>
+<p>
+    To use the framework in your app, you create a special type of Android service that 
+    generates a spell checker <strong>session</strong> object. Based on text you provide,
+    the session object returns spelling suggestions generated by the spell checker.
+</p>
+<h2 id="SpellCheckLifeCycle">Spell Checker Lifecycle</h2>
+<p>
+    The following diagram shows the lifecycle of the spell checker service:
+</p>
+<img src="{@docRoot}resources/articles/images/spellcheck_lifecycle.png" alt="" height="596"
+    id="figure1" />
+<p class="img-caption">
+  <strong>Figure 1.</strong> The spell checker service lifecycle.
+</p>
+<p>
+    To initiate spell checking, your app starts its implementation of the spell checker
+    service. Clients in your app, such as activities or individual UI elements, request a
+    spell checker session from the service, then use the session to get suggestions for text.
+    As a client terminates its operation, it closes its spell checker session. If necessary, your
+    app can shut down the spell checker service at any time.
+</p>
+<h2 id="SpellCheckImplementation">Implementing a Spell Checker Service</h2>
+<p>
+    To use the spell checker framework in your app, add a spell checker service component including
+    the session object definition. You can also add to your app an optional activity that
+    controls settings. You must also add an XML metadata file that describes
+    the spell checker service, and add the appropriate elements to your manifest file.
+</p>
+<h3 id="SpellCheckCode">Spell checker classes</h3>
+<p>
+    Define the service and session object with the following classes:
+</p>
+<dl>
+    <dt>
+        A subclass of {@link android.service.textservice.SpellCheckerService}
+    </dt>
+    <dd>
+        The {@link android.service.textservice.SpellCheckerService} implements both the
+        {@link android.app.Service} class and the spell checker framework interface. Within your
+        subclass, you must implement the following method:
+        <dl>
+            <dt>{@link android.service.textservice.SpellCheckerService#createSession()}</dt>
+            <dd>
+                A factory method that returns a
+                {@link android.service.textservice.SpellCheckerService.Session} object to a
+                client that wants to do spell checking.
+            </dd>
+        </dl>
+        <p>
+            See the
+            <a href="{@docRoot}resources/samples/SpellChecker/SampleSpellCheckerService/index.html">
+            Spell Checker Service</a> sample app to learn more about implementing this class.
+        </p>
+    </dd>
+    <dt>
+        An implementation of {@link android.service.textservice.SpellCheckerService.Session}
+    </dt>
+    <dd>
+        An object that the spell checker service provides to clients, to let them pass text to
+        the spell checker and receive suggestions. Within this class, you must implement the
+        following methods:
+        <dl>
+            <dt>
+                {@link android.service.textservice.SpellCheckerService.Session#onCreate()}
+            </dt>
+            <dd>
+                Called by the system in response to
+                {@link android.service.textservice.SpellCheckerService#createSession()}. In this
+                method, you can initialize the
+                {@link android.service.textservice.SpellCheckerService.Session} object based on
+                the current locale and so forth.
+            </dd>
+            <dt>
+     {@link android.service.textservice.SpellCheckerService.Session#onGetSuggestions(TextInfo, int)
+     onGetSuggestions()}
+            </dt>
+            <dd>
+                Does the actual spell checking. This method returns an object containing
+                suggestions for the text passed to it.
+            </dd>
+        </dl>
+        <p>
+            Optionally, you can implement
+            {@link android.service.textservice.SpellCheckerService.Session#onCancel()}, which
+            handles requests to cancel spell checking, or
+{@link android.service.textservice.SpellCheckerService.Session#onGetSuggestionsMultiple(TextInfo[], int, boolean)
+onGetSuggestionsMultiple()}, which handles batches of suggestion requests, or both.
+        </p>
+        <p>
+            See the
+            <a href="{@docRoot}resources/samples/SpellChecker/HelloSpellChecker/index.html">
+            Spell Checker Client</a> sample app to learn more about implementing this class.
+        </p>
+    </dd>
+</dl>
+<p class="note">
+    <strong>Note:</strong> You must implement all aspects of spell checking as asynchronous and
+    thread-safe. A spell checker may be called simultaneously by different threads running on
+    different cores. The {@link android.service.textservice.SpellCheckerService} and
+    {@link android.service.textservice.SpellCheckerService.Session} take care of this
+    automatically.
+</p>
+<h3 id="SpellCheckXML">Spell checker manifest and metadata</h3>
+<p>
+    In addition to code, you need to provide the appropriate manifest file and a metadata file for
+    the spell checker.
+</p>
+<p>
+    The manifest file defines the application, the service, and the activity for controlling
+    settings, as shown in the following snippet:
+</p>
+<pre>
+&lt;manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.example.android.samplespellcheckerservice" &gt;
+    &lt;application
+        android:label="&#64;string/app_name" &gt;
+        &lt;service
+            android:label="&#64;string/app_name"
+            android:name=".SampleSpellCheckerService"
+            android:permission="android.permission.BIND_TEXT_SERVICE" &gt;
+            &lt;intent-filter &gt;
+                &lt;action android:name="android.service.textservice.SpellCheckerService" /&gt;
+            &lt;/intent-filter&gt;
+
+            &lt;meta-data
+                android:name="android.view.textservice.scs"
+                android:resource="&#64;xml/spellchecker" /&gt;
+        &lt;/service&gt;
+
+        &lt;activity
+            android:label="&#64;string/sample_settings"
+            android:name="SpellCheckerSettingsActivity" &gt;
+            &lt;intent-filter &gt;
+                &lt;action android:name="android.intent.action.MAIN" /&gt;
+            &lt;/intent-filter&gt;
+        &lt;/activity&gt;
+    &lt;/application&gt;
+&lt;/manifest&gt;
+</pre>
+<p>
+    Notice that components that want to use the service must request the permission
+    {@link android.Manifest.permission#BIND_TEXT_SERVICE} to ensure that only the system binds to
+    the service. The service's definition also specifies the <code>spellchecker.xml</code> metadata
+    file, which is described in the next section.
+</p>
+<p>
+    The metadata file <code>spellchecker.xml</code> contains the following XML:
+</p>
+<pre>
+&lt;spell-checker xmlns:android="http://schemas.android.com/apk/res/android"
+        android:label="&#64;string/spellchecker_name"
+        android:settingsActivity="com.example.SpellCheckerSettingsActivity"&gt;
+    &lt;subtype
+            android:label="&#64;string/subtype_generic"
+            android:subtypeLocale="en”
+    /&gt;
+    &lt;subtype
+            android:label="&#64;string/subtype_generic"
+            android:subtypeLocale="fr”
+    /&gt;
+&lt;/spell-checker&gt;
+</pre>
+<p>
+    The metadata specifies the activity that the spell checker uses for controlling settings. It
+    also defines subtypes for the spell checker; in this case, the subtypes define locales that
+    the spell checker can handle.
+</p>
+
+
+<!--  Accessing the Spell Checker Service from a Client -->
+<h2 id="SpellCheckClient">Accessing the Spell Checker Service from a Client</h2>
+<p>
+    Applications that use {@link android.widget.TextView} views automatically benefit from spell
+    checking,  because {@link android.widget.TextView} automatically uses a spell checker. The
+    following screenshots show this:
+</p>
+<img src="{@docRoot}resources/articles/images/textview_spellcheck_screenshot_1.png" alt=""
+    height="45" id="figure2a" />
+<br>
+<img src="{@docRoot}resources/articles/images/textview_spellcheck_screenshot_2.png" alt=""
+    height="121" id="figure2b" />
+<p class="img-caption">
+  <strong>Figure 2.</strong> Spell checking in TextView.
+</p>
+<p>
+    However, you may want to interact directly with a spell checker service in other cases as well.
+    The following diagram shows the flow of control for interacting with a spell checker service:
+</p>
+<img src="{@docRoot}resources/articles/images/spellcheck_client_flow.png" alt=""
+    height="394" id="figure3" />
+<p class="img-caption">
+  <strong>Figure 3.</strong> Interacting with a spell checker service.
+</p>
+<p>
+    The <a href="{@docRoot}resources/samples/SpellChecker/HelloSpellChecker/index.html">
+    Spell Checker Client</a> sample app shows how to interact with a spell checker service. The
+    LatinIME input method editor in the Android Open Source Project also contains an example of
+    spell checking.
+</p>
\ No newline at end of file
diff --git a/docs/html/resources/resources-data.js b/docs/html/resources/resources-data.js
index 8ad970b..0b82aee 100644
--- a/docs/html/resources/resources-data.js
+++ b/docs/html/resources/resources-data.js
@@ -263,6 +263,17 @@
     }
   },
   {
+    tags: ['article', 'input', 'ui'],
+    path: 'articles/spell-checker-framework.html',
+    title: {
+      en: 'The Android Spell Checker Framework'
+    },
+    description: {
+      en: 'This article describes the Android spell checker framework and how to use to implement spell checking in applications.'
+    }
+  },
+  
+  {
     tags: ['article', 'ui'],
     path: 'articles/touch-mode.html',
     title: {
diff --git a/graphics/java/android/renderscript/Allocation.java b/graphics/java/android/renderscript/Allocation.java
index a76a628..6b59b10 100644
--- a/graphics/java/android/renderscript/Allocation.java
+++ b/graphics/java/android/renderscript/Allocation.java
@@ -22,6 +22,7 @@
 import android.content.res.AssetManager;
 import android.graphics.Bitmap;
 import android.graphics.BitmapFactory;
+import android.view.Surface;
 import android.graphics.SurfaceTexture;
 import android.util.Log;
 import android.util.TypedValue;
@@ -1185,17 +1186,38 @@
     }
 
     /**
+     *
+     * @hide
+     *
+     */
+    public Surface getSurface() {
+        return new Surface(getSurfaceTexture());
+    }
+
+    /**
      * @hide
      */
-    public void setSurfaceTexture(SurfaceTexture sur) {
+    public void setSurface(Surface sur) {
+        mRS.validate();
         if ((mUsage & USAGE_IO_OUTPUT) == 0) {
             throw new RSInvalidStateException("Allocation is not USAGE_IO_OUTPUT.");
         }
 
-        mRS.validate();
-        mRS.nAllocationSetSurfaceTexture(getID(), sur);
+        mRS.nAllocationSetSurface(getID(), sur);
     }
 
+    /**
+     * @hide
+     */
+    public void setSurfaceTexture(SurfaceTexture st) {
+        mRS.validate();
+        if ((mUsage & USAGE_IO_OUTPUT) == 0) {
+            throw new RSInvalidStateException("Allocation is not USAGE_IO_OUTPUT.");
+        }
+
+        Surface s = new Surface(st);
+        mRS.nAllocationSetSurface(getID(), s);
+    }
 
     /**
      * Creates a non-mipmapped renderscript allocation to use as a
diff --git a/graphics/java/android/renderscript/RenderScript.java b/graphics/java/android/renderscript/RenderScript.java
index ab6ba54..dffd400 100644
--- a/graphics/java/android/renderscript/RenderScript.java
+++ b/graphics/java/android/renderscript/RenderScript.java
@@ -299,10 +299,10 @@
         validate();
         rsnAllocationGetSurfaceTextureID2(mContext, alloc, st);
     }
-    native void rsnAllocationSetSurfaceTexture(int con, int alloc, SurfaceTexture sur);
-    synchronized void nAllocationSetSurfaceTexture(int alloc, SurfaceTexture sur) {
+    native void rsnAllocationSetSurface(int con, int alloc, Surface sur);
+    synchronized void nAllocationSetSurface(int alloc, Surface sur) {
         validate();
-        rsnAllocationSetSurfaceTexture(mContext, alloc, sur);
+        rsnAllocationSetSurface(mContext, alloc, sur);
     }
     native void rsnAllocationIoSend(int con, int alloc);
     synchronized void nAllocationIoSend(int alloc) {
diff --git a/graphics/jni/android_renderscript_RenderScript.cpp b/graphics/jni/android_renderscript_RenderScript.cpp
index 9d4c64f..1b7ef22 100644
--- a/graphics/jni/android_renderscript_RenderScript.cpp
+++ b/graphics/jni/android_renderscript_RenderScript.cpp
@@ -41,6 +41,7 @@
 
 #include <rs.h>
 #include <rsEnv.h>
+#include <gui/Surface.h>
 #include <gui/SurfaceTexture.h>
 #include <gui/SurfaceTextureClient.h>
 #include <android_runtime/android_graphics_SurfaceTexture.h>
@@ -486,19 +487,17 @@
 }
 
 static void
-nAllocationSetSurfaceTexture(JNIEnv *_env, jobject _this, RsContext con,
-                             RsAllocation alloc, jobject sur)
+nAllocationSetSurface(JNIEnv *_env, jobject _this, RsContext con, RsAllocation alloc, jobject sur)
 {
     LOG_API("nAllocationSetSurfaceTexture, con(%p), alloc(%p), surface(%p)",
             con, alloc, (Surface *)sur);
 
-    sp<ANativeWindow> window;
+    sp<Surface> s;
     if (sur != 0) {
-        sp<SurfaceTexture> st = SurfaceTexture_getSurfaceTexture(_env, sur);
-        window = new SurfaceTextureClient(st);
+        s = Surface_getSurface(_env, sur);
     }
 
-    rsAllocationSetSurface(con, alloc, window.get());
+    rsAllocationSetSurface(con, alloc, static_cast<ANativeWindow *>(s.get()));
 }
 
 static void
@@ -1362,7 +1361,7 @@
 {"rsnAllocationSyncAll",             "(III)V",                                (void*)nAllocationSyncAll },
 {"rsnAllocationGetSurfaceTextureID", "(II)I",                                 (void*)nAllocationGetSurfaceTextureID },
 {"rsnAllocationGetSurfaceTextureID2","(IILandroid/graphics/SurfaceTexture;)V",(void*)nAllocationGetSurfaceTextureID2 },
-{"rsnAllocationSetSurfaceTexture",   "(IILandroid/graphics/SurfaceTexture;)V",(void*)nAllocationSetSurfaceTexture },
+{"rsnAllocationSetSurface",          "(IILandroid/view/Surface;)V",           (void*)nAllocationSetSurface },
 {"rsnAllocationIoSend",              "(II)V",                                 (void*)nAllocationIoSend },
 {"rsnAllocationIoReceive",           "(II)V",                                 (void*)nAllocationIoReceive },
 {"rsnAllocationData1D",              "(IIIII[II)V",                           (void*)nAllocationData1D_i },
diff --git a/libs/hwui/DisplayListRenderer.h b/libs/hwui/DisplayListRenderer.h
index 4a9886b..fff1d7c 100644
--- a/libs/hwui/DisplayListRenderer.h
+++ b/libs/hwui/DisplayListRenderer.h
@@ -51,7 +51,7 @@
 
 // Set to 1 to enable native processing of View properties. 0 by default. Eventually this
 // will go away and we will always use this approach for accelerated apps.
-#define USE_DISPLAY_LIST_PROPERTIES 1
+#define USE_DISPLAY_LIST_PROPERTIES 0
 
 #define TRANSLATION 0x0001
 #define ROTATION    0x0002
diff --git a/media/java/android/media/MediaScanner.java b/media/java/android/media/MediaScanner.java
index a8144a7..43df8a1 100644
--- a/media/java/android/media/MediaScanner.java
+++ b/media/java/android/media/MediaScanner.java
@@ -839,7 +839,7 @@
                         // and EXIF local time is not less than 1 Day, otherwise MediaProvider
                         // will use file time as taken time.
                         time = exif.getDateTime();
-                        if (Math.abs(mLastModified * 1000 - time) >= 86400000) {
+                        if (time != -1 && Math.abs(mLastModified * 1000 - time) >= 86400000) {
                             values.put(Images.Media.DATE_TAKEN, time);
                         }
                     }
@@ -1183,7 +1183,7 @@
 
     static class MediaBulkDeleter {
         StringBuilder whereClause = new StringBuilder();
-        ArrayList<String> whereArgs = new ArrayList<String>(100); 
+        ArrayList<String> whereArgs = new ArrayList<String>(100);
         IContentProvider mProvider;
         Uri mBaseUri;
 
diff --git a/opengl/java/android/opengl/GLSurfaceView.java b/opengl/java/android/opengl/GLSurfaceView.java
index f69fc53..c937a09 100644
--- a/opengl/java/android/opengl/GLSurfaceView.java
+++ b/opengl/java/android/opengl/GLSurfaceView.java
@@ -779,8 +779,7 @@
                 if (LOG_THREADS) {
                     Log.i("DefaultContextFactory", "tid=" + Thread.currentThread().getId());
                 }
-                throw new RuntimeException("eglDestroyContext failed: "
-                        + EGLLogWrapper.getErrorString(egl.eglGetError()));
+                EglHelper.throwEglException("eglDestroyContex", egl.eglGetError());
             }
         }
     }
@@ -1094,7 +1093,12 @@
              * the context is current and bound to a surface.
              */
             if (!mEgl.eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mEglContext)) {
-                throwEglException("eglMakeCurrent");
+                /*
+                 * Could not make the context current, probably because the underlying
+                 * SurfaceView surface has been destroyed.
+                 */
+                logEglErrorAsWarning("EGLHelper", "eglMakeCurrent", mEgl.eglGetError());
+                return false;
             }
 
             return true;
@@ -1130,36 +1134,13 @@
 
         /**
          * Display the current render surface.
-         * @return false if the context has been lost.
+         * @return the EGL error code from eglSwapBuffers.
          */
-        public boolean swap() {
+        public int swap() {
             if (! mEgl.eglSwapBuffers(mEglDisplay, mEglSurface)) {
-
-                /*
-                 * Check for EGL_CONTEXT_LOST, which means the context
-                 * and all associated data were lost (For instance because
-                 * the device went to sleep). We need to sleep until we
-                 * get a new surface.
-                 */
-                int error = mEgl.eglGetError();
-                switch(error) {
-                case EGL11.EGL_CONTEXT_LOST:
-                    return false;
-                case EGL10.EGL_BAD_CURRENT_SURFACE:
-                    // The current surface is bad, probably because the window manager has closed
-                    // the associated window. Ignore this error, on the assumption that the
-                    // application will be closed soon.
-                    break;
-                case EGL10.EGL_BAD_NATIVE_WINDOW:
-                    // The native window is bad, probably because the window manager has closed it.
-                    // Ignore this error, on the assumption that the application will be closed
-                    // soon.
-                    break;
-                default:
-                    throwEglException("eglSwapBuffers", error);
-                }
+                return mEgl.eglGetError();
             }
-            return true;
+            return EGL10.EGL_SUCCESS;
         }
 
         public void destroySurface() {
@@ -1203,14 +1184,23 @@
             throwEglException(function, mEgl.eglGetError());
         }
 
-        private void throwEglException(String function, int error) {
-            String message = function + " failed: " + EGLLogWrapper.getErrorString(error);
+        public static void throwEglException(String function, int error) {
+            String message = formatEglError(function, error);
             if (LOG_THREADS) {
-                Log.e("EglHelper", "throwEglException tid=" + Thread.currentThread().getId() + " " + message);
+                Log.e("EglHelper", "throwEglException tid=" + Thread.currentThread().getId() + " "
+                        + message);
             }
             throw new RuntimeException(message);
         }
 
+        public static void logEglErrorAsWarning(String tag, String function, int error) {
+            Log.w(tag, formatEglError(function, error));
+        }
+
+        public static String formatEglError(String function, int error) {
+            return function + " failed: " + EGLLogWrapper.getErrorString(error);
+        }
+
         private WeakReference<GLSurfaceView> mGLSurfaceViewWeakRef;
         EGL10 mEgl;
         EGLDisplay mEglDisplay;
@@ -1357,7 +1347,7 @@
                                 }
                             }
 
-                            // Have we lost the surface view surface?
+                            // Have we lost the SurfaceView surface?
                             if ((! mHasSurface) && (! mWaitingForSurface)) {
                                 if (LOG_SURFACE) {
                                     Log.i("GLThread", "noticed surfaceView surface lost tid=" + getId());
@@ -1366,6 +1356,7 @@
                                     stopEglSurfaceLocked();
                                 }
                                 mWaitingForSurface = true;
+                                mSurfaceIsBad = false;
                                 sGLThreadManager.notifyAll();
                             }
 
@@ -1423,7 +1414,9 @@
                                         h = mHeight;
                                         wantRenderNotification = true;
                                         if (LOG_SURFACE) {
-                                            Log.i("GLThread", "noticing that we want render notification tid=" + getId());
+                                            Log.i("GLThread",
+                                                    "noticing that we want render notification tid="
+                                                    + getId());
                                         }
 
                                         // Destroy and recreate the EGL surface.
@@ -1444,6 +1437,7 @@
                                     + " mHaveEglSurface: " + mHaveEglSurface
                                     + " mPaused: " + mPaused
                                     + " mHasSurface: " + mHasSurface
+                                    + " mSurfaceIsBad: " + mSurfaceIsBad
                                     + " mWaitingForSurface: " + mWaitingForSurface
                                     + " mWidth: " + mWidth
                                     + " mHeight: " + mHeight
@@ -1465,8 +1459,8 @@
                             Log.w("GLThread", "egl createSurface");
                         }
                         if (!mEglHelper.createSurface()) {
-                            // Couldn't create a surface. Quit quietly.
-                            break;
+                            mSurfaceIsBad = true;
+                            continue;
                         }
                         createEglSurface = false;
                     }
@@ -1509,11 +1503,24 @@
                             view.mRenderer.onDrawFrame(gl);
                         }
                     }
-                    if (!mEglHelper.swap()) {
-                        if (LOG_SURFACE) {
-                            Log.i("GLThread", "egl context lost tid=" + getId());
-                        }
-                        lostEglContext = true;
+                    int swapError = mEglHelper.swap();
+                    switch (swapError) {
+                        case EGL10.EGL_SUCCESS:
+                            break;
+                        case EGL11.EGL_CONTEXT_LOST:
+                            if (LOG_SURFACE) {
+                                Log.i("GLThread", "egl context lost tid=" + getId());
+                            }
+                            lostEglContext = true;
+                            break;
+                        default:
+                            // Other errors typically mean that the current surface is bad,
+                            // probably because the SurfaceView surface has been destroyed,
+                            // but we haven't been notified yet.
+                            // Log the error to help developers understand why rendering stopped.
+                            EglHelper.logEglErrorAsWarning("GLThread", "eglSwapBuffers", swapError);
+                            mSurfaceIsBad = true;
+                            break;
                     }
 
                     if (wantRenderNotification) {
@@ -1537,7 +1544,7 @@
         }
 
         private boolean readyToDraw() {
-            return (!mPaused) && mHasSurface
+            return (!mPaused) && mHasSurface && (!mSurfaceIsBad)
                 && (mWidth > 0) && (mHeight > 0)
                 && (mRequestRender || (mRenderMode == RENDERMODE_CONTINUOUSLY));
         }
@@ -1707,6 +1714,7 @@
         private boolean mRequestPaused;
         private boolean mPaused;
         private boolean mHasSurface;
+        private boolean mSurfaceIsBad;
         private boolean mWaitingForSurface;
         private boolean mHaveEglContext;
         private boolean mHaveEglSurface;
diff --git a/packages/SystemUI/res/layout/status_bar_notification_row.xml b/packages/SystemUI/res/layout/status_bar_notification_row.xml
index abbc89a..c307c6e 100644
--- a/packages/SystemUI/res/layout/status_bar_notification_row.xml
+++ b/packages/SystemUI/res/layout/status_bar_notification_row.xml
@@ -1,4 +1,4 @@
-<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="match_parent"
     android:layout_height="@dimen/notification_height"
     >
@@ -7,31 +7,17 @@
         android:id="@+id/veto"
         android:layout_width="48dp"
         android:layout_height="match_parent"
-        android:layout_centerVertical="true"
-        android:layout_alignParentRight="true"
+        android:gravity="right"
         android:layout_marginRight="-80dp"
         android:background="@null"
         android:paddingRight="8dp"
         android:paddingLeft="8dp"
         />
 
-    <ImageView
-        android:id="@+id/large_icon"
-        android:layout_width="@android:dimen/notification_large_icon_width"
-        android:layout_height="@android:dimen/notification_large_icon_height"
-        android:layout_alignParentTop="true"
-        android:layout_alignParentLeft="true"
-        android:scaleType="center"
-        android:clickable="true"
-        android:background="@*android:drawable/notify_panel_notification_icon_bg_tile"
-        />
-
     <com.android.systemui.statusbar.LatestItemView android:id="@+id/content"
         android:layout_width="match_parent"
-        android:layout_height="64dp"
-        android:layout_alignParentTop="true"
-        android:layout_toRightOf="@id/large_icon"
-        android:layout_alignParentRight="true"
+        android:layout_height="wrap_content"
+        android:layout_marginBottom="@dimen/notification_divider_height"
         android:focusable="true"
         android:clickable="true"
         android:background="@drawable/notification_row_bg"
@@ -40,8 +26,8 @@
     <View
         android:layout_width="match_parent"
         android:layout_height="@dimen/notification_divider_height"
-        android:layout_alignParentBottom="true"
+        android:gravity="bottom"
         android:background="@drawable/status_bar_notification_row_background_color"
         />
 
-</RelativeLayout>
+</FrameLayout>
diff --git a/packages/SystemUI/src/com/android/systemui/recent/RecentsPanelView.java b/packages/SystemUI/src/com/android/systemui/recent/RecentsPanelView.java
index 564b07b..ebed522 100644
--- a/packages/SystemUI/src/com/android/systemui/recent/RecentsPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/recent/RecentsPanelView.java
@@ -456,6 +456,9 @@
 
         mPreloadTasksRunnable = new Runnable() {
             public void run() {
+                // If we set our visibility to INVISIBLE here, we avoid an extra call to
+                // onLayout later when we become visible (because onLayout is always called
+                // when going from GONE)
                 if (!mShowing) {
                     setVisibility(INVISIBLE);
                     refreshRecentTasksList();
@@ -562,9 +565,6 @@
         if (!mShowing) {
             int action = ev.getAction() & MotionEvent.ACTION_MASK;
             if (action == MotionEvent.ACTION_DOWN) {
-                // If we set our visibility to INVISIBLE here, we avoid an extra call to
-                // onLayout later when we become visible (because onLayout is always called
-                // when going from GONE)
                 post(mPreloadTasksRunnable);
             } else if (action == MotionEvent.ACTION_CANCEL) {
                 setVisibility(GONE);
@@ -583,9 +583,15 @@
         return false;
     }
 
+    public void preloadRecentTasksList() {
+        if (!mShowing) {
+            mPreloadTasksRunnable.run();
+        }
+    }
+
     public void clearRecentTasksList() {
         // Clear memory used by screenshots
-        if (mRecentTaskDescriptions != null) {
+        if (!mShowing && mRecentTaskDescriptions != null) {
             mRecentTasksLoader.cancelLoadingThumbnailsAndIcons();
             mRecentTaskDescriptions.clear();
             mListAdapter.notifyDataSetInvalidated();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
index 3a06127..23222f2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
@@ -19,31 +19,53 @@
 import java.util.ArrayList;
 
 import android.content.Context;
+import android.os.Handler;
 import android.os.IBinder;
+import android.os.Message;
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.util.Log;
 import android.util.Slog;
 import android.view.Display;
 import android.view.IWindowManager;
+import android.view.LayoutInflater;
+import android.view.MotionEvent;
 import android.view.View;
+import android.view.ViewGroup.LayoutParams;
 import android.view.WindowManager;
+import android.view.WindowManagerImpl;
+import android.widget.LinearLayout;
 
 import com.android.internal.statusbar.IStatusBarService;
 import com.android.internal.statusbar.StatusBarIcon;
 import com.android.internal.statusbar.StatusBarIconList;
 import com.android.internal.statusbar.StatusBarNotification;
 import com.android.systemui.SystemUI;
+import com.android.systemui.recent.RecentsPanelView;
+import com.android.systemui.recent.RecentTasksLoader;
+import com.android.systemui.recent.TaskDescription;
 import com.android.systemui.statusbar.CommandQueue;
+import com.android.systemui.statusbar.tablet.StatusBarPanel;
 
 import com.android.systemui.R;
 
-public abstract class BaseStatusBar extends SystemUI implements CommandQueue.Callbacks {
+public abstract class BaseStatusBar extends SystemUI implements
+    CommandQueue.Callbacks, RecentsPanelView.OnRecentsPanelVisibilityChangedListener {
     static final String TAG = "StatusBar";
     private static final boolean DEBUG = false;
 
+    protected static final int MSG_OPEN_RECENTS_PANEL = 1020;
+    protected static final int MSG_CLOSE_RECENTS_PANEL = 1021;
+    protected static final int MSG_PRELOAD_RECENT_APPS = 1022;
+    protected static final int MSG_CANCEL_PRELOAD_RECENT_APPS = 1023;
+
     protected CommandQueue mCommandQueue;
     protected IStatusBarService mBarService;
+    protected H mHandler = createHandler();
+
+    // Recent apps
+    protected RecentsPanelView mRecentsPanel;
+    protected RecentTasksLoader mRecentTasksLoader;
 
     // UI-specific methods
     
@@ -162,4 +184,121 @@
     public void dismissIntruder() {
         // pass
     }
+
+    @Override
+    public void toggleRecentApps() {
+        int msg = (mRecentsPanel.getVisibility() == View.VISIBLE)
+            ? MSG_CLOSE_RECENTS_PANEL : MSG_OPEN_RECENTS_PANEL;
+        mHandler.removeMessages(msg);
+        mHandler.sendEmptyMessage(msg);
+    }
+
+    @Override
+    public void preloadRecentApps() {
+        int msg = MSG_PRELOAD_RECENT_APPS;
+        mHandler.removeMessages(msg);
+        mHandler.sendEmptyMessage(msg);
+    }
+
+    @Override
+    public void cancelPreloadRecentApps() {
+        int msg = MSG_CANCEL_PRELOAD_RECENT_APPS;
+        mHandler.removeMessages(msg);
+        mHandler.sendEmptyMessage(msg);
+    }
+
+    @Override
+    public void onRecentsPanelVisibilityChanged(boolean visible) {
+    }
+
+    protected abstract WindowManager.LayoutParams getRecentsLayoutParams(
+            LayoutParams layoutParams);
+
+    protected void updateRecentsPanel() {
+        // Recents Panel
+        boolean visible = false;
+        ArrayList<TaskDescription> recentTasksList = null;
+        boolean firstScreenful = false;
+        if (mRecentsPanel != null) {
+            visible = mRecentsPanel.isShowing();
+            WindowManagerImpl.getDefault().removeView(mRecentsPanel);
+            if (visible) {
+                recentTasksList = mRecentsPanel.getRecentTasksList();
+                firstScreenful = mRecentsPanel.getFirstScreenful();
+            }
+        }
+
+        // Provide RecentsPanelView with a temporary parent to allow layout params to work.
+        LinearLayout tmpRoot = new LinearLayout(mContext);
+        mRecentsPanel = (RecentsPanelView) LayoutInflater.from(mContext).inflate(
+                 R.layout.status_bar_recent_panel, tmpRoot, false);
+        mRecentsPanel.setRecentTasksLoader(mRecentTasksLoader);
+        mRecentTasksLoader.setRecentsPanel(mRecentsPanel);
+        mRecentsPanel.setOnTouchListener(
+                 new TouchOutsideListener(MSG_CLOSE_RECENTS_PANEL, mRecentsPanel));
+        mRecentsPanel.setVisibility(View.GONE);
+
+
+        WindowManager.LayoutParams lp = getRecentsLayoutParams(mRecentsPanel.getLayoutParams());
+
+        WindowManagerImpl.getDefault().addView(mRecentsPanel, lp);
+        mRecentsPanel.setBar(this);
+        if (visible) {
+            mRecentsPanel.show(true, false, recentTasksList, firstScreenful);
+        }
+
+    }
+
+    protected H createHandler() {
+         return new H();
+    }
+
+    protected class H extends Handler {
+        public void handleMessage(Message m) {
+            switch (m.what) {
+             case MSG_OPEN_RECENTS_PANEL:
+                  if (DEBUG) Slog.d(TAG, "opening recents panel");
+                  if (mRecentsPanel != null) {
+                      mRecentsPanel.show(true, true);
+                  }
+                  break;
+             case MSG_CLOSE_RECENTS_PANEL:
+                  if (DEBUG) Slog.d(TAG, "closing recents panel");
+                  if (mRecentsPanel != null && mRecentsPanel.isShowing()) {
+                      mRecentsPanel.show(false, true);
+                  }
+                  break;
+             case MSG_PRELOAD_RECENT_APPS:
+                  if (DEBUG) Slog.d(TAG, "preloading recents");
+                  mRecentsPanel.preloadRecentTasksList();
+                  break;
+             case MSG_CANCEL_PRELOAD_RECENT_APPS:
+                  if (DEBUG) Slog.d(TAG, "cancel preloading recents");
+                  mRecentsPanel.clearRecentTasksList();
+                  break;
+            }
+        }
+    }
+
+    public class TouchOutsideListener implements View.OnTouchListener {
+        private int mMsg;
+        private StatusBarPanel mPanel;
+
+        public TouchOutsideListener(int msg, StatusBarPanel panel) {
+            mMsg = msg;
+            mPanel = panel;
+        }
+
+        public boolean onTouch(View v, MotionEvent ev) {
+            final int action = ev.getAction();
+            if (action == MotionEvent.ACTION_OUTSIDE
+                || (action == MotionEvent.ACTION_DOWN
+                    && !mPanel.isInContentArea((int)ev.getX(), (int)ev.getY()))) {
+                mHandler.removeMessages(mMsg);
+                mHandler.sendEmptyMessage(mMsg);
+                return true;
+            }
+            return false;
+        }
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
index f8dfa8f..37ab58a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
@@ -61,8 +61,10 @@
     private static final int MSG_SET_HARD_KEYBOARD_STATUS = 10 << MSG_SHIFT;
     
     private static final int MSG_TOGGLE_RECENT_APPS       = 11 << MSG_SHIFT;
+    private static final int MSG_PRELOAD_RECENT_APPS      = 12 << MSG_SHIFT;
+    private static final int MSG_CANCEL_PRELOAD_RECENT_APPS       = 13 << MSG_SHIFT;
 
-    private static final int MSG_SET_NAVIGATION_ICON_HINTS = 13 << MSG_SHIFT;
+    private static final int MSG_SET_NAVIGATION_ICON_HINTS = 14 << MSG_SHIFT;
 
     private StatusBarIconList mList;
     private Callbacks mCallbacks;
@@ -92,6 +94,8 @@
         public void setImeWindowStatus(IBinder token, int vis, int backDisposition);
         public void setHardKeyboardStatus(boolean available, boolean enabled);
         public void toggleRecentApps();
+        public void preloadRecentApps();
+        public void cancelPreloadRecentApps();
         public void setNavigationIconHints(int hints);
     }
 
@@ -199,6 +203,20 @@
         }
     }
 
+    public void preloadRecentApps() {
+        synchronized (mList) {
+            mHandler.removeMessages(MSG_PRELOAD_RECENT_APPS);
+            mHandler.obtainMessage(MSG_PRELOAD_RECENT_APPS, 0, 0, null).sendToTarget();
+        }
+    }
+
+    public void cancelPreloadRecentApps() {
+        synchronized (mList) {
+            mHandler.removeMessages(MSG_CANCEL_PRELOAD_RECENT_APPS);
+            mHandler.obtainMessage(MSG_CANCEL_PRELOAD_RECENT_APPS, 0, 0, null).sendToTarget();
+        }
+    }
+
     public void setNavigationIconHints(int hints) {
         synchronized (mList) {
             mHandler.removeMessages(MSG_SET_NAVIGATION_ICON_HINTS);
@@ -275,6 +293,12 @@
                 case MSG_TOGGLE_RECENT_APPS:
                     mCallbacks.toggleRecentApps();
                     break;
+                case MSG_PRELOAD_RECENT_APPS:
+                    mCallbacks.preloadRecentApps();
+                    break;
+                case MSG_CANCEL_PRELOAD_RECENT_APPS:
+                    mCallbacks.cancelPreloadRecentApps();
+                    break;
                 case MSG_SET_NAVIGATION_ICON_HINTS:
                     mCallbacks.setNavigationIconHints(msg.arg1);
                     break;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
index 85e0a8a..7c679b2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -30,24 +30,22 @@
 import android.content.IntentFilter;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager.NameNotFoundException;
-import android.content.res.Resources;
 import android.content.res.Configuration;
-import android.inputmethodservice.InputMethodService;
+import android.content.res.Resources;
 import android.graphics.PixelFormat;
 import android.graphics.Rect;
-import android.graphics.drawable.Drawable;
+import android.inputmethodservice.InputMethodService;
 import android.os.Build;
 import android.os.IBinder;
-import android.os.RemoteException;
-import android.os.Handler;
 import android.os.Message;
+import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.os.SystemClock;
 import android.provider.Settings;
 import android.text.TextUtils;
 import android.util.DisplayMetrics;
-import android.util.Slog;
 import android.util.Log;
+import android.util.Slog;
 import android.view.Display;
 import android.view.Gravity;
 import android.view.IWindowManager;
@@ -76,19 +74,16 @@
 
 import com.android.internal.statusbar.StatusBarIcon;
 import com.android.internal.statusbar.StatusBarNotification;
-
 import com.android.systemui.R;
 import com.android.systemui.SwipeHelper;
 import com.android.systemui.recent.RecentTasksLoader;
-import com.android.systemui.recent.RecentsPanelView;
-import com.android.systemui.recent.TaskDescription;
-import com.android.systemui.statusbar.NotificationData;
 import com.android.systemui.statusbar.BaseStatusBar;
-import com.android.systemui.statusbar.StatusBarIconView;
+import com.android.systemui.statusbar.NotificationData;
 import com.android.systemui.statusbar.SignalClusterView;
-import com.android.systemui.statusbar.policy.DateView;
+import com.android.systemui.statusbar.StatusBarIconView;
 import com.android.systemui.statusbar.policy.BatteryController;
 import com.android.systemui.statusbar.policy.IntruderAlertView;
+import com.android.systemui.statusbar.policy.DateView;
 import com.android.systemui.statusbar.policy.LocationController;
 import com.android.systemui.statusbar.policy.NetworkController;
 import com.android.systemui.statusbar.policy.NotificationRowLayout;
@@ -116,8 +111,7 @@
     private static final int MSG_CLOSE_NOTIFICATION_PANEL = 1001;
     private static final int MSG_SHOW_INTRUDER = 1002;
     private static final int MSG_HIDE_INTRUDER = 1003;
-    private static final int MSG_OPEN_RECENTS_PANEL = 1020;
-    private static final int MSG_CLOSE_RECENTS_PANEL = 1021;
+    // 1020-1030 reserved for BaseStatusBar
 
     // will likely move to a resource or other tunable param at some point
     private static final int INTRUDER_ALERT_DECAY_MS = 0; // disabled, was 10000;
@@ -152,7 +146,6 @@
 
     PhoneStatusBarView mStatusBarView;
     int mPixelFormat;
-    H mHandler = new H();
     Object mQueueLock = new Object();
 
     // icons
@@ -202,10 +195,6 @@
     private View mTickerView;
     private boolean mTicking;
 
-    // Recent apps
-    private RecentsPanelView mRecentsPanel;
-    private RecentTasksLoader mRecentTasksLoader;
-
     // Tracking finger for opening/closing.
     int mEdgeBorder; // corresponds to R.dimen.status_bar_edge_ignore
     boolean mTracking;
@@ -382,6 +371,7 @@
         return sb;
     }
 
+    @Override
     protected WindowManager.LayoutParams getRecentsLayoutParams(LayoutParams layoutParams) {
         boolean opaque = false;
         WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
@@ -406,42 +396,13 @@
         return lp;
     }
 
+    @Override
     protected void updateRecentsPanel() {
-        // Recents Panel
-        boolean visible = false;
-        ArrayList<TaskDescription> recentTasksList = null;
-        boolean firstScreenful = false;
-        if (mRecentsPanel != null) {
-            visible = mRecentsPanel.isShowing();
-            WindowManagerImpl.getDefault().removeView(mRecentsPanel);
-            if (visible) {
-                recentTasksList = mRecentsPanel.getRecentTasksList();
-                firstScreenful = mRecentsPanel.getFirstScreenful();
-            }
-        }
-
-        // Provide RecentsPanelView with a temporary parent to allow layout params to work.
-        LinearLayout tmpRoot = new LinearLayout(mContext);
-        mRecentsPanel = (RecentsPanelView) LayoutInflater.from(mContext).inflate(
-                R.layout.status_bar_recent_panel, tmpRoot, false);
-        mRecentsPanel.setRecentTasksLoader(mRecentTasksLoader);
-        mRecentTasksLoader.setRecentsPanel(mRecentsPanel);
-        mRecentsPanel.setOnTouchListener(new TouchOutsideListener(MSG_CLOSE_RECENTS_PANEL,
-                mRecentsPanel));
-        mRecentsPanel.setVisibility(View.GONE);
-
+        super.updateRecentsPanel();
         // Make .03 alpha the minimum so you always see the item a bit-- slightly below
         // .03, the item disappears entirely (as if alpha = 0) and that discontinuity looks
         // a bit jarring
         mRecentsPanel.setMinSwipeAlpha(0.03f);
-        WindowManager.LayoutParams lp = getRecentsLayoutParams(mRecentsPanel.getLayoutParams());
-
-        WindowManagerImpl.getDefault().addView(mRecentsPanel, lp);
-        mRecentsPanel.setBar(this);
-        if (visible) {
-            mRecentsPanel.show(true, false, recentTasksList, firstScreenful);
-        }
-
     }
 
     protected int getStatusBarGravity() {
@@ -675,10 +636,8 @@
                 if (contentIntent != null) {
                     final View.OnClickListener listener = new NotificationClicker(contentIntent,
                             notification.pkg, notification.tag, notification.id);
-                    oldEntry.largeIcon.setOnClickListener(listener);
                     oldEntry.content.setOnClickListener(listener);
                 } else {
-                    oldEntry.largeIcon.setOnClickListener(null);
                     oldEntry.content.setOnClickListener(null);
                 }
                 // Update the icon.
@@ -690,13 +649,6 @@
                     handleNotificationError(key, notification, "Couldn't update icon: " + ic);
                     return;
                 }
-                // Update the large icon
-                if (notification.notification.largeIcon != null) {
-                    oldEntry.largeIcon.setImageBitmap(notification.notification.largeIcon);
-                } else {
-                    oldEntry.largeIcon.getLayoutParams().width = 0;
-                    oldEntry.largeIcon.setVisibility(View.INVISIBLE);
-                }
             }
             catch (RuntimeException e) {
                 // It failed to add cleanly.  Log, and remove the view from the panel.
@@ -878,7 +830,10 @@
 
     private boolean inflateViews(NotificationData.Entry entry, ViewGroup parent) {
         StatusBarNotification sbn = entry.notification;
-        RemoteViews remoteViews = sbn.notification.contentView;
+        // XXX: temporary: while testing big notifications, auto-expand all of them
+        final boolean big = (sbn.notification.bigContentView != null);
+        RemoteViews remoteViews = big ? sbn.notification.bigContentView
+                                      : sbn.notification.contentView;
         if (remoteViews == null) {
             return false;
         }
@@ -887,20 +842,18 @@
         LayoutInflater inflater = (LayoutInflater)mContext.getSystemService(
                 Context.LAYOUT_INFLATER_SERVICE);
         View row = inflater.inflate(R.layout.status_bar_notification_row, parent, false);
+        ViewGroup.LayoutParams lp = row.getLayoutParams();
+        if (big) {
+            lp.height = ViewGroup.LayoutParams.WRAP_CONTENT;
+        } else {
+            lp.height = mContext.getResources().getDimensionPixelSize(R.dimen.notification_height);
+        }
+        row.setLayoutParams(lp);
         View vetoButton = updateNotificationVetoButton(row, sbn);
         vetoButton.setContentDescription(mContext.getString(
                 R.string.accessibility_remove_notification));
 
-        // the large icon
-        ImageView largeIcon = (ImageView)row.findViewById(R.id.large_icon);
-        if (sbn.notification.largeIcon != null) {
-            largeIcon.setImageBitmap(sbn.notification.largeIcon);
-            largeIcon.setContentDescription(sbn.notification.tickerText);
-        } else {
-            largeIcon.getLayoutParams().width = 0;
-            largeIcon.setVisibility(View.INVISIBLE);
-        }
-        largeIcon.setContentDescription(sbn.notification.tickerText);
+        // NB: the large icon is now handled entirely by the template
 
         // bind the click event to the content area
         ViewGroup content = (ViewGroup)row.findViewById(R.id.content);
@@ -911,10 +864,8 @@
         if (contentIntent != null) {
             final View.OnClickListener listener = new NotificationClicker(contentIntent,
                     sbn.pkg, sbn.tag, sbn.id);
-            largeIcon.setOnClickListener(listener);
             content.setOnClickListener(listener);
         } else {
-            largeIcon.setOnClickListener(null);
             content.setOnClickListener(null);
         }
 
@@ -940,7 +891,6 @@
         entry.row = row;
         entry.content = content;
         entry.expanded = expanded;
-        entry.largeIcon = largeIcon;
 
         return true;
     }
@@ -1087,11 +1037,17 @@
         }
     }
 
+    @Override
+    protected BaseStatusBar.H createHandler() {
+        return new PhoneStatusBar.H();
+    }
+
     /**
      * All changes to the status bar and notifications funnel through here and are batched.
      */
-    private class H extends Handler {
+    private class H extends BaseStatusBar.H {
         public void handleMessage(Message m) {
+            super.handleMessage(m);
             switch (m.what) {
                 case MSG_ANIMATE:
                     doAnimation();
@@ -1112,18 +1068,6 @@
                     setIntruderAlertVisibility(false);
                     mCurrentlyIntrudingNotification = null;
                     break;
-                case MSG_OPEN_RECENTS_PANEL:
-                    if (DEBUG) Slog.d(TAG, "opening recents panel");
-                    if (mRecentsPanel != null) {
-                        mRecentsPanel.show(true, true);
-                    }
-                    break;
-                case MSG_CLOSE_RECENTS_PANEL:
-                    if (DEBUG) Slog.d(TAG, "closing recents panel");
-                    if (mRecentsPanel != null && mRecentsPanel.isShowing()) {
-                        mRecentsPanel.show(false, true);
-                    }
-                    break;
             }
         }
     }
@@ -2052,13 +1996,6 @@
         }
     }
 
-    public void toggleRecentApps() {
-        int msg = (mRecentsPanel.getVisibility() == View.VISIBLE)
-                ? MSG_CLOSE_RECENTS_PANEL : MSG_OPEN_RECENTS_PANEL;
-        mHandler.removeMessages(msg);
-        mHandler.sendEmptyMessage(msg);
-    }
-
     /**
      * The LEDs are turned o)ff when the notification panel is shown, even just a little bit.
      * This was added last-minute and is inconsistent with the way the rest of the notifications
@@ -2346,27 +2283,5 @@
             vibrate();
         }
     };
-
-    public class TouchOutsideListener implements View.OnTouchListener {
-        private int mMsg;
-        private RecentsPanelView mPanel;
-
-        public TouchOutsideListener(int msg, RecentsPanelView panel) {
-            mMsg = msg;
-            mPanel = panel;
-        }
-
-        public boolean onTouch(View v, MotionEvent ev) {
-            final int action = ev.getAction();
-            if (action == MotionEvent.ACTION_OUTSIDE
-                || (action == MotionEvent.ACTION_DOWN
-                    && !mPanel.isInContentArea((int)ev.getX(), (int)ev.getY()))) {
-                mHandler.removeMessages(mMsg);
-                mHandler.sendEmptyMessage(mMsg);
-                return true;
-            }
-            return false;
-        }
-    }
 }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBar.java
index 87eb9cc..2491d18 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBar.java
@@ -36,21 +36,19 @@
 import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.res.Configuration;
 import android.content.res.Resources;
-import android.inputmethodservice.InputMethodService;
 import android.graphics.PixelFormat;
 import android.graphics.Point;
 import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
 import android.graphics.drawable.LayerDrawable;
+import android.inputmethodservice.InputMethodService;
 import android.os.Build;
-import android.os.Handler;
 import android.os.IBinder;
 import android.os.Message;
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.text.TextUtils;
 import android.util.Slog;
-import android.view.accessibility.AccessibilityEvent;
 import android.view.Display;
 import android.view.Gravity;
 import android.view.IWindowManager;
@@ -62,8 +60,10 @@
 import android.view.View;
 import android.view.ViewConfiguration;
 import android.view.ViewGroup;
+import android.view.ViewGroup.LayoutParams;
 import android.view.WindowManager;
 import android.view.WindowManagerImpl;
+import android.view.accessibility.AccessibilityEvent;
 import android.widget.ImageView;
 import android.widget.LinearLayout;
 import android.widget.RemoteViews;
@@ -78,7 +78,6 @@
 import com.android.systemui.statusbar.BaseStatusBar;
 import com.android.systemui.statusbar.NotificationData;
 import com.android.systemui.statusbar.SignalClusterView;
-import com.android.systemui.statusbar.BaseStatusBar;
 import com.android.systemui.statusbar.StatusBarIconView;
 import com.android.systemui.statusbar.policy.BatteryController;
 import com.android.systemui.statusbar.policy.BluetoothController;
@@ -100,8 +99,7 @@
     public static final int MSG_CLOSE_NOTIFICATION_PANEL = 1001;
     public static final int MSG_OPEN_NOTIFICATION_PEEK = 1002;
     public static final int MSG_CLOSE_NOTIFICATION_PEEK = 1003;
-    public static final int MSG_OPEN_RECENTS_PANEL = 1020;
-    public static final int MSG_CLOSE_RECENTS_PANEL = 1021;
+    // 1020-1029 reserved for BaseStatusBar
     public static final int MSG_SHOW_CHROME = 1030;
     public static final int MSG_HIDE_CHROME = 1031;
     public static final int MSG_OPEN_INPUT_METHODS_PANEL = 1040;
@@ -127,8 +125,6 @@
     int mMenuNavIconWidth = -1;
     private int mMaxNotificationIcons = 5;
 
-    H mHandler = new H();
-
     IWindowManager mWindowManager;
 
     // tracking all current notifications
@@ -189,8 +185,6 @@
     // for disabling the status bar
     int mDisabled = 0;
 
-    private RecentsPanelView mRecentsPanel;
-    private RecentTasksLoader mRecentTasksLoader;
     private InputMethodsPanel mInputMethodsPanel;
     private CompatModePanel mCompatModePanel;
 
@@ -348,33 +342,7 @@
 
         // Recents Panel
         mRecentTasksLoader = new RecentTasksLoader(context);
-        mRecentsPanel = (RecentsPanelView) View.inflate(context,
-                R.layout.status_bar_recent_panel, null);
-        mRecentsPanel.setVisibility(View.GONE);
-        mRecentsPanel.setOnTouchListener(new TouchOutsideListener(MSG_CLOSE_RECENTS_PANEL,
-                mRecentsPanel));
-        mRecentsPanel.setOnVisibilityChangedListener(this);
-        mRecentsPanel.setRecentTasksLoader(mRecentTasksLoader);
-        mRecentTasksLoader.setRecentsPanel(mRecentsPanel);
-
-        lp = new WindowManager.LayoutParams(
-                (int) res.getDimension(R.dimen.status_bar_recents_width),
-                ViewGroup.LayoutParams.MATCH_PARENT,
-                WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL,
-                WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
-                    | WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM
-                    | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH
-                    | WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED,
-                PixelFormat.TRANSLUCENT);
-        lp.gravity = Gravity.BOTTOM | Gravity.LEFT;
-        lp.setTitle("RecentsPanel");
-        lp.windowAnimations = R.style.Animation_RecentPanel;
-        lp.softInputMode = WindowManager.LayoutParams.SOFT_INPUT_STATE_UNCHANGED
-                | WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING;
-
-        WindowManagerImpl.getDefault().addView(mRecentsPanel, lp);
-        mRecentsPanel.setBar(this);
-        mRecentsPanel.setStatusBarView(mStatusBarView);
+        updateRecentsPanel();
 
         // Input methods Panel
         mInputMethodsPanel = (InputMethodsPanel) View.inflate(context,
@@ -680,6 +648,31 @@
         return sb;
     }
 
+    @Override
+    protected WindowManager.LayoutParams getRecentsLayoutParams(LayoutParams layoutParams) {
+        WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
+                (int) mContext.getResources().getDimension(R.dimen.status_bar_recents_width),
+                ViewGroup.LayoutParams.MATCH_PARENT,
+                WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL,
+                WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
+                | WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM
+                | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH
+                | WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED,
+                PixelFormat.TRANSLUCENT);
+        lp.gravity = Gravity.BOTTOM | Gravity.LEFT;
+        lp.setTitle("RecentsPanel");
+        lp.windowAnimations = com.android.internal.R.style.Animation_RecentApplications;
+        lp.softInputMode = WindowManager.LayoutParams.SOFT_INPUT_STATE_UNCHANGED
+            | WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING;
+
+        return lp;
+    }
+
+    protected void updateRecentsPanel() {
+        super.updateRecentsPanel();
+        mRecentsPanel.setStatusBarView(mStatusBarView);
+    }
+
     public int getStatusBarHeight() {
         return mHeightReceiver.getHeight();
     }
@@ -702,8 +695,14 @@
         }
     }
 
-    private class H extends Handler {
+    @Override
+    protected BaseStatusBar.H createHandler() {
+        return new TabletStatusBar.H();
+    }
+
+    private class H extends BaseStatusBar.H {
         public void handleMessage(Message m) {
+            super.handleMessage(m);
             switch (m.what) {
                 case MSG_OPEN_NOTIFICATION_PEEK:
                     if (DEBUG) Slog.d(TAG, "opening notification peek window; arg=" + m.arg1);
@@ -798,18 +797,6 @@
                         mNotificationArea.setVisibility(View.VISIBLE);
                     }
                     break;
-                case MSG_OPEN_RECENTS_PANEL:
-                    if (DEBUG) Slog.d(TAG, "opening recents panel");
-                    if (mRecentsPanel != null) {
-                        mRecentsPanel.show(true, true);
-                    }
-                    break;
-                case MSG_CLOSE_RECENTS_PANEL:
-                    if (DEBUG) Slog.d(TAG, "closing recents panel");
-                    if (mRecentsPanel != null && mRecentsPanel.isShowing()) {
-                        mRecentsPanel.show(false, true);
-                    }
-                    break;
                 case MSG_OPEN_INPUT_METHODS_PANEL:
                     if (DEBUG) Slog.d(TAG, "opening input methods panel");
                     if (mInputMethodsPanel != null) mInputMethodsPanel.openPanel();
@@ -936,10 +923,8 @@
                 if (contentIntent != null) {
                     final View.OnClickListener listener = new NotificationClicker(contentIntent,
                             notification.pkg, notification.tag, notification.id);
-                    oldEntry.largeIcon.setOnClickListener(listener);
                     oldEntry.content.setOnClickListener(listener);
                 } else {
-                    oldEntry.largeIcon.setOnClickListener(null);
                     oldEntry.content.setOnClickListener(null);
                 }
                 // Update the icon.
@@ -951,13 +936,6 @@
                     handleNotificationError(key, notification, "Couldn't update icon: " + ic);
                     return;
                 }
-                // Update the large icon
-                if (notification.notification.largeIcon != null) {
-                    oldEntry.largeIcon.setImageBitmap(notification.notification.largeIcon);
-                } else {
-                    oldEntry.largeIcon.getLayoutParams().width = 0;
-                    oldEntry.largeIcon.setVisibility(View.INVISIBLE);
-                }
 
                 if (NOTIFICATION_PEEK_ENABLED && key == mNotificationPeekKey) {
                     // must update the peek window
@@ -1860,16 +1838,7 @@
         vetoButton.setContentDescription(mContext.getString(
                 R.string.accessibility_remove_notification));
 
-        // the large icon
-        ImageView largeIcon = (ImageView)row.findViewById(R.id.large_icon);
-        if (sbn.notification.largeIcon != null) {
-            largeIcon.setImageBitmap(sbn.notification.largeIcon);
-            largeIcon.setContentDescription(sbn.notification.tickerText);
-        } else {
-            largeIcon.getLayoutParams().width = 0;
-            largeIcon.setVisibility(View.INVISIBLE);
-        }
-        largeIcon.setContentDescription(sbn.notification.tickerText);
+        // NB: the large icon is now handled entirely by the template
 
         // bind the click event to the content area
         ViewGroup content = (ViewGroup)row.findViewById(R.id.content);
@@ -1880,10 +1849,8 @@
         if (contentIntent != null) {
             final View.OnClickListener listener = new NotificationClicker(
                     contentIntent, sbn.pkg, sbn.tag, sbn.id);
-            largeIcon.setOnClickListener(listener);
             content.setOnClickListener(listener);
         } else {
-            largeIcon.setOnClickListener(null);
             content.setOnClickListener(null);
         }
 
@@ -1909,7 +1876,6 @@
         entry.row = row;
         entry.content = content;
         entry.expanded = expanded;
-        entry.largeIcon = largeIcon;
 
         return true;
     }
@@ -1942,13 +1908,6 @@
         visibilityChanged(false);
     }
 
-    public void toggleRecentApps() {
-        int msg = (mRecentsPanel.getVisibility() == View.VISIBLE)
-                ? MSG_CLOSE_RECENTS_PANEL : MSG_OPEN_RECENTS_PANEL;
-        mHandler.removeMessages(msg);
-        mHandler.sendEmptyMessage(msg);
-    }
-
     private BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
         public void onReceive(Context context, Intent intent) {
             String action = intent.getAction();
@@ -1974,28 +1933,6 @@
         }
     };
 
-    public class TouchOutsideListener implements View.OnTouchListener {
-        private int mMsg;
-        private StatusBarPanel mPanel;
-
-        public TouchOutsideListener(int msg, StatusBarPanel panel) {
-            mMsg = msg;
-            mPanel = panel;
-        }
-
-        public boolean onTouch(View v, MotionEvent ev) {
-            final int action = ev.getAction();
-            if (action == MotionEvent.ACTION_OUTSIDE
-                    || (action == MotionEvent.ACTION_DOWN
-                        && !mPanel.isInContentArea((int)ev.getX(), (int)ev.getY()))) {
-                mHandler.removeMessages(mMsg);
-                mHandler.sendEmptyMessage(mMsg);
-                return true;
-            }
-            return false;
-        }
-    }
-
     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
         pw.print("mDisabled=0x");
         pw.println(Integer.toHexString(mDisabled));
diff --git a/policy/src/com/android/internal/policy/impl/GlobalActions.java b/policy/src/com/android/internal/policy/impl/GlobalActions.java
index 4ebabd3..aeb518c 100644
--- a/policy/src/com/android/internal/policy/impl/GlobalActions.java
+++ b/policy/src/com/android/internal/policy/impl/GlobalActions.java
@@ -200,10 +200,19 @@
 
         List<UserInfo> users = mContext.getPackageManager().getUsers();
         if (users.size() > 1) {
+            UserInfo currentUser;
+            try {
+                currentUser = ActivityManagerNative.getDefault().getCurrentUser();
+            } catch (RemoteException re) {
+                currentUser = null;
+            }
             for (final UserInfo user : users) {
+                boolean isCurrentUser = currentUser == null
+                        ? user.id == 0 : (currentUser.id == user.id);
                 SinglePressAction switchToUser = new SinglePressAction(
                         com.android.internal.R.drawable.ic_menu_cc,
-                        user.name != null ? user.name : "Primary") {
+                        (user.name != null ? user.name : "Primary")
+                        + (isCurrentUser ? " \u2714" : "")) {
                     public void onPress() {
                         try {
                             ActivityManagerNative.getDefault().switchUser(user.id);
diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
index b5dace0..6214086 100755
--- a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
+++ b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
@@ -346,17 +346,19 @@
     int mAccelerometerDefault = DEFAULT_ACCELEROMETER_ROTATION;
     boolean mHasSoftInput = false;
     
-    int mPointerLocationMode = 0;
-    PointerLocationView mPointerLocationView = null;
-    InputChannel mPointerLocationInputChannel;
+    int mPointerLocationMode = 0; // guarded by mLock
 
     // The last window we were told about in focusChanged.
     WindowState mFocusedWindow;
     IApplicationToken mFocusedApp;
 
-    final class PointerLocationInputEventReceiver extends InputEventReceiver {
-        public PointerLocationInputEventReceiver(InputChannel inputChannel, Looper looper) {
+    private static final class PointerLocationInputEventReceiver extends InputEventReceiver {
+        private final PointerLocationView mView;
+
+        public PointerLocationInputEventReceiver(InputChannel inputChannel, Looper looper,
+                PointerLocationView view) {
             super(inputChannel, looper);
+            mView = view;
         }
 
         @Override
@@ -366,19 +368,19 @@
                 if (event instanceof MotionEvent
                         && (event.getSource() & InputDevice.SOURCE_CLASS_POINTER) != 0) {
                     final MotionEvent motionEvent = (MotionEvent)event;
-                    synchronized (mLock) {
-                        if (mPointerLocationView != null) {
-                            mPointerLocationView.addPointerEvent(motionEvent);
-                            handled = true;
-                        }
-                    }
+                    mView.addPointerEvent(motionEvent);
+                    handled = true;
                 }
             } finally {
                 finishInputEvent(event, handled);
             }
         }
     }
+
+    // Pointer location view state, only modified on the mHandler Looper.
     PointerLocationInputEventReceiver mPointerLocationInputEventReceiver;
+    PointerLocationView mPointerLocationView;
+    InputChannel mPointerLocationInputChannel;
 
     // The current size of the screen; really; (ir)regardless of whether the status
     // bar can be hidden or not
@@ -428,6 +430,7 @@
     boolean mHideLockScreen;
     boolean mDismissKeyguard;
     boolean mHomePressed;
+    boolean mHomeLongPressed;
     Intent mHomeIntent;
     Intent mCarDockIntent;
     Intent mDeskDockIntent;
@@ -476,6 +479,23 @@
 
     final KeyCharacterMap.FallbackAction mFallbackAction = new KeyCharacterMap.FallbackAction();
 
+    private static final int MSG_ENABLE_POINTER_LOCATION = 1;
+    private static final int MSG_DISABLE_POINTER_LOCATION = 2;
+
+    private class PolicyHandler extends Handler {
+        @Override
+        public void handleMessage(Message msg) {
+            switch (msg.what) {
+                case MSG_ENABLE_POINTER_LOCATION:
+                    enablePointerLocation();
+                    break;
+                case MSG_DISABLE_POINTER_LOCATION:
+                    disablePointerLocation();
+                    break;
+            }
+        }
+    }
+
     private UEventObserver mHDMIObserver = new UEventObserver() {
         @Override
         public void onUEvent(UEventObserver.UEvent event) {
@@ -725,7 +745,7 @@
 
             // Eat the longpress so it won't dismiss the recent apps dialog when
             // the user lets go of the home key
-            mHomePressed = false;
+            mHomeLongPressed = true;
         }
 
         if (mLongPressOnHomeBehavior == LONG_PRESS_HOME_RECENT_DIALOG) {
@@ -808,7 +828,7 @@
             // don't create KeyguardViewMediator if headless
             mKeyguardMediator = new KeyguardViewMediator(context, this, powerManager);
         }
-        mHandler = new Handler();
+        mHandler = new PolicyHandler();
         mOrientationListener = new MyOrientationListener(mContext);
         try {
             mOrientationListener.setCurrentRotation(windowManager.getRotation());
@@ -967,8 +987,6 @@
     public void updateSettings() {
         ContentResolver resolver = mContext.getContentResolver();
         boolean updateRotation = false;
-        View addView = null;
-        View removeView = null;
         synchronized (mLock) {
             mEndcallBehavior = Settings.System.getInt(resolver,
                     Settings.System.END_BUTTON_BEHAVIOR,
@@ -1001,16 +1019,8 @@
                         Settings.System.POINTER_LOCATION, 0);
                 if (mPointerLocationMode != pointerLocation) {
                     mPointerLocationMode = pointerLocation;
-                    if (pointerLocation != 0) {
-                        if (mPointerLocationView == null) {
-                            mPointerLocationView = new PointerLocationView(mContext);
-                            mPointerLocationView.setPrintCoords(false);
-                            addView = mPointerLocationView;
-                        }
-                    } else {
-                        removeView = mPointerLocationView;
-                        mPointerLocationView = null;
-                    }
+                    mHandler.sendEmptyMessage(pointerLocation != 0 ?
+                            MSG_ENABLE_POINTER_LOCATION : MSG_DISABLE_POINTER_LOCATION);
                 }
             }
             // use screen off timeout setting as the timeout for the lockscreen
@@ -1044,7 +1054,13 @@
         if (updateRotation) {
             updateRotation(true);
         }
-        if (addView != null) {
+    }
+
+    private void enablePointerLocation() {
+        if (mPointerLocationView == null) {
+            mPointerLocationView = new PointerLocationView(mContext);
+            mPointerLocationView.setPrintCoords(false);
+
             WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
                     WindowManager.LayoutParams.MATCH_PARENT,
                     WindowManager.LayoutParams.MATCH_PARENT);
@@ -1058,37 +1074,40 @@
             WindowManager wm = (WindowManager)
                     mContext.getSystemService(Context.WINDOW_SERVICE);
             lp.inputFeatures |= WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL;
-            wm.addView(addView, lp);
-            
-            if (mPointerLocationInputChannel == null) {
-                try {
-                    mPointerLocationInputChannel =
-                            mWindowManager.monitorInput("PointerLocationView");
-                    mPointerLocationInputEventReceiver =
-                            new PointerLocationInputEventReceiver(
-                                    mPointerLocationInputChannel, mHandler.getLooper());
-                } catch (RemoteException ex) {
-                    Slog.e(TAG, "Could not set up input monitoring channel for PointerLocation.",
-                            ex);
-                }
+            wm.addView(mPointerLocationView, lp);
+
+            try {
+                mPointerLocationInputChannel =
+                        mWindowManager.monitorInput("PointerLocationView");
+                mPointerLocationInputEventReceiver =
+                        new PointerLocationInputEventReceiver(mPointerLocationInputChannel,
+                                Looper.myLooper(), mPointerLocationView);
+            } catch (RemoteException ex) {
+                Slog.e(TAG, "Could not set up input monitoring channel for PointerLocation.",
+                        ex);
             }
         }
-        if (removeView != null) {
-            if (mPointerLocationInputEventReceiver != null) {
-                mPointerLocationInputEventReceiver.dispose();
-                mPointerLocationInputEventReceiver = null;
-            }
-            if (mPointerLocationInputChannel != null) {
-                mPointerLocationInputChannel.dispose();
-                mPointerLocationInputChannel = null;
-            }
-            
-            WindowManager wm = (WindowManager)
-                    mContext.getSystemService(Context.WINDOW_SERVICE);
-            wm.removeView(removeView);
-        }
     }
-    
+
+    private void disablePointerLocation() {
+        if (mPointerLocationInputEventReceiver != null) {
+            mPointerLocationInputEventReceiver.dispose();
+            mPointerLocationInputEventReceiver = null;
+        }
+
+        if (mPointerLocationInputChannel != null) {
+            mPointerLocationInputChannel.dispose();
+            mPointerLocationInputChannel = null;
+        }
+
+        if (mPointerLocationView != null) {
+            WindowManager wm = (WindowManager)
+                    mContext.getSystemService(Context.WINDOW_SERVICE);
+            wm.removeView(mPointerLocationView);
+            mPointerLocationView = null;
+        }
+    }
+
     private int readRotation(int resID) {
         try {
             int rotation = mContext.getResources().getInteger(resID);
@@ -1601,33 +1620,45 @@
         // it handle it, because that gives us the correct 5 second
         // timeout.
         if (keyCode == KeyEvent.KEYCODE_HOME) {
+
             // If we have released the home key, and didn't do anything else
             // while it was pressed, then it is time to go home!
-            if (mHomePressed && !down) {
+            if (!down) {
+                final boolean homeWasLongPressed = mHomeLongPressed;
                 mHomePressed = false;
-                if (!canceled) {
-                    // If an incoming call is ringing, HOME is totally disabled.
-                    // (The user is already on the InCallScreen at this point,
-                    // and his ONLY options are to answer or reject the call.)
-                    boolean incomingRinging = false;
+                mHomeLongPressed = false;
+                if (!homeWasLongPressed) {
                     try {
-                        ITelephony telephonyService = getTelephonyService();
-                        if (telephonyService != null) {
-                            incomingRinging = telephonyService.isRinging();
-                        }
-                    } catch (RemoteException ex) {
-                        Log.w(TAG, "RemoteException from getPhoneInterface()", ex);
+                        mStatusBarService.cancelPreloadRecentApps();
+                    } catch (RemoteException e) {
+                        Slog.e(TAG, "RemoteException when showing recent apps", e);
                     }
 
-                    if (incomingRinging) {
-                        Log.i(TAG, "Ignoring HOME; there's a ringing incoming call.");
+                    mHomePressed = false;
+                    if (!canceled) {
+                        // If an incoming call is ringing, HOME is totally disabled.
+                        // (The user is already on the InCallScreen at this point,
+                        // and his ONLY options are to answer or reject the call.)
+                        boolean incomingRinging = false;
+                        try {
+                            ITelephony telephonyService = getTelephonyService();
+                            if (telephonyService != null) {
+                                incomingRinging = telephonyService.isRinging();
+                            }
+                        } catch (RemoteException ex) {
+                            Log.w(TAG, "RemoteException from getPhoneInterface()", ex);
+                        }
+
+                        if (incomingRinging) {
+                            Log.i(TAG, "Ignoring HOME; there's a ringing incoming call.");
+                        } else {
+                            launchHomeFromHotKey();
+                        }
                     } else {
-                        launchHomeFromHotKey();
+                        Log.i(TAG, "Ignoring HOME; event canceled.");
                     }
-                } else {
-                    Log.i(TAG, "Ignoring HOME; event canceled.");
+                    return -1;
                 }
-                return -1;
             }
 
             // If a system window has focus, then it doesn't make sense
@@ -1648,8 +1679,14 @@
                     }
                 }
             }
-
             if (down) {
+                if (!mHomePressed) {
+                    try {
+                        mStatusBarService.preloadRecentApps();
+                    } catch (RemoteException e) {
+                        Slog.e(TAG, "RemoteException when preloading recent apps", e);
+                    }
+                }
                 if (repeatCount == 0) {
                     mHomePressed = true;
                 } else if ((event.getFlags() & KeyEvent.FLAG_LONG_PRESS) != 0) {
diff --git a/services/java/com/android/server/NotificationManagerService.java b/services/java/com/android/server/NotificationManagerService.java
index 624ee98..6833a57 100755
--- a/services/java/com/android/server/NotificationManagerService.java
+++ b/services/java/com/android/server/NotificationManagerService.java
@@ -79,6 +79,7 @@
     private static final long[] DEFAULT_VIBRATE_PATTERN = {0, 250, 250, 250};
 
     private static final int DEFAULT_STREAM_TYPE = AudioManager.STREAM_NOTIFICATION;
+    private static final boolean SCORE_ONGOING_HIGHER = false;
 
     final Context mContext;
     final IActivityManager mAm;
@@ -720,7 +721,7 @@
         // Migrate notification flags to scores
         if (0 != (notification.flags & Notification.FLAG_HIGH_PRIORITY)) {
             if (notification.priority < Notification.PRIORITY_MAX) notification.priority = Notification.PRIORITY_MAX;
-        } else if (0 != (notification.flags & Notification.FLAG_ONGOING_EVENT)) {
+        } else if (SCORE_ONGOING_HIGHER && 0 != (notification.flags & Notification.FLAG_ONGOING_EVENT)) {
             if (notification.priority < Notification.PRIORITY_HIGH) notification.priority = Notification.PRIORITY_HIGH;
         }
         
diff --git a/services/java/com/android/server/StatusBarManagerService.java b/services/java/com/android/server/StatusBarManagerService.java
index a9ff6c5..6452be7 100644
--- a/services/java/com/android/server/StatusBarManagerService.java
+++ b/services/java/com/android/server/StatusBarManagerService.java
@@ -352,6 +352,24 @@
         }
     }
 
+    @Override
+    public void preloadRecentApps() {
+        if (mBar != null) {
+            try {
+                mBar.preloadRecentApps();
+            } catch (RemoteException ex) {}
+        }
+    }
+
+    @Override
+    public void cancelPreloadRecentApps() {
+        if (mBar != null) {
+            try {
+                mBar.cancelPreloadRecentApps();
+            } catch (RemoteException ex) {}
+        }
+    }
+
     private void enforceStatusBar() {
         mContext.enforceCallingOrSelfPermission(android.Manifest.permission.STATUS_BAR,
                 "StatusBarManagerService");
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index 78f17bc..bbaebd9 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -1794,7 +1794,8 @@
 
     final ProcessRecord getProcessRecordLocked(
             String processName, int uid) {
-        if (uid == Process.SYSTEM_UID) {
+        // Temporary hack to make Settings run per user
+        if (uid == Process.SYSTEM_UID && !processName.equals("com.android.settings")) {
             // The system gets to run in any process.  If there are multiple
             // processes with the same uid, just pick the first (this
             // should never happen).
@@ -14645,6 +14646,16 @@
         return true;
     }
 
+    @Override
+    public UserInfo getCurrentUser() throws RemoteException {
+        final int callingUid = Binder.getCallingUid();
+        if (callingUid != 0 && callingUid != Process.myUid()) {
+            Slog.e(TAG, "Trying to get user from unauthorized app");
+            return null;
+        }
+        return AppGlobals.getPackageManager().getUser(mCurrentUserId);
+    }
+
     private void onUserRemoved(Intent intent) {
         int extraUserId = intent.getIntExtra(Intent.EXTRA_USERID, -1);
         if (extraUserId < 1) return;
diff --git a/services/java/com/android/server/usb/UsbDeviceManager.java b/services/java/com/android/server/usb/UsbDeviceManager.java
index c2ded8a..4bea5e4 100644
--- a/services/java/com/android/server/usb/UsbDeviceManager.java
+++ b/services/java/com/android/server/usb/UsbDeviceManager.java
@@ -591,6 +591,7 @@
                     notification.defaults = 0; // please be quiet
                     notification.sound = null;
                     notification.vibrate = null;
+                    notification.priority = Notification.PRIORITY_MIN;
 
                     Intent intent = Intent.makeRestartActivityTask(
                             new ComponentName("com.android.settings",
@@ -624,6 +625,7 @@
                     notification.defaults = 0; // please be quiet
                     notification.sound = null;
                     notification.vibrate = null;
+                    notification.priority = Notification.PRIORITY_MIN;
 
                     Intent intent = Intent.makeRestartActivityTask(
                             new ComponentName("com.android.settings",
diff --git a/services/java/com/android/server/wm/AppWindowToken.java b/services/java/com/android/server/wm/AppWindowToken.java
index 6269420..c24c2d9 100644
--- a/services/java/com/android/server/wm/AppWindowToken.java
+++ b/services/java/com/android/server/wm/AppWindowToken.java
@@ -184,13 +184,14 @@
         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;
+            final WindowState w = allAppWindows.get(i);
+            final WindowStateAnimator winAnimator = w.mWinAnimator;
+            winAnimator.mAnimLayer = w.mLayer + adj;
+            if (winAnimator.mAnimLayer > thumbnailLayer) {
+                thumbnailLayer = winAnimator.mAnimLayer;
             }
             if (WindowManagerService.DEBUG_LAYERS) Slog.v(WindowManagerService.TAG, "Updating layer " + w + ": "
-                    + w.mAnimLayer);
+                    + winAnimator.mAnimLayer);
             if (w == service.mInputMethodTarget && !service.mInputMethodTargetWaitingAnim) {
                 service.setInputMethodAnimLayerAdjustment(adj);
             }
@@ -221,11 +222,11 @@
         boolean isAnimating = false;
         final int NW = allAppWindows.size();
         for (int i=0; i<NW; i++) {
-            WindowState w = allAppWindows.get(i);
+            WindowStateAnimator winAnimator = allAppWindows.get(i).mWinAnimator;
             if (WindowManagerService.DEBUG_VISIBILITY) Slog.v(WindowManagerService.TAG,
-                    "performing show on: " + w);
-            w.performShowLocked();
-            isAnimating |= w.mWinAnimator.isAnimating();
+                    "performing show on: " + winAnimator);
+            winAnimator.performShowLocked();
+            isAnimating |= winAnimator.isAnimating();
         }
         return isAnimating;
     }
@@ -390,7 +391,7 @@
                         + win.isDrawnLw()
                         + ", isAnimating=" + win.mWinAnimator.isAnimating());
                 if (!win.isDrawnLw()) {
-                    Slog.v(WindowManagerService.TAG, "Not displayed: s=" + win.mSurface
+                    Slog.v(WindowManagerService.TAG, "Not displayed: s=" + win.mWinAnimator.mSurface
                             + " pv=" + win.mPolicyVisibility
                             + " dp=" + win.mDrawPending
                             + " cdp=" + win.mCommitDrawPending
diff --git a/services/java/com/android/server/wm/DimAnimator.java b/services/java/com/android/server/wm/DimAnimator.java
index d8f2254..0051d98 100644
--- a/services/java/com/android/server/wm/DimAnimator.java
+++ b/services/java/com/android/server/wm/DimAnimator.java
@@ -85,17 +85,18 @@
      * {@link #updateSurface} after all windows are examined.
      */
     void updateParameters(Resources res, WindowState w, long currentTime) {
-        mDimSurface.setLayer(w.mAnimLayer - WindowManagerService.LAYER_OFFSET_DIM);
+        final WindowStateAnimator winAnimator = w.mWinAnimator;
+        mDimSurface.setLayer(winAnimator.mAnimLayer - WindowManagerService.LAYER_OFFSET_DIM);
 
         final float target = w.mExiting ? 0 : w.mAttrs.dimAmount;
-        if (WindowManagerService.SHOW_TRANSACTIONS) Slog.i(WindowManagerService.TAG, "  DIM " + mDimSurface
-                + ": layer=" + (w.mAnimLayer-1) + " target=" + target);
+        if (WindowManagerService.SHOW_TRANSACTIONS) Slog.i(WindowManagerService.TAG, "  DIM "
+                + mDimSurface + ": layer=" + (winAnimator.mAnimLayer-1) + " target=" + target);
         if (mDimTargetAlpha != target) {
             // If the desired dim level has changed, then
             // start an animation to it.
             mLastDimAnimTime = currentTime;
-            long duration = (w.mWinAnimator.mAnimating && w.mWinAnimator.mAnimation != null)
-                    ? w.mWinAnimator.mAnimation.computeDurationHint()
+            long duration = (winAnimator.mAnimating && winAnimator.mAnimation != null)
+                    ? winAnimator.mAnimation.computeDurationHint()
                     : WindowManagerService.DEFAULT_DIM_DURATION;
             if (target > mDimTargetAlpha) {
                 TypedValue tv = new TypedValue();
diff --git a/services/java/com/android/server/wm/WindowAnimator.java b/services/java/com/android/server/wm/WindowAnimator.java
index 81e0a17..eddfe92 100644
--- a/services/java/com/android/server/wm/WindowAnimator.java
+++ b/services/java/com/android/server/wm/WindowAnimator.java
@@ -10,13 +10,13 @@
 import android.util.Slog;
 import android.view.Surface;
 import android.view.WindowManager;
-import android.view.WindowManager.LayoutParams;
 import android.view.WindowManagerPolicy;
 import android.view.animation.Animation;
-import android.view.animation.AnimationUtils;
 
 import com.android.internal.policy.impl.PhoneWindowManager;
 
+import java.io.PrintWriter;
+
 /**
  * @author cmautner@google.com (Craig Mautner)
  * Singleton class that carries out the animations and Surface operations in a separate task
@@ -55,6 +55,15 @@
     /** The one and only screen rotation if one is happening */
     ScreenRotationAnimation mScreenRotationAnimation = null;
 
+    // Window currently running an animation that has requested it be detached
+    // from the wallpaper.  This means we need to ensure the wallpaper is
+    // visible behind it in case it animates in a way that would allow it to be
+    // seen.
+    WindowState mWindowDetachedWallpaper = null;
+    WindowState mDetachedWallpaper = null;
+    boolean mWallpaperMayChange;
+    DimSurface mWindowAnimationBackgroundSurface = null;
+
     WindowAnimator(final WindowManagerService service, final Context context,
             final WindowManagerPolicy policy) {
         mService = service;
@@ -62,6 +71,44 @@
         mPolicy = policy;
     }
 
+    private void testWallpaperAndBackgroundLocked() {
+        if (mWindowDetachedWallpaper != mDetachedWallpaper) {
+            if (WindowManagerService.DEBUG_WALLPAPER) Slog.v(TAG,
+                    "Detached wallpaper changed from " + mWindowDetachedWallpaper
+                    + " to " + mDetachedWallpaper);
+            mWindowDetachedWallpaper = mDetachedWallpaper;
+            mWallpaperMayChange = true;
+        }
+
+        if (mWindowAnimationBackgroundColor != 0) {
+            // If the window that wants black is the current wallpaper
+            // target, then the black goes *below* the wallpaper so we
+            // don't cause the wallpaper to suddenly disappear.
+            WindowState target = mWindowAnimationBackground;
+            if (mService.mWallpaperTarget == target
+                    || mService.mLowerWallpaperTarget == target
+                    || mService.mUpperWallpaperTarget == target) {
+                for (int i=0; i<mService.mWindows.size(); i++) {
+                    WindowState w = mService.mWindows.get(i);
+                    if (w.mIsWallpaper) {
+                        target = w;
+                        break;
+                    }
+                }
+            }
+            if (mWindowAnimationBackgroundSurface == null) {
+                mWindowAnimationBackgroundSurface = new DimSurface(mService.mFxSession);
+            }
+            final int dw = mDw;
+            final int dh = mDh;
+            mWindowAnimationBackgroundSurface.show(dw, dh,
+                    target.mWinAnimator.mAnimLayer - WindowManagerService.LAYER_OFFSET_DIM,
+                    mWindowAnimationBackgroundColor);
+        } else if (mWindowAnimationBackgroundSurface != null) {
+            mWindowAnimationBackgroundSurface.hide();
+        }
+    }
+
     private void updateWindowsAppsAndRotationAnimationsLocked() {
         int i;
         final int NAT = mService.mAppTokens.size();
@@ -116,42 +163,9 @@
         for (int i = mService.mWindows.size() - 1; i >= 0; i--) {
             WindowState w = mService.mWindows.get(i);
             WindowStateAnimator winAnimator = w.mWinAnimator;
-
             final WindowManager.LayoutParams attrs = w.mAttrs;
 
-            if (w.mSurface != null) {
-                // Take care of the window being ready to display.
-                if (w.commitFinishDrawingLocked(mCurrentTime)) {
-                    if ((w.mAttrs.flags
-                            & WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER) != 0) {
-                        if (WindowManagerService.DEBUG_WALLPAPER) Slog.v(TAG,
-                                "First draw done in potential wallpaper target " + w);
-                        mService.mInnerFields.mWallpaperMayChange = true;
-                        mPendingLayoutChanges |= WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
-                        if (WindowManagerService.DEBUG_LAYOUT_REPEATS) {
-                            mService.debugLayoutRepeats("updateWindowsAndWallpaperLocked 1");
-                        }
-                    }
-                }
-
-                // If the window has moved due to its containing
-                // content frame changing, then we'd like to animate
-                // it.  The checks here are ordered by what is least
-                // likely to be true first.
-                if (w.shouldAnimateMove()) {
-                    // Frame has moved, containing content frame
-                    // has also moved, and we're not currently animating...
-                    // let's do something.
-                    Animation a = AnimationUtils.loadAnimation(mContext,
-                            com.android.internal.R.anim.window_move_from_decor);
-                    winAnimator.setAnimation(a);
-                    w.mAnimDw = w.mLastFrame.left - w.mFrame.left;
-                    w.mAnimDh = w.mLastFrame.top - w.mFrame.top;
-                } else {
-                    w.mAnimDw = mInnerDw;
-                    w.mAnimDh = mInnerDh;
-                }
-
+            if (winAnimator.mSurface != null) {
                 final boolean wasAnimating = winAnimator.mWasAnimating;
                 final boolean nowAnimating = winAnimator.stepAnimationLocked(mCurrentTime);
 
@@ -165,13 +179,14 @@
                 // a detached wallpaper animation.
                 if (nowAnimating) {
                     if (winAnimator.mAnimation != null) {
-                        if ((w.mAttrs.flags&FLAG_SHOW_WALLPAPER) != 0
+                        if ((attrs.flags&FLAG_SHOW_WALLPAPER) != 0
                                 && winAnimator.mAnimation.getDetachWallpaper()) {
-                            mService.mInnerFields.mDetachedWallpaper = w;
+                            mDetachedWallpaper = w;
                         }
                         if (winAnimator.mAnimation.getBackgroundColor() != 0) {
                             if (mWindowAnimationBackground == null
-                                    || (w.mAnimLayer < mWindowAnimationBackground.mAnimLayer)) {
+                                    || (winAnimator.mAnimLayer <
+                                            mWindowAnimationBackground.mWinAnimator.mAnimLayer)) {
                                 mWindowAnimationBackground = w;
                                 mWindowAnimationBackgroundColor =
                                         winAnimator.mAnimation.getBackgroundColor();
@@ -186,14 +201,14 @@
                 // displayed behind it.
                 if (w.mAppToken != null && w.mAppToken.animation != null
                         && w.mAppToken.animating) {
-                    if ((w.mAttrs.flags&FLAG_SHOW_WALLPAPER) != 0
+                    if ((attrs.flags&FLAG_SHOW_WALLPAPER) != 0
                             && w.mAppToken.animation.getDetachWallpaper()) {
-                        mService.mInnerFields.mDetachedWallpaper = w;
+                        mDetachedWallpaper = w;
                     }
                     if (w.mAppToken.animation.getBackgroundColor() != 0) {
                         if (mWindowAnimationBackground == null
-                                || (w.mAnimLayer <
-                                        mWindowAnimationBackground.mAnimLayer)) {
+                                || (winAnimator.mAnimLayer <
+                                        mWindowAnimationBackground.mWinAnimator.mAnimLayer)) {
                             mWindowAnimationBackground = w;
                             mWindowAnimationBackgroundColor =
                                     w.mAppToken.animation.getBackgroundColor();
@@ -202,7 +217,7 @@
                 }
 
                 if (wasAnimating && !winAnimator.mAnimating && mService.mWallpaperTarget == w) {
-                    mService.mInnerFields.mWallpaperMayChange = true;
+                    mWallpaperMayChange = true;
                     mPendingLayoutChanges |= WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
                     if (WindowManagerService.DEBUG_LAYOUT_REPEATS) {
                         mService.debugLayoutRepeats("updateWindowsAndWallpaperLocked 2");
@@ -255,7 +270,7 @@
                     }
                     if (changed && (attrs.flags
                             & WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER) != 0) {
-                        mService.mInnerFields.mWallpaperMayChange = true;
+                        mWallpaperMayChange = true;
                         mPendingLayoutChanges |= WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
                         if (WindowManagerService.DEBUG_LAYOUT_REPEATS) {
                             mService.debugLayoutRepeats("updateWindowsAndWallpaperLocked 4");
@@ -280,7 +295,7 @@
                                 + w.isDrawnLw()
                                 + ", isAnimating=" + winAnimator.isAnimating());
                         if (!w.isDrawnLw()) {
-                            Slog.v(TAG, "Not displayed: s=" + w.mSurface
+                            Slog.v(TAG, "Not displayed: s=" + winAnimator.mSurface
                                     + " pv=" + w.mPolicyVisibility
                                     + " dp=" + w.mDrawPending
                                     + " cdp=" + w.mCommitDrawPending
@@ -307,7 +322,7 @@
                     }
                 }
             } else if (w.mReadyToShow) {
-                if (w.performShowLocked()) {
+                if (winAnimator.performShowLocked()) {
                     mPendingLayoutChanges |= WindowManagerPolicy.FINISH_LAYOUT_REDO_ANIM;
                     if (WindowManagerService.DEBUG_LAYOUT_REPEATS) {
                         mService.debugLayoutRepeats("updateWindowsAndWallpaperLocked 5");
@@ -319,8 +334,8 @@
                     atoken.thumbnailTransactionSeq = mTransactionSequence;
                     atoken.thumbnailLayer = 0;
                 }
-                if (atoken.thumbnailLayer < w.mAnimLayer) {
-                    atoken.thumbnailLayer = w.mAnimLayer;
+                if (atoken.thumbnailLayer < winAnimator.mAnimLayer) {
+                    atoken.thumbnailLayer = winAnimator.mAnimLayer;
                 }
             }
         } // end forall windows
@@ -370,14 +385,10 @@
     }
 
     private void performAnimationsLocked() {
-        if (WindowManagerService.DEBUG_APP_TRANSITIONS) Slog.v(TAG, "*** ANIM STEP: seq="
-                + mTransactionSequence + " mAnimating="
-                + mAnimating);
-
         mTokenMayBeDrawn = false;
         mService.mInnerFields.mWallpaperMayChange = false;
         mForceHiding = false;
-        mService.mInnerFields.mDetachedWallpaper = null;
+        mDetachedWallpaper = null;
         mWindowAnimationBackground = null;
         mWindowAnimationBackgroundColor = 0;
 
@@ -386,191 +397,12 @@
         if (mTokenMayBeDrawn) {
             testTokenMayBeDrawnLocked();
         }
-
-        if (WindowManagerService.DEBUG_APP_TRANSITIONS) Slog.v(TAG, "*** ANIM STEP: changes=0x"
-                + Integer.toHexString(mPendingLayoutChanges));
     }
 
-    public void prepareSurfaceLocked(final WindowState w, final boolean recoveringMemory) {
-        if (w.mSurface == null) {
-            if (w.mOrientationChanging) {
-                if (WindowManagerService.DEBUG_ORIENTATION) {
-                    Slog.v(TAG, "Orientation change skips hidden " + w);
-                }
-                w.mOrientationChanging = false;
-            }
-            return;
-        }
-
-        boolean displayed = false;
-
-        w.computeShownFrameLocked();
-
-        int width, height;
-        if ((w.mAttrs.flags & LayoutParams.FLAG_SCALED) != 0) {
-            // for a scaled surface, we just want to use
-            // the requested size.
-            width  = w.mRequestedWidth;
-            height = w.mRequestedHeight;
-        } else {
-            width = w.mCompatFrame.width();
-            height = w.mCompatFrame.height();
-        }
-
-        if (width < 1) {
-            width = 1;
-        }
-        if (height < 1) {
-            height = 1;
-        }
-        final boolean surfaceResized = w.mSurfaceW != width || w.mSurfaceH != height;
-        if (surfaceResized) {
-            w.mSurfaceW = width;
-            w.mSurfaceH = height;
-        }
-
-        if (w.mSurfaceX != w.mShownFrame.left
-                || w.mSurfaceY != w.mShownFrame.top) {
-            try {
-                if (WindowManagerService.SHOW_TRANSACTIONS) WindowManagerService.logSurface(w,
-                        "POS " + w.mShownFrame.left
-                        + ", " + w.mShownFrame.top, null);
-                w.mSurfaceX = w.mShownFrame.left;
-                w.mSurfaceY = w.mShownFrame.top;
-                w.mSurface.setPosition(w.mShownFrame.left, w.mShownFrame.top);
-            } catch (RuntimeException e) {
-                Slog.w(TAG, "Error positioning surface of " + w
-                        + " pos=(" + w.mShownFrame.left
-                        + "," + w.mShownFrame.top + ")", e);
-                if (!recoveringMemory) {
-                    mService.reclaimSomeSurfaceMemoryLocked(w, "position", true);
-                }
-            }
-        }
-
-        if (surfaceResized) {
-            try {
-                if (WindowManagerService.SHOW_TRANSACTIONS) WindowManagerService.logSurface(w,
-                        "SIZE " + width + "x" + height, null);
-                w.mSurfaceResized = true;
-                w.mSurface.setSize(width, height);
-            } catch (RuntimeException e) {
-                // If something goes wrong with the surface (such
-                // as running out of memory), don't take down the
-                // entire system.
-                Slog.e(TAG, "Error resizing surface of " + w
-                        + " size=(" + width + "x" + height + ")", e);
-                if (!recoveringMemory) {
-                    mService.reclaimSomeSurfaceMemoryLocked(w, "size", true);
-                }
-            }
-        }
-
-        if (w.mAttachedHidden || !w.isReadyForDisplay()) {
-            if (!w.mLastHidden) {
-                //dump();
-                w.mLastHidden = true;
-                if (WindowManagerService.SHOW_TRANSACTIONS) WindowManagerService.logSurface(w,
-                        "HIDE (performLayout)", null);
-                if (w.mSurface != null) {
-                    w.mSurfaceShown = false;
-                    try {
-                        w.mSurface.hide();
-                    } catch (RuntimeException e) {
-                        Slog.w(TAG, "Exception hiding surface in " + w);
-                    }
-                }
-            }
-            // If we are waiting for this window to handle an
-            // orientation change, well, it is hidden, so
-            // doesn't really matter.  Note that this does
-            // introduce a potential glitch if the window
-            // becomes unhidden before it has drawn for the
-            // new orientation.
-            if (w.mOrientationChanging) {
-                w.mOrientationChanging = false;
-                if (WindowManagerService.DEBUG_ORIENTATION) Slog.v(TAG,
-                        "Orientation change skips hidden " + w);
-            }
-        } else if (w.mLastLayer != w.mAnimLayer
-                || w.mLastAlpha != w.mShownAlpha
-                || w.mLastDsDx != w.mDsDx
-                || w.mLastDtDx != w.mDtDx
-                || w.mLastDsDy != w.mDsDy
-                || w.mLastDtDy != w.mDtDy
-                || w.mLastHScale != w.mHScale
-                || w.mLastVScale != w.mVScale
-                || w.mLastHidden) {
-            displayed = true;
-            w.mLastAlpha = w.mShownAlpha;
-            w.mLastLayer = w.mAnimLayer;
-            w.mLastDsDx = w.mDsDx;
-            w.mLastDtDx = w.mDtDx;
-            w.mLastDsDy = w.mDsDy;
-            w.mLastDtDy = w.mDtDy;
-            w.mLastHScale = w.mHScale;
-            w.mLastVScale = w.mVScale;
-            if (WindowManagerService.SHOW_TRANSACTIONS) WindowManagerService.logSurface(w,
-                    "alpha=" + w.mShownAlpha + " layer=" + w.mAnimLayer
-                    + " matrix=[" + (w.mDsDx*w.mHScale)
-                    + "," + (w.mDtDx*w.mVScale)
-                    + "][" + (w.mDsDy*w.mHScale)
-                    + "," + (w.mDtDy*w.mVScale) + "]", null);
-            if (w.mSurface != null) {
-                try {
-                    w.mSurfaceAlpha = w.mShownAlpha;
-                    w.mSurface.setAlpha(w.mShownAlpha);
-                    w.mSurfaceLayer = w.mAnimLayer;
-                    w.mSurface.setLayer(w.mAnimLayer);
-                    w.mSurface.setMatrix(
-                            w.mDsDx*w.mHScale, w.mDtDx*w.mVScale,
-                            w.mDsDy*w.mHScale, w.mDtDy*w.mVScale);
-                } catch (RuntimeException e) {
-                    Slog.w(TAG, "Error updating surface in " + w, e);
-                    if (!recoveringMemory) {
-                        mService.reclaimSomeSurfaceMemoryLocked(w, "update", true);
-                    }
-                }
-            }
-
-            if (w.mLastHidden && w.isDrawnLw()
-                    && !w.mReadyToShow) {
-                if (WindowManagerService.SHOW_TRANSACTIONS) WindowManagerService.logSurface(w,
-                        "SHOW (performLayout)", null);
-                if (WindowManagerService.DEBUG_VISIBILITY) Slog.v(TAG, "Showing " + w
-                        + " during relayout");
-                if (mService.showSurfaceRobustlyLocked(w)) {
-                    w.mHasDrawn = true;
-                    w.mLastHidden = false;
-                } else {
-                    w.mOrientationChanging = false;
-                }
-            }
-            if (w.mSurface != null) {
-                w.mToken.hasVisible = true;
-            }
-        } else {
-            displayed = true;
-        }
-
-        if (displayed) {
-            if (w.mOrientationChanging) {
-                if (!w.isDrawnLw()) {
-                    mService.mInnerFields.mOrientationChangeComplete = false;
-                    if (WindowManagerService.DEBUG_ORIENTATION) Slog.v(TAG,
-                            "Orientation continue waiting for draw in " + w);
-                } else {
-                    w.mOrientationChanging = false;
-                    if (WindowManagerService.DEBUG_ORIENTATION) Slog.v(TAG,
-                            "Orientation change complete in " + w);
-                }
-            }
-            w.mToken.hasVisible = true;
-        }
-    }
 
     void animate() {
         mPendingLayoutChanges = 0;
+        mWallpaperMayChange = false;
         mCurrentTime = SystemClock.uptimeMillis();
 
         // Update animations of all applications, including those
@@ -578,6 +410,7 @@
         Surface.openTransaction();
 
         try {
+            testWallpaperAndBackgroundLocked();
             updateWindowsAppsAndRotationAnimationsLocked();
             performAnimationsLocked();
 
@@ -590,7 +423,7 @@
             final int N = mService.mWindows.size();
             for (int i=N-1; i>=0; i--) {
                 WindowState w = mService.mWindows.get(i);
-                prepareSurfaceLocked(w, true);
+                w.mWinAnimator.prepareSurfaceLocked(true);
             }
 
             if (mService.mDimAnimator != null && mService.mDimAnimator.mDimShown) {
@@ -611,6 +444,10 @@
         } finally {
             Surface.closeTransaction();
         }
+
+        if (mWallpaperMayChange) {
+            mService.notifyWallpaperMayChange();
+        }
     }
 
     WindowState mCurrentFocus;
@@ -626,4 +463,13 @@
         mInnerDh = appHeight;
     }
 
+    public void dump(PrintWriter pw, String prefix, boolean dumpAll) {
+        if (mWindowDetachedWallpaper != null) {
+            pw.print("  mWindowDetachedWallpaper="); pw.println(mWindowDetachedWallpaper);
+        }
+        if (mWindowAnimationBackgroundSurface != null) {
+            pw.println("  mWindowAnimationBackgroundSurface:");
+            mWindowAnimationBackgroundSurface.printTo("    ", pw);
+        }
+    }
 }
diff --git a/services/java/com/android/server/wm/WindowManagerService.java b/services/java/com/android/server/wm/WindowManagerService.java
index 9635b33..3d60c6b 100644
--- a/services/java/com/android/server/wm/WindowManagerService.java
+++ b/services/java/com/android/server/wm/WindowManagerService.java
@@ -554,12 +554,6 @@
     // If non-null, we are in the middle of animating from one wallpaper target
     // to another, and this is the higher one in Z-order.
     WindowState mUpperWallpaperTarget = null;
-    // Window currently running an animation that has requested it be detached
-    // from the wallpaper.  This means we need to ensure the wallpaper is
-    // visible behind it in case it animates in a way that would allow it to be
-    // seen.
-    WindowState mWindowDetachedWallpaper = null;
-    DimSurface mWindowAnimationBackgroundSurface = null;
     int mWallpaperAnimLayerAdjustment;
     float mLastWallpaperX = -1;
     float mLastWallpaperY = -1;
@@ -598,16 +592,14 @@
     class LayoutAndSurfaceFields {
         boolean mWallpaperForceHidingChanged = false;
         boolean mWallpaperMayChange = false;
-        WindowState mDetachedWallpaper = null;
         boolean mOrientationChangeComplete = true;
-        private int mAdjResult = 0;
+        int mAdjResult = 0;
         private Session mHoldScreen = null;
         private boolean mObscured = false;
         boolean mDimming = false;
         private boolean mSyswin = false;
         private float mScreenBrightness = -1;
         private float mButtonBrightness = -1;
-        private boolean mUpdateRotation = false;
     }
     LayoutAndSurfaceFields mInnerFields = new LayoutAndSurfaceFields();
 
@@ -1132,7 +1124,8 @@
             if (DEBUG_INPUT_METHOD) {
                 Slog.i(TAG, "isVisibleOrAdding " + w + ": " + w.isVisibleOrAdding());
                 if (!w.isVisibleOrAdding()) {
-                    Slog.i(TAG, "  mSurface=" + w.mSurface + " reportDestroy=" + w.mReportDestroySurface
+                    Slog.i(TAG, "  mSurface=" + w.mWinAnimator.mSurface + " reportDestroy="
+                            + w.mWinAnimator.mReportDestroySurface
                             + " relayoutCalled=" + w.mRelayoutCalled + " viewVis=" + w.mViewVisibility
                             + " policyVis=" + w.mPolicyVisibility + " attachHid=" + w.mAttachedHidden
                             + " exiting=" + w.mExiting + " destroying=" + w.mDestroying);
@@ -1196,7 +1189,7 @@
         if (mInputMethodTarget != null && w != null
                 && mInputMethodTarget.isDisplayedLw()
                 && mInputMethodTarget.mExiting) {
-            if (mInputMethodTarget.mAnimLayer > w.mAnimLayer) {
+            if (mInputMethodTarget.mWinAnimator.mAnimLayer > w.mWinAnimator.mAnimLayer) {
                 w = mInputMethodTarget;
                 i = localmWindows.indexOf(w);
                 if (DEBUG_INPUT_METHOD) Slog.v(TAG, "Current target higher, switching to: " + w);
@@ -1225,8 +1218,8 @@
                             break;
                         }
                         if (!win.mRemoved) {
-                            if (highestTarget == null || win.mAnimLayer >
-                                    highestTarget.mAnimLayer) {
+                            if (highestTarget == null || win.mWinAnimator.mAnimLayer >
+                                    highestTarget.mWinAnimator.mAnimLayer) {
                                 highestTarget = win;
                                 highestPos = pos;
                             }
@@ -1239,8 +1232,8 @@
                     if (DEBUG_INPUT_METHOD) Slog.v(TAG, "mNextAppTransition="
                             + mNextAppTransition + " " + highestTarget
                             + " animating=" + highestTarget.mWinAnimator.isAnimating()
-                            + " layer=" + highestTarget.mAnimLayer
-                            + " new layer=" + w.mAnimLayer);
+                            + " layer=" + highestTarget.mWinAnimator.mAnimLayer
+                            + " new layer=" + w.mWinAnimator.mAnimLayer);
 
                     if (mNextAppTransition != WindowManagerPolicy.TRANSIT_UNSET) {
                         // If we are currently setting up for an animation,
@@ -1249,7 +1242,7 @@
                         mInputMethodTarget = highestTarget;
                         return highestPos + 1;
                     } else if (highestTarget.mWinAnimator.isAnimating() &&
-                            highestTarget.mAnimLayer > w.mAnimLayer) {
+                            highestTarget.mWinAnimator.mAnimLayer > w.mWinAnimator.mAnimLayer) {
                         // If the window we are currently targeting is involved
                         // with an animation, and it is on top of the next target
                         // we will be over, then hold off on moving until
@@ -1321,25 +1314,25 @@
         mInputMethodAnimLayerAdjustment = adj;
         WindowState imw = mInputMethodWindow;
         if (imw != null) {
-            imw.mAnimLayer = imw.mLayer + adj;
+            imw.mWinAnimator.mAnimLayer = imw.mLayer + adj;
             if (DEBUG_LAYERS) Slog.v(TAG, "IM win " + imw
-                    + " anim layer: " + imw.mAnimLayer);
+                    + " anim layer: " + imw.mWinAnimator.mAnimLayer);
             int wi = imw.mChildWindows.size();
             while (wi > 0) {
                 wi--;
                 WindowState cw = imw.mChildWindows.get(wi);
-                cw.mAnimLayer = cw.mLayer + adj;
+                cw.mWinAnimator.mAnimLayer = cw.mLayer + adj;
                 if (DEBUG_LAYERS) Slog.v(TAG, "IM win " + cw
-                        + " anim layer: " + cw.mAnimLayer);
+                        + " anim layer: " + cw.mWinAnimator.mAnimLayer);
             }
         }
         int di = mInputMethodDialogs.size();
         while (di > 0) {
             di --;
             imw = mInputMethodDialogs.get(di);
-            imw.mAnimLayer = imw.mLayer + adj;
+            imw.mWinAnimator.mAnimLayer = imw.mLayer + adj;
             if (DEBUG_LAYERS) Slog.v(TAG, "IM win " + imw
-                    + " anim layer: " + imw.mAnimLayer);
+                    + " anim layer: " + imw.mWinAnimator.mAnimLayer);
         }
     }
 
@@ -1581,7 +1574,7 @@
                 continue;
             }
             topCurW = null;
-            if (w != mWindowDetachedWallpaper && w.mAppToken != null) {
+            if (w != mAnimator.mWindowDetachedWallpaper && w.mAppToken != null) {
                 // If this window's app token is hidden and not animating,
                 // it is of no interest to us.
                 if (w.mAppToken.hidden && w.mAppToken.animation == null) {
@@ -1610,7 +1603,7 @@
                     continue;
                 }
                 break;
-            } else if (w == mWindowDetachedWallpaper) {
+            } else if (w == mAnimator.mWindowDetachedWallpaper) {
                 windowDetachedI = i;
             }
         }
@@ -1826,9 +1819,9 @@
                     }
                 }
 
-                wallpaper.mAnimLayer = wallpaper.mLayer + mWallpaperAnimLayerAdjustment;
+                wallpaper.mWinAnimator.mAnimLayer = wallpaper.mLayer + mWallpaperAnimLayerAdjustment;
                 if (DEBUG_LAYERS || DEBUG_WALLPAPER) Slog.v(TAG, "Wallpaper win "
-                        + wallpaper + " anim layer: " + wallpaper.mAnimLayer);
+                        + wallpaper + " anim layer: " + wallpaper.mWinAnimator.mAnimLayer);
 
                 // First, if this window is at the current index, then all
                 // is well.
@@ -1880,9 +1873,9 @@
             while (curWallpaperIndex > 0) {
                 curWallpaperIndex--;
                 WindowState wallpaper = token.windows.get(curWallpaperIndex);
-                wallpaper.mAnimLayer = wallpaper.mLayer + adj;
+                wallpaper.mWinAnimator.mAnimLayer = wallpaper.mLayer + adj;
                 if (DEBUG_LAYERS || DEBUG_WALLPAPER) Slog.v(TAG, "Wallpaper win "
-                        + wallpaper + " anim layer: " + wallpaper.mAnimLayer);
+                        + wallpaper + " anim layer: " + wallpaper.mWinAnimator.mAnimLayer);
             }
         }
     }
@@ -2001,19 +1994,20 @@
                 curWallpaperIndex--;
                 WindowState wallpaper = token.windows.get(curWallpaperIndex);
                 if (updateWallpaperOffsetLocked(wallpaper, dw, dh, sync)) {
-                    wallpaper.computeShownFrameLocked();
+                    WindowStateAnimator winAnimator = wallpaper.mWinAnimator;
+                    winAnimator.computeShownFrameLocked();
                     // No need to lay out the windows - we can just set the wallpaper position
                     // directly.
-                    if (wallpaper.mSurfaceX != wallpaper.mShownFrame.left
-                            || wallpaper.mSurfaceY != wallpaper.mShownFrame.top) {
+                    if (winAnimator.mSurfaceX != wallpaper.mShownFrame.left
+                            || winAnimator.mSurfaceY != wallpaper.mShownFrame.top) {
                         Surface.openTransaction();
                         try {
                             if (SHOW_TRANSACTIONS) logSurface(wallpaper,
                                     "POS " + wallpaper.mShownFrame.left
                                     + ", " + wallpaper.mShownFrame.top, null);
-                            wallpaper.mSurfaceX = wallpaper.mShownFrame.left;
-                            wallpaper.mSurfaceY = wallpaper.mShownFrame.top;
-                            wallpaper.mSurface.setPosition(wallpaper.mShownFrame.left,
+                            winAnimator.mSurfaceX = wallpaper.mShownFrame.left;
+                            winAnimator.mSurfaceY = wallpaper.mShownFrame.top;
+                            winAnimator.mSurface.setPosition(wallpaper.mShownFrame.left,
                                     wallpaper.mShownFrame.top);
                         } catch (RuntimeException e) {
                             Slog.w(TAG, "Error positioning surface of " + wallpaper
@@ -2223,7 +2217,7 @@
                 }
             }
 
-            win.mEnterAnimationPending = true;
+            win.mWinAnimator.mEnterAnimationPending = true;
 
             mPolicy.getContentInsetHintLw(attrs, outContentInsets);
 
@@ -2294,14 +2288,14 @@
             TAG, "Remove " + win + " client="
             + Integer.toHexString(System.identityHashCode(
                 win.mClient.asBinder()))
-            + ", surface=" + win.mSurface);
+            + ", surface=" + win.mWinAnimator.mSurface);
 
         final long origId = Binder.clearCallingIdentity();
 
         win.disposeInputChannel();
 
         if (DEBUG_APP_TRANSITIONS) Slog.v(
-                TAG, "Remove " + win + ": mSurface=" + win.mSurface
+                TAG, "Remove " + win + ": mSurface=" + win.mWinAnimator.mSurface
                 + " mExiting=" + win.mExiting
                 + " isAnimating=" + win.mWinAnimator.isAnimating()
                 + " app-animation="
@@ -2315,7 +2309,7 @@
         // to hold off on removing the window until the animation is done.
         // If the display is frozen, just remove immediately, since the
         // animation wouldn't be seen.
-        if (win.mSurface != null && okToDisplay()) {
+        if (win.mWinAnimator.mSurface != null && okToDisplay()) {
             // If we are not currently running the exit animation, we
             // need to see about starting one.
             wasVisible = win.isWinVisibleLw();
@@ -2326,7 +2320,7 @@
                     transit = WindowManagerPolicy.TRANSIT_PREVIEW_DONE;
                 }
                 // Try starting an animation.
-                if (applyAnimationLocked(win, transit, false)) {
+                if (win.mWinAnimator.applyAnimationLocked(transit, false)) {
                     win.mExiting = true;
                 }
             }
@@ -2482,14 +2476,15 @@
         try {
             synchronized (mWindowMap) {
                 WindowState w = windowForClientLocked(session, client, false);
-                if ((w != null) && (w.mSurface != null)) {
+                WindowStateAnimator winAnimator = w.mWinAnimator;
+                if ((w != null) && (winAnimator.mSurface != null)) {
                     if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG,
                             ">>> OPEN TRANSACTION setTransparentRegion");
                     Surface.openTransaction();
                     try {
                         if (SHOW_TRANSACTIONS) logSurface(w,
                                 "transparentRegionHint=" + region, null);
-                        w.mSurface.setTransparentRegionHint(region);
+                        winAnimator.mSurface.setTransparentRegionHint(region);
                     } finally {
                         Surface.closeTransaction();
                         if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG,
@@ -2618,6 +2613,7 @@
 
         synchronized(mWindowMap) {
             WindowState win = windowForClientLocked(session, client, false);
+            WindowStateAnimator winAnimator = win.mWinAnimator;
             if (win == null) {
                 return 0;
             }
@@ -2635,7 +2631,7 @@
                 mPolicy.adjustWindowParamsLw(attrs);
             }
 
-            win.mSurfaceDestroyDeferred =
+            winAnimator.mSurfaceDestroyDeferred =
                     (flags&WindowManagerImpl.RELAYOUT_DEFER_SURFACE_DESTROY) != 0;
 
             int attrChanges = 0;
@@ -2657,7 +2653,7 @@
             win.mEnforceSizeCompat = (win.mAttrs.flags & FLAG_COMPATIBLE_WINDOW) != 0;
 
             if ((attrChanges & WindowManager.LayoutParams.ALPHA_CHANGED) != 0) {
-                win.mAlpha = attrs.alpha;
+                winAnimator.mAlpha = attrs.alpha;
             }
 
             final boolean scaledWindow =
@@ -2699,18 +2695,18 @@
                     (win.mAppToken == null || !win.mAppToken.clientHidden)) {
                 displayed = !win.isVisibleLw();
                 if (win.mExiting) {
-                    win.mWinAnimator.cancelExitAnimationForNextAnimationLocked();
+                    winAnimator.cancelExitAnimationForNextAnimationLocked();
                 }
                 if (win.mDestroying) {
                     win.mDestroying = false;
                     mDestroySurface.remove(win);
                 }
                 if (oldVisibility == View.GONE) {
-                    win.mEnterAnimationPending = true;
+                    winAnimator.mEnterAnimationPending = true;
                 }
                 if (displayed) {
                     if (win.isDrawnLw() && okToDisplay()) {
-                        applyEnterAnimationLocked(win);
+                        winAnimator.applyEnterAnimationLocked();
                     }
                     if ((win.mAttrs.flags
                             & WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON) != 0) {
@@ -2733,19 +2729,19 @@
                 }
                 if ((attrChanges&WindowManager.LayoutParams.FORMAT_CHANGED) != 0) {
                     // To change the format, we need to re-build the surface.
-                    win.destroySurfaceLocked();
+                    winAnimator.destroySurfaceLocked();
                     displayed = true;
                     surfaceChanged = true;
                 }
                 try {
-                    if (win.mSurface == null) {
+                    if (winAnimator.mSurface == null) {
                         surfaceChanged = true;
                     }
-                    Surface surface = win.createSurfaceLocked();
+                    Surface surface = winAnimator.createSurfaceLocked();
                     if (surface != null) {
                         outSurface.copyFrom(surface);
-                        win.mReportDestroySurface = false;
-                        win.mSurfacePendingDestroy = false;
+                        winAnimator.mReportDestroySurface = false;
+                        winAnimator.mSurfacePendingDestroy = false;
                         if (SHOW_TRANSACTIONS) Slog.i(TAG,
                                 "  OUT SURFACE " + outSurface + ": copied");
                     } else {
@@ -2784,14 +2780,14 @@
                     sa.flags = (sa.flags&~mask) | (win.mAttrs.flags&mask);
                 }
             } else {
-                win.mEnterAnimationPending = false;
-                if (win.mSurface != null) {
+                winAnimator.mEnterAnimationPending = false;
+                if (winAnimator.mSurface != null) {
                     if (DEBUG_VISIBILITY) Slog.i(TAG, "Relayout invis " + win
                             + ": mExiting=" + win.mExiting
-                            + " mSurfacePendingDestroy=" + win.mSurfacePendingDestroy);
+                            + " mSurfacePendingDestroy=" + winAnimator.mSurfacePendingDestroy);
                     // If we are not currently running the exit animation, we
                     // need to see about starting one.
-                    if (!win.mExiting || win.mSurfacePendingDestroy) {
+                    if (!win.mExiting || winAnimator.mSurfacePendingDestroy) {
                         surfaceChanged = true;
                         // Try starting an animation; if there isn't one, we
                         // can destroy the surface right away.
@@ -2799,8 +2795,8 @@
                         if (win.mAttrs.type == TYPE_APPLICATION_STARTING) {
                             transit = WindowManagerPolicy.TRANSIT_PREVIEW_DONE;
                         }
-                        if (!win.mSurfacePendingDestroy && win.isWinVisibleLw() &&
-                              applyAnimationLocked(win, transit, false)) {
+                        if (!winAnimator.mSurfacePendingDestroy && win.isWinVisibleLw() &&
+                                winAnimator.applyAnimationLocked(transit, false)) {
                             focusMayChange = true;
                             win.mExiting = true;
                         } else if (win.mWinAnimator.isAnimating()) {
@@ -2817,26 +2813,26 @@
                             if (mInputMethodWindow == win) {
                                 mInputMethodWindow = null;
                             }
-                            win.destroySurfaceLocked();
+                            winAnimator.destroySurfaceLocked();
                         }
                     }
                 }
 
-                if (win.mSurface == null || (win.getAttrs().flags
+                if (winAnimator.mSurface == null || (win.getAttrs().flags
                         & WindowManager.LayoutParams.FLAG_KEEP_SURFACE_WHILE_ANIMATING) == 0
-                        || win.mSurfacePendingDestroy) {
+                        || winAnimator.mSurfacePendingDestroy) {
                     // We could be called from a local process, which
                     // means outSurface holds its current surface.  Ensure the
                     // surface object is cleared, but we don't necessarily want
                     // it actually destroyed at this point.
-                    win.mSurfacePendingDestroy = false;
+                    winAnimator.mSurfacePendingDestroy = false;
                     outSurface.release();
                     if (DEBUG_VISIBILITY) Slog.i(TAG, "Releasing surface in: " + win);
-                } else if (win.mSurface != null) {
+                } else if (winAnimator.mSurface != null) {
                     if (DEBUG_VISIBILITY) Slog.i(TAG,
                             "Keeping surface, will report destroy: " + win);
-                    win.mReportDestroySurface = true;
-                    outSurface.copyFrom(win.mSurface);
+                    winAnimator.mReportDestroySurface = true;
+                    outSurface.copyFrom(winAnimator.mSurface);
                 }
             }
 
@@ -2921,7 +2917,7 @@
                 if (win == null) {
                     return;
                 }
-                win.destroyDeferredSurfaceLocked();
+                win.mWinAnimator.destroyDeferredSurfaceLocked();
             }
         } finally {
             Binder.restoreCallingIdentity(origId);
@@ -2937,7 +2933,7 @@
                 if (win == null) {
                     return false;
                 }
-                return reclaimSomeSurfaceMemoryLocked(win, "from-client", false);
+                return reclaimSomeSurfaceMemoryLocked(win.mWinAnimator, "from-client", false);
             }
         } finally {
             Binder.restoreCallingIdentity(origId);
@@ -3002,90 +2998,7 @@
         return null;
     }
 
-    void applyEnterAnimationLocked(WindowState win) {
-        final int transit;
-        if (win.mEnterAnimationPending) {
-            win.mEnterAnimationPending = false;
-            transit = WindowManagerPolicy.TRANSIT_ENTER;
-        } else {
-            transit = WindowManagerPolicy.TRANSIT_SHOW;
-        }
-
-        applyAnimationLocked(win, transit, true);
-    }
-
-    /**
-     * Choose the correct animation and set it to the passed WindowState.
-     * @param win The window to add the animation to.
-     * @param transit If WindowManagerPolicy.TRANSIT_PREVIEW_DONE and the app window has been drawn
-     *      then the animation will be app_starting_exit. Any other value loads the animation from
-     *      the switch statement below.
-     * @param isEntrance The animation type the last time this was called. Used to keep from
-     *      loading the same animation twice.
-     * @return true if an animation has been loaded.
-     */
-    boolean applyAnimationLocked(WindowState win,
-            int transit, boolean isEntrance) {
-        if (win.mWinAnimator.mLocalAnimating &&
-                win.mWinAnimator.mAnimationIsEntrance == isEntrance) {
-            // If we are trying to apply an animation, but already running
-            // an animation of the same type, then just leave that one alone.
-            return true;
-        }
-
-        // Only apply an animation if the display isn't frozen.  If it is
-        // frozen, there is no reason to animate and it can cause strange
-        // artifacts when we unfreeze the display if some different animation
-        // is running.
-        if (okToDisplay()) {
-            int anim = mPolicy.selectAnimationLw(win, transit);
-            int attr = -1;
-            Animation a = null;
-            if (anim != 0) {
-                a = AnimationUtils.loadAnimation(mContext, anim);
-            } else {
-                switch (transit) {
-                    case WindowManagerPolicy.TRANSIT_ENTER:
-                        attr = com.android.internal.R.styleable.WindowAnimation_windowEnterAnimation;
-                        break;
-                    case WindowManagerPolicy.TRANSIT_EXIT:
-                        attr = com.android.internal.R.styleable.WindowAnimation_windowExitAnimation;
-                        break;
-                    case WindowManagerPolicy.TRANSIT_SHOW:
-                        attr = com.android.internal.R.styleable.WindowAnimation_windowShowAnimation;
-                        break;
-                    case WindowManagerPolicy.TRANSIT_HIDE:
-                        attr = com.android.internal.R.styleable.WindowAnimation_windowHideAnimation;
-                        break;
-                }
-                if (attr >= 0) {
-                    a = loadAnimation(win.mAttrs, attr);
-                }
-            }
-            if (DEBUG_ANIM) Slog.v(TAG, "applyAnimation: win=" + win
-                    + " anim=" + anim + " attr=0x" + Integer.toHexString(attr)
-                    + " mAnimation=" + win.mWinAnimator.mAnimation
-                    + " isEntrance=" + isEntrance);
-            if (a != null) {
-                if (DEBUG_ANIM) {
-                    RuntimeException e = null;
-                    if (!HIDE_STACK_CRAWLS) {
-                        e = new RuntimeException();
-                        e.fillInStackTrace();
-                    }
-                    Slog.v(TAG, "Loaded animation " + a + " for " + win, e);
-                }
-                win.mWinAnimator.setAnimation(a);
-                win.mWinAnimator.mAnimationIsEntrance = isEntrance;
-            }
-        } else {
-            win.mWinAnimator.clearAnimation();
-        }
-
-        return win.mWinAnimator.mAnimation != null;
-    }
-
-    private Animation loadAnimation(WindowManager.LayoutParams lp, int animAttr) {
+    Animation loadAnimation(WindowManager.LayoutParams lp, int animAttr) {
         int anim = 0;
         Context context = mContext;
         if (animAttr >= 0) {
@@ -3376,8 +3289,7 @@
                         }
 
                         if (win.isVisibleNow()) {
-                            applyAnimationLocked(win,
-                                    WindowManagerPolicy.TRANSIT_EXIT, false);
+                            win.mWinAnimator.applyAnimationLocked(WindowManagerPolicy.TRANSIT_EXIT, false);
                             changed = true;
                         }
                     }
@@ -3636,7 +3548,6 @@
      * android.os.IBinder)
      */
     boolean updateOrientationFromAppTokensLocked(boolean inTransaction) {
-        boolean changed = false;
         long ident = Binder.clearCallingIdentity();
         try {
             int req = computeForcedAppOrientationLocked();
@@ -3647,11 +3558,12 @@
                 //action like disabling/enabling sensors etc.,
                 mPolicy.setCurrentOrientationLw(req);
                 if (updateRotationUncheckedLocked(inTransaction)) {
-                    changed = true;
+                    // changed
+                    return true;
                 }
             }
 
-            return changed;
+            return false;
         } finally {
             Binder.restoreCallingIdentity(ident);
         }
@@ -4073,14 +3985,14 @@
                 if (visible) {
                     if (!win.isVisibleNow()) {
                         if (!runningAppAnimation) {
-                            applyAnimationLocked(win,
+                            win.mWinAnimator.applyAnimationLocked(
                                     WindowManagerPolicy.TRANSIT_ENTER, true);
                         }
                         changed = true;
                     }
                 } else if (win.isVisibleNow()) {
                     if (!runningAppAnimation) {
-                        applyAnimationLocked(win,
+                        win.mWinAnimator.applyAnimationLocked(
                                 WindowManagerPolicy.TRANSIT_EXIT, false);
                     }
                     changed = true;
@@ -4220,7 +4132,7 @@
                 WindowState w = wtoken.allAppWindows.get(i);
                 if (w.mAppFreezing) {
                     w.mAppFreezing = false;
-                    if (w.mSurface != null && !w.mOrientationChanging) {
+                    if (w.mWinAnimator.mSurface != null && !w.mOrientationChanging) {
                         if (DEBUG_ORIENTATION) Slog.v(TAG, "set mOrientationChanging of " + w);
                         w.mOrientationChanging = true;
                     }
@@ -4808,7 +4720,7 @@
         synchronized(mWindowMap) {
             for (int i=mWindows.size()-1; i>=0; i--) {
                 WindowState w = mWindows.get(i);
-                if (w.mSurface != null) {
+                if (w.mWinAnimator.mSurface != null) {
                     try {
                         w.mClient.closeSystemDialogs(reason);
                     } catch (RemoteException e) {
@@ -5280,7 +5192,7 @@
             boolean including = false;
             for (int i=mWindows.size()-1; i>=0; i--) {
                 WindowState ws = mWindows.get(i);
-                if (ws.mSurface == null) {
+                if (ws.mWinAnimator.mSurface == null) {
                     continue;
                 }
                 if (ws.mLayer >= aboveAppLayer) {
@@ -5306,8 +5218,8 @@
                 // window.
                 including = !ws.mIsImWindow && !ws.isFullscreen(dw, dh);
 
-                if (maxLayer < ws.mAnimLayer) {
-                    maxLayer = ws.mAnimLayer;
+                if (maxLayer < ws.mWinAnimator.mAnimLayer) {
+                    maxLayer = ws.mWinAnimator.mAnimLayer;
                 }
                 
                 // Don't include wallpaper in bounds calculation
@@ -5368,8 +5280,8 @@
                 Slog.i(TAG, "Screenshot: " + dw + "x" + dh + " from 0 to " + maxLayer);
                 for (int i=0; i<mWindows.size(); i++) {
                     Slog.i(TAG, mWindows.get(i) + ": " + mWindows.get(i).mLayer
-                            + " animLayer=" + mWindows.get(i).mAnimLayer
-                            + " surfaceLayer=" + mWindows.get(i).mSurfaceLayer);
+                            + " animLayer=" + mWindows.get(i).mWinAnimator.mAnimLayer
+                            + " surfaceLayer=" + mWindows.get(i).mWinAnimator.mSurfaceLayer);
                 }
             }
             rawss = Surface.screenshot(dw, dh, 0, maxLayer);
@@ -5594,7 +5506,7 @@
 
         for (int i=mWindows.size()-1; i>=0; i--) {
             WindowState w = mWindows.get(i);
-            if (w.mSurface != null) {
+            if (w.mWinAnimator.mSurface != null) {
                 if (DEBUG_ORIENTATION) Slog.v(TAG, "Set mOrientationChanging of " + w);
                 w.mOrientationChanging = true;
             }
@@ -7495,19 +7407,19 @@
                 w.mLayer = curLayer;
             }
             if (w.mTargetAppToken != null) {
-                w.mAnimLayer = w.mLayer + w.mTargetAppToken.animLayerAdjustment;
+                w.mWinAnimator.mAnimLayer = w.mLayer + w.mTargetAppToken.animLayerAdjustment;
             } else if (w.mAppToken != null) {
-                w.mAnimLayer = w.mLayer + w.mAppToken.animLayerAdjustment;
+                w.mWinAnimator.mAnimLayer = w.mLayer + w.mAppToken.animLayerAdjustment;
             } else {
-                w.mAnimLayer = w.mLayer;
+                w.mWinAnimator.mAnimLayer = w.mLayer;
             }
             if (w.mIsImWindow) {
-                w.mAnimLayer += mInputMethodAnimLayerAdjustment;
+                w.mWinAnimator.mAnimLayer += mInputMethodAnimLayerAdjustment;
             } else if (w.mIsWallpaper) {
-                w.mAnimLayer += mWallpaperAnimLayerAdjustment;
+                w.mWinAnimator.mAnimLayer += mWallpaperAnimLayerAdjustment;
             }
             if (DEBUG_LAYERS) Slog.v(TAG, "Assign layer " + w + ": "
-                    + w.mAnimLayer);
+                    + w.mWinAnimator.mAnimLayer);
             //System.out.println(
             //    "Assigned layer " + curLayer + " to " + w.mClient.asBinder());
         }
@@ -7944,8 +7856,8 @@
                     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 (win.mWinAnimator.mAnimLayer > layer) {
+                            layer = win.mWinAnimator.mAnimLayer;
                         }
                     }
                     if (topOpeningApp == null || layer > topOpeningLayer) {
@@ -8094,7 +8006,7 @@
             mAnimator.mForceHiding = false;
             for (int i=mWindows.size()-1; i>=0; i--) {
                 WindowState w = mWindows.get(i);
-                if (w.mSurface != null) {
+                if (w.mWinAnimator.mSurface != null) {
                     final WindowManager.LayoutParams attrs = w.mAttrs;
                     if (mPolicy.doesForceHide(w, attrs) && w.isVisibleLw()) {
                         if (DEBUG_FOCUS) Slog.i(TAG, "win=" + w + " force hides other windows");
@@ -8113,79 +8025,6 @@
         return changes;
     }
 
-    /**
-     * Extracted from {@link #performLayoutAndPlaceSurfacesLockedInner} to reduce size of method.
-     *
-     * @return bitmap indicating if another pass through layout must be made.
-     */
-    private int testWallpaperAndBackgroundLocked() {
-        int changes = 0;
-
-        if (mWindowDetachedWallpaper != mInnerFields.mDetachedWallpaper) {
-            if (DEBUG_WALLPAPER) Slog.v(TAG,
-                    "Detached wallpaper changed from " + mWindowDetachedWallpaper
-                    + " to " + mInnerFields.mDetachedWallpaper);
-            mWindowDetachedWallpaper = mInnerFields.mDetachedWallpaper;
-            mInnerFields.mWallpaperMayChange = true;
-        }
-
-        if (mAnimator.mWindowAnimationBackgroundColor != 0) {
-            // If the window that wants black is the current wallpaper
-            // target, then the black goes *below* the wallpaper so we
-            // don't cause the wallpaper to suddenly disappear.
-            WindowState target = mAnimator.mWindowAnimationBackground;
-            if (mWallpaperTarget == target
-                    || mLowerWallpaperTarget == target
-                    || mUpperWallpaperTarget == target) {
-                for (int i=0; i<mWindows.size(); i++) {
-                    WindowState w = mWindows.get(i);
-                    if (w.mIsWallpaper) {
-                        target = w;
-                        break;
-                    }
-                }
-            }
-            if (mWindowAnimationBackgroundSurface == null) {
-                mWindowAnimationBackgroundSurface = new DimSurface(mFxSession);
-            }
-            final int dw = mCurDisplayWidth;
-            final int dh = mCurDisplayHeight;
-            mWindowAnimationBackgroundSurface.show(dw, dh,
-                    target.mAnimLayer - LAYER_OFFSET_DIM,
-                    mAnimator.mWindowAnimationBackgroundColor);
-        } else if (mWindowAnimationBackgroundSurface != null) {
-            mWindowAnimationBackgroundSurface.hide();
-        }
-
-        if (mInnerFields.mWallpaperMayChange) {
-            if (DEBUG_WALLPAPER) Slog.v(TAG,
-                    "Wallpaper may change!  Adjusting");
-            mInnerFields.mAdjResult |= adjustWallpaperWindowsLocked();
-        }
-
-        if ((mInnerFields.mAdjResult&ADJUST_WALLPAPER_LAYERS_CHANGED) != 0) {
-            if (DEBUG_WALLPAPER) Slog.v(TAG,
-                    "Wallpaper layer changed: assigning layers + relayout");
-            changes |= PhoneWindowManager.FINISH_LAYOUT_REDO_LAYOUT;
-            assignLayersLocked();
-        } else if ((mInnerFields.mAdjResult&ADJUST_WALLPAPER_VISIBILITY_CHANGED) != 0) {
-            if (DEBUG_WALLPAPER) Slog.v(TAG,
-                    "Wallpaper visibility changed: relayout");
-            changes |= PhoneWindowManager.FINISH_LAYOUT_REDO_LAYOUT;
-        }
-
-        if (mFocusMayChange) {
-            mFocusMayChange = false;
-            if (updateFocusedWindowLocked(UPDATE_FOCUS_PLACING_SURFACES,
-                    false /*updateInputWindows*/)) {
-                changes |= PhoneWindowManager.FINISH_LAYOUT_REDO_ANIM;
-                mInnerFields.mAdjResult = 0;
-            }
-        }
-
-        return changes;
-    }
-
     private void updateResizingWindows(final WindowState w) {
         if (!w.mAppFreezing && w.mLayoutSeq == mLayoutSeq) {
             w.mContentInsetsChanged |=
@@ -8206,13 +8045,13 @@
             w.mLastFrame.set(w.mFrame);
             if (w.mContentInsetsChanged
                     || w.mVisibleInsetsChanged
-                    || w.mSurfaceResized
+                    || w.mWinAnimator.mSurfaceResized
                     || configChanged) {
                 if (DEBUG_RESIZE || DEBUG_ORIENTATION) {
                     Slog.v(TAG, "Resize reasons: "
                             + " contentInsetsChanged=" + w.mContentInsetsChanged
                             + " visibleInsetsChanged=" + w.mVisibleInsetsChanged
-                            + " surfaceResized=" + w.mSurfaceResized
+                            + " surfaceResized=" + w.mWinAnimator.mSurfaceResized
                             + " configChanged=" + configChanged);
                 }
 
@@ -8227,7 +8066,7 @@
                 if (w.mOrientationChanging) {
                     if (DEBUG_ORIENTATION) Slog.v(TAG,
                             "Orientation start waiting for draw in "
-                            + w + ", surface " + w.mSurface);
+                            + w + ", surface " + w.mWinAnimator.mSurface);
                     w.mDrawPending = true;
                     w.mCommitDrawPending = false;
                     w.mReadyToShow = false;
@@ -8237,15 +8076,15 @@
                 }
                 if (!mResizingWindows.contains(w)) {
                     if (DEBUG_RESIZE || DEBUG_ORIENTATION) Slog.v(TAG,
-                            "Resizing window " + w + " to " + w.mSurfaceW
-                            + "x" + w.mSurfaceH);
+                            "Resizing window " + w + " to " + w.mWinAnimator.mSurfaceW
+                            + "x" + w.mWinAnimator.mSurfaceH);
                     mResizingWindows.add(w);
                 }
             } else if (w.mOrientationChanging) {
                 if (w.isDrawnLw()) {
                     if (DEBUG_ORIENTATION) Slog.v(TAG,
                             "Orientation not waiting for draw in "
-                            + w + ", surface " + w.mSurface);
+                            + w + ", surface " + w.mWinAnimator.mSurface);
                     w.mOrientationChanging = false;
                 }
             }
@@ -8266,7 +8105,7 @@
         final int attrFlags = attrs.flags;
         final boolean canBeSeen = w.isDisplayedLw();
 
-        if (w.mSurface != null) {
+        if (w.mWinAnimator.mSurface != null) {
             if ((attrFlags&FLAG_KEEP_SCREEN_ON) != 0) {
                 mInnerFields.mHoldScreen = w.mSession;
             }
@@ -8418,7 +8257,7 @@
                 mPolicy.beginAnimationLw(dw, dh);
                 for (i = mWindows.size() - 1; i >= 0; i--) {
                     WindowState w = mWindows.get(i);
-                    if (w.mSurface != null) {
+                    if (w.mWinAnimator.mSurface != null) {
                         mPolicy.animatingWindowLw(w, w.mAttrs);
                     }
                 }
@@ -8495,9 +8334,33 @@
             mPendingLayoutChanges |= animateAwayWallpaperLocked();
             if (DEBUG_LAYOUT_REPEATS) debugLayoutRepeats("after animateAwayWallpaperLocked");
         }
+        
 
-        mPendingLayoutChanges |= testWallpaperAndBackgroundLocked();
-        if (DEBUG_LAYOUT_REPEATS) debugLayoutRepeats("after testWallpaperAndBackgroundLocked");
+        if (mInnerFields.mWallpaperMayChange) {
+            if (WindowManagerService.DEBUG_WALLPAPER) Slog.v(TAG,
+                    "Wallpaper may change!  Adjusting");
+            mInnerFields.mAdjResult |= adjustWallpaperWindowsLocked();
+        }
+
+        if ((mInnerFields.mAdjResult&ADJUST_WALLPAPER_LAYERS_CHANGED) != 0) {
+            if (DEBUG_WALLPAPER) Slog.v(TAG,
+                    "Wallpaper layer changed: assigning layers + relayout");
+            mPendingLayoutChanges |= PhoneWindowManager.FINISH_LAYOUT_REDO_LAYOUT;
+            assignLayersLocked();
+        } else if ((mInnerFields.mAdjResult&ADJUST_WALLPAPER_VISIBILITY_CHANGED) != 0) {
+            if (DEBUG_WALLPAPER) Slog.v(TAG,
+                    "Wallpaper visibility changed: relayout");
+            mPendingLayoutChanges |= PhoneWindowManager.FINISH_LAYOUT_REDO_LAYOUT;
+        }
+
+        if (mFocusMayChange) {
+            mFocusMayChange = false;
+            if (updateFocusedWindowLocked(UPDATE_FOCUS_PLACING_SURFACES,
+                    false /*updateInputWindows*/)) {
+                mPendingLayoutChanges |= PhoneWindowManager.FINISH_LAYOUT_REDO_ANIM;
+                mInnerFields.mAdjResult = 0;
+            }
+        }
 
         if (mLayoutNeeded) {
             mPendingLayoutChanges |= PhoneWindowManager.FINISH_LAYOUT_REDO_LAYOUT;
@@ -8506,9 +8369,45 @@
 
         final int N = mWindows.size();
         for (i=N-1; i>=0; i--) {
-            WindowState w = mWindows.get(i);
+            final WindowState w = mWindows.get(i);
+            final WindowStateAnimator winAnimator = w.mWinAnimator; 
             // TODO(cmautner): Can this move up to the loop at the end of try/catch above?
             updateResizingWindows(w);
+
+            // Moved from updateWindowsAndWallpaperLocked().
+            if (winAnimator.mSurface != null) {
+                // Take care of the window being ready to display.
+                if (w.commitFinishDrawingLocked(currentTime)) {
+                    if ((w.mAttrs.flags
+                            & WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER) != 0) {
+                        if (WindowManagerService.DEBUG_WALLPAPER) Slog.v(TAG,
+                                "First draw done in potential wallpaper target " + w);
+                        mInnerFields.mWallpaperMayChange = true;
+                        mPendingLayoutChanges |= WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
+                        if (WindowManagerService.DEBUG_LAYOUT_REPEATS) {
+                            debugLayoutRepeats("updateWindowsAndWallpaperLocked 1");
+                        }
+                    }
+                }
+    
+                // If the window has moved due to its containing
+                // content frame changing, then we'd like to animate
+                // it.  The checks here are ordered by what is least
+                // likely to be true first.
+                if (w.shouldAnimateMove()) {
+                    // Frame has moved, containing content frame
+                    // has also moved, and we're not currently animating...
+                    // let's do something.
+                    Animation a = AnimationUtils.loadAnimation(mContext,
+                            com.android.internal.R.anim.window_move_from_decor);
+                    winAnimator.setAnimation(a);
+                    winAnimator.mAnimDw = w.mLastFrame.left - w.mFrame.left;
+                    winAnimator.mAnimDh = w.mLastFrame.top - w.mFrame.top;
+                } else {
+                    winAnimator.mAnimDw = innerDw;
+                    winAnimator.mAnimDh = innerDh;
+                }
+            }
         }
 
         // Update animations of all applications, including those
@@ -8551,19 +8450,20 @@
                     if ((DEBUG_RESIZE || DEBUG_ORIENTATION || DEBUG_CONFIGURATION)
                             && configChanged) {
                         Slog.i(TAG, "Sending new config to window " + win + ": "
-                                + win.mSurfaceW + "x" + win.mSurfaceH
+                                + win.mWinAnimator.mSurfaceW + "x" + win.mWinAnimator.mSurfaceH
                                 + " / " + mCurConfiguration + " / 0x"
                                 + Integer.toHexString(diff));
                     }
                     win.mConfiguration = mCurConfiguration;
                     if (DEBUG_ORIENTATION && win.mDrawPending) Slog.i(
                             TAG, "Resizing " + win + " WITH DRAW PENDING"); 
-                    win.mClient.resized((int)win.mSurfaceW, (int)win.mSurfaceH,
+                    win.mClient.resized((int)win.mWinAnimator.mSurfaceW,
+                            (int)win.mWinAnimator.mSurfaceH,
                             win.mLastContentInsets, win.mLastVisibleInsets, win.mDrawPending,
                             configChanged ? win.mConfiguration : null);
                     win.mContentInsetsChanged = false;
                     win.mVisibleInsetsChanged = false;
-                    win.mSurfaceResized = false;
+                    win.mWinAnimator.mSurfaceResized = false;
                 } catch (RemoteException e) {
                     win.mOrientationChanging = false;
                 }
@@ -8585,7 +8485,7 @@
                 if (win == mWallpaperTarget) {
                     wallpaperDestroyed = true;
                 }
-                win.destroySurfaceLocked();
+                win.mWinAnimator.destroySurfaceLocked();
             } while (i > 0);
             mDestroySurface.clear();
         }
@@ -8674,17 +8574,17 @@
             mTurnOnScreen = false;
         }
 
-        if (mInnerFields.mUpdateRotation) {
+        if (mAnimator.mUpdateRotation) {
             if (DEBUG_ORIENTATION) Slog.d(TAG, "Performing post-rotate rotation");
             if (updateRotationUncheckedLocked(false)) {
                 mH.sendEmptyMessage(H.SEND_NEW_CONFIGURATION);
             } else {
-                mInnerFields.mUpdateRotation = false;
+                mAnimator.mUpdateRotation = false;
             }
         }
 
         if (mInnerFields.mOrientationChangeComplete && !mLayoutNeeded &&
-                !mInnerFields.mUpdateRotation) {
+                !mAnimator.mUpdateRotation) {
             checkDrawnWindowsLocked();
         }
 
@@ -8711,7 +8611,7 @@
                     }
                     mWaitingForDrawn.remove(pair);
                     mH.removeMessages(H.WAITING_FOR_DRAWN_TIMEOUT, pair);
-                } else if (win.mSurfaceShown) {
+                } else if (win.mWinAnimator.mSurfaceShown) {
                     // Window is now drawn (and shown).
                     try {
                         pair.second.sendResult(null);
@@ -8763,48 +8663,19 @@
 
     void scheduleAnimationLocked() {
         if (!mAnimationScheduled) {
-            mChoreographer.postAnimationCallback(mAnimationRunnable, null);
+            mChoreographer.postCallback(Choreographer.CALLBACK_ANIMATION, mAnimationRunnable, null);
             mAnimationScheduled = true;
         }
     }
 
-    /**
-     * Have the surface flinger show a surface, robustly dealing with
-     * error conditions.  In particular, if there is not enough memory
-     * to show the surface, then we will try to get rid of other surfaces
-     * in order to succeed.
-     *
-     * @return Returns true if the surface was successfully shown.
-     */
-    boolean showSurfaceRobustlyLocked(WindowState win) {
-        try {
-            if (win.mSurface != null) {
-                win.mSurfaceShown = true;
-                win.mSurface.show();
-                if (win.mTurnOnScreen) {
-                    if (DEBUG_VISIBILITY) Slog.v(TAG,
-                            "Show surface turning screen on: " + win);
-                    win.mTurnOnScreen = false;
-                    mTurnOnScreen = true;
-                }
-            }
-            return true;
-        } catch (RuntimeException e) {
-            Slog.w(TAG, "Failure showing surface " + win.mSurface + " in " + win, e);
-        }
-
-        reclaimSomeSurfaceMemoryLocked(win, "show", true);
-
-        return false;
-    }
-
-    boolean reclaimSomeSurfaceMemoryLocked(WindowState win, String operation, boolean secure) {
-        final Surface surface = win.mSurface;
+    boolean reclaimSomeSurfaceMemoryLocked(WindowStateAnimator winAnimator, String operation,
+                                           boolean secure) {
+        final Surface surface = winAnimator.mSurface;
         boolean leakedSurface = false;
         boolean killedApps = false;
 
-        EventLog.writeEvent(EventLogTags.WM_NO_SURFACE_MEMORY, win.toString(),
-                win.mSession.mPid, operation);
+        EventLog.writeEvent(EventLogTags.WM_NO_SURFACE_MEMORY, winAnimator.mWin.toString(),
+                winAnimator.mSession.mPid, operation);
 
         if (mForceRemoves == null) {
             mForceRemoves = new ArrayList<WindowState>();
@@ -8819,29 +8690,30 @@
             Slog.i(TAG, "Out of memory for surface!  Looking for leaks...");
             for (int i=0; i<N; i++) {
                 WindowState ws = mWindows.get(i);
-                if (ws.mSurface != null) {
-                    if (!mSessions.contains(ws.mSession)) {
+                WindowStateAnimator wsa = ws.mWinAnimator;
+                if (wsa.mSurface != null) {
+                    if (!mSessions.contains(wsa.mSession)) {
                         Slog.w(TAG, "LEAKED SURFACE (session doesn't exist): "
-                                + ws + " surface=" + ws.mSurface
-                                + " token=" + win.mToken
+                                + ws + " surface=" + wsa.mSurface
+                                + " token=" + ws.mToken
                                 + " pid=" + ws.mSession.mPid
                                 + " uid=" + ws.mSession.mUid);
                         if (SHOW_TRANSACTIONS) logSurface(ws, "LEAK DESTROY", null);
-                        ws.mSurface.destroy();
-                        ws.mSurfaceShown = false;
-                        ws.mSurface = null;
+                        wsa.mSurface.destroy();
+                        wsa.mSurfaceShown = false;
+                        wsa.mSurface = null;
                         mForceRemoves.add(ws);
                         i--;
                         N--;
                         leakedSurface = true;
                     } else if (ws.mAppToken != null && ws.mAppToken.clientHidden) {
                         Slog.w(TAG, "LEAKED SURFACE (app token hidden): "
-                                + ws + " surface=" + ws.mSurface
-                                + " token=" + win.mAppToken);
+                                + ws + " surface=" + wsa.mSurface
+                                + " token=" + ws.mAppToken);
                         if (SHOW_TRANSACTIONS) logSurface(ws, "LEAK DESTROY", null);
-                        ws.mSurface.destroy();
-                        ws.mSurfaceShown = false;
-                        ws.mSurface = null;
+                        wsa.mSurface.destroy();
+                        wsa.mSurfaceShown = false;
+                        wsa.mSurface = null;
                         leakedSurface = true;
                     }
                 }
@@ -8851,9 +8723,9 @@
                 Slog.w(TAG, "No leaked surfaces; killing applicatons!");
                 SparseIntArray pidCandidates = new SparseIntArray();
                 for (int i=0; i<N; i++) {
-                    WindowState ws = mWindows.get(i);
-                    if (ws.mSurface != null) {
-                        pidCandidates.append(ws.mSession.mPid, ws.mSession.mPid);
+                    WindowStateAnimator wsa = mWindows.get(i).mWinAnimator;
+                    if (wsa.mSurface != null) {
+                        pidCandidates.append(wsa.mSession.mPid, wsa.mSession.mPid);
                     }
                 }
                 if (pidCandidates.size() > 0) {
@@ -8875,15 +8747,15 @@
                 // surface and ask the app to request another one.
                 Slog.w(TAG, "Looks like we have reclaimed some memory, clearing surface for retry.");
                 if (surface != null) {
-                    if (SHOW_TRANSACTIONS || SHOW_SURFACE_ALLOC) logSurface(win,
+                    if (SHOW_TRANSACTIONS || SHOW_SURFACE_ALLOC) logSurface(winAnimator.mWin,
                             "RECOVER DESTROY", null);
                     surface.destroy();
-                    win.mSurfaceShown = false;
-                    win.mSurface = null;
+                    winAnimator.mSurfaceShown = false;
+                    winAnimator.mSurface = null;
                 }
 
                 try {
-                    win.mClient.dispatchGetNewSurface();
+                    winAnimator.mWin.mClient.dispatchGetNewSurface();
                 } catch (RemoteException e) {
                 }
             }
@@ -9051,11 +8923,11 @@
                 mAnimator.mScreenRotationAnimation.kill();
                 mAnimator.mScreenRotationAnimation = null;
             }
-            if (mAnimator.mScreenRotationAnimation == null) {
-                mAnimator.mScreenRotationAnimation = new ScreenRotationAnimation(mContext,
-                        mFxSession, inTransaction, mCurDisplayWidth, mCurDisplayHeight,
-                        mDisplay.getRotation());
-            }
+
+            mAnimator.mScreenRotationAnimation = new ScreenRotationAnimation(mContext,
+                    mFxSession, inTransaction, mCurDisplayWidth, mCurDisplayHeight,
+                    mDisplay.getRotation());
+
             if (!mAnimator.mScreenRotationAnimation.hasScreenshot()) {
                 Surface.freezeDisplay(0);
             }
@@ -9070,6 +8942,10 @@
         }
 
         if (mWaitingForConfig || mAppsFreezingScreen > 0 || mWindowsFreezingScreen) {
+            if (DEBUG_ORIENTATION) Slog.d(TAG,
+                "stopFreezingDisplayLocked: Returning mWaitingForConfig=" + mWaitingForConfig
+                + ", mAppsFreezingScreen=" + mAppsFreezingScreen
+                + ", mWindowsFreezingScreen=" + mWindowsFreezingScreen);
             return;
         }
         
@@ -9543,9 +9419,6 @@
                 pw.print("  mLowerWallpaperTarget="); pw.println(mLowerWallpaperTarget);
                 pw.print("  mUpperWallpaperTarget="); pw.println(mUpperWallpaperTarget);
             }
-            if (mWindowDetachedWallpaper != null) {
-                pw.print("  mWindowDetachedWallpaper="); pw.println(mWindowDetachedWallpaper);
-            }
             pw.print("  mLastWallpaperX="); pw.print(mLastWallpaperX);
                     pw.print(" mLastWallpaperY="); pw.println(mLastWallpaperY);
             if (mInputMethodAnimLayerAdjustment != 0 ||
@@ -9555,10 +9428,6 @@
                         pw.print("  mWallpaperAnimLayerAdjustment=");
                         pw.println(mWallpaperAnimLayerAdjustment);
             }
-            if (mWindowAnimationBackgroundSurface != null) {
-                pw.println("  mWindowAnimationBackgroundSurface:");
-                mWindowAnimationBackgroundSurface.printTo("    ", pw);
-            }
             pw.print("  mSystemBooted="); pw.print(mSystemBooted);
                     pw.print(" mDisplayEnabled="); pw.println(mDisplayEnabled);
             pw.print("  mLayoutNeeded="); pw.println(mLayoutNeeded);
@@ -9616,7 +9485,7 @@
             synchronized(mWindowMap) {
                 for (int i=mWindows.size()-1; i>=0; i--) {
                     WindowState w = mWindows.get(i);
-                    if (w.mSurfaceShown) {
+                    if (w.mWinAnimator.mSurfaceShown) {
                         windows.add(w);
                     }
                 }
@@ -9772,6 +9641,16 @@
         public void onHardKeyboardStatusChange(boolean available, boolean enabled);
     }
 
+    void notifyAnimationChangedLayout(final int pendingLayoutChanges) {
+        mPendingLayoutChanges |= pendingLayoutChanges;
+        requestTraversalLocked();
+    }
+
+    void notifyWallpaperMayChange() {
+        mInnerFields.mWallpaperMayChange = true;
+        requestTraversalLocked();
+    }
+
     void debugLayoutRepeats(final String msg) {
         if (mLayoutRepeatCount >= LAYOUT_REPEAT_THRESHOLD) {
             Slog.v(TAG, "Layouts looping: " + msg);
diff --git a/services/java/com/android/server/wm/WindowState.java b/services/java/com/android/server/wm/WindowState.java
index 615cd80..d22b17c 100644
--- a/services/java/com/android/server/wm/WindowState.java
+++ b/services/java/com/android/server/wm/WindowState.java
@@ -24,8 +24,7 @@
 import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD_DIALOG;
 import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
 
-import com.android.server.wm.WindowManagerService.H;
-
+import android.content.Context;
 import android.content.res.Configuration;
 import android.graphics.Matrix;
 import android.graphics.PixelFormat;
@@ -39,13 +38,10 @@
 import android.view.IApplicationToken;
 import android.view.IWindow;
 import android.view.InputChannel;
-import android.view.Surface;
 import android.view.View;
 import android.view.ViewTreeObserver;
 import android.view.WindowManager;
 import android.view.WindowManagerPolicy;
-import android.view.WindowManager.LayoutParams;
-import android.view.animation.Transformation;
 
 import java.io.PrintWriter;
 import java.util.ArrayList;
@@ -54,12 +50,16 @@
  * A window in the window manager.
  */
 final class WindowState implements WindowManagerPolicy.WindowState {
+    static final String TAG = "WindowState";
+    
     static final boolean DEBUG_VISIBILITY = WindowManagerService.DEBUG_VISIBILITY;
     static final boolean SHOW_TRANSACTIONS = WindowManagerService.SHOW_TRANSACTIONS;
     static final boolean SHOW_LIGHT_TRANSACTIONS = WindowManagerService.SHOW_LIGHT_TRANSACTIONS;
     static final boolean SHOW_SURFACE_ALLOC = WindowManagerService.SHOW_SURFACE_ALLOC;
 
     final WindowManagerService mService;
+    final WindowManagerPolicy mPolicy;
+    final Context mContext;
     final Session mSession;
     final IWindow mClient;
     WindowToken mToken;
@@ -83,10 +83,6 @@
     boolean mPolicyVisibility = true;
     boolean mPolicyVisibilityAfterAnim = true;
     boolean mAppFreezing;
-    Surface mSurface;
-    Surface mPendingDestroySurface;
-    boolean mReportDestroySurface;
-    boolean mSurfacePendingDestroy;
     boolean mAttachedHidden;    // is our parent window hidden?
     boolean mLastHidden;        // was this window last hidden?
     boolean mWallpaperVisible;  // for wallpaper, what was last vis report?
@@ -101,8 +97,6 @@
     int mLastRequestedHeight;
 
     int mLayer;
-    int mAnimLayer;
-    int mLastLayer;
     boolean mHaveFrame;
     boolean mObscured;
     boolean mTurnOnScreen;
@@ -119,18 +113,6 @@
     final RectF mShownFrame = new RectF();
 
     /**
-     * Set when we have changed the size of the surface, to know that
-     * we must tell them application to resize (and thus redraw itself).
-     */
-    boolean mSurfaceResized;
-
-    /**
-     * Set if the client has asked that the destroy of its surface be delayed
-     * until it explicitly says it is okay.
-     */
-    boolean mSurfaceDestroyDeferred;
-
-    /**
      * Insets that determine the actually visible area.  These are in the application's
      * coordinate space (without compatibility scale applied).
      */
@@ -178,11 +160,8 @@
     int mTouchableInsets = ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_FRAME;
 
     // Current transformation being applied.
-    boolean mHaveMatrix;
     float mGlobalScale=1;
     float mInvGlobalScale=1;
-    float mDsDx=1, mDtDx=0, mDsDy=0, mDtDy=1;
-    float mLastDsDx=1, mLastDtDx=0, mLastDsDy=0, mLastDtDy=1;
     float mHScale=1, mVScale=1;
     float mLastHScale=1, mLastVScale=1;
     final Matrix mTmpMatrix = new Matrix();
@@ -202,14 +181,6 @@
 
     boolean mContentChanged;
 
-    float mShownAlpha = 1;
-    float mAlpha = 1;
-    float mLastAlpha = 1;
-
-    // Set to true if, when the window gets displayed, it should perform
-    // an enter animation.
-    boolean mEnterAnimationPending;
-
     // If a window showing a wallpaper: the requested offset for the
     // wallpaper; if a wallpaper window: the currently applied offset.
     float mWallpaperX = -1;
@@ -274,12 +245,6 @@
     // rebuilding window list.
     boolean mRebuilding;
 
-    // For debugging, this is the last information given to the surface flinger.
-    boolean mSurfaceShown;
-    float mSurfaceX, mSurfaceY, mSurfaceW, mSurfaceH;
-    int mSurfaceLayer;
-    float mSurfaceAlpha;
-    
     // Input channel and input window handle used by the input dispatcher.
     final InputWindowHandle mInputWindowHandle;
     InputChannel mInputChannel;
@@ -289,11 +254,6 @@
     CharSequence mLastTitle;
     boolean mWasPaused;
 
-    // Used to save animation distances between the time they are calculated and when they are 
-    // used.
-    int mAnimDw;
-    int mAnimDh;
-
     final WindowStateAnimator mWinAnimator;
 
     WindowState(WindowManagerService service, Session s, IWindow c, WindowToken token,
@@ -305,12 +265,13 @@
         mToken = token;
         mAttrs.copyFrom(a);
         mViewVisibility = viewVisibility;
+        mPolicy = mService.mPolicy;
+        mContext = mService.mContext;
         DeathRecipient deathRecipient = new DeathRecipient();
-        mAlpha = a.alpha;
         mSeq = seq;
         mEnforceSizeCompat = (mAttrs.flags & FLAG_COMPATIBLE_WINDOW) != 0;
         if (WindowManagerService.localLOGV) Slog.v(
-            WindowManagerService.TAG, "Window " + this + " client=" + c.asBinder()
+            TAG, "Window " + this + " client=" + c.asBinder()
             + " token=" + token + " (" + mAttrs.token + ")");
         try {
             c.asBinder().linkToDeath(deathRecipient, 0);
@@ -333,12 +294,12 @@
                 mAttrs.type <= LAST_SUB_WINDOW)) {
             // The multiplier here is to reserve space for multiple
             // windows in the same type layer.
-            mBaseLayer = mService.mPolicy.windowTypeToLayerLw(
+            mBaseLayer = mPolicy.windowTypeToLayerLw(
                     attachedWindow.mAttrs.type) * WindowManagerService.TYPE_LAYER_MULTIPLIER
                     + WindowManagerService.TYPE_LAYER_OFFSET;
-            mSubLayer = mService.mPolicy.subWindowTypeToLayerLw(a.type);
+            mSubLayer = mPolicy.subWindowTypeToLayerLw(a.type);
             mAttachedWindow = attachedWindow;
-            if (WindowManagerService.DEBUG_ADD_REMOVE) Slog.v(WindowManagerService.TAG, "Adding " + this + " to " + mAttachedWindow);
+            if (WindowManagerService.DEBUG_ADD_REMOVE) Slog.v(TAG, "Adding " + this + " to " + mAttachedWindow);
             mAttachedWindow.mChildWindows.add(this);
             mLayoutAttached = mAttrs.type !=
                     WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG;
@@ -349,7 +310,7 @@
         } else {
             // The multiplier here is to reserve space for multiple
             // windows in the same type layer.
-            mBaseLayer = mService.mPolicy.windowTypeToLayerLw(a.type)
+            mBaseLayer = mPolicy.windowTypeToLayerLw(a.type)
                     * WindowManagerService.TYPE_LAYER_MULTIPLIER
                     + WindowManagerService.TYPE_LAYER_OFFSET;
             mSubLayer = 0;
@@ -362,6 +323,7 @@
         }
 
         mWinAnimator = new WindowStateAnimator(service, this, mAttachedWindow);
+        mWinAnimator.mAlpha = a.alpha;
 
         WindowState appWin = this;
         while (appWin.mAttachedWindow != null) {
@@ -378,7 +340,6 @@
         mRootToken = appToken;
         mAppToken = appToken.appWindowToken;
 
-        mSurface = null;
         mRequestedWidth = 0;
         mRequestedHeight = 0;
         mLastRequestedWidth = 0;
@@ -386,15 +347,13 @@
         mXOffset = 0;
         mYOffset = 0;
         mLayer = 0;
-        mAnimLayer = 0;
-        mLastLayer = 0;
         mInputWindowHandle = new InputWindowHandle(
                 mAppToken != null ? mAppToken.mInputApplicationHandle : null, this);
     }
 
     void attach() {
         if (WindowManagerService.localLOGV) Slog.v(
-            WindowManagerService.TAG, "Attaching " + this + " token=" + mToken
+            TAG, "Attaching " + this + " token=" + mToken
             + ", list=" + mToken.windows);
         mSession.windowAddedLocked();
     }
@@ -532,7 +491,7 @@
         if (WindowManagerService.localLOGV) {
             //if ("com.google.android.youtube".equals(mAttrs.packageName)
             //        && mAttrs.type == WindowManager.LayoutParams.TYPE_APPLICATION_PANEL) {
-                Slog.v(WindowManagerService.TAG, "Resolving (mRequestedWidth="
+                Slog.v(TAG, "Resolving (mRequestedWidth="
                         + mRequestedWidth + ", mRequestedheight="
                         + mRequestedHeight + ") to" + " (pw=" + pw + ", ph=" + ph
                         + "): frame=" + mFrame.toShortString()
@@ -636,225 +595,12 @@
         return mAppToken != null ? mAppToken.firstWindowDrawn : false;
     }
 
-    Surface createSurfaceLocked() {
-        if (mSurface == null) {
-            mReportDestroySurface = false;
-            mSurfacePendingDestroy = false;
-            if (WindowManagerService.DEBUG_ORIENTATION) Slog.i(WindowManagerService.TAG,
-                    "createSurface " + this + ": DRAW NOW PENDING");
-            mDrawPending = true;
-            mCommitDrawPending = false;
-            mReadyToShow = false;
-            if (mAppToken != null) {
-                mAppToken.allDrawn = false;
-            }
-
-            mService.makeWindowFreezingScreenIfNeededLocked(this);
-
-            int flags = 0;
-
-            if ((mAttrs.flags&WindowManager.LayoutParams.FLAG_SECURE) != 0) {
-                flags |= Surface.SECURE;
-            }
-            if (DEBUG_VISIBILITY) Slog.v(
-                WindowManagerService.TAG, "Creating surface in session "
-                + mSession.mSurfaceSession + " window " + this
-                + " w=" + mCompatFrame.width()
-                + " h=" + mCompatFrame.height() + " format="
-                + mAttrs.format + " flags=" + flags);
-
-            int w = mCompatFrame.width();
-            int h = mCompatFrame.height();
-            if ((mAttrs.flags & LayoutParams.FLAG_SCALED) != 0) {
-                // for a scaled surface, we always want the requested
-                // size.
-                w = mRequestedWidth;
-                h = mRequestedHeight;
-            }
-
-            // Something is wrong and SurfaceFlinger will not like this,
-            // try to revert to sane values
-            if (w <= 0) w = 1;
-            if (h <= 0) h = 1;
-
-            mSurfaceShown = false;
-            mSurfaceLayer = 0;
-            mSurfaceAlpha = 1;
-            mSurfaceX = 0;
-            mSurfaceY = 0;
-            mSurfaceW = w;
-            mSurfaceH = h;
-            try {
-                final boolean isHwAccelerated = (mAttrs.flags &
-                        WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED) != 0;
-                final int format = isHwAccelerated ? PixelFormat.TRANSLUCENT : mAttrs.format;
-                if (!PixelFormat.formatHasAlpha(mAttrs.format)) {
-                    flags |= Surface.OPAQUE;
-                }
-                mSurface = new Surface(
-                        mSession.mSurfaceSession, mSession.mPid,
-                        mAttrs.getTitle().toString(),
-                        0, w, h, format, flags);
-                if (SHOW_TRANSACTIONS || SHOW_SURFACE_ALLOC) Slog.i(WindowManagerService.TAG,
-                        "  CREATE SURFACE "
-                        + mSurface + " IN SESSION "
-                        + mSession.mSurfaceSession
-                        + ": pid=" + mSession.mPid + " format="
-                        + mAttrs.format + " flags=0x"
-                        + Integer.toHexString(flags)
-                        + " / " + this);
-            } catch (Surface.OutOfResourcesException e) {
-                Slog.w(WindowManagerService.TAG, "OutOfResourcesException creating surface");
-                mService.reclaimSomeSurfaceMemoryLocked(this, "create", true);
-                return null;
-            } catch (Exception e) {
-                Slog.e(WindowManagerService.TAG, "Exception creating surface", e);
-                return null;
-            }
-
-            if (WindowManagerService.localLOGV) Slog.v(
-                WindowManagerService.TAG, "Got surface: " + mSurface
-                + ", set left=" + mFrame.left + " top=" + mFrame.top
-                + ", animLayer=" + mAnimLayer);
-            if (SHOW_LIGHT_TRANSACTIONS) {
-                Slog.i(WindowManagerService.TAG, ">>> OPEN TRANSACTION createSurfaceLocked");
-                WindowManagerService.logSurface(this, "CREATE pos=(" + mFrame.left
-                        + "," + mFrame.top + ") (" +
-                        mCompatFrame.width() + "x" + mCompatFrame.height() + "), layer=" +
-                        mAnimLayer + " HIDE", null);
-            }
-            Surface.openTransaction();
-            try {
-                try {
-                    mSurfaceX = mFrame.left + mXOffset;
-                    mSurfaceY = mFrame.top + mYOffset;
-                    mSurface.setPosition(mSurfaceX, mSurfaceY);
-                    mSurfaceLayer = mAnimLayer;
-                    mSurface.setLayer(mAnimLayer);
-                    mSurfaceShown = false;
-                    mSurface.hide();
-                    if ((mAttrs.flags&WindowManager.LayoutParams.FLAG_DITHER) != 0) {
-                        if (SHOW_TRANSACTIONS) WindowManagerService.logSurface(this, "DITHER", null);
-                        mSurface.setFlags(Surface.SURFACE_DITHER,
-                                Surface.SURFACE_DITHER);
-                    }
-                } catch (RuntimeException e) {
-                    Slog.w(WindowManagerService.TAG, "Error creating surface in " + w, e);
-                    mService.reclaimSomeSurfaceMemoryLocked(this, "create-init", true);
-                }
-                mLastHidden = true;
-            } finally {
-                Surface.closeTransaction();
-                if (SHOW_LIGHT_TRANSACTIONS) Slog.i(WindowManagerService.TAG,
-                        "<<< CLOSE TRANSACTION createSurfaceLocked");
-            }
-            if (WindowManagerService.localLOGV) Slog.v(
-                    WindowManagerService.TAG, "Created surface " + this);
-        }
-        return mSurface;
-    }
-
-    void destroySurfaceLocked() {
-        if (mAppToken != null && this == mAppToken.startingWindow) {
-            mAppToken.startingDisplayed = false;
-        }
-
-        if (mSurface != null) {
-            mDrawPending = false;
-            mCommitDrawPending = false;
-            mReadyToShow = false;
-
-            int i = mChildWindows.size();
-            while (i > 0) {
-                i--;
-                WindowState c = mChildWindows.get(i);
-                c.mAttachedHidden = true;
-            }
-
-            if (mReportDestroySurface) {
-                mReportDestroySurface = false;
-                mSurfacePendingDestroy = true;
-                try {
-                    mClient.dispatchGetNewSurface();
-                    // We'll really destroy on the next time around.
-                    return;
-                } catch (RemoteException e) {
-                }
-            }
-
-            try {
-                if (DEBUG_VISIBILITY) {
-                    RuntimeException e = null;
-                    if (!WindowManagerService.HIDE_STACK_CRAWLS) {
-                        e = new RuntimeException();
-                        e.fillInStackTrace();
-                    }
-                    Slog.w(WindowManagerService.TAG, "Window " + this + " destroying surface "
-                            + mSurface + ", session " + mSession, e);
-                }
-                if (mSurfaceDestroyDeferred) {
-                    if (mSurface != null && mPendingDestroySurface != mSurface) {
-                        if (mPendingDestroySurface != null) {
-                            if (SHOW_TRANSACTIONS || SHOW_SURFACE_ALLOC) {
-                                RuntimeException e = null;
-                                if (!WindowManagerService.HIDE_STACK_CRAWLS) {
-                                    e = new RuntimeException();
-                                    e.fillInStackTrace();
-                                }
-                                WindowManagerService.logSurface(this, "DESTROY PENDING", e);
-                            }
-                            mPendingDestroySurface.destroy();
-                        }
-                        mPendingDestroySurface = mSurface;
-                    }
-                } else {
-                    if (SHOW_TRANSACTIONS || SHOW_SURFACE_ALLOC) {
-                        RuntimeException e = null;
-                        if (!WindowManagerService.HIDE_STACK_CRAWLS) {
-                            e = new RuntimeException();
-                            e.fillInStackTrace();
-                        }
-                        WindowManagerService.logSurface(this, "DESTROY", e);
-                    }
-                    mSurface.destroy();
-                }
-            } catch (RuntimeException e) {
-                Slog.w(WindowManagerService.TAG, "Exception thrown when destroying Window " + this
-                    + " surface " + mSurface + " session " + mSession
-                    + ": " + e.toString());
-            }
-
-            mSurfaceShown = false;
-            mSurface = null;
-        }
-    }
-
-    void destroyDeferredSurfaceLocked() {
-        try {
-            if (mPendingDestroySurface != null) {
-                if (SHOW_TRANSACTIONS || SHOW_SURFACE_ALLOC) {
-                    RuntimeException e = null;
-                    if (!WindowManagerService.HIDE_STACK_CRAWLS) {
-                        e = new RuntimeException();
-                        e.fillInStackTrace();
-                    }
-                    mService.logSurface(this, "DESTROY PENDING", e);
-                }
-                mPendingDestroySurface.destroy();
-            }
-        } catch (RuntimeException e) {
-            Slog.w(WindowManagerService.TAG, "Exception thrown when destroying Window "
-                    + this + " surface " + mPendingDestroySurface
-                    + " session " + mSession + ": " + e.toString());
-        }
-        mSurfaceDestroyDeferred = false;
-        mPendingDestroySurface = null;
-    }
-
+    // TODO(cmautner): Move to WindowStateAnimator
     boolean finishDrawingLocked() {
         if (mDrawPending) {
             if (SHOW_TRANSACTIONS || WindowManagerService.DEBUG_ORIENTATION) Slog.v(
-                WindowManagerService.TAG, "finishDrawingLocked: " + this + " in " + mSurface);
+                TAG, "finishDrawingLocked: " + this + " in "
+                        + mWinAnimator.mSurface);
             mCommitDrawPending = true;
             mDrawPending = false;
             return true;
@@ -862,6 +608,7 @@
         return false;
     }
 
+    // TODO(cmautner): Move to WindowStateAnimator
     // This must be called while inside a transaction.
     boolean commitFinishDrawingLocked(long currentTime) {
         //Slog.i(TAG, "commitFinishDrawingLocked: " + mSurface);
@@ -873,93 +620,7 @@
         final boolean starting = mAttrs.type == TYPE_APPLICATION_STARTING;
         final AppWindowToken atoken = mAppToken;
         if (atoken == null || atoken.allDrawn || starting) {
-            performShowLocked();
-        }
-        return true;
-    }
-
-    // This must be called while inside a transaction.
-    boolean performShowLocked() {
-        if (DEBUG_VISIBILITY) {
-            RuntimeException e = null;
-            if (!WindowManagerService.HIDE_STACK_CRAWLS) {
-                e = new RuntimeException();
-                e.fillInStackTrace();
-            }
-            Slog.v(WindowManagerService.TAG, "performShow on " + this
-                    + ": readyToShow=" + mReadyToShow + " readyForDisplay=" + isReadyForDisplay()
-                    + " starting=" + (mAttrs.type == TYPE_APPLICATION_STARTING), e);
-        }
-        if (mReadyToShow && isReadyForDisplay()) {
-            if (SHOW_TRANSACTIONS || WindowManagerService.DEBUG_ORIENTATION) WindowManagerService.logSurface(this,
-                    "SHOW (performShowLocked)", null);
-            if (DEBUG_VISIBILITY) Slog.v(WindowManagerService.TAG, "Showing " + this
-                    + " during animation: policyVis=" + mPolicyVisibility
-                    + " attHidden=" + mAttachedHidden
-                    + " tok.hiddenRequested="
-                    + (mAppToken != null ? mAppToken.hiddenRequested : false)
-                    + " tok.hidden="
-                    + (mAppToken != null ? mAppToken.hidden : false)
-                    + " animating=" + mWinAnimator.mAnimating
-                    + " tok animating="
-                    + (mAppToken != null ? mAppToken.animating : false));
-            if (!mService.showSurfaceRobustlyLocked(this)) {
-                return false;
-            }
-            
-            mService.enableScreenIfNeededLocked();
-
-            mService.applyEnterAnimationLocked(this);
-
-            mLastAlpha = -1;
-            mHasDrawn = true;
-            mLastHidden = false;
-            mReadyToShow = false;
-
-            int i = mChildWindows.size();
-            while (i > 0) {
-                i--;
-                WindowState c = mChildWindows.get(i);
-                if (c.mAttachedHidden) {
-                    c.mAttachedHidden = false;
-                    if (c.mSurface != null) {
-                        c.performShowLocked();
-                        // It hadn't been shown, which means layout not
-                        // performed on it, so now we want to make sure to
-                        // do a layout.  If called from within the transaction
-                        // loop, this will cause it to restart with a new
-                        // layout.
-                        mService.mLayoutNeeded = true;
-                    }
-                }
-            }
-
-            if (mAttrs.type != TYPE_APPLICATION_STARTING
-                    && mAppToken != null) {
-                mAppToken.firstWindowDrawn = true;
-
-                if (mAppToken.startingData != null) {
-                    if (WindowManagerService.DEBUG_STARTING_WINDOW ||
-                            WindowManagerService.DEBUG_ANIM) Slog.v(WindowManagerService.TAG,
-                            "Finish starting " + mToken
-                            + ": first real window is shown, no animation");
-                    // If this initial window is animating, stop it -- we
-                    // will do an animation to reveal it from behind the
-                    // starting window, so there is no need for it to also
-                    // be doing its own stuff.
-                    if (mWinAnimator.mAnimation != null) {
-                        mWinAnimator.mAnimation.cancel();
-                        mWinAnimator.mAnimation = null;
-                        // Make sure we clean up the animation.
-                        mWinAnimator.mAnimating = true;
-                    }
-                    mService.mFinishedStarting.add(mAppToken);
-                    mService.mH.sendEmptyMessage(H.FINISHED_STARTING);
-                }
-                mAppToken.updateReportedVisibilityLocked();
-            }
-        } else {
-            return false;
+            mWinAnimator.performShowLocked();
         }
         return true;
     }
@@ -981,150 +642,6 @@
         }
     }
 
-    void computeShownFrameLocked() {
-        final boolean selfTransformation = mWinAnimator.mHasLocalTransformation;
-        Transformation attachedTransformation =
-                (mAttachedWindow != null && mAttachedWindow.mWinAnimator.mHasLocalTransformation)
-                ? mAttachedWindow.mWinAnimator.mTransformation : null;
-        Transformation appTransformation =
-                (mAppToken != null && mAppToken.hasTransformation)
-                ? mAppToken.transformation : null;
-
-        // Wallpapers are animated based on the "real" window they
-        // are currently targeting.
-        if (mAttrs.type == TYPE_WALLPAPER && mService.mLowerWallpaperTarget == null
-                && mService.mWallpaperTarget != null) {
-            if (mService.mWallpaperTarget.mWinAnimator.mHasLocalTransformation &&
-                    mService.mWallpaperTarget.mWinAnimator.mAnimation != null &&
-                    !mService.mWallpaperTarget.mWinAnimator.mAnimation.getDetachWallpaper()) {
-                attachedTransformation = mService.mWallpaperTarget.mWinAnimator.mTransformation;
-                if (WindowManagerService.DEBUG_WALLPAPER && attachedTransformation != null) {
-                    Slog.v(WindowManagerService.TAG, "WP target attached xform: " + attachedTransformation);
-                }
-            }
-            if (mService.mWallpaperTarget.mAppToken != null &&
-                    mService.mWallpaperTarget.mAppToken.hasTransformation &&
-                    mService.mWallpaperTarget.mAppToken.animation != null &&
-                    !mService.mWallpaperTarget.mAppToken.animation.getDetachWallpaper()) {
-                appTransformation = mService.mWallpaperTarget.mAppToken.transformation;
-                if (WindowManagerService.DEBUG_WALLPAPER && appTransformation != null) {
-                    Slog.v(WindowManagerService.TAG, "WP target app xform: " + appTransformation);
-                }
-            }
-        }
-
-        final boolean screenAnimation = mService.mAnimator.mScreenRotationAnimation != null
-                && mService.mAnimator.mScreenRotationAnimation.isAnimating();
-        if (selfTransformation || attachedTransformation != null
-                || appTransformation != null || screenAnimation) {
-            // cache often used attributes locally
-            final Rect frame = mFrame;
-            final float tmpFloats[] = mService.mTmpFloats;
-            final Matrix tmpMatrix = mTmpMatrix;
-
-            // Compute the desired transformation.
-            if (screenAnimation) {
-                // If we are doing a screen animation, the global rotation
-                // applied to windows can result in windows that are carefully
-                // aligned with each other to slightly separate, allowing you
-                // to see what is behind them.  An unsightly mess.  This...
-                // thing...  magically makes it call good: scale each window
-                // slightly (two pixels larger in each dimension, from the
-                // window's center).
-                final float w = frame.width();
-                final float h = frame.height();
-                if (w>=1 && h>=1) {
-                    tmpMatrix.setScale(1 + 2/w, 1 + 2/h, w/2, h/2);
-                } else {
-                    tmpMatrix.reset();
-                }
-            } else {
-                tmpMatrix.reset();
-            }
-            tmpMatrix.postScale(mGlobalScale, mGlobalScale);
-            if (selfTransformation) {
-                tmpMatrix.postConcat(mWinAnimator.mTransformation.getMatrix());
-            }
-            tmpMatrix.postTranslate(frame.left + mXOffset, frame.top + mYOffset);
-            if (attachedTransformation != null) {
-                tmpMatrix.postConcat(attachedTransformation.getMatrix());
-            }
-            if (appTransformation != null) {
-                tmpMatrix.postConcat(appTransformation.getMatrix());
-            }
-            if (screenAnimation) {
-                tmpMatrix.postConcat(
-                        mService.mAnimator.mScreenRotationAnimation.getEnterTransformation().getMatrix());
-            }
-
-            // "convert" it into SurfaceFlinger's format
-            // (a 2x2 matrix + an offset)
-            // Here we must not transform the position of the surface
-            // since it is already included in the transformation.
-            //Slog.i(TAG, "Transform: " + matrix);
-
-            mHaveMatrix = true;
-            tmpMatrix.getValues(tmpFloats);
-            mDsDx = tmpFloats[Matrix.MSCALE_X];
-            mDtDx = tmpFloats[Matrix.MSKEW_Y];
-            mDsDy = tmpFloats[Matrix.MSKEW_X];
-            mDtDy = tmpFloats[Matrix.MSCALE_Y];
-            float x = tmpFloats[Matrix.MTRANS_X];
-            float y = tmpFloats[Matrix.MTRANS_Y];
-            int w = frame.width();
-            int h = frame.height();
-            mShownFrame.set(x, y, x+w, y+h);
-
-            // Now set the alpha...  but because our current hardware
-            // can't do alpha transformation on a non-opaque surface,
-            // turn it off if we are running an animation that is also
-            // transforming since it is more important to have that
-            // animation be smooth.
-            mShownAlpha = mAlpha;
-            if (!mService.mLimitedAlphaCompositing
-                    || (!PixelFormat.formatHasAlpha(mAttrs.format)
-                    || (isIdentityMatrix(mDsDx, mDtDx, mDsDy, mDtDy)
-                            && x == frame.left && y == frame.top))) {
-                //Slog.i(TAG, "Applying alpha transform");
-                if (selfTransformation) {
-                    mShownAlpha *= mWinAnimator.mTransformation.getAlpha();
-                }
-                if (attachedTransformation != null) {
-                    mShownAlpha *= attachedTransformation.getAlpha();
-                }
-                if (appTransformation != null) {
-                    mShownAlpha *= appTransformation.getAlpha();
-                }
-                if (screenAnimation) {
-                    mShownAlpha *=
-                        mService.mAnimator.mScreenRotationAnimation.getEnterTransformation().getAlpha();
-                }
-            } else {
-                //Slog.i(TAG, "Not applying alpha transform");
-            }
-
-            if (WindowManagerService.localLOGV) Slog.v(
-                WindowManagerService.TAG, "computeShownFrameLocked: Animating " + this +
-                ": " + mShownFrame +
-                ", alpha=" + mWinAnimator.mTransformation.getAlpha() + ", mShownAlpha=" + mShownAlpha);
-            return;
-        }
-
-        if (WindowManagerService.localLOGV) Slog.v(
-            WindowManagerService.TAG, "computeShownFrameLocked: " + this +
-            " not attached, mAlpha=" + mAlpha);
-        mShownFrame.set(mFrame);
-        if (mXOffset != 0 || mYOffset != 0) {
-            mShownFrame.offset(mXOffset, mYOffset);
-        }
-        mShownAlpha = mAlpha;
-        mHaveMatrix = false;
-        mDsDx = mGlobalScale;
-        mDtDx = 0;
-        mDsDy = 0;
-        mDtDy = mGlobalScale;
-    }
-
     /**
      * Is this window visible?  It is not visible if there is no
      * surface, or we are in the process of running an exit animation
@@ -1132,7 +649,7 @@
      */
     public boolean isVisibleLw() {
         final AppWindowToken atoken = mAppToken;
-        return mSurface != null && mPolicyVisibility && !mAttachedHidden
+        return mWinAnimator.mSurface != null && mPolicyVisibility && !mAttachedHidden
                 && (atoken == null || !atoken.hiddenRequested)
                 && !mExiting && !mDestroying;
     }
@@ -1153,7 +670,7 @@
         final AppWindowToken atoken = mAppToken;
         final boolean animating = atoken != null
                 ? (atoken.animation != null) : false;
-        return mSurface != null && !mDestroying && !mExiting
+        return mWinAnimator.mSurface != null && !mDestroying && !mExiting
                 && (atoken == null ? mPolicyVisibility : !atoken.hiddenRequested)
                 && ((!mAttachedHidden && mViewVisibility == View.VISIBLE
                                 && !mRootToken.hidden)
@@ -1167,7 +684,7 @@
      */
     public boolean isWinVisibleLw() {
         final AppWindowToken atoken = mAppToken;
-        return mSurface != null && mPolicyVisibility && !mAttachedHidden
+        return mWinAnimator.mSurface != null && mPolicyVisibility && !mAttachedHidden
                 && (atoken == null || !atoken.hiddenRequested || atoken.animating)
                 && !mExiting && !mDestroying;
     }
@@ -1177,7 +694,7 @@
      * the associated app token, not the pending requested hidden state.
      */
     boolean isVisibleNow() {
-        return mSurface != null && mPolicyVisibility && !mAttachedHidden
+        return mWinAnimator.mSurface != null && mPolicyVisibility && !mAttachedHidden
                 && !mRootToken.hidden && !mExiting && !mDestroying;
     }
 
@@ -1197,7 +714,7 @@
      */
     boolean isVisibleOrAdding() {
         final AppWindowToken atoken = mAppToken;
-        return ((mSurface != null && !mReportDestroySurface)
+        return ((mWinAnimator.mSurface != null && !mWinAnimator.mReportDestroySurface)
                         || (!mRelayoutCalled && mViewVisibility == View.VISIBLE))
                 && mPolicyVisibility && !mAttachedHidden
                 && (atoken == null || !atoken.hiddenRequested)
@@ -1212,11 +729,11 @@
     boolean isOnScreen() {
         final AppWindowToken atoken = mAppToken;
         if (atoken != null) {
-            return mSurface != null && mPolicyVisibility && !mDestroying
+            return mWinAnimator.mSurface != null && mPolicyVisibility && !mDestroying
                     && ((!mAttachedHidden && !atoken.hiddenRequested)
                             || mWinAnimator.mAnimation != null || atoken.animation != null);
         } else {
-            return mSurface != null && mPolicyVisibility && !mDestroying
+            return mWinAnimator.mSurface != null && mPolicyVisibility && !mDestroying
                     && (!mAttachedHidden || mWinAnimator.mAnimation != null);
         }
     }
@@ -1230,7 +747,7 @@
                 mService.mNextAppTransition != WindowManagerPolicy.TRANSIT_UNSET) {
             return false;
         }
-        return mSurface != null && mPolicyVisibility && !mDestroying
+        return mWinAnimator.mSurface != null && mPolicyVisibility && !mDestroying
                 && ((!mAttachedHidden && mViewVisibility == View.VISIBLE
                                 && !mRootToken.hidden)
                         || mWinAnimator.mAnimation != null
@@ -1264,7 +781,7 @@
      * complete UI in to.
      */
     public boolean isDrawnLw() {
-        return mSurface != null && !mDestroying
+        return mWinAnimator.mSurface != null && !mDestroying
             && !mDrawPending && !mCommitDrawPending;
     }
 
@@ -1300,11 +817,11 @@
         disposeInputChannel();
         
         if (mAttachedWindow != null) {
-            if (WindowManagerService.DEBUG_ADD_REMOVE) Slog.v(WindowManagerService.TAG, "Removing " + this + " from " + mAttachedWindow);
+            if (WindowManagerService.DEBUG_ADD_REMOVE) Slog.v(TAG, "Removing " + this + " from " + mAttachedWindow);
             mAttachedWindow.mChildWindows.remove(this);
         }
-        destroyDeferredSurfaceLocked();
-        destroySurfaceLocked();
+        mWinAnimator.destroyDeferredSurfaceLocked();
+        mWinAnimator.destroySurfaceLocked();
         mSession.windowRemovedLocked();
         try {
             mClient.asBinder().unlinkToDeath(mDeathRecipient, 0);
@@ -1339,7 +856,7 @@
             try {
                 synchronized(mService.mWindowMap) {
                     WindowState win = mService.windowForClientLocked(mSession, mClient, false);
-                    Slog.i(WindowManagerService.TAG, "WIN DEATH: " + win);
+                    Slog.i(TAG, "WIN DEATH: " + win);
                     if (win != null) {
                         mService.removeWindowLocked(mSession, win);
                     }
@@ -1371,9 +888,9 @@
             // Already showing.
             return false;
         }
-        if (DEBUG_VISIBILITY) Slog.v(WindowManagerService.TAG, "Policy visibility true: " + this);
+        if (DEBUG_VISIBILITY) Slog.v(TAG, "Policy visibility true: " + this);
         if (doAnimation) {
-            if (DEBUG_VISIBILITY) Slog.v(WindowManagerService.TAG, "doAnimation: mPolicyVisibility="
+            if (DEBUG_VISIBILITY) Slog.v(TAG, "doAnimation: mPolicyVisibility="
                     + mPolicyVisibility + " mAnimation=" + mWinAnimator.mAnimation);
             if (!mService.okToDisplay()) {
                 doAnimation = false;
@@ -1387,7 +904,7 @@
         mPolicyVisibility = true;
         mPolicyVisibilityAfterAnim = true;
         if (doAnimation) {
-            mService.applyAnimationLocked(this, WindowManagerPolicy.TRANSIT_ENTER, true);
+            mWinAnimator.applyAnimationLocked(WindowManagerPolicy.TRANSIT_ENTER, true);
         }
         if (requestAnim) {
             mService.scheduleAnimationLocked();
@@ -1412,7 +929,7 @@
             return false;
         }
         if (doAnimation) {
-            mService.applyAnimationLocked(this, WindowManagerPolicy.TRANSIT_EXIT, false);
+            mWinAnimator.applyAnimationLocked(WindowManagerPolicy.TRANSIT_EXIT, false);
             if (mWinAnimator.mAnimation == null) {
                 doAnimation = false;
             }
@@ -1420,7 +937,7 @@
         if (doAnimation) {
             mPolicyVisibilityAfterAnim = false;
         } else {
-            if (DEBUG_VISIBILITY) Slog.v(WindowManagerService.TAG, "Policy visibility false: " + this);
+            if (DEBUG_VISIBILITY) Slog.v(TAG, "Policy visibility false: " + this);
             mPolicyVisibilityAfterAnim = false;
             mPolicyVisibility = false;
             // Window is no longer visible -- make sure if we were waiting
@@ -1492,24 +1009,8 @@
                     pw.print(" mAnimLayer="); pw.print(mLayer); pw.print("+");
                     pw.print((mTargetAppToken != null ? mTargetAppToken.animLayerAdjustment
                           : (mAppToken != null ? mAppToken.animLayerAdjustment : 0)));
-                    pw.print("="); pw.print(mAnimLayer);
-                    pw.print(" mLastLayer="); pw.println(mLastLayer);
-        }
-        if (mSurface != null) {
-            if (dumpAll) {
-                pw.print(prefix); pw.print("mSurface="); pw.println(mSurface);
-            }
-            pw.print(prefix); pw.print("Surface: shown="); pw.print(mSurfaceShown);
-                    pw.print(" layer="); pw.print(mSurfaceLayer);
-                    pw.print(" alpha="); pw.print(mSurfaceAlpha);
-                    pw.print(" rect=("); pw.print(mSurfaceX);
-                    pw.print(","); pw.print(mSurfaceY);
-                    pw.print(") "); pw.print(mSurfaceW);
-                    pw.print(" x "); pw.println(mSurfaceH);
-        }
-        if (mPendingDestroySurface != null) {
-            pw.print(prefix); pw.print("mPendingDestroySurface=");
-                    pw.println(mPendingDestroySurface);
+                    pw.print("="); pw.print(mWinAnimator.mAnimLayer);
+                    pw.print(" mLastLayer="); pw.println(mWinAnimator.mLastLayer);
         }
         if (dumpAll) {
             pw.print(prefix); pw.print("mToken="); pw.println(mToken);
@@ -1540,10 +1041,6 @@
             pw.print(prefix); pw.print("mRelayoutCalled="); pw.print(mRelayoutCalled);
                     pw.print(" mLayoutNeeded="); pw.println(mLayoutNeeded);
         }
-        if (mSurfaceResized || mSurfaceDestroyDeferred) {
-            pw.print(prefix); pw.print("mSurfaceResized="); pw.print(mSurfaceResized);
-                    pw.print(" mSurfaceDestroyDeferred="); pw.println(mSurfaceDestroyDeferred);
-        }
         if (mXOffset != 0 || mYOffset != 0) {
             pw.print(prefix); pw.print("Offsets x="); pw.print(mXOffset);
                     pw.print(" y="); pw.println(mYOffset);
@@ -1589,18 +1086,6 @@
                     pw.println();
         }
         mWinAnimator.dump(pw, prefix, dumpAll);
-        if (mShownAlpha != 1 || mAlpha != 1 || mLastAlpha != 1) {
-            pw.print(prefix); pw.print("mShownAlpha="); pw.print(mShownAlpha);
-                    pw.print(" mAlpha="); pw.print(mAlpha);
-                    pw.print(" mLastAlpha="); pw.println(mLastAlpha);
-        }
-        if (mHaveMatrix || mGlobalScale != 1) {
-            pw.print(prefix); pw.print("mGlobalScale="); pw.print(mGlobalScale);
-                    pw.print(" mDsDx="); pw.print(mDsDx);
-                    pw.print(" mDtDx="); pw.print(mDtDx);
-                    pw.print(" mDsDy="); pw.print(mDsDy);
-                    pw.print(" mDtDy="); pw.println(mDtDy);
-        }
         if (dumpAll) {
             pw.print(prefix); pw.print("mDrawPending="); pw.print(mDrawPending);
                     pw.print(" mCommitDrawPending="); pw.print(mCommitDrawPending);
diff --git a/services/java/com/android/server/wm/WindowStateAnimator.java b/services/java/com/android/server/wm/WindowStateAnimator.java
index d86d411..d1539ba 100644
--- a/services/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/java/com/android/server/wm/WindowStateAnimator.java
@@ -2,10 +2,21 @@
 
 package com.android.server.wm;
 
+import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
+import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
+
+import android.content.Context;
+import android.graphics.Matrix;
+import android.graphics.PixelFormat;
+import android.graphics.Rect;
+import android.os.RemoteException;
 import android.util.Slog;
+import android.view.Surface;
 import android.view.WindowManager;
 import android.view.WindowManagerPolicy;
+import android.view.WindowManager.LayoutParams;
 import android.view.animation.Animation;
+import android.view.animation.AnimationUtils;
 import android.view.animation.Transformation;
 
 import com.android.server.wm.WindowManagerService.H;
@@ -13,14 +24,27 @@
 import java.io.PrintWriter;
 
 /**
- * @author cmautner@google.com (Craig Mautner)
- *
- */
+ * Keep track of animations and surface operations for a single WindowState.
+ **/
 class WindowStateAnimator {
+    static final boolean DEBUG_VISIBILITY = WindowManagerService.DEBUG_VISIBILITY;
+    static final boolean DEBUG_ANIM = WindowManagerService.DEBUG_ANIM;
+    static final boolean DEBUG_LAYERS = WindowManagerService.DEBUG_LAYERS;
+    static final boolean DEBUG_STARTING_WINDOW = WindowManagerService.DEBUG_STARTING_WINDOW;
+    static final boolean SHOW_TRANSACTIONS = WindowManagerService.SHOW_TRANSACTIONS;
+    static final boolean SHOW_LIGHT_TRANSACTIONS = WindowManagerService.SHOW_LIGHT_TRANSACTIONS;
+    static final boolean SHOW_SURFACE_ALLOC = WindowManagerService.SHOW_SURFACE_ALLOC;
+    static final boolean localLOGV = WindowManagerService.localLOGV;
+
+    static final String TAG = "WindowStateAnimator";
 
     final WindowManagerService mService;
     final WindowState mWin;
     final WindowState mAttachedWindow;
+    final WindowAnimator mAnimator;
+    final Session mSession;
+    final WindowManagerPolicy mPolicy;
+    final Context mContext;
 
     // Currently running animation.
     boolean mAnimating;
@@ -31,17 +55,63 @@
     boolean mHasLocalTransformation;
     final Transformation mTransformation = new Transformation();
     boolean mWasAnimating;      // Were we animating going into the most recent animation step?
+    int mAnimLayer;
+    int mLastLayer;
+
+    Surface mSurface;
+    Surface mPendingDestroySurface;
+    boolean mReportDestroySurface;
+    boolean mSurfacePendingDestroy;
+
+    /**
+     * Set when we have changed the size of the surface, to know that
+     * we must tell them application to resize (and thus redraw itself).
+     */
+    boolean mSurfaceResized;
+
+    /**
+     * Set if the client has asked that the destroy of its surface be delayed
+     * until it explicitly says it is okay.
+     */
+    boolean mSurfaceDestroyDeferred;
+
+    float mShownAlpha = 1;
+    float mAlpha = 1;
+    float mLastAlpha = 1;
+
+    // Used to save animation distances between the time they are calculated and when they are
+    // used.
+    int mAnimDw;
+    int mAnimDh;
+    float mDsDx=1, mDtDx=0, mDsDy=0, mDtDy=1;
+    float mLastDsDx=1, mLastDtDx=0, mLastDsDy=0, mLastDtDy=1;
+
+    boolean mHaveMatrix;
+
+    // For debugging, this is the last information given to the surface flinger.
+    boolean mSurfaceShown;
+    float mSurfaceX, mSurfaceY, mSurfaceW, mSurfaceH;
+    int mSurfaceLayer;
+    float mSurfaceAlpha;
+
+    // Set to true if, when the window gets displayed, it should perform
+    // an enter animation.
+    boolean mEnterAnimationPending;
 
     public WindowStateAnimator(final WindowManagerService service, final WindowState win,
                                final WindowState attachedWindow) {
         mService = service;
         mWin = win;
         mAttachedWindow = attachedWindow;
+        mAnimator = mService.mAnimator;
+        mSession = win.mSession;
+        mPolicy = mService.mPolicy;
+        mContext = mService.mContext;
     }
 
     public void setAnimation(Animation anim) {
-        if (WindowManagerService.localLOGV) Slog.v(
-            WindowManagerService.TAG, "Setting animation in " + this + ": " + anim);
+        if (localLOGV) Slog.v(
+            TAG, "Setting animation in " + this + ": " + anim);
         mAnimating = false;
         mLocalAnimating = false;
         mAnimation = anim;
@@ -78,14 +148,12 @@
         return mAnimation != null;
     }
 
-    // TODO: Fix and call finishExit() instead of cancelExitAnimationForNextAnimationLocked()
-    // for avoiding the code duplication.
     void cancelExitAnimationForNextAnimationLocked() {
         if (!mWin.mExiting) return;
         if (mAnimation != null) {
             mAnimation.cancel();
             mAnimation = null;
-            mWin.destroySurfaceLocked();
+            destroySurfaceLocked();
         }
         mWin.mExiting = false;
     }
@@ -96,8 +164,8 @@
         }
         mTransformation.clear();
         final boolean more = mAnimation.getTransformation(currentTime, mTransformation);
-        if (WindowManagerService.DEBUG_ANIM) Slog.v(
-            WindowManagerService.TAG, "Stepped animation in " + this +
+        if (DEBUG_ANIM) Slog.v(
+            TAG, "Stepped animation in " + this +
             ": more=" + more + ", xform=" + mTransformation);
         return more;
     }
@@ -115,14 +183,14 @@
                 mHasTransformation = true;
                 mHasLocalTransformation = true;
                 if (!mLocalAnimating) {
-                    if (WindowManagerService.DEBUG_ANIM) Slog.v(
-                        WindowManagerService.TAG, "Starting animation in " + this +
+                    if (DEBUG_ANIM) Slog.v(
+                        TAG, "Starting animation in " + this +
                         " @ " + currentTime + ": ww=" + mWin.mFrame.width() +
                         " wh=" + mWin.mFrame.height() +
-                        " dw=" + mWin.mAnimDw + " dh=" + mWin.mAnimDh +
+                        " dw=" + mAnimDw + " dh=" + mAnimDh +
                         " scale=" + mService.mWindowAnimationScale);
-                    mAnimation.initialize(mWin.mFrame.width(), mWin.mFrame.height(), mWin.mAnimDw,
-                        mWin.mAnimDh);
+                    mAnimation.initialize(mWin.mFrame.width(), mWin.mFrame.height(),
+                            mAnimDw, mAnimDh);
                     mAnimation.setStartTime(currentTime);
                     mLocalAnimating = true;
                     mAnimating = true;
@@ -132,8 +200,8 @@
                         return true;
                     }
                 }
-                if (WindowManagerService.DEBUG_ANIM) Slog.v(
-                    WindowManagerService.TAG, "Finished animation in " + this +
+                if (DEBUG_ANIM) Slog.v(
+                    TAG, "Finished animation in " + this +
                     " @ " + currentTime);
                 //WindowManagerService.this.dump();
             }
@@ -171,8 +239,9 @@
             return false;
         }
 
-        if (WindowManagerService.DEBUG_ANIM) Slog.v(
-            WindowManagerService.TAG, "Animation done in " + this + ": exiting=" + mWin.mExiting
+        // Done animating, clean up.
+        if (DEBUG_ANIM) Slog.v(
+            TAG, "Animation done in " + this + ": exiting=" + mWin.mExiting
             + ", reportedVisible="
             + (mWin.mAppToken != null ? mWin.mAppToken.reportedVisible : false));
 
@@ -182,22 +251,22 @@
             mAnimation.cancel();
             mAnimation = null;
         }
-        if (mService.mWindowDetachedWallpaper == mWin) {
-            mService.mWindowDetachedWallpaper = null;
+        if (mAnimator.mWindowDetachedWallpaper == mWin) {
+            mAnimator.mWindowDetachedWallpaper = null;
         }
-        mWin.mAnimLayer = mWin.mLayer;
+        mAnimLayer = mWin.mLayer;
         if (mWin.mIsImWindow) {
-            mWin.mAnimLayer += mService.mInputMethodAnimLayerAdjustment;
+            mAnimLayer += mService.mInputMethodAnimLayerAdjustment;
         } else if (mWin.mIsWallpaper) {
-            mWin.mAnimLayer += mService.mWallpaperAnimLayerAdjustment;
+            mAnimLayer += mService.mWallpaperAnimLayerAdjustment;
         }
-        if (WindowManagerService.DEBUG_LAYERS) Slog.v(WindowManagerService.TAG, "Stepping win " + this
-                + " anim layer: " + mWin.mAnimLayer);
+        if (DEBUG_LAYERS) Slog.v(TAG, "Stepping win " + this
+                + " anim layer: " + mAnimLayer);
         mHasTransformation = false;
         mHasLocalTransformation = false;
         if (mWin.mPolicyVisibility != mWin.mPolicyVisibilityAfterAnim) {
             if (WindowState.DEBUG_VISIBILITY) {
-                Slog.v(WindowManagerService.TAG, "Policy visibility changing after anim in " + this + ": "
+                Slog.v(TAG, "Policy visibility changing after anim in " + this + ": "
                         + mWin.mPolicyVisibilityAfterAnim);
             }
             mWin.mPolicyVisibility = mWin.mPolicyVisibilityAfterAnim;
@@ -218,7 +287,7 @@
                 && mWin.mAppToken != null
                 && mWin.mAppToken.firstWindowDrawn
                 && mWin.mAppToken.startingData != null) {
-            if (WindowManagerService.DEBUG_STARTING_WINDOW) Slog.v(WindowManagerService.TAG, "Finish starting "
+            if (DEBUG_STARTING_WINDOW) Slog.v(TAG, "Finish starting "
                     + mWin.mToken + ": first real window done animating");
             mService.mFinishedStarting.add(mWin.mAppToken);
             mService.mH.sendEmptyMessage(H.FINISHED_STARTING);
@@ -237,7 +306,7 @@
 
     void finishExit() {
         if (WindowManagerService.DEBUG_ANIM) Slog.v(
-                WindowManagerService.TAG, "finishExit in " + this
+                TAG, "finishExit in " + this
                 + ": exiting=" + mWin.mExiting
                 + " remove=" + mWin.mRemoveOnExit
                 + " windowAnimating=" + isWindowAnimating());
@@ -256,18 +325,18 @@
         }
 
         if (WindowManagerService.localLOGV) Slog.v(
-                WindowManagerService.TAG, "Exit animation finished in " + this
+                TAG, "Exit animation finished in " + this
                 + ": remove=" + mWin.mRemoveOnExit);
-        if (mWin.mSurface != null) {
+        if (mSurface != null) {
             mService.mDestroySurface.add(mWin);
             mWin.mDestroying = true;
             if (WindowState.SHOW_TRANSACTIONS) WindowManagerService.logSurface(
                 mWin, "HIDE (finishExit)", null);
-            mWin.mSurfaceShown = false;
+            mSurfaceShown = false;
             try {
-                mWin.mSurface.hide();
+                mSurface.hide();
             } catch (RuntimeException e) {
-                Slog.w(WindowManagerService.TAG, "Error hiding surface in " + this, e);
+                Slog.w(TAG, "Error hiding surface in " + this, e);
             }
             mWin.mLastHidden = true;
         }
@@ -278,6 +347,743 @@
         }
     }
 
+    Surface createSurfaceLocked() {
+        if (mSurface == null) {
+            mReportDestroySurface = false;
+            mSurfacePendingDestroy = false;
+            if (WindowManagerService.DEBUG_ORIENTATION) Slog.i(TAG,
+                    "createSurface " + this + ": DRAW NOW PENDING");
+            mWin.mDrawPending = true;
+            mWin.mCommitDrawPending = false;
+            mWin.mReadyToShow = false;
+            if (mWin.mAppToken != null) {
+                mWin.mAppToken.allDrawn = false;
+            }
+
+            mService.makeWindowFreezingScreenIfNeededLocked(mWin);
+
+            int flags = 0;
+            final WindowManager.LayoutParams attrs = mWin.mAttrs;
+
+            if ((attrs.flags&WindowManager.LayoutParams.FLAG_SECURE) != 0) {
+                flags |= Surface.SECURE;
+            }
+            if (WindowState.DEBUG_VISIBILITY) Slog.v(
+                TAG, "Creating surface in session "
+                + mSession.mSurfaceSession + " window " + this
+                + " w=" + mWin.mCompatFrame.width()
+                + " h=" + mWin.mCompatFrame.height() + " format="
+                + attrs.format + " flags=" + flags);
+
+            int w = mWin.mCompatFrame.width();
+            int h = mWin.mCompatFrame.height();
+            if ((attrs.flags & LayoutParams.FLAG_SCALED) != 0) {
+                // for a scaled surface, we always want the requested
+                // size.
+                w = mWin.mRequestedWidth;
+                h = mWin.mRequestedHeight;
+            }
+
+            // Something is wrong and SurfaceFlinger will not like this,
+            // try to revert to sane values
+            if (w <= 0) w = 1;
+            if (h <= 0) h = 1;
+
+            mSurfaceShown = false;
+            mSurfaceLayer = 0;
+            mSurfaceAlpha = 1;
+            mSurfaceX = 0;
+            mSurfaceY = 0;
+            mSurfaceW = w;
+            mSurfaceH = h;
+            try {
+                final boolean isHwAccelerated = (attrs.flags &
+                        WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED) != 0;
+                final int format = isHwAccelerated ? PixelFormat.TRANSLUCENT : attrs.format;
+                if (!PixelFormat.formatHasAlpha(attrs.format)) {
+                    flags |= Surface.OPAQUE;
+                }
+                mSurface = new Surface(
+                        mSession.mSurfaceSession, mSession.mPid,
+                        attrs.getTitle().toString(),
+                        0, w, h, format, flags);
+                if (SHOW_TRANSACTIONS || SHOW_SURFACE_ALLOC) Slog.i(TAG,
+                        "  CREATE SURFACE "
+                        + mSurface + " IN SESSION "
+                        + mSession.mSurfaceSession
+                        + ": pid=" + mSession.mPid + " format="
+                        + attrs.format + " flags=0x"
+                        + Integer.toHexString(flags)
+                        + " / " + this);
+            } catch (Surface.OutOfResourcesException e) {
+                Slog.w(TAG, "OutOfResourcesException creating surface");
+                mService.reclaimSomeSurfaceMemoryLocked(this, "create", true);
+                return null;
+            } catch (Exception e) {
+                Slog.e(TAG, "Exception creating surface", e);
+                return null;
+            }
+
+            if (WindowManagerService.localLOGV) Slog.v(
+                TAG, "Got surface: " + mSurface
+                + ", set left=" + mWin.mFrame.left + " top=" + mWin.mFrame.top
+                + ", animLayer=" + mAnimLayer);
+            if (SHOW_LIGHT_TRANSACTIONS) {
+                Slog.i(TAG, ">>> OPEN TRANSACTION createSurfaceLocked");
+                WindowManagerService.logSurface(mWin, "CREATE pos=("
+                        + mWin.mFrame.left + "," + mWin.mFrame.top + ") ("
+                        + mWin.mCompatFrame.width() + "x" + mWin.mCompatFrame.height()
+                        + "), layer=" + mAnimLayer + " HIDE", null);
+            }
+            Surface.openTransaction();
+            try {
+                try {
+                    mSurfaceX = mWin.mFrame.left + mWin.mXOffset;
+                    mSurfaceY = mWin.mFrame.top + mWin.mYOffset;
+                    mSurface.setPosition(mSurfaceX, mSurfaceY);
+                    mSurfaceLayer = mAnimLayer;
+                    mSurface.setLayer(mAnimLayer);
+                    mSurfaceShown = false;
+                    mSurface.hide();
+                    if ((mWin.mAttrs.flags&WindowManager.LayoutParams.FLAG_DITHER) != 0) {
+                        if (SHOW_TRANSACTIONS) WindowManagerService.logSurface(mWin, "DITHER", null);
+                        mSurface.setFlags(Surface.SURFACE_DITHER, Surface.SURFACE_DITHER);
+                    }
+                } catch (RuntimeException e) {
+                    Slog.w(TAG, "Error creating surface in " + w, e);
+                    mService.reclaimSomeSurfaceMemoryLocked(this, "create-init", true);
+                }
+                mWin.mLastHidden = true;
+            } finally {
+                Surface.closeTransaction();
+                if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG,
+                        "<<< CLOSE TRANSACTION createSurfaceLocked");
+            }
+            if (WindowManagerService.localLOGV) Slog.v(
+                    TAG, "Created surface " + this);
+        }
+        return mSurface;
+    }
+
+    void destroySurfaceLocked() {
+        if (mWin.mAppToken != null && mWin == mWin.mAppToken.startingWindow) {
+            mWin.mAppToken.startingDisplayed = false;
+        }
+
+        if (mSurface != null) {
+            mWin.mDrawPending = false;
+            mWin.mCommitDrawPending = false;
+            mWin.mReadyToShow = false;
+
+            int i = mWin.mChildWindows.size();
+            while (i > 0) {
+                i--;
+                WindowState c = mWin.mChildWindows.get(i);
+                c.mAttachedHidden = true;
+            }
+
+            if (mReportDestroySurface) {
+                mReportDestroySurface = false;
+                mSurfacePendingDestroy = true;
+                try {
+                    mWin.mClient.dispatchGetNewSurface();
+                    // We'll really destroy on the next time around.
+                    return;
+                } catch (RemoteException e) {
+                }
+            }
+
+            try {
+                if (DEBUG_VISIBILITY) {
+                    RuntimeException e = null;
+                    if (!WindowManagerService.HIDE_STACK_CRAWLS) {
+                        e = new RuntimeException();
+                        e.fillInStackTrace();
+                    }
+                    Slog.w(TAG, "Window " + this + " destroying surface "
+                            + mSurface + ", session " + mSession, e);
+                }
+                if (mSurfaceDestroyDeferred) {
+                    if (mSurface != null && mPendingDestroySurface != mSurface) {
+                        if (mPendingDestroySurface != null) {
+                            if (SHOW_TRANSACTIONS || SHOW_SURFACE_ALLOC) {
+                                RuntimeException e = null;
+                                if (!WindowManagerService.HIDE_STACK_CRAWLS) {
+                                    e = new RuntimeException();
+                                    e.fillInStackTrace();
+                                }
+                                WindowManagerService.logSurface(mWin, "DESTROY PENDING", e);
+                            }
+                            mPendingDestroySurface.destroy();
+                        }
+                        mPendingDestroySurface = mSurface;
+                    }
+                } else {
+                    if (SHOW_TRANSACTIONS || SHOW_SURFACE_ALLOC) {
+                        RuntimeException e = null;
+                        if (!WindowManagerService.HIDE_STACK_CRAWLS) {
+                            e = new RuntimeException();
+                            e.fillInStackTrace();
+                        }
+                        WindowManagerService.logSurface(mWin, "DESTROY", e);
+                    }
+                    mSurface.destroy();
+                }
+            } catch (RuntimeException e) {
+                Slog.w(TAG, "Exception thrown when destroying Window " + this
+                    + " surface " + mSurface + " session " + mSession
+                    + ": " + e.toString());
+            }
+
+            mSurfaceShown = false;
+            mSurface = null;
+        }
+    }
+
+    void destroyDeferredSurfaceLocked() {
+        try {
+            if (mPendingDestroySurface != null) {
+                if (SHOW_TRANSACTIONS || SHOW_SURFACE_ALLOC) {
+                    RuntimeException e = null;
+                    if (!WindowManagerService.HIDE_STACK_CRAWLS) {
+                        e = new RuntimeException();
+                        e.fillInStackTrace();
+                    }
+                    WindowManagerService.logSurface(mWin, "DESTROY PENDING", e);
+                }
+                mPendingDestroySurface.destroy();
+            }
+        } catch (RuntimeException e) {
+            Slog.w(TAG, "Exception thrown when destroying Window "
+                    + this + " surface " + mPendingDestroySurface
+                    + " session " + mSession + ": " + e.toString());
+        }
+        mSurfaceDestroyDeferred = false;
+        mPendingDestroySurface = null;
+    }
+
+    void computeShownFrameLocked() {
+        final boolean selfTransformation = mHasLocalTransformation;
+        Transformation attachedTransformation =
+                (mAttachedWindow != null && mAttachedWindow.mWinAnimator.mHasLocalTransformation)
+                ? mAttachedWindow.mWinAnimator.mTransformation : null;
+        Transformation appTransformation =
+                (mWin.mAppToken != null && mWin.mAppToken.hasTransformation)
+                ? mWin.mAppToken.transformation : null;
+
+        // Wallpapers are animated based on the "real" window they
+        // are currently targeting.
+        if (mWin.mAttrs.type == TYPE_WALLPAPER && mService.mLowerWallpaperTarget == null
+                && mService.mWallpaperTarget != null) {
+            if (mService.mWallpaperTarget.mWinAnimator.mHasLocalTransformation &&
+                    mService.mWallpaperTarget.mWinAnimator.mAnimation != null &&
+                    !mService.mWallpaperTarget.mWinAnimator.mAnimation.getDetachWallpaper()) {
+                attachedTransformation = mService.mWallpaperTarget.mWinAnimator.mTransformation;
+                if (WindowManagerService.DEBUG_WALLPAPER && attachedTransformation != null) {
+                    Slog.v(TAG, "WP target attached xform: " + attachedTransformation);
+                }
+            }
+            if (mService.mWallpaperTarget.mAppToken != null &&
+                    mService.mWallpaperTarget.mAppToken.hasTransformation &&
+                    mService.mWallpaperTarget.mAppToken.animation != null &&
+                    !mService.mWallpaperTarget.mAppToken.animation.getDetachWallpaper()) {
+                appTransformation = mService.mWallpaperTarget.mAppToken.transformation;
+                if (WindowManagerService.DEBUG_WALLPAPER && appTransformation != null) {
+                    Slog.v(TAG, "WP target app xform: " + appTransformation);
+                }
+            }
+        }
+
+        final boolean screenAnimation = mService.mAnimator.mScreenRotationAnimation != null
+                && mService.mAnimator.mScreenRotationAnimation.isAnimating();
+        if (selfTransformation || attachedTransformation != null
+                || appTransformation != null || screenAnimation) {
+            // cache often used attributes locally
+            final Rect frame = mWin.mFrame;
+            final float tmpFloats[] = mService.mTmpFloats;
+            final Matrix tmpMatrix = mWin.mTmpMatrix;
+
+            // Compute the desired transformation.
+            if (screenAnimation) {
+                // If we are doing a screen animation, the global rotation
+                // applied to windows can result in windows that are carefully
+                // aligned with each other to slightly separate, allowing you
+                // to see what is behind them.  An unsightly mess.  This...
+                // thing...  magically makes it call good: scale each window
+                // slightly (two pixels larger in each dimension, from the
+                // window's center).
+                final float w = frame.width();
+                final float h = frame.height();
+                if (w>=1 && h>=1) {
+                    tmpMatrix.setScale(1 + 2/w, 1 + 2/h, w/2, h/2);
+                } else {
+                    tmpMatrix.reset();
+                }
+            } else {
+                tmpMatrix.reset();
+            }
+            tmpMatrix.postScale(mWin.mGlobalScale, mWin.mGlobalScale);
+            if (selfTransformation) {
+                tmpMatrix.postConcat(mTransformation.getMatrix());
+            }
+            tmpMatrix.postTranslate(frame.left + mWin.mXOffset, frame.top + mWin.mYOffset);
+            if (attachedTransformation != null) {
+                tmpMatrix.postConcat(attachedTransformation.getMatrix());
+            }
+            if (appTransformation != null) {
+                tmpMatrix.postConcat(appTransformation.getMatrix());
+            }
+            if (screenAnimation) {
+                tmpMatrix.postConcat(
+                        mService.mAnimator.mScreenRotationAnimation.getEnterTransformation().getMatrix());
+            }
+
+            // "convert" it into SurfaceFlinger's format
+            // (a 2x2 matrix + an offset)
+            // Here we must not transform the position of the surface
+            // since it is already included in the transformation.
+            //Slog.i(TAG, "Transform: " + matrix);
+
+            mHaveMatrix = true;
+            tmpMatrix.getValues(tmpFloats);
+            mDsDx = tmpFloats[Matrix.MSCALE_X];
+            mDtDx = tmpFloats[Matrix.MSKEW_Y];
+            mDsDy = tmpFloats[Matrix.MSKEW_X];
+            mDtDy = tmpFloats[Matrix.MSCALE_Y];
+            float x = tmpFloats[Matrix.MTRANS_X];
+            float y = tmpFloats[Matrix.MTRANS_Y];
+            int w = frame.width();
+            int h = frame.height();
+            mWin.mShownFrame.set(x, y, x+w, y+h);
+
+            // Now set the alpha...  but because our current hardware
+            // can't do alpha transformation on a non-opaque surface,
+            // turn it off if we are running an animation that is also
+            // transforming since it is more important to have that
+            // animation be smooth.
+            mShownAlpha = mAlpha;
+            if (!mService.mLimitedAlphaCompositing
+                    || (!PixelFormat.formatHasAlpha(mWin.mAttrs.format)
+                    || (mWin.isIdentityMatrix(mDsDx, mDtDx, mDsDy, mDtDy)
+                            && x == frame.left && y == frame.top))) {
+                //Slog.i(TAG, "Applying alpha transform");
+                if (selfTransformation) {
+                    mShownAlpha *= mTransformation.getAlpha();
+                }
+                if (attachedTransformation != null) {
+                    mShownAlpha *= attachedTransformation.getAlpha();
+                }
+                if (appTransformation != null) {
+                    mShownAlpha *= appTransformation.getAlpha();
+                }
+                if (screenAnimation) {
+                    mShownAlpha *=
+                        mService.mAnimator.mScreenRotationAnimation.getEnterTransformation().getAlpha();
+                }
+            } else {
+                //Slog.i(TAG, "Not applying alpha transform");
+            }
+
+            if (WindowManagerService.localLOGV) Slog.v(
+                TAG, "computeShownFrameLocked: Animating " + this +
+                ": " + mWin.mShownFrame +
+                ", alpha=" + mTransformation.getAlpha() + ", mShownAlpha=" + mShownAlpha);
+            return;
+        }
+
+        if (WindowManagerService.localLOGV) Slog.v(
+            TAG, "computeShownFrameLocked: " + this +
+            " not attached, mAlpha=" + mAlpha);
+        mWin.mShownFrame.set(mWin.mFrame);
+        if (mWin.mXOffset != 0 || mWin.mYOffset != 0) {
+            mWin.mShownFrame.offset(mWin.mXOffset, mWin.mYOffset);
+        }
+        mShownAlpha = mAlpha;
+        mHaveMatrix = false;
+        mDsDx = mWin.mGlobalScale;
+        mDtDx = 0;
+        mDsDy = 0;
+        mDtDy = mWin.mGlobalScale;
+    }
+
+    public void prepareSurfaceLocked(final boolean recoveringMemory) {
+        final WindowState w = mWin;
+        if (mSurface == null) {
+            if (w.mOrientationChanging) {
+                if (WindowManagerService.DEBUG_ORIENTATION) {
+                    Slog.v(TAG, "Orientation change skips hidden " + w);
+                }
+                w.mOrientationChanging = false;
+            }
+            return;
+        }
+
+        boolean displayed = false;
+
+        computeShownFrameLocked();
+
+        int width, height;
+        if ((w.mAttrs.flags & LayoutParams.FLAG_SCALED) != 0) {
+            // for a scaled surface, we just want to use
+            // the requested size.
+            width  = w.mRequestedWidth;
+            height = w.mRequestedHeight;
+        } else {
+            width = w.mCompatFrame.width();
+            height = w.mCompatFrame.height();
+        }
+
+        if (width < 1) {
+            width = 1;
+        }
+        if (height < 1) {
+            height = 1;
+        }
+        final boolean surfaceResized = mSurfaceW != width || mSurfaceH != height;
+        if (surfaceResized) {
+            mSurfaceW = width;
+            mSurfaceH = height;
+        }
+
+        if (mSurfaceX != w.mShownFrame.left
+                || mSurfaceY != w.mShownFrame.top) {
+            try {
+                if (WindowManagerService.SHOW_TRANSACTIONS) WindowManagerService.logSurface(w,
+                        "POS " + w.mShownFrame.left
+                        + ", " + w.mShownFrame.top, null);
+                mSurfaceX = w.mShownFrame.left;
+                mSurfaceY = w.mShownFrame.top;
+                mSurface.setPosition(w.mShownFrame.left, w.mShownFrame.top);
+            } catch (RuntimeException e) {
+                Slog.w(TAG, "Error positioning surface of " + w
+                        + " pos=(" + w.mShownFrame.left
+                        + "," + w.mShownFrame.top + ")", e);
+                if (!recoveringMemory) {
+                    mService.reclaimSomeSurfaceMemoryLocked(this, "position", true);
+                }
+            }
+        }
+
+        if (surfaceResized) {
+            try {
+                if (WindowManagerService.SHOW_TRANSACTIONS) WindowManagerService.logSurface(w,
+                        "SIZE " + width + "x" + height, null);
+                mSurfaceResized = true;
+                mSurface.setSize(width, height);
+            } catch (RuntimeException e) {
+                // If something goes wrong with the surface (such
+                // as running out of memory), don't take down the
+                // entire system.
+                Slog.e(TAG, "Error resizing surface of " + w
+                        + " size=(" + width + "x" + height + ")", e);
+                if (!recoveringMemory) {
+                    mService.reclaimSomeSurfaceMemoryLocked(this, "size", true);
+                }
+            }
+        }
+
+        if (w.mAttachedHidden || !w.isReadyForDisplay()) {
+            if (!w.mLastHidden) {
+                //dump();
+                w.mLastHidden = true;
+                if (WindowManagerService.SHOW_TRANSACTIONS) WindowManagerService.logSurface(w,
+                        "HIDE (performLayout)", null);
+                if (mSurface != null) {
+                    mSurfaceShown = false;
+                    try {
+                        mSurface.hide();
+                    } catch (RuntimeException e) {
+                        Slog.w(TAG, "Exception hiding surface in " + w);
+                    }
+                }
+            }
+            // If we are waiting for this window to handle an
+            // orientation change, well, it is hidden, so
+            // doesn't really matter.  Note that this does
+            // introduce a potential glitch if the window
+            // becomes unhidden before it has drawn for the
+            // new orientation.
+            if (w.mOrientationChanging) {
+                w.mOrientationChanging = false;
+                if (WindowManagerService.DEBUG_ORIENTATION) Slog.v(TAG,
+                        "Orientation change skips hidden " + w);
+            }
+        } else if (mLastLayer != mAnimLayer
+                || mLastAlpha != mShownAlpha
+                || mLastDsDx != mDsDx
+                || mLastDtDx != mDtDx
+                || mLastDsDy != mDsDy
+                || mLastDtDy != mDtDy
+                || w.mLastHScale != w.mHScale
+                || w.mLastVScale != w.mVScale
+                || w.mLastHidden) {
+            displayed = true;
+            mLastAlpha = mShownAlpha;
+            mLastLayer = mAnimLayer;
+            mLastDsDx = mDsDx;
+            mLastDtDx = mDtDx;
+            mLastDsDy = mDsDy;
+            mLastDtDy = mDtDy;
+            w.mLastHScale = w.mHScale;
+            w.mLastVScale = w.mVScale;
+            if (WindowManagerService.SHOW_TRANSACTIONS) WindowManagerService.logSurface(w,
+                    "alpha=" + mShownAlpha + " layer=" + mAnimLayer
+                    + " matrix=[" + (mDsDx*w.mHScale)
+                    + "," + (mDtDx*w.mVScale)
+                    + "][" + (mDsDy*w.mHScale)
+                    + "," + (mDtDy*w.mVScale) + "]", null);
+            if (mSurface != null) {
+                try {
+                    mSurfaceAlpha = mShownAlpha;
+                    mSurface.setAlpha(mShownAlpha);
+                    mSurfaceLayer = w.mWinAnimator.mAnimLayer;
+                    mSurface.setLayer(w.mWinAnimator.mAnimLayer);
+                    mSurface.setMatrix(
+                        mDsDx*w.mHScale, mDtDx*w.mVScale,
+                        mDsDy*w.mHScale, mDtDy*w.mVScale);
+                } catch (RuntimeException e) {
+                    Slog.w(TAG, "Error updating surface in " + w, e);
+                    if (!recoveringMemory) {
+                        mService.reclaimSomeSurfaceMemoryLocked(this, "update", true);
+                    }
+                }
+            }
+
+            if (w.mLastHidden && w.isDrawnLw()
+                    && !w.mReadyToShow) {
+                if (WindowManagerService.SHOW_TRANSACTIONS) WindowManagerService.logSurface(w,
+                        "SHOW (performLayout)", null);
+                if (WindowManagerService.DEBUG_VISIBILITY) Slog.v(TAG, "Showing " + w
+                        + " during relayout");
+                if (showSurfaceRobustlyLocked()) {
+                    w.mHasDrawn = true;
+                    w.mLastHidden = false;
+                } else {
+                    w.mOrientationChanging = false;
+                }
+            }
+            if (mSurface != null) {
+                w.mToken.hasVisible = true;
+            }
+        } else {
+            displayed = true;
+        }
+
+        if (displayed) {
+            if (w.mOrientationChanging) {
+                if (!w.isDrawnLw()) {
+                    mService.mInnerFields.mOrientationChangeComplete = false;
+                    if (WindowManagerService.DEBUG_ORIENTATION) Slog.v(TAG,
+                            "Orientation continue waiting for draw in " + w);
+                } else {
+                    w.mOrientationChanging = false;
+                    if (WindowManagerService.DEBUG_ORIENTATION) Slog.v(TAG,
+                            "Orientation change complete in " + w);
+                }
+            }
+            w.mToken.hasVisible = true;
+        }
+    }
+
+    // This must be called while inside a transaction.
+    boolean performShowLocked() {
+        if (DEBUG_VISIBILITY) {
+            RuntimeException e = null;
+            if (!WindowManagerService.HIDE_STACK_CRAWLS) {
+                e = new RuntimeException();
+                e.fillInStackTrace();
+            }
+            Slog.v(TAG, "performShow on " + this
+                    + ": readyToShow=" + mWin.mReadyToShow + " readyForDisplay="
+                    + mWin.isReadyForDisplay()
+                    + " starting=" + (mWin.mAttrs.type == TYPE_APPLICATION_STARTING), e);
+        }
+        if (mWin.mReadyToShow && mWin.isReadyForDisplay()) {
+            if (SHOW_TRANSACTIONS || WindowManagerService.DEBUG_ORIENTATION)
+                WindowManagerService.logSurface(mWin, "SHOW (performShowLocked)", null);
+            if (DEBUG_VISIBILITY) Slog.v(TAG, "Showing " + this
+                    + " during animation: policyVis=" + mWin.mPolicyVisibility
+                    + " attHidden=" + mWin.mAttachedHidden
+                    + " tok.hiddenRequested="
+                    + (mWin.mAppToken != null ? mWin.mAppToken.hiddenRequested : false)
+                    + " tok.hidden="
+                    + (mWin.mAppToken != null ? mWin.mAppToken.hidden : false)
+                    + " animating=" + mAnimating
+                    + " tok animating="
+                    + (mWin.mAppToken != null ? mWin.mAppToken.animating : false));
+            if (!showSurfaceRobustlyLocked()) {
+                return false;
+            }
+
+            mService.enableScreenIfNeededLocked();
+
+            applyEnterAnimationLocked();
+
+            mLastAlpha = -1;
+            mWin.mHasDrawn = true;
+            mWin.mLastHidden = false;
+            mWin.mReadyToShow = false;
+
+            int i = mWin.mChildWindows.size();
+            while (i > 0) {
+                i--;
+                WindowState c = mWin.mChildWindows.get(i);
+                if (c.mAttachedHidden) {
+                    c.mAttachedHidden = false;
+                    if (c.mWinAnimator.mSurface != null) {
+                        c.mWinAnimator.performShowLocked();
+                        // It hadn't been shown, which means layout not
+                        // performed on it, so now we want to make sure to
+                        // do a layout.  If called from within the transaction
+                        // loop, this will cause it to restart with a new
+                        // layout.
+                        mService.mLayoutNeeded = true;
+                    }
+                }
+            }
+
+            if (mWin.mAttrs.type != TYPE_APPLICATION_STARTING
+                    && mWin.mAppToken != null) {
+                mWin.mAppToken.firstWindowDrawn = true;
+
+                if (mWin.mAppToken.startingData != null) {
+                    if (WindowManagerService.DEBUG_STARTING_WINDOW ||
+                            WindowManagerService.DEBUG_ANIM) Slog.v(TAG,
+                            "Finish starting " + mWin.mToken
+                            + ": first real window is shown, no animation");
+                    // If this initial window is animating, stop it -- we
+                    // will do an animation to reveal it from behind the
+                    // starting window, so there is no need for it to also
+                    // be doing its own stuff.
+                    if (mAnimation != null) {
+                        mAnimation.cancel();
+                        mAnimation = null;
+                        // Make sure we clean up the animation.
+                        mAnimating = true;
+                    }
+                    mService.mFinishedStarting.add(mWin.mAppToken);
+                    mService.mH.sendEmptyMessage(H.FINISHED_STARTING);
+                }
+                mWin.mAppToken.updateReportedVisibilityLocked();
+            }
+
+            return true;
+        }
+
+        return false;
+    }
+
+    /**
+     * Have the surface flinger show a surface, robustly dealing with
+     * error conditions.  In particular, if there is not enough memory
+     * to show the surface, then we will try to get rid of other surfaces
+     * in order to succeed.
+     *
+     * @return Returns true if the surface was successfully shown.
+     */
+    boolean showSurfaceRobustlyLocked() {
+        try {
+            if (mSurface != null) {
+                mSurfaceShown = true;
+                mSurface.show();
+                if (mWin.mTurnOnScreen) {
+                    if (DEBUG_VISIBILITY) Slog.v(TAG,
+                            "Show surface turning screen on: " + mWin);
+                    mWin.mTurnOnScreen = false;
+                    mService.mTurnOnScreen = true;
+                }
+            }
+            return true;
+        } catch (RuntimeException e) {
+            Slog.w(TAG, "Failure showing surface " + mSurface + " in " + mWin, e);
+        }
+
+        mService.reclaimSomeSurfaceMemoryLocked(this, "show", true);
+
+        return false;
+    }
+
+    void applyEnterAnimationLocked() {
+        final int transit;
+        if (mEnterAnimationPending) {
+            mEnterAnimationPending = false;
+            transit = WindowManagerPolicy.TRANSIT_ENTER;
+        } else {
+            transit = WindowManagerPolicy.TRANSIT_SHOW;
+        }
+
+        applyAnimationLocked(transit, true);
+    }
+
+    /**
+     * Choose the correct animation and set it to the passed WindowState.
+     * @param transit If WindowManagerPolicy.TRANSIT_PREVIEW_DONE and the app window has been drawn
+     *      then the animation will be app_starting_exit. Any other value loads the animation from
+     *      the switch statement below.
+     * @param isEntrance The animation type the last time this was called. Used to keep from
+     *      loading the same animation twice.
+     * @return true if an animation has been loaded.
+     */
+    boolean applyAnimationLocked(int transit, boolean isEntrance) {
+        if (mLocalAnimating && mAnimationIsEntrance == isEntrance) {
+            // If we are trying to apply an animation, but already running
+            // an animation of the same type, then just leave that one alone.
+            return true;
+        }
+
+        // Only apply an animation if the display isn't frozen.  If it is
+        // frozen, there is no reason to animate and it can cause strange
+        // artifacts when we unfreeze the display if some different animation
+        // is running.
+        if (mService.okToDisplay()) {
+            int anim = mPolicy.selectAnimationLw(mWin, transit);
+            int attr = -1;
+            Animation a = null;
+            if (anim != 0) {
+                a = AnimationUtils.loadAnimation(mContext, anim);
+            } else {
+                switch (transit) {
+                    case WindowManagerPolicy.TRANSIT_ENTER:
+                        attr = com.android.internal.R.styleable.WindowAnimation_windowEnterAnimation;
+                        break;
+                    case WindowManagerPolicy.TRANSIT_EXIT:
+                        attr = com.android.internal.R.styleable.WindowAnimation_windowExitAnimation;
+                        break;
+                    case WindowManagerPolicy.TRANSIT_SHOW:
+                        attr = com.android.internal.R.styleable.WindowAnimation_windowShowAnimation;
+                        break;
+                    case WindowManagerPolicy.TRANSIT_HIDE:
+                        attr = com.android.internal.R.styleable.WindowAnimation_windowHideAnimation;
+                        break;
+                }
+                if (attr >= 0) {
+                    a = mService.loadAnimation(mWin.mAttrs, attr);
+                }
+            }
+            if (WindowManagerService.DEBUG_ANIM) Slog.v(TAG,
+                    "applyAnimation: win=" + this
+                    + " anim=" + anim + " attr=0x" + Integer.toHexString(attr)
+                    + " mAnimation=" + mAnimation
+                    + " isEntrance=" + isEntrance);
+            if (a != null) {
+                if (WindowManagerService.DEBUG_ANIM) {
+                    RuntimeException e = null;
+                    if (!WindowManagerService.HIDE_STACK_CRAWLS) {
+                        e = new RuntimeException();
+                        e.fillInStackTrace();
+                    }
+                    Slog.v(TAG, "Loaded animation " + a + " for " + this, e);
+                }
+                setAnimation(a);
+                mAnimationIsEntrance = isEntrance;
+            }
+        } else {
+            clearAnimation();
+        }
+
+        return mAnimation != null;
+    }
+
     public void dump(PrintWriter pw, String prefix, boolean dumpAll) {
         if (mAnimating || mLocalAnimating || mAnimationIsEntrance
                 || mAnimation != null) {
@@ -293,6 +1099,38 @@
                     pw.print(" "); mTransformation.printShortString(pw);
                     pw.println();
         }
+        if (mSurface != null) {
+            if (dumpAll) {
+                pw.print(prefix); pw.print("mSurface="); pw.println(mSurface);
+            }
+            pw.print(prefix); pw.print("Surface: shown="); pw.print(mSurfaceShown);
+                    pw.print(" layer="); pw.print(mSurfaceLayer);
+                    pw.print(" alpha="); pw.print(mSurfaceAlpha);
+                    pw.print(" rect=("); pw.print(mSurfaceX);
+                    pw.print(","); pw.print(mSurfaceY);
+                    pw.print(") "); pw.print(mSurfaceW);
+                    pw.print(" x "); pw.println(mSurfaceH);
+        }
+        if (mPendingDestroySurface != null) {
+            pw.print(prefix); pw.print("mPendingDestroySurface=");
+                    pw.println(mPendingDestroySurface);
+        }
+        if (mSurfaceResized || mSurfaceDestroyDeferred) {
+            pw.print(prefix); pw.print("mSurfaceResized="); pw.print(mSurfaceResized);
+                    pw.print(" mSurfaceDestroyDeferred="); pw.println(mSurfaceDestroyDeferred);
+        }
+        if (mShownAlpha != 1 || mAlpha != 1 || mLastAlpha != 1) {
+            pw.print(prefix); pw.print("mShownAlpha="); pw.print(mShownAlpha);
+                    pw.print(" mAlpha="); pw.print(mAlpha);
+                    pw.print(" mLastAlpha="); pw.println(mLastAlpha);
+        }
+        if (mHaveMatrix || mWin.mGlobalScale != 1) {
+            pw.print(prefix); pw.print("mGlobalScale="); pw.print(mWin.mGlobalScale);
+                    pw.print(" mDsDx="); pw.print(mDsDx);
+                    pw.print(" mDtDx="); pw.print(mDtDx);
+                    pw.print(" mDsDy="); pw.print(mDsDy);
+                    pw.print(" mDtDy="); pw.println(mDtDy);
+        }
     }
 
 }
diff --git a/telephony/java/com/android/internal/telephony/TelephonyCapabilities.java b/telephony/java/com/android/internal/telephony/TelephonyCapabilities.java
new file mode 100644
index 0000000..061a6cb
--- /dev/null
+++ b/telephony/java/com/android/internal/telephony/TelephonyCapabilities.java
@@ -0,0 +1,160 @@
+/*
+ * 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.internal.telephony;
+
+import android.util.Log;
+
+import com.android.internal.telephony.Phone;
+
+/**
+ * Utilities that check if the phone supports specified capabilities.
+ */
+public class TelephonyCapabilities {
+    private static final String LOG_TAG = "TelephonyCapabilities";
+
+    /** This class is never instantiated. */
+    private TelephonyCapabilities() {
+    }
+
+    /**
+     * Return true if the current phone supports ECM ("Emergency Callback
+     * Mode"), which is a feature where the device goes into a special
+     * state for a short period of time after making an outgoing emergency
+     * call.
+     *
+     * (On current devices, that state lasts 5 minutes.  It prevents data
+     * usage by other apps, to avoid conflicts with any possible incoming
+     * calls.  It also puts up a notification in the status bar, showing a
+     * countdown while ECM is active, and allowing the user to exit ECM.)
+     *
+     * Currently this is assumed to be true for CDMA phones, and false
+     * otherwise.
+     */
+    public static boolean supportsEcm(Phone phone) {
+        return (phone.getPhoneType() == Phone.PHONE_TYPE_CDMA);
+    }
+
+    /**
+     * Return true if the current phone supports Over The Air Service
+     * Provisioning (OTASP)
+     *
+     * Currently this is assumed to be true for CDMA phones, and false
+     * otherwise.
+     *
+     * TODO: Watch out: this is also highly carrier-specific, since the
+     * OTASP procedure is different from one carrier to the next, *and* the
+     * different carriers may want very different onscreen UI as well.
+     * The procedure may even be different for different devices with the
+     * same carrier.
+     *
+     * So we eventually will need a much more flexible, pluggable design.
+     * This method here is just a placeholder to reduce hardcoded
+     * "if (CDMA)" checks sprinkled throughout the phone app.
+     */
+    public static boolean supportsOtasp(Phone phone) {
+        return (phone.getPhoneType() == Phone.PHONE_TYPE_CDMA);
+    }
+
+    /**
+     * Return true if the current phone can retrieve the voice message count.
+     *
+     * Currently this is assumed to be true on CDMA phones and false otherwise.
+     */
+    public static boolean supportsVoiceMessageCount(Phone phone) {
+        return (phone.getPhoneType() == Phone.PHONE_TYPE_CDMA);
+    }
+
+    /**
+     * Return true if this phone allows the user to select which
+     * network to use.
+     *
+     * Currently this is assumed to be true only on GSM phones.
+     *
+     * TODO: Should CDMA phones allow this as well?
+     */
+    public static boolean supportsNetworkSelection(Phone phone) {
+        return (phone.getPhoneType() == Phone.PHONE_TYPE_GSM);
+    }
+
+    /**
+     * Returns a resource ID for a label to use when displaying the
+     * "device id" of the current device.  (This is currently used as the
+     * title of the "device id" dialog.)
+     *
+     * This is specific to the device's telephony technology: the device
+     * id is called "IMEI" on GSM phones and "MEID" on CDMA phones.
+     */
+    public static int getDeviceIdLabel(Phone phone) {
+        if (phone.getPhoneType() == Phone.PHONE_TYPE_GSM) {
+            return com.android.internal.R.string.imei;
+        } else if (phone.getPhoneType() == Phone.PHONE_TYPE_CDMA) {
+            return com.android.internal.R.string.meid;
+        } else {
+            Log.w(LOG_TAG, "getDeviceIdLabel: no known label for phone "
+                  + phone.getPhoneName());
+            return 0;
+        }
+    }
+
+    /**
+     * Return true if the current phone supports the ability to explicitly
+     * manage the state of a conference call (i.e. view the participants,
+     * and hangup or separate individual callers.)
+     *
+     * The in-call screen's "Manage conference" UI is available only on
+     * devices that support this feature.
+     *
+     * Currently this is assumed to be true on GSM phones and false otherwise.
+     */
+    public static boolean supportsConferenceCallManagement(Phone phone) {
+        return ((phone.getPhoneType() == Phone.PHONE_TYPE_GSM)
+                || (phone.getPhoneType() == Phone.PHONE_TYPE_SIP));
+    }
+
+    /**
+     * Return true if the current phone supports explicit "Hold" and
+     * "Unhold" actions for an active call.  (If so, the in-call UI will
+     * provide onscreen "Hold" / "Unhold" buttons.)
+     *
+     * Currently this is assumed to be true on GSM phones and false
+     * otherwise.  (In particular, CDMA has no concept of "putting a call
+     * on hold.")
+     */
+    public static boolean supportsHoldAndUnhold(Phone phone) {
+        return ((phone.getPhoneType() == Phone.PHONE_TYPE_GSM)
+                || (phone.getPhoneType() == Phone.PHONE_TYPE_SIP));
+    }
+
+    /**
+     * Return true if the current phone supports distinct "Answer & Hold"
+     * and "Answer & End" behaviors in the call-waiting scenario.  If so,
+     * the in-call UI may provide separate buttons or menu items for these
+     * two actions.
+     *
+     * Currently this is assumed to be true on GSM phones and false
+     * otherwise.  (In particular, CDMA has no concept of explicitly
+     * managing the background call, or "putting a call on hold.")
+     *
+     * TODO: It might be better to expose this capability in a more
+     * generic form, like maybe "supportsExplicitMultipleLineManagement()"
+     * rather than focusing specifically on call-waiting behavior.
+     */
+    public static boolean supportsAnswerAndHold(Phone phone) {
+        return ((phone.getPhoneType() == Phone.PHONE_TYPE_GSM)
+                || (phone.getPhoneType() == Phone.PHONE_TYPE_SIP));
+    }
+}
diff --git a/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/horizontal_blur.rs b/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/horizontal_blur.rs
index 86479d5..ee83496 100644
--- a/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/horizontal_blur.rs
+++ b/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/horizontal_blur.rs
@@ -1,4 +1,5 @@
 #pragma version(1)
+#pragma rs_fp_relaxed
 
 #include "ip.rsh"
 
diff --git a/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/vertical_blur.rs b/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/vertical_blur.rs
index 8b07bc2..a6da192 100644
--- a/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/vertical_blur.rs
+++ b/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/vertical_blur.rs
@@ -1,4 +1,5 @@
 #pragma version(1)
+#pragma rs_fp_relaxed
 
 #include "ip.rsh"
 
diff --git a/wifi/java/android/net/wifi/WifiMonitor.java b/wifi/java/android/net/wifi/WifiMonitor.java
index f07e0de..03d5134 100644
--- a/wifi/java/android/net/wifi/WifiMonitor.java
+++ b/wifi/java/android/net/wifi/WifiMonitor.java
@@ -166,6 +166,9 @@
     /* P2P-DEVICE-LOST p2p_dev_addr=42:fc:89:e1:e2:27 */
     private static final String P2P_DEVICE_LOST_STR = "P2P-DEVICE-LOST";
 
+    /* P2P-FIND-STOPPED */
+    private static final String P2P_FIND_STOPPED_STR = "P2P-FIND-STOPPED";
+
     /* P2P-GO-NEG-REQUEST 42:fc:89:a8:96:09 dev_passwd_id=4 */
     private static final String P2P_GO_NEG_REQUEST_STR = "P2P-GO-NEG-REQUEST";
 
@@ -264,6 +267,7 @@
     public static final int P2P_PROV_DISC_PBC_RSP_EVENT          = BASE + 34;
     public static final int P2P_PROV_DISC_ENTER_PIN_EVENT        = BASE + 35;
     public static final int P2P_PROV_DISC_SHOW_PIN_EVENT         = BASE + 36;
+    public static final int P2P_FIND_STOPPED_EVENT               = BASE + 37;
 
     /* hostap events */
     public static final int AP_STA_DISCONNECTED_EVENT            = BASE + 41;
@@ -516,6 +520,8 @@
                 mStateMachine.sendMessage(P2P_DEVICE_FOUND_EVENT, new WifiP2pDevice(dataString));
             } else if (dataString.startsWith(P2P_DEVICE_LOST_STR)) {
                 mStateMachine.sendMessage(P2P_DEVICE_LOST_EVENT, new WifiP2pDevice(dataString));
+            } else if (dataString.startsWith(P2P_FIND_STOPPED_STR)) {
+                mStateMachine.sendMessage(P2P_FIND_STOPPED_EVENT);
             } else if (dataString.startsWith(P2P_GO_NEG_REQUEST_STR)) {
                 mStateMachine.sendMessage(P2P_GO_NEGOTIATION_REQUEST_EVENT,
                         new WifiP2pConfig(dataString));
diff --git a/wifi/java/android/net/wifi/p2p/WifiP2pManager.java b/wifi/java/android/net/wifi/p2p/WifiP2pManager.java
index 4fd0a57..2fc6c20 100644
--- a/wifi/java/android/net/wifi/p2p/WifiP2pManager.java
+++ b/wifi/java/android/net/wifi/p2p/WifiP2pManager.java
@@ -182,6 +182,41 @@
         "android.net.wifi.p2p.PEERS_CHANGED";
 
     /**
+     * Broadcast intent action indicating that peer discovery has either started or stopped.
+     * One extra {@link #EXTRA_DISCOVERY_STATE} indicates whether discovery has started
+     * or stopped.
+     *
+     * Note that discovery will be stopped during a connection setup. If the application tries
+     * to re-initiate discovery during this time, it can fail.
+     */
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    public static final String WIFI_P2P_DISCOVERY_CHANGED_ACTION =
+        "android.net.wifi.p2p.DISCOVERY_STATE_CHANGE";
+
+    /**
+     * The lookup key for an int that indicates whether p2p discovery has started or stopped.
+     * Retrieve it with {@link android.content.Intent#getIntExtra(String,int)}.
+     *
+     * @see #WIFI_P2P_DISCOVERY_STARTED
+     * @see #WIFI_P2P_DISCOVERY_STOPPED
+     */
+    public static final String EXTRA_DISCOVERY_STATE = "discoveryState";
+
+    /**
+     * p2p discovery has stopped
+     *
+     * @see #WIFI_P2P_DISCOVERY_CHANGED_ACTION
+     */
+    public static final int WIFI_P2P_DISCOVERY_STOPPED = 1;
+
+    /**
+     * p2p discovery has started
+     *
+     * @see #WIFI_P2P_DISCOVERY_CHANGED_ACTION
+     */
+    public static final int WIFI_P2P_DISCOVERY_STARTED = 2;
+
+    /**
      * Broadcast intent action indicating that this device details have changed.
      */
     @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
diff --git a/wifi/java/android/net/wifi/p2p/WifiP2pService.java b/wifi/java/android/net/wifi/p2p/WifiP2pService.java
index 02ca926..3d3a746 100644
--- a/wifi/java/android/net/wifi/p2p/WifiP2pService.java
+++ b/wifi/java/android/net/wifi/p2p/WifiP2pService.java
@@ -140,6 +140,11 @@
      * is invoked */
     private boolean mPersistGroup;
 
+    /* Track whether we are in p2p discovery. This is used to avoid sending duplicate
+     * broadcasts
+     */
+    private boolean mDiscoveryStarted;
+
     private NetworkInfo mNetworkInfo;
 
     /* Is chosen as a unique range to avoid conflict with
@@ -489,11 +494,15 @@
                 case WifiP2pManager.DISCOVER_PEERS:
                     if (mWifiNative.p2pFind(DISCOVER_TIMEOUT_S)) {
                         replyToMessage(message, WifiP2pManager.DISCOVER_PEERS_SUCCEEDED);
+                        sendP2pDiscoveryChangedBroadcast(true);
                     } else {
                         replyToMessage(message, WifiP2pManager.DISCOVER_PEERS_FAILED,
                                 WifiP2pManager.ERROR);
                     }
                     break;
+                case WifiMonitor.P2P_FIND_STOPPED_EVENT:
+                    sendP2pDiscoveryChangedBroadcast(false);
+                    break;
                 case WifiP2pManager.STOP_DISCOVERY:
                     if (mWifiNative.p2pStopFind()) {
                         replyToMessage(message, WifiP2pManager.STOP_DISCOVERY_SUCCEEDED);
@@ -1030,6 +1039,20 @@
         mContext.sendStickyBroadcast(intent);
     }
 
+    private void sendP2pDiscoveryChangedBroadcast(boolean started) {
+        if (mDiscoveryStarted == started) return;
+        mDiscoveryStarted = started;
+
+        if (DBG) logd("discovery change broadcast " + started);
+
+        final Intent intent = new Intent(WifiP2pManager.WIFI_P2P_DISCOVERY_CHANGED_ACTION);
+        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
+        intent.putExtra(WifiP2pManager.EXTRA_DISCOVERY_STATE, started ?
+                WifiP2pManager.WIFI_P2P_DISCOVERY_STARTED :
+                WifiP2pManager.WIFI_P2P_DISCOVERY_STOPPED);
+        mContext.sendStickyBroadcast(intent);
+    }
+
     private void sendThisDeviceChangedBroadcast() {
         final Intent intent = new Intent(WifiP2pManager.WIFI_P2P_THIS_DEVICE_CHANGED_ACTION);
         intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);