Merge "Remove dependency on audio_* location"
diff --git a/api/current.txt b/api/current.txt
index 6bac58a..93d9d44 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -26377,6 +26377,7 @@
     ctor public EdgeEffect(android.content.Context);
     method public boolean draw(android.graphics.Canvas);
     method public void finish();
+    method public android.graphics.Rect getBounds();
     method public boolean isFinished();
     method public void onAbsorb(int);
     method public void onPull(float);
diff --git a/cmds/system_server/library/Android.mk b/cmds/system_server/library/Android.mk
index 7d08a8c..9f92330 100644
--- a/cmds/system_server/library/Android.mk
+++ b/cmds/system_server/library/Android.mk
@@ -8,10 +8,7 @@
 native = $(LOCAL_PATH)/../../../../native
 
 LOCAL_C_INCLUDES := \
-	$(base)/services/camera/libcameraservice \
-	$(base)/services/audioflinger \
 	$(base)/services/sensorservice \
-	$(base)/media/libmediaplayerservice \
 	$(native)/services/surfaceflinger \
 	$(JNI_H_INCLUDE)
 
@@ -19,9 +16,6 @@
 	libandroid_runtime \
 	libsensorservice \
 	libsurfaceflinger \
-	libaudioflinger \
-    libcameraservice \
-    libmediaplayerservice \
     libinput \
 	libutils \
 	libbinder \
diff --git a/cmds/system_server/library/system_init.cpp b/cmds/system_server/library/system_init.cpp
index bfbc138..745c34a 100644
--- a/cmds/system_server/library/system_init.cpp
+++ b/cmds/system_server/library/system_init.cpp
@@ -15,10 +15,6 @@
 #include <utils/Log.h>
 
 #include <SurfaceFlinger.h>
-#include <AudioFlinger.h>
-#include <CameraService.h>
-#include <AudioPolicyService.h>
-#include <MediaPlayerService.h>
 #include <SensorService.h>
 
 #include <android_runtime/AndroidRuntime.h>
diff --git a/core/java/android/content/ContentProviderNative.java b/core/java/android/content/ContentProviderNative.java
index eb83dbc..4b31552 100644
--- a/core/java/android/content/ContentProviderNative.java
+++ b/core/java/android/content/ContentProviderNative.java
@@ -17,6 +17,7 @@
 package android.content;
 
 import android.content.res.AssetFileDescriptor;
+import android.database.BulkCursorDescriptor;
 import android.database.BulkCursorNative;
 import android.database.BulkCursorToCursorAdaptor;
 import android.database.Cursor;
@@ -113,20 +114,14 @@
                     if (cursor != null) {
                         CursorToBulkCursorAdaptor adaptor = new CursorToBulkCursorAdaptor(
                                 cursor, observer, getProviderName());
-                        final IBinder binder = adaptor.asBinder();
-                        final int count = adaptor.count();
-                        final int index = BulkCursorToCursorAdaptor.findRowIdColumnIndex(
-                                adaptor.getColumnNames());
-                        final boolean wantsAllOnMoveCalls = adaptor.getWantsAllOnMoveCalls();
+                        BulkCursorDescriptor d = adaptor.getBulkCursorDescriptor();
 
                         reply.writeNoException();
-                        reply.writeStrongBinder(binder);
-                        reply.writeInt(count);
-                        reply.writeInt(index);
-                        reply.writeInt(wantsAllOnMoveCalls ? 1 : 0);
+                        reply.writeInt(1);
+                        d.writeToParcel(reply, Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
                     } else {
                         reply.writeNoException();
-                        reply.writeStrongBinder(null);
+                        reply.writeInt(0);
                     }
 
                     return true;
@@ -369,12 +364,9 @@
 
             DatabaseUtils.readExceptionFromParcel(reply);
 
-            IBulkCursor bulkCursor = BulkCursorNative.asInterface(reply.readStrongBinder());
-            if (bulkCursor != null) {
-                int rowCount = reply.readInt();
-                int idColumnPosition = reply.readInt();
-                boolean wantsAllOnMoveCalls = reply.readInt() != 0;
-                adaptor.initialize(bulkCursor, rowCount, idColumnPosition, wantsAllOnMoveCalls);
+            if (reply.readInt() != 0) {
+                BulkCursorDescriptor d = BulkCursorDescriptor.CREATOR.createFromParcel(reply);
+                adaptor.initialize(d);
             } else {
                 adaptor.close();
                 adaptor = null;
diff --git a/core/java/android/database/AbstractCursor.java b/core/java/android/database/AbstractCursor.java
index b28ed8d..dd6692c 100644
--- a/core/java/android/database/AbstractCursor.java
+++ b/core/java/android/database/AbstractCursor.java
@@ -33,10 +33,39 @@
 public abstract class AbstractCursor implements CrossProcessCursor {
     private static final String TAG = "Cursor";
 
-    DataSetObservable mDataSetObservable = new DataSetObservable();
-    ContentObservable mContentObservable = new ContentObservable();
+    /**
+     * @deprecated This is never updated by this class and should not be used
+     */
+    @Deprecated
+    protected HashMap<Long, Map<String, Object>> mUpdatedRows;
 
-    Bundle mExtras = Bundle.EMPTY;
+    protected int mPos;
+
+    /**
+     * This must be set to the index of the row ID column by any
+     * subclass that wishes to support updates.
+     */
+    protected int mRowIdColumnIndex;
+
+    /**
+     * If {@link #mRowIdColumnIndex} is not -1 this contains contains the value of
+     * the column at {@link #mRowIdColumnIndex} for the current row this cursor is
+     * pointing at.
+     */
+    protected Long mCurrentRowID;
+
+    protected boolean mClosed;
+    protected ContentResolver mContentResolver;
+    private Uri mNotifyUri;
+
+    private final Object mSelfObserverLock = new Object();
+    private ContentObserver mSelfObserver;
+    private boolean mSelfObserverRegistered;
+
+    private DataSetObservable mDataSetObservable = new DataSetObservable();
+    private ContentObservable mContentObservable = new ContentObservable();
+
+    private Bundle mExtras = Bundle.EMPTY;
 
     /* -------------------------------------------------------- */
     /* These need to be implemented by subclasses */
@@ -415,30 +444,4 @@
             }
         }
     }
-
-    /**
-     * @deprecated This is never updated by this class and should not be used
-     */
-    @Deprecated
-    protected HashMap<Long, Map<String, Object>> mUpdatedRows;
-
-    /**
-     * This must be set to the index of the row ID column by any
-     * subclass that wishes to support updates.
-     */
-    protected int mRowIdColumnIndex;
-
-    protected int mPos;
-    /**
-     * If {@link #mRowIdColumnIndex} is not -1 this contains contains the value of
-     * the column at {@link #mRowIdColumnIndex} for the current row this cursor is
-     * pointing at.
-     */
-    protected Long mCurrentRowID;
-    protected ContentResolver mContentResolver;
-    protected boolean mClosed = false;
-    private Uri mNotifyUri;
-    private ContentObserver mSelfObserver;
-    final private Object mSelfObserverLock = new Object();
-    private boolean mSelfObserverRegistered;
 }
diff --git a/core/java/android/database/BulkCursorDescriptor.java b/core/java/android/database/BulkCursorDescriptor.java
new file mode 100644
index 0000000..c1e5e63
--- /dev/null
+++ b/core/java/android/database/BulkCursorDescriptor.java
@@ -0,0 +1,78 @@
+/*
+ * 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.database;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * Describes the properties of a {@link CursorToBulkCursorAdaptor} that are
+ * needed to initialize its {@link BulkCursorToCursorAdaptor} counterpart on the client's end.
+ *
+ * {@hide}
+ */
+public final class BulkCursorDescriptor implements Parcelable {
+    public static final Parcelable.Creator<BulkCursorDescriptor> CREATOR =
+            new Parcelable.Creator<BulkCursorDescriptor>() {
+        @Override
+        public BulkCursorDescriptor createFromParcel(Parcel in) {
+            BulkCursorDescriptor d = new BulkCursorDescriptor();
+            d.readFromParcel(in);
+            return d;
+        }
+
+        @Override
+        public BulkCursorDescriptor[] newArray(int size) {
+            return new BulkCursorDescriptor[size];
+        }
+    };
+
+    public IBulkCursor cursor;
+    public String[] columnNames;
+    public boolean wantsAllOnMoveCalls;
+    public int count;
+    public CursorWindow window;
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel out, int flags) {
+        out.writeStrongBinder(cursor.asBinder());
+        out.writeStringArray(columnNames);
+        out.writeInt(wantsAllOnMoveCalls ? 1 : 0);
+        out.writeInt(count);
+        if (window != null) {
+            out.writeInt(1);
+            window.writeToParcel(out, flags);
+        } else {
+            out.writeInt(0);
+        }
+    }
+
+    public void readFromParcel(Parcel in) {
+        cursor = BulkCursorNative.asInterface(in.readStrongBinder());
+        columnNames = in.readStringArray();
+        wantsAllOnMoveCalls = in.readInt() != 0;
+        count = in.readInt();
+        if (in.readInt() != 0) {
+            window = CursorWindow.CREATOR.createFromParcel(in);
+        }
+    }
+}
diff --git a/core/java/android/database/BulkCursorNative.java b/core/java/android/database/BulkCursorNative.java
index 67cf0f8..d3c11e78 100644
--- a/core/java/android/database/BulkCursorNative.java
+++ b/core/java/android/database/BulkCursorNative.java
@@ -72,26 +72,6 @@
                     return true;
                 }
 
-                case COUNT_TRANSACTION: {
-                    data.enforceInterface(IBulkCursor.descriptor);
-                    int count = count();
-                    reply.writeNoException();
-                    reply.writeInt(count);
-                    return true;
-                }
-
-                case GET_COLUMN_NAMES_TRANSACTION: {
-                    data.enforceInterface(IBulkCursor.descriptor);
-                    String[] columnNames = getColumnNames();
-                    reply.writeNoException();
-                    reply.writeInt(columnNames.length);
-                    int length = columnNames.length;
-                    for (int i = 0; i < length; i++) {
-                        reply.writeString(columnNames[i]);
-                    }
-                    return true;
-                }
-
                 case DEACTIVATE_TRANSACTION: {
                     data.enforceInterface(IBulkCursor.descriptor);
                     deactivate();
@@ -125,14 +105,6 @@
                     return true;
                 }
 
-                case WANTS_ON_MOVE_TRANSACTION: {
-                    data.enforceInterface(IBulkCursor.descriptor);
-                    boolean result = getWantsAllOnMoveCalls();
-                    reply.writeNoException();
-                    reply.writeInt(result ? 1 : 0);
-                    return true;
-                }
-
                 case GET_EXTRAS_TRANSACTION: {
                     data.enforceInterface(IBulkCursor.descriptor);
                     Bundle extras = getExtras();
@@ -217,52 +189,6 @@
         }
     }
 
-    public int count() throws RemoteException
-    {
-        Parcel data = Parcel.obtain();
-        Parcel reply = Parcel.obtain();
-        try {
-            data.writeInterfaceToken(IBulkCursor.descriptor);
-
-            boolean result = mRemote.transact(COUNT_TRANSACTION, data, reply, 0);
-            DatabaseUtils.readExceptionFromParcel(reply);
-
-            int count;
-            if (result == false) {
-                count = -1;
-            } else {
-                count = reply.readInt();
-            }
-            return count;
-        } finally {
-            data.recycle();
-            reply.recycle();
-        }
-    }
-
-    public String[] getColumnNames() throws RemoteException
-    {
-        Parcel data = Parcel.obtain();
-        Parcel reply = Parcel.obtain();
-        try {
-            data.writeInterfaceToken(IBulkCursor.descriptor);
-
-            mRemote.transact(GET_COLUMN_NAMES_TRANSACTION, data, reply, 0);
-            DatabaseUtils.readExceptionFromParcel(reply);
-
-            String[] columnNames = null;
-            int numColumns = reply.readInt();
-            columnNames = new String[numColumns];
-            for (int i = 0; i < numColumns; i++) {
-                columnNames[i] = reply.readString();
-            }
-            return columnNames;
-        } finally {
-            data.recycle();
-            reply.recycle();
-        }
-    }
-
     public void deactivate() throws RemoteException
     {
         Parcel data = Parcel.obtain();
@@ -317,23 +243,6 @@
         }
     }
 
-    public boolean getWantsAllOnMoveCalls() throws RemoteException {
-        Parcel data = Parcel.obtain();
-        Parcel reply = Parcel.obtain();
-        try {
-            data.writeInterfaceToken(IBulkCursor.descriptor);
-
-            mRemote.transact(WANTS_ON_MOVE_TRANSACTION, data, reply, 0);
-            DatabaseUtils.readExceptionFromParcel(reply);
-
-            int result = reply.readInt();
-            return result != 0;
-        } finally {
-            data.recycle();
-            reply.recycle();
-        }
-    }
-
     public Bundle getExtras() throws RemoteException {
         if (mExtras == null) {
             Parcel data = Parcel.obtain();
diff --git a/core/java/android/database/BulkCursorToCursorAdaptor.java b/core/java/android/database/BulkCursorToCursorAdaptor.java
index 885046b..98c7043 100644
--- a/core/java/android/database/BulkCursorToCursorAdaptor.java
+++ b/core/java/android/database/BulkCursorToCursorAdaptor.java
@@ -30,34 +30,23 @@
 
     private SelfContentObserver mObserverBridge = new SelfContentObserver(this);
     private IBulkCursor mBulkCursor;
-    private int mCount;
     private String[] mColumns;
     private boolean mWantsAllOnMoveCalls;
+    private int mCount;
 
     /**
      * Initializes the adaptor.
      * Must be called before first use.
      */
-    public void initialize(IBulkCursor bulkCursor, int count, int idIndex,
-            boolean wantsAllOnMoveCalls) {
-        mBulkCursor = bulkCursor;
-        mColumns = null;  // lazily retrieved
-        mCount = count;
-        mRowIdColumnIndex = idIndex;
-        mWantsAllOnMoveCalls = wantsAllOnMoveCalls;
-    }
-
-    /**
-     * Returns column index of "_id" column, or -1 if not found.
-     */
-    public static int findRowIdColumnIndex(String[] columnNames) {
-        int length = columnNames.length;
-        for (int i = 0; i < length; i++) {
-            if (columnNames[i].equals("_id")) {
-                return i;
-            }
+    public void initialize(BulkCursorDescriptor d) {
+        mBulkCursor = d.cursor;
+        mColumns = d.columnNames;
+        mRowIdColumnIndex = DatabaseUtils.findRowIdColumnIndex(mColumns);
+        mWantsAllOnMoveCalls = d.wantsAllOnMoveCalls;
+        mCount = d.count;
+        if (d.window != null) {
+            setWindow(d.window);
         }
-        return -1;
     }
 
     /**
@@ -169,14 +158,6 @@
     public String[] getColumnNames() {
         throwIfCursorIsClosed();
 
-        if (mColumns == null) {
-            try {
-                mColumns = mBulkCursor.getColumnNames();
-            } catch (RemoteException ex) {
-                Log.e(TAG, "Unable to fetch column names because the remote process is dead");
-                return null;
-            }
-        }
         return mColumns;
     }
 
diff --git a/core/java/android/database/CursorToBulkCursorAdaptor.java b/core/java/android/database/CursorToBulkCursorAdaptor.java
index 167278a..525be96 100644
--- a/core/java/android/database/CursorToBulkCursorAdaptor.java
+++ b/core/java/android/database/CursorToBulkCursorAdaptor.java
@@ -132,6 +132,25 @@
         }
     }
 
+    public BulkCursorDescriptor getBulkCursorDescriptor() {
+        synchronized (mLock) {
+            throwIfCursorIsClosed();
+
+            BulkCursorDescriptor d = new BulkCursorDescriptor();
+            d.cursor = this;
+            d.columnNames = mCursor.getColumnNames();
+            d.wantsAllOnMoveCalls = mCursor.getWantsAllOnMoveCalls();
+            d.count = mCursor.getCount();
+            d.window = mCursor.getWindow();
+            if (d.window != null) {
+                // Acquire a reference to the window because its reference count will be
+                // decremented when it is returned as part of the binder call reply parcel.
+                d.window.acquireReference();
+            }
+            return d;
+        }
+    }
+
     @Override
     public CursorWindow getWindow(int position) {
         synchronized (mLock) {
@@ -157,10 +176,9 @@
                 mCursor.fillWindow(position, window);
             }
 
-            // Acquire a reference before returning from this RPC.
-            // The Binder proxy will decrement the reference count again as part of writing
-            // the CursorWindow to the reply parcel as a return value.
             if (window != null) {
+                // Acquire a reference to the window because its reference count will be
+                // decremented when it is returned as part of the binder call reply parcel.
                 window.acquireReference();
             }
             return window;
@@ -177,24 +195,6 @@
     }
 
     @Override
-    public int count() {
-        synchronized (mLock) {
-            throwIfCursorIsClosed();
-
-            return mCursor.getCount();
-        }
-    }
-
-    @Override
-    public String[] getColumnNames() {
-        synchronized (mLock) {
-            throwIfCursorIsClosed();
-
-            return mCursor.getColumnNames();
-        }
-    }
-
-    @Override
     public void deactivate() {
         synchronized (mLock) {
             if (mCursor != null) {
@@ -237,15 +237,6 @@
         }
     }
 
-    @Override
-    public boolean getWantsAllOnMoveCalls() {
-        synchronized (mLock) {
-            throwIfCursorIsClosed();
-
-            return mCursor.getWantsAllOnMoveCalls();
-        }
-    }
-
     /**
      * Create a ContentObserver from the observer and register it as an observer on the
      * underlying cursor.
diff --git a/core/java/android/database/DatabaseUtils.java b/core/java/android/database/DatabaseUtils.java
index 99d260e..40a54cf 100644
--- a/core/java/android/database/DatabaseUtils.java
+++ b/core/java/android/database/DatabaseUtils.java
@@ -1386,4 +1386,18 @@
         System.arraycopy(newValues, 0, result, originalValues.length, newValues.length);
         return result;
     }
+
+    /**
+     * Returns column index of "_id" column, or -1 if not found.
+     * @hide
+     */
+    public static int findRowIdColumnIndex(String[] columnNames) {
+        int length = columnNames.length;
+        for (int i = 0; i < length; i++) {
+            if (columnNames[i].equals("_id")) {
+                return i;
+            }
+        }
+        return -1;
+    }
 }
diff --git a/core/java/android/database/IBulkCursor.java b/core/java/android/database/IBulkCursor.java
index 0f4500a..b551116 100644
--- a/core/java/android/database/IBulkCursor.java
+++ b/core/java/android/database/IBulkCursor.java
@@ -43,29 +43,12 @@
      */
     public void onMove(int position) throws RemoteException;
 
-    /**
-     * Returns the number of rows in the cursor.
-     *
-     * @return the number of rows in the cursor.
-     */
-    public int count() throws RemoteException;
-
-    /**
-     * Returns a string array holding the names of all of the columns in the
-     * cursor in the order in which they were listed in the result.
-     *
-     * @return the names of the columns returned in this query.
-     */
-    public String[] getColumnNames() throws RemoteException;
-
     public void deactivate() throws RemoteException;
 
     public void close() throws RemoteException;
 
     public int requery(IContentObserver observer) throws RemoteException;
 
-    boolean getWantsAllOnMoveCalls() throws RemoteException;
-
     Bundle getExtras() throws RemoteException;
 
     Bundle respond(Bundle extras) throws RemoteException;
@@ -74,13 +57,10 @@
     static final String descriptor = "android.content.IBulkCursor";
 
     static final int GET_CURSOR_WINDOW_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION;
-    static final int COUNT_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 1;
-    static final int GET_COLUMN_NAMES_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 2;
-    static final int DEACTIVATE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 5;
-    static final int REQUERY_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 6;
-    static final int ON_MOVE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 7;
-    static final int WANTS_ON_MOVE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 8;
-    static final int GET_EXTRAS_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 9;
-    static final int RESPOND_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 10;
-    static final int CLOSE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 11;
+    static final int DEACTIVATE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 1;
+    static final int REQUERY_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 2;
+    static final int ON_MOVE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 3;
+    static final int GET_EXTRAS_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 4;
+    static final int RESPOND_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 5;
+    static final int CLOSE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 6;
 }
diff --git a/core/java/android/database/sqlite/SQLiteCursor.java b/core/java/android/database/sqlite/SQLiteCursor.java
index 82bb23e..b29897e 100644
--- a/core/java/android/database/sqlite/SQLiteCursor.java
+++ b/core/java/android/database/sqlite/SQLiteCursor.java
@@ -105,12 +105,7 @@
         mQuery = query;
 
         mColumns = query.getColumnNames();
-        for (int i = 0; i < mColumns.length; i++) {
-            // Make note of the row ID column index for quick access to it
-            if ("_id".equals(mColumns[i])) {
-                mRowIdColumnIndex = i;
-            }
-        }
+        mRowIdColumnIndex = DatabaseUtils.findRowIdColumnIndex(mColumns);
     }
 
     /**
diff --git a/core/java/android/webkit/BrowserFrame.java b/core/java/android/webkit/BrowserFrame.java
index f09e29d..dbcea71 100644
--- a/core/java/android/webkit/BrowserFrame.java
+++ b/core/java/android/webkit/BrowserFrame.java
@@ -742,7 +742,8 @@
             url = url.replaceFirst(ANDROID_ASSET, "");
             try {
                 AssetManager assets = mContext.getAssets();
-                return assets.open(url, AssetManager.ACCESS_STREAMING);
+                Uri uri = Uri.parse(url);
+                return assets.open(uri.getPath(), AssetManager.ACCESS_STREAMING);
             } catch (IOException e) {
                 return null;
             }
diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java
index 5774440..9e07151 100644
--- a/core/java/android/widget/AbsListView.java
+++ b/core/java/android/widget/AbsListView.java
@@ -88,6 +88,7 @@
         ViewTreeObserver.OnTouchModeChangeListener,
         RemoteViewsAdapter.RemoteAdapterConnectionCallback {
 
+    @SuppressWarnings("UnusedDeclaration")
     private static final String TAG = "AbsListView";
 
     /**
@@ -2429,7 +2430,7 @@
         final ViewTreeObserver treeObserver = getViewTreeObserver();
         treeObserver.removeOnTouchModeChangeListener(this);
         if (mTextFilterEnabled && mPopup != null) {
-            treeObserver.removeGlobalOnLayoutListener(this);
+            treeObserver.removeOnGlobalLayoutListener(this);
             mGlobalLayoutListenerAddedFilter = false;
         }
 
@@ -2943,11 +2944,23 @@
                             mDirection = 0; // Reset when entering overscroll.
                             mTouchMode = TOUCH_MODE_OVERSCROLL;
                             if (rawDeltaY > 0) {
+                                if (!mEdgeGlowTop.isIdle()) {
+                                    invalidate(mEdgeGlowTop.getBounds());
+                                } else {
+                                    invalidate();
+                                }
+
                                 mEdgeGlowTop.onPull((float) overscroll / getHeight());
                                 if (!mEdgeGlowBottom.isFinished()) {
                                     mEdgeGlowBottom.onRelease();
                                 }
                             } else if (rawDeltaY < 0) {
+                                if (!mEdgeGlowBottom.isIdle()) {
+                                    invalidate(mEdgeGlowBottom.getBounds());
+                                } else {
+                                    invalidate();
+                                }
+
                                 mEdgeGlowBottom.onPull((float) overscroll / getHeight());
                                 if (!mEdgeGlowTop.isFinished()) {
                                     mEdgeGlowTop.onRelease();
@@ -2956,7 +2969,6 @@
                         }
                     }
                     mMotionY = y;
-                    invalidate();
                 }
                 mLastY = y;
             }
@@ -2990,26 +3002,26 @@
                             if (!mEdgeGlowBottom.isFinished()) {
                                 mEdgeGlowBottom.onRelease();
                             }
+                            invalidate(mEdgeGlowTop.getBounds());
                         } else if (rawDeltaY < 0) {
                             mEdgeGlowBottom.onPull((float) overScrollDistance / getHeight());
                             if (!mEdgeGlowTop.isFinished()) {
                                 mEdgeGlowTop.onRelease();
                             }
+                            invalidate(mEdgeGlowBottom.getBounds());
                         }
-                        invalidate();
                     }
                 }
 
                 if (incrementalDeltaY != 0) {
                     // Coming back to 'real' list scrolling
-                    mScrollY = 0;
-                    invalidateParentIfNeeded();
-
-                    // No need to do all this work if we're not going to move anyway
-                    if (incrementalDeltaY != 0) {
-                        trackMotionScroll(incrementalDeltaY, incrementalDeltaY);
+                    if (mScrollY != 0) {
+                        mScrollY = 0;
+                        invalidateParentIfNeeded();
                     }
 
+                    trackMotionScroll(incrementalDeltaY, incrementalDeltaY);
+
                     mTouchMode = TOUCH_MODE_SCROLL;
 
                     // We did not scroll the full amount. Treat this essentially like the
@@ -3468,11 +3480,12 @@
                 final int rightPadding = mListPadding.right + mGlowPaddingRight;
                 final int width = getWidth() - leftPadding - rightPadding;
 
-                canvas.translate(leftPadding,
-                        Math.min(0, scrollY + mFirstPositionDistanceGuess));
+                int edgeY = Math.min(0, scrollY + mFirstPositionDistanceGuess);
+                canvas.translate(leftPadding, edgeY);
                 mEdgeGlowTop.setSize(width, getHeight());
                 if (mEdgeGlowTop.draw(canvas)) {
-                    invalidate();
+                    mEdgeGlowTop.setPosition(leftPadding, edgeY);
+                    invalidate(mEdgeGlowTop.getBounds());
                 }
                 canvas.restoreToCount(restoreCount);
             }
@@ -3483,12 +3496,15 @@
                 final int width = getWidth() - leftPadding - rightPadding;
                 final int height = getHeight();
 
-                canvas.translate(-width + leftPadding,
-                        Math.max(height, scrollY + mLastPositionDistanceGuess));
+                int edgeX = -width + leftPadding;
+                int edgeY = Math.max(height, scrollY + mLastPositionDistanceGuess);
+                canvas.translate(edgeX, edgeY);
                 canvas.rotate(180, width, 0);
                 mEdgeGlowBottom.setSize(width, height);
                 if (mEdgeGlowBottom.draw(canvas)) {
-                    invalidate();
+                    // Account for the rotation
+                    mEdgeGlowBottom.setPosition(edgeX + width, edgeY - mEdgeGlowBottom.getHeight());
+                    invalidate(mEdgeGlowBottom.getBounds());
                 }
                 canvas.restoreToCount(restoreCount);
             }
@@ -3874,7 +3890,8 @@
                 }
 
                 // Don't stop just because delta is zero (it could have been rounded)
-                final boolean atEnd = trackMotionScroll(delta, delta) && (delta != 0);
+                final boolean atEdge = trackMotionScroll(delta, delta);
+                final boolean atEnd = atEdge && (delta != 0);
                 if (atEnd) {
                     if (motionView != null) {
                         // Tweak the scroll for how far we overshot
@@ -3889,7 +3906,7 @@
                 }
 
                 if (more && !atEnd) {
-                    invalidate();
+                    if (atEdge) invalidate();
                     mLastFlingY = y;
                     post(this);
                 } else {
@@ -4431,7 +4448,7 @@
     }
 
     private void createScrollingCache() {
-        if (mScrollingCacheEnabled && !mCachingStarted) {
+        if (mScrollingCacheEnabled && !mCachingStarted && !isHardwareAccelerated()) {
             setChildrenDrawnWithCacheEnabled(true);
             setChildrenDrawingCacheEnabled(true);
             mCachingStarted = mCachingActive = true;
@@ -4439,23 +4456,25 @@
     }
 
     private void clearScrollingCache() {
-        if (mClearScrollingCache == null) {
-            mClearScrollingCache = new Runnable() {
-                public void run() {
-                    if (mCachingStarted) {
-                        mCachingStarted = mCachingActive = false;
-                        setChildrenDrawnWithCacheEnabled(false);
-                        if ((mPersistentDrawingCache & PERSISTENT_SCROLLING_CACHE) == 0) {
-                            setChildrenDrawingCacheEnabled(false);
-                        }
-                        if (!isAlwaysDrawnWithCacheEnabled()) {
-                            invalidate();
+        if (!isHardwareAccelerated()) {
+            if (mClearScrollingCache == null) {
+                mClearScrollingCache = new Runnable() {
+                    public void run() {
+                        if (mCachingStarted) {
+                            mCachingStarted = mCachingActive = false;
+                            setChildrenDrawnWithCacheEnabled(false);
+                            if ((mPersistentDrawingCache & PERSISTENT_SCROLLING_CACHE) == 0) {
+                                setChildrenDrawingCacheEnabled(false);
+                            }
+                            if (!isAlwaysDrawnWithCacheEnabled()) {
+                                invalidate();
+                            }
                         }
                     }
-                }
-            };
+                };
+            }
+            post(mClearScrollingCache);
         }
-        post(mClearScrollingCache);
     }
 
     /**
@@ -4599,14 +4618,18 @@
             mRecycler.removeSkippedScrap();
         }
 
+        // invalidate before moving the children to avoid unnecessary invalidate
+        // calls to bubble up from the children all the way to the top
+        if (!awakenScrollBars()) {
+            invalidate();
+        }
+
         offsetChildrenTopAndBottom(incrementalDeltaY);
 
         if (down) {
             mFirstPosition += count;
         }
 
-        invalidate();
-
         final int absIncrementalDeltaY = Math.abs(incrementalDeltaY);
         if (spaceAbove < absIncrementalDeltaY || spaceBelow < absIncrementalDeltaY) {
             fillGap(down);
@@ -4629,7 +4652,6 @@
         mBlockLayoutRequests = false;
 
         invokeOnItemScrollListener();
-        awakenScrollBars();
 
         return false;
     }
diff --git a/core/java/android/widget/EdgeEffect.java b/core/java/android/widget/EdgeEffect.java
index 83aa8ba..c1f31bb 100644
--- a/core/java/android/widget/EdgeEffect.java
+++ b/core/java/android/widget/EdgeEffect.java
@@ -16,6 +16,7 @@
 
 package android.widget;
 
+import android.graphics.Rect;
 import com.android.internal.R;
 
 import android.content.Context;
@@ -45,6 +46,7 @@
  * {@link #draw(Canvas)} method.</p>
  */
 public class EdgeEffect {
+    @SuppressWarnings("UnusedDeclaration")
     private static final String TAG = "EdgeEffect";
 
     // Time it will take the effect to fully recede in ms
@@ -57,10 +59,7 @@
     private static final int PULL_DECAY_TIME = 1000;
 
     private static final float MAX_ALPHA = 1.f;
-    private static final float HELD_EDGE_ALPHA = 0.7f;
     private static final float HELD_EDGE_SCALE_Y = 0.5f;
-    private static final float HELD_GLOW_ALPHA = 0.5f;
-    private static final float HELD_GLOW_SCALE_Y = 0.5f;
 
     private static final float MAX_GLOW_HEIGHT = 4.f;
 
@@ -76,7 +75,9 @@
     private final Drawable mGlow;
     private int mWidth;
     private int mHeight;
-    private final int MIN_WIDTH = 300;
+    private int mX;
+    private int mY;
+    private static final int MIN_WIDTH = 300;
     private final int mMinWidth;
 
     private float mEdgeAlpha;
@@ -119,6 +120,8 @@
     private int mState = STATE_IDLE;
 
     private float mPullDistance;
+    
+    private final Rect mBounds = new Rect();
 
     /**
      * Construct a new EdgeEffect with a theme appropriate for the provided context.
@@ -145,6 +148,29 @@
     }
 
     /**
+     * Set the position of this edge effect in pixels. This position is
+     * only used by {@link #getBounds()}.
+     * 
+     * @param x The position of the edge effect on the X axis
+     * @param y The position of the edge effect on the Y axis
+     */
+    void setPosition(int x, int y) {
+        mX = x;
+        mY = y;
+    }
+
+    boolean isIdle() {
+        return mState == STATE_IDLE;
+    }
+
+    /**
+     * Returns the height of the effect itself.
+     */
+    int getHeight() {
+        return Math.max(mGlow.getBounds().height(), mEdge.getBounds().height());
+    }
+    
+    /**
      * Reports if this EdgeEffect's animation is finished. If this method returns false
      * after a call to {@link #draw(Canvas)} the host widget should schedule another
      * drawing pass to continue the animation.
@@ -301,7 +327,6 @@
         update();
 
         final int edgeHeight = mEdge.getIntrinsicHeight();
-        final int edgeWidth = mEdge.getIntrinsicWidth();
         final int glowHeight = mGlow.getIntrinsicHeight();
         final int glowWidth = mGlow.getIntrinsicWidth();
 
@@ -334,9 +359,23 @@
         }
         mEdge.draw(canvas);
 
+        if (mState == STATE_RECEDE && glowBottom == 0 && edgeBottom == 0) {
+            mState = STATE_IDLE;
+        }
+
         return mState != STATE_IDLE;
     }
 
+    /**
+     * Returns the bounds of the edge effect.
+     */
+    public Rect getBounds() {
+        mBounds.set(mGlow.getBounds());
+        mBounds.union(mEdge.getBounds());
+        mBounds.offset(mX, mY);
+        return mBounds;
+    }
+
     private void update() {
         final long time = AnimationUtils.currentAnimationTimeMillis();
         final float t = Math.min((time - mStartTime) / mDuration, 1.f);
diff --git a/core/java/android/widget/GridLayout.java b/core/java/android/widget/GridLayout.java
index fc08cc5..60dd55c 100644
--- a/core/java/android/widget/GridLayout.java
+++ b/core/java/android/widget/GridLayout.java
@@ -29,8 +29,8 @@
 import android.view.ViewGroup;
 import android.view.accessibility.AccessibilityEvent;
 import android.view.accessibility.AccessibilityNodeInfo;
-
 import com.android.internal.R;
+import android.widget.RemoteViews.RemoteView;
 
 import java.lang.reflect.Array;
 import java.util.ArrayList;
@@ -146,6 +146,7 @@
  * @attr ref android.R.styleable#GridLayout_rowOrderPreserved
  * @attr ref android.R.styleable#GridLayout_columnOrderPreserved
  */
+@RemoteView
 public class GridLayout extends ViewGroup {
 
     // Public constants
@@ -234,7 +235,6 @@
 
     final Axis horizontalAxis = new Axis(true);
     final Axis verticalAxis = new Axis(false);
-    boolean layoutParamsValid = false;
     int orientation = DEFAULT_ORIENTATION;
     boolean useDefaultMargins = DEFAULT_USE_DEFAULT_MARGINS;
     int alignmentMode = DEFAULT_ALIGNMENT_MODE;
@@ -713,12 +713,10 @@
 
             minor = minor + minorSpan;
         }
-        lastLayoutParamsHashCode = computeLayoutParamsHashCode();
-        invalidateStructure();
     }
 
     private void invalidateStructure() {
-        layoutParamsValid = false;
+        lastLayoutParamsHashCode = UNINITIALIZED_HASH;
         horizontalAxis.invalidateStructure();
         verticalAxis.invalidateStructure();
         // This can end up being done twice. Better twice than not at all.
@@ -742,10 +740,6 @@
     }
 
     final LayoutParams getLayoutParams(View c) {
-        if (!layoutParamsValid) {
-            validateLayoutParams();
-            layoutParamsValid = true;
-        }
         return (LayoutParams) c.getLayoutParams();
     }
 
@@ -874,20 +868,22 @@
         return result;
     }
 
-    private void checkForLayoutParamsModification() {
-        int layoutParamsHashCode = computeLayoutParamsHashCode();
-        if (lastLayoutParamsHashCode != UNINITIALIZED_HASH &&
-                lastLayoutParamsHashCode != layoutParamsHashCode) {
-            invalidateStructure();
+    private void consistencyCheck() {
+        if (lastLayoutParamsHashCode == UNINITIALIZED_HASH) {
+            validateLayoutParams();
+            lastLayoutParamsHashCode = computeLayoutParamsHashCode();
+        } else if (lastLayoutParamsHashCode != computeLayoutParamsHashCode()) {
             Log.w(TAG, "The fields of some layout parameters were modified in between layout " +
                     "operations. Check the javadoc for GridLayout.LayoutParams#rowSpec.");
+            invalidateStructure();
+            consistencyCheck();
         }
     }
 
     // Measurement
 
     private void measureChildWithMargins2(View child, int parentWidthSpec, int parentHeightSpec,
-                                          int childWidth, int childHeight) {
+            int childWidth, int childHeight) {
         int childWidthSpec = getChildMeasureSpec(parentWidthSpec,
                 mPaddingLeft + mPaddingRight + getTotalMargin(child, true), childWidth);
         int childHeightSpec = getChildMeasureSpec(parentHeightSpec,
@@ -923,7 +919,7 @@
 
     @Override
     protected void onMeasure(int widthSpec, int heightSpec) {
-        checkForLayoutParamsModification();
+        consistencyCheck();
 
         /** If we have been called by {@link View#measure(int, int)}, one of width or height
          *  is  likely to have changed. We must invalidate if so. */
@@ -993,7 +989,7 @@
      */
     @Override
     protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
-        checkForLayoutParamsModification();
+        consistencyCheck();
 
         int targetWidth = right - left;
         int targetHeight = bottom - top;
@@ -1250,7 +1246,7 @@
         }
 
         private void include(List<Arc> arcs, Interval key, MutableInt size,
-                             boolean ignoreIfAlreadyPresent) {
+                boolean ignoreIfAlreadyPresent) {
             /*
             Remove self referential links.
             These appear:
@@ -1429,8 +1425,8 @@
                 int dst = arc.span.max;
                 int value = arc.value.value;
                 result.append((src < dst) ?
-                        var + dst + " - " + var + src + " > " + value :
-                        var + src + " - " + var + dst + " < " + -value);
+                        var + dst + "-" + var + src + ">=" + value :
+                        var + src + "-" + var + dst + "<=" + -value);
 
             }
             return result.toString();
diff --git a/include/media/mediaplayer.h b/include/media/mediaplayer.h
index 662dd13af..a68ab4e 100644
--- a/include/media/mediaplayer.h
+++ b/include/media/mediaplayer.h
@@ -120,6 +120,9 @@
     MEDIA_INFO_NOT_SEEKABLE = 801,
     // New media metadata is available.
     MEDIA_INFO_METADATA_UPDATE = 802,
+
+    //9xx
+    MEDIA_INFO_TIMED_TEXT_ERROR = 900,
 };
 
 
@@ -140,9 +143,6 @@
 // The same enum space is used for both set and get, in case there are future keys that
 // can be both set and get.  But as of now, all parameters are either set only or get only.
 enum media_parameter_keys {
-    KEY_PARAMETER_TIMED_TEXT_TRACK_INDEX = 1000,                // set only
-    KEY_PARAMETER_TIMED_TEXT_ADD_OUT_OF_BAND_SOURCE = 1001,     // set only
-
     // Streaming/buffering parameters
     KEY_PARAMETER_CACHE_STAT_COLLECT_FREQ_MS = 1100,            // set only
 
@@ -155,6 +155,23 @@
     KEY_PARAMETER_PLAYBACK_RATE_PERMILLE = 1300,                // set only
 };
 
+// Keep INVOKE_ID_* in sync with MediaPlayer.java.
+enum media_player_invoke_ids {
+    INVOKE_ID_GET_TRACK_INFO = 1,
+    INVOKE_ID_ADD_EXTERNAL_SOURCE = 2,
+    INVOKE_ID_ADD_EXTERNAL_SOURCE_FD = 3,
+    INVOKE_ID_SELECT_TRACK = 4,
+    INVOKE_ID_UNSELECT_TRACK = 5,
+};
+
+// Keep MEDIA_TRACK_TYPE_* in sync with MediaPlayer.java.
+enum media_track_type {
+    MEDIA_TRACK_TYPE_UNKNOWN = 0,
+    MEDIA_TRACK_TYPE_VIDEO = 1,
+    MEDIA_TRACK_TYPE_AUDIO = 2,
+    MEDIA_TRACK_TYPE_TIMEDTEXT = 3,
+};
+
 // ----------------------------------------------------------------------------
 // ref-counted object for callbacks
 class MediaPlayerListener: virtual public RefBase
diff --git a/include/media/stagefright/MediaDefs.h b/include/media/stagefright/MediaDefs.h
index 2eb259e..457d5d7 100644
--- a/include/media/stagefright/MediaDefs.h
+++ b/include/media/stagefright/MediaDefs.h
@@ -54,6 +54,7 @@
 extern const char *MEDIA_MIMETYPE_CONTAINER_WVM;
 
 extern const char *MEDIA_MIMETYPE_TEXT_3GPP;
+extern const char *MEDIA_MIMETYPE_TEXT_SUBRIP;
 
 }  // namespace android
 
diff --git a/include/media/stagefright/timedtext/TimedTextDriver.h b/include/media/stagefright/timedtext/TimedTextDriver.h
index efedb6e..b9752df 100644
--- a/include/media/stagefright/timedtext/TimedTextDriver.h
+++ b/include/media/stagefright/timedtext/TimedTextDriver.h
@@ -37,26 +37,26 @@
 
     ~TimedTextDriver();
 
-    // TODO: pause-resume pair seems equivalent to stop-start pair.
-    // Check if it is replaceable with stop-start.
     status_t start();
-    status_t stop();
     status_t pause();
-    status_t resume();
+    status_t selectTrack(int32_t index);
+    status_t unselectTrack(int32_t index);
 
     status_t seekToAsync(int64_t timeUs);
 
     status_t addInBandTextSource(const sp<MediaSource>& source);
-    status_t addOutOfBandTextSource(const Parcel &request);
+    status_t addOutOfBandTextSource(const char *uri, const char *mimeType);
+    // Caller owns the file desriptor and caller is responsible for closing it.
+    status_t addOutOfBandTextSource(
+            int fd, off64_t offset, size_t length, const char *mimeType);
 
-    status_t setTimedTextTrackIndex(int32_t index);
+    void getTrackInfo(Parcel *parcel);
 
 private:
     Mutex mLock;
 
     enum State {
         UNINITIALIZED,
-        STOPPED,
         PLAYING,
         PAUSED,
     };
@@ -67,11 +67,11 @@
 
     // Variables to be guarded by mLock.
     State mState;
-    Vector<sp<TimedTextSource> > mTextInBandVector;
-    Vector<sp<TimedTextSource> > mTextOutOfBandVector;
+    int32_t mCurrentTrackIndex;
+    Vector<sp<TimedTextSource> > mTextSourceVector;
     // -- End of variables to be guarded by mLock
 
-    status_t setTimedTextTrackIndex_l(int32_t index);
+    status_t selectTrack_l(int32_t index);
 
     DISALLOW_EVIL_CONSTRUCTORS(TimedTextDriver);
 };
diff --git a/media/java/android/media/MediaPlayer.java b/media/java/android/media/MediaPlayer.java
index f5fa877..d92180d 100644
--- a/media/java/android/media/MediaPlayer.java
+++ b/media/java/android/media/MediaPlayer.java
@@ -24,6 +24,7 @@
 import android.os.Looper;
 import android.os.Message;
 import android.os.Parcel;
+import android.os.Parcelable;
 import android.os.ParcelFileDescriptor;
 import android.os.PowerManager;
 import android.util.Log;
@@ -455,6 +456,22 @@
  *     <td>Successful invoke of this method in a valid state transfers the
  *         object to the <em>Stopped</em> state. Calling this method in an
  *         invalid state transfers the object to the <em>Error</em> state.</p></td></tr>
+ * <tr><td>getTrackInfo </p></td>
+ *     <td>{Prepared, Started, Stopped, Paused, PlaybackCompleted}</p></td>
+ *     <td>{Idle, Initialized, Error}</p></td>
+ *     <td>Successful invoke of this method does not change the state.</p></td></tr>
+ * <tr><td>addExternalSource </p></td>
+ *     <td>{Prepared, Started, Stopped, Paused, PlaybackCompleted}</p></td>
+ *     <td>{Idle, Initialized, Error}</p></td>
+ *     <td>Successful invoke of this method does not change the state.</p></td></tr>
+ * <tr><td>selectTrack </p></td>
+ *     <td>{Prepared, Started, Stopped, Paused, PlaybackCompleted}</p></td>
+ *     <td>{Idle, Initialized, Error}</p></td>
+ *     <td>Successful invoke of this method does not change the state.</p></td></tr>
+ * <tr><td>disableTrack </p></td>
+ *     <td>{Prepared, Started, Stopped, Paused, PlaybackCompleted}</p></td>
+ *     <td>{Idle, Initialized, Error}</p></td>
+ *     <td>Successful invoke of this method does not change the state.</p></td></tr>
  *
  * </table>
  *
@@ -572,6 +589,15 @@
      */
     private native void _setVideoSurface(Surface surface);
 
+    /* Do not change these values (starting with INVOKE_ID) without updating
+     * their counterparts in include/media/mediaplayer.h!
+     */
+    private static final int INVOKE_ID_GET_TRACK_INFO = 1;
+    private static final int INVOKE_ID_ADD_EXTERNAL_SOURCE = 2;
+    private static final int INVOKE_ID_ADD_EXTERNAL_SOURCE_FD = 3;
+    private static final int INVOKE_ID_SELECT_TRACK = 4;
+    private static final int INVOKE_ID_UNSELECT_TRACK = 5;
+
     /**
      * Create a request parcel which can be routed to the native media
      * player using {@link #invoke(Parcel, Parcel)}. The Parcel
@@ -1312,23 +1338,6 @@
     /* Do not change these values (starting with KEY_PARAMETER) without updating
      * their counterparts in include/media/mediaplayer.h!
      */
-    /*
-     * Key used in setParameter method.
-     * Indicates the index of the timed text track to be enabled/disabled.
-     * The index includes both the in-band and out-of-band timed text.
-     * The index should start from in-band text if any. Application can retrieve the number
-     * of in-band text tracks by using MediaMetadataRetriever::extractMetadata().
-     * Note it might take a few hundred ms to scan an out-of-band text file
-     * before displaying it.
-     */
-    private static final int KEY_PARAMETER_TIMED_TEXT_TRACK_INDEX = 1000;
-    /*
-     * Key used in setParameter method.
-     * Used to add out-of-band timed text source path.
-     * Application can add multiple text sources by calling setParameter() with
-     * KEY_PARAMETER_TIMED_TEXT_ADD_OUT_OF_BAND_SOURCE multiple times.
-     */
-    private static final int KEY_PARAMETER_TIMED_TEXT_ADD_OUT_OF_BAND_SOURCE = 1001;
 
     // There are currently no defined keys usable from Java with get*Parameter.
     // But if any keys are defined, the order must be kept in sync with include/media/mediaplayer.h.
@@ -1373,7 +1382,7 @@
         return ret;
     }
 
-    /**
+    /*
      * Gets the value of the parameter indicated by key.
      * @param key key indicates the parameter to get.
      * @param reply value of the parameter to get.
@@ -1435,7 +1444,7 @@
      */
     public native void setAuxEffectSendLevel(float level);
 
-    /**
+    /*
      * @param request Parcel destinated to the media player. The
      *                Interface token must be set to the IMediaPlayer
      *                one to be routed correctly through the system.
@@ -1445,7 +1454,7 @@
     private native final int native_invoke(Parcel request, Parcel reply);
 
 
-    /**
+    /*
      * @param update_only If true fetch only the set of metadata that have
      *                    changed since the last invocation of getMetadata.
      *                    The set is built using the unfiltered
@@ -1462,7 +1471,7 @@
                                                     boolean apply_filter,
                                                     Parcel reply);
 
-    /**
+    /*
      * @param request Parcel with the 2 serialized lists of allowed
      *                metadata types followed by the one to be
      *                dropped. Each list starts with an integer
@@ -1476,33 +1485,289 @@
     private native final void native_finalize();
 
     /**
-     * @param index The index of the text track to be turned on.
-     * @return true if the text track is enabled successfully.
+     * Class for MediaPlayer to return each audio/video/subtitle track's metadata.
+     *
+     * {@see #getTrackInfo()}.
      * {@hide}
      */
-    public boolean enableTimedTextTrackIndex(int index) {
-        if (index < 0) {
-            return false;
+    static public class TrackInfo implements Parcelable {
+        /**
+         * Gets the track type.
+         * @return TrackType which indicates if the track is video, audio, timed text.
+         */
+        public int getTrackType() {
+            return mTrackType;
         }
-        return setParameter(KEY_PARAMETER_TIMED_TEXT_TRACK_INDEX, index);
+
+        /**
+         * Gets the language code of the track.
+         * @return a language code in either way of ISO-639-1 or ISO-639-2.
+         * When the language is unknown or could not be determined,
+         * ISO-639-2 language code, "und", is returned.
+         */
+        public String getLanguage() {
+            return mLanguage;
+        }
+
+        public static final int MEDIA_TRACK_TYPE_UNKNOWN = 0;
+        public static final int MEDIA_TRACK_TYPE_VIDEO = 1;
+        public static final int MEDIA_TRACK_TYPE_AUDIO = 2;
+        public static final int MEDIA_TRACK_TYPE_TIMEDTEXT = 3;
+
+        final int mTrackType;
+        final String mLanguage;
+
+        TrackInfo(Parcel in) {
+            mTrackType = in.readInt();
+            mLanguage = in.readString();
+        }
+
+        /*
+         * No special parcel contents. Keep it as hide.
+         * {@hide}
+         */
+        @Override
+        public int describeContents() {
+            return 0;
+        }
+
+        /*
+         * {@hide}
+         */
+        @Override
+        public void writeToParcel(Parcel dest, int flags) {
+            dest.writeInt(mTrackType);
+            dest.writeString(mLanguage);
+        }
+
+        /**
+         * Used to read a TrackInfo from a Parcel.
+         */
+        static final Parcelable.Creator<TrackInfo> CREATOR
+                = new Parcelable.Creator<TrackInfo>() {
+                    @Override
+                    public TrackInfo createFromParcel(Parcel in) {
+                        return new TrackInfo(in);
+                    }
+
+                    @Override
+                    public TrackInfo[] newArray(int size) {
+                        return new TrackInfo[size];
+                    }
+                };
+
+    };
+
+    /**
+     * Returns an array of track information.
+     *
+     * @return Array of track info. null if an error occured.
+     * {@hide}
+     */
+    // FIXME: It returns timed text tracks' information for now. Other types of tracks will be
+    // supported in future.
+    public TrackInfo[] getTrackInfo() {
+        Parcel request = Parcel.obtain();
+        Parcel reply = Parcel.obtain();
+        request.writeInterfaceToken(IMEDIA_PLAYER);
+        request.writeInt(INVOKE_ID_GET_TRACK_INFO);
+        invoke(request, reply);
+        TrackInfo trackInfo[] = reply.createTypedArray(TrackInfo.CREATOR);
+        return trackInfo;
+    }
+
+    /*
+     * A helper function to check if the mime type is supported by media framework.
+     */
+    private boolean availableMimeTypeForExternalSource(String mimeType) {
+        if (mimeType == MEDIA_MIMETYPE_TEXT_SUBRIP) {
+            return true;
+        }
+        return false;
+    }
+
+    /* TODO: Limit the total number of external timed text source to a reasonable number.
+     */
+    /**
+     * Adds an external source file.
+     *
+     * Currently supported format is SubRip with the file extension .srt, case insensitive.
+     * Note that a single external source may contain multiple tracks in it.
+     * One can find the total number of available tracks using {@link #getTrackInfo()} to see what
+     * additional tracks become available after this method call.
+     *
+     * @param path The file path of external source file.
+     * {@hide}
+     */
+    // FIXME: define error codes and throws exceptions according to the error codes.
+    // (IllegalStateException, IOException).
+    public void addExternalSource(String path, String mimeType)
+            throws IllegalArgumentException {
+        if (!availableMimeTypeForExternalSource(mimeType)) {
+            throw new IllegalArgumentException("Illegal mimeType for external source: " + mimeType);
+        }
+
+        Parcel request = Parcel.obtain();
+        Parcel reply = Parcel.obtain();
+        request.writeInterfaceToken(IMEDIA_PLAYER);
+        request.writeInt(INVOKE_ID_ADD_EXTERNAL_SOURCE);
+        request.writeString(path);
+        request.writeString(mimeType);
+        invoke(request, reply);
     }
 
     /**
-     * Enables the first timed text track if any.
-     * @return true if the text track is enabled successfully
+     * Adds an external source file (Uri).
+     *
+     * Currently supported format is SubRip with the file extension .srt, case insensitive.
+     * Note that a single external source may contain multiple tracks in it.
+     * One can find the total number of available tracks using {@link #getTrackInfo()} to see what
+     * additional tracks become available after this method call.
+     *
+     * @param context the Context to use when resolving the Uri
+     * @param uri the Content URI of the data you want to play
      * {@hide}
      */
-    public boolean enableTimedText() {
-        return enableTimedTextTrackIndex(0);
+    // FIXME: define error codes and throws exceptions according to the error codes.
+    // (IllegalStateException, IOException).
+    public void addExternalSource(Context context, Uri uri, String mimeType)
+            throws IOException, IllegalArgumentException {
+        String scheme = uri.getScheme();
+        if(scheme == null || scheme.equals("file")) {
+            addExternalSource(uri.getPath(), mimeType);
+            return;
+        }
+
+        AssetFileDescriptor fd = null;
+        try {
+            ContentResolver resolver = context.getContentResolver();
+            fd = resolver.openAssetFileDescriptor(uri, "r");
+            if (fd == null) {
+                return;
+            }
+            addExternalSource(fd.getFileDescriptor(), mimeType);
+            return;
+        } catch (SecurityException ex) {
+        } catch (IOException ex) {
+        } finally {
+            if (fd != null) {
+                fd.close();
+            }
+        }
+
+        // TODO: try server side.
+    }
+
+    /* Do not change these values without updating their counterparts
+     * in include/media/stagefright/MediaDefs.h and media/libstagefright/MediaDefs.cpp!
+     */
+    /**
+     * MIME type for SubRip (SRT) container. Used in {@link #addExternalSource()} APIs.
+     * {@hide}
+     */
+    public static final String MEDIA_MIMETYPE_TEXT_SUBRIP = "application/x-subrip";
+
+    /**
+     * Adds an external source file (FileDescriptor).
+     * It is the caller's responsibility to close the file descriptor.
+     * It is safe to do so as soon as this call returns.
+     *
+     * Currently supported format is SubRip with the file extension .srt, case insensitive.
+     * Note that a single external source may contain multiple tracks in it.
+     * One can find the total number of available tracks using {@link #getTrackInfo()} to see what
+     * additional tracks become available after this method call.
+     *
+     * @param fd the FileDescriptor for the file you want to play
+     * @param mimeType A MIME type for the content. It can be null.
+     * <ul>
+     * <li>{@link #MEDIA_MIMETYPE_TEXT_SUBRIP}
+     * </ul>
+     * {@hide}
+     */
+    // FIXME: define error codes and throws exceptions according to the error codes.
+    // (IllegalStateException, IOException).
+    public void addExternalSource(FileDescriptor fd, String mimeType)
+            throws IllegalArgumentException {
+        // intentionally less than LONG_MAX
+        addExternalSource(fd, 0, 0x7ffffffffffffffL, mimeType);
     }
 
     /**
-     * Disables timed text display.
-     * @return true if the text track is disabled successfully.
+     * Adds an external timed text file (FileDescriptor).
+     * It is the caller's responsibility to close the file descriptor.
+     * It is safe to do so as soon as this call returns.
+     *
+     * Currently supported format is SubRip with the file extension .srt, case insensitive.
+     * Note that a single external source may contain multiple tracks in it.
+     * One can find the total number of available tracks using {@link #getTrackInfo()} to see what
+     * additional tracks become available after this method call.
+     *
+     * @param fd the FileDescriptor for the file you want to play
+     * @param offset the offset into the file where the data to be played starts, in bytes
+     * @param length the length in bytes of the data to be played
+     * @param mimeType A MIME type for the content. It can be null.
      * {@hide}
      */
-    public boolean disableTimedText() {
-        return setParameter(KEY_PARAMETER_TIMED_TEXT_TRACK_INDEX, -1);
+    // FIXME: define error codes and throws exceptions according to the error codes.
+    // (IllegalStateException, IOException).
+    public void addExternalSource(FileDescriptor fd, long offset, long length, String mimeType)
+            throws IllegalArgumentException {
+        if (!availableMimeTypeForExternalSource(mimeType)) {
+            throw new IllegalArgumentException("Illegal mimeType for external source: " + mimeType);
+        }
+        Parcel request = Parcel.obtain();
+        Parcel reply = Parcel.obtain();
+        request.writeInterfaceToken(IMEDIA_PLAYER);
+        request.writeInt(INVOKE_ID_ADD_EXTERNAL_SOURCE_FD);
+        request.writeFileDescriptor(fd);
+        request.writeLong(offset);
+        request.writeLong(length);
+        request.writeString(mimeType);
+        invoke(request, reply);
+    }
+
+    /**
+     * Selects a track.
+     * <p>
+     * If a MediaPlayer is in invalid state, it throws exception.
+     * If a MediaPlayer is in Started state, the selected track will be presented immediately.
+     * If a MediaPlayer is not in Started state, it just marks the track to be played.
+     * </p>
+     * <p>
+     * In any valid state, if it is called multiple times on the same type of track (ie. Video,
+     * Audio, Timed Text), the most recent one will be chosen.
+     * </p>
+     * <p>
+     * The first audio and video tracks will be selected by default, even though this function is not
+     * called. However, no timed text track will be selected until this function is called.
+     * </p>
+     * {@hide}
+     */
+    // FIXME: define error codes and throws exceptions according to the error codes.
+    // (IllegalStateException, IOException, IllegalArgumentException).
+    public void selectTrack(int index) {
+        Parcel request = Parcel.obtain();
+        Parcel reply = Parcel.obtain();
+        request.writeInterfaceToken(IMEDIA_PLAYER);
+        request.writeInt(INVOKE_ID_SELECT_TRACK);
+        request.writeInt(index);
+        invoke(request, reply);
+    }
+
+    /**
+     * Unselect a track.
+     * If the track identified by index has not been selected before, it throws an exception.
+     * {@hide}
+     */
+    // FIXME: define error codes and throws exceptions according to the error codes.
+    // (IllegalStateException, IOException, IllegalArgumentException).
+    public void unselectTrack(int index) {
+        Parcel request = Parcel.obtain();
+        Parcel reply = Parcel.obtain();
+        request.writeInterfaceToken(IMEDIA_PLAYER);
+        request.writeInt(INVOKE_ID_UNSELECT_TRACK);
+        request.writeInt(index);
+        invoke(request, reply);
     }
 
     /**
@@ -1641,14 +1906,14 @@
                 // No real default action so far.
                 return;
             case MEDIA_TIMED_TEXT:
-                if (mOnTimedTextListener != null) {
-                    if (msg.obj == null) {
-                        mOnTimedTextListener.onTimedText(mMediaPlayer, null);
-                    } else {
-                        if (msg.obj instanceof byte[]) {
-                            TimedText text = new TimedText((byte[])(msg.obj));
-                            mOnTimedTextListener.onTimedText(mMediaPlayer, text);
-                        }
+                if (mOnTimedTextListener == null)
+                    return;
+                if (msg.obj == null) {
+                    mOnTimedTextListener.onTimedText(mMediaPlayer, null);
+                } else {
+                    if (msg.obj instanceof byte[]) {
+                        TimedText text = new TimedText((byte[])(msg.obj));
+                        mOnTimedTextListener.onTimedText(mMediaPlayer, text);
                     }
                 }
                 return;
@@ -1663,7 +1928,7 @@
         }
     }
 
-    /**
+    /*
      * Called from native code when an interesting event happens.  This method
      * just uses the EventHandler system to post the event back to the main app thread.
      * We use a weak reference to the original MediaPlayer object so that the native
@@ -1977,6 +2242,13 @@
      */
     public static final int MEDIA_INFO_METADATA_UPDATE = 802;
 
+    /** Failed to handle timed text track properly.
+     * @see android.media.MediaPlayer.OnInfoListener
+     *
+     * {@hide}
+     */
+    public static final int MEDIA_INFO_TIMED_TEXT_ERROR = 900;
+
     /**
      * Interface definition of a callback to be invoked to communicate some
      * info and/or warning about the media or its playback.
diff --git a/media/libmediaplayerservice/StagefrightPlayer.cpp b/media/libmediaplayerservice/StagefrightPlayer.cpp
index 052ebf0..619c149 100644
--- a/media/libmediaplayerservice/StagefrightPlayer.cpp
+++ b/media/libmediaplayerservice/StagefrightPlayer.cpp
@@ -166,7 +166,8 @@
 }
 
 status_t StagefrightPlayer::invoke(const Parcel &request, Parcel *reply) {
-    return INVALID_OPERATION;
+    ALOGV("invoke()");
+    return mPlayer->invoke(request, reply);
 }
 
 void StagefrightPlayer::setAudioSink(const sp<AudioSink> &audioSink) {
diff --git a/media/libstagefright/AwesomePlayer.cpp b/media/libstagefright/AwesomePlayer.cpp
index 9e00bb3..b4cb1ab 100644
--- a/media/libstagefright/AwesomePlayer.cpp
+++ b/media/libstagefright/AwesomePlayer.cpp
@@ -1114,7 +1114,7 @@
         modifyFlags(AUDIO_RUNNING, CLEAR);
     }
 
-    if (mFlags & TEXTPLAYER_STARTED) {
+    if (mFlags & TEXTPLAYER_INITIALIZED) {
         mTextDriver->pause();
         modifyFlags(TEXT_RUNNING, CLEAR);
     }
@@ -1268,32 +1268,6 @@
     return OK;
 }
 
-status_t AwesomePlayer::setTimedTextTrackIndex(int32_t index) {
-    if (mTextDriver != NULL) {
-        if (index >= 0) { // to turn on a text track
-            status_t err = mTextDriver->setTimedTextTrackIndex(index);
-            if (err != OK) {
-                return err;
-            }
-
-            modifyFlags(TEXT_RUNNING, SET);
-            modifyFlags(TEXTPLAYER_STARTED, SET);
-            return OK;
-        } else { // to turn off the text track display
-            if (mFlags  & TEXT_RUNNING) {
-                modifyFlags(TEXT_RUNNING, CLEAR);
-            }
-            if (mFlags  & TEXTPLAYER_STARTED) {
-                modifyFlags(TEXTPLAYER_STARTED, CLEAR);
-            }
-
-            return mTextDriver->setTimedTextTrackIndex(index);
-        }
-    } else {
-        return INVALID_OPERATION;
-    }
-}
-
 status_t AwesomePlayer::seekTo_l(int64_t timeUs) {
     if (mFlags & CACHE_UNDERRUN) {
         modifyFlags(CACHE_UNDERRUN, CLEAR);
@@ -1315,7 +1289,7 @@
 
     seekAudioIfNecessary_l();
 
-    if (mFlags & TEXTPLAYER_STARTED) {
+    if (mFlags & TEXTPLAYER_INITIALIZED) {
         mTextDriver->seekToAsync(mSeekTimeUs);
     }
 
@@ -1691,8 +1665,8 @@
         }
     }
 
-    if ((mFlags & TEXTPLAYER_STARTED) && !(mFlags & (TEXT_RUNNING | SEEK_PREVIEW))) {
-        mTextDriver->resume();
+    if ((mFlags & TEXTPLAYER_INITIALIZED) && !(mFlags & (TEXT_RUNNING | SEEK_PREVIEW))) {
+        mTextDriver->start();
         modifyFlags(TEXT_RUNNING, SET);
     }
 
@@ -2232,20 +2206,6 @@
 
 status_t AwesomePlayer::setParameter(int key, const Parcel &request) {
     switch (key) {
-        case KEY_PARAMETER_TIMED_TEXT_TRACK_INDEX:
-        {
-            Mutex::Autolock autoLock(mTimedTextLock);
-            return setTimedTextTrackIndex(request.readInt32());
-        }
-        case KEY_PARAMETER_TIMED_TEXT_ADD_OUT_OF_BAND_SOURCE:
-        {
-            Mutex::Autolock autoLock(mTimedTextLock);
-            if (mTextDriver == NULL) {
-                mTextDriver = new TimedTextDriver(mListener);
-            }
-
-            return mTextDriver->addOutOfBandTextSource(request);
-        }
         case KEY_PARAMETER_CACHE_STAT_COLLECT_FREQ_MS:
         {
             return setCacheStatCollectFreq(request);
@@ -2294,6 +2254,103 @@
     }
 }
 
+status_t AwesomePlayer::invoke(const Parcel &request, Parcel *reply) {
+    if (NULL == reply) {
+        return android::BAD_VALUE;
+    }
+    int32_t methodId;
+    status_t ret = request.readInt32(&methodId);
+    if (ret != android::OK) {
+        return ret;
+    }
+    switch(methodId) {
+        case INVOKE_ID_GET_TRACK_INFO:
+        {
+            Mutex::Autolock autoLock(mTimedTextLock);
+            if (mTextDriver == NULL) {
+                return INVALID_OPERATION;
+            }
+            mTextDriver->getTrackInfo(reply);
+            return OK;
+        }
+        case INVOKE_ID_ADD_EXTERNAL_SOURCE:
+        {
+            Mutex::Autolock autoLock(mTimedTextLock);
+            if (mTextDriver == NULL) {
+                mTextDriver = new TimedTextDriver(mListener);
+            }
+            // String values written in Parcel are UTF-16 values.
+            String16 uri16 = request.readString16();
+            const char *uri = NULL;
+            if (uri16 != NULL) {
+                uri = String8(uri16).string();
+            }
+            String16 mimeType16 = request.readString16();
+            const char *mimeType = NULL;
+            if (mimeType16 != NULL) {
+                mimeType = String8(mimeType16).string();
+            }
+            return mTextDriver->addOutOfBandTextSource(uri, mimeType);
+        }
+        case INVOKE_ID_ADD_EXTERNAL_SOURCE_FD:
+        {
+            Mutex::Autolock autoLock(mTimedTextLock);
+            if (mTextDriver == NULL) {
+                mTextDriver = new TimedTextDriver(mListener);
+            }
+            int fd         = request.readFileDescriptor();
+            off64_t offset = request.readInt64();
+            size_t length  = request.readInt64();
+            String16 mimeType16 = request.readString16();
+            const char *mimeType = NULL;
+            if (mimeType16 != NULL) {
+                mimeType = String8(mimeType16).string();
+            }
+
+            return mTextDriver->addOutOfBandTextSource(
+                    fd, offset, length, mimeType);
+        }
+        case INVOKE_ID_SELECT_TRACK:
+        {
+            Mutex::Autolock autoLock(mTimedTextLock);
+            if (mTextDriver == NULL) {
+                return INVALID_OPERATION;
+            }
+
+            status_t err = mTextDriver->selectTrack(
+                    request.readInt32());
+            if (err == OK) {
+                modifyFlags(TEXTPLAYER_INITIALIZED, SET);
+                if (mFlags & PLAYING && !(mFlags & TEXT_RUNNING)) {
+                    mTextDriver->start();
+                    modifyFlags(TEXT_RUNNING, SET);
+                }
+            }
+            return err;
+        }
+        case INVOKE_ID_UNSELECT_TRACK:
+        {
+            Mutex::Autolock autoLock(mTimedTextLock);
+            if (mTextDriver == NULL) {
+                return INVALID_OPERATION;
+            }
+            status_t err = mTextDriver->unselectTrack(
+                    request.readInt32());
+            if (err == OK) {
+                modifyFlags(TEXTPLAYER_INITIALIZED, CLEAR);
+                modifyFlags(TEXT_RUNNING, CLEAR);
+            }
+            return err;
+        }
+        default:
+        {
+            return ERROR_UNSUPPORTED;
+        }
+    }
+    // It will not reach here.
+    return OK;
+}
+
 bool AwesomePlayer::isStreamingHTTP() const {
     return mCachedSource != NULL || mWVMExtractor != NULL;
 }
diff --git a/media/libstagefright/MediaDefs.cpp b/media/libstagefright/MediaDefs.cpp
index 444e823..2549de6 100644
--- a/media/libstagefright/MediaDefs.cpp
+++ b/media/libstagefright/MediaDefs.cpp
@@ -52,5 +52,6 @@
 const char *MEDIA_MIMETYPE_CONTAINER_WVM = "video/wvm";
 
 const char *MEDIA_MIMETYPE_TEXT_3GPP = "text/3gpp-tt";
+const char *MEDIA_MIMETYPE_TEXT_SUBRIP = "application/x-subrip";
 
 }  // namespace android
diff --git a/media/libstagefright/XINGSeeker.cpp b/media/libstagefright/XINGSeeker.cpp
index 2091381..e36d619 100644
--- a/media/libstagefright/XINGSeeker.cpp
+++ b/media/libstagefright/XINGSeeker.cpp
@@ -24,7 +24,7 @@
 static bool parse_xing_header(
         const sp<DataSource> &source, off64_t first_frame_pos,
         int32_t *frame_number = NULL, int32_t *byte_number = NULL,
-        unsigned char *table_of_contents = NULL,
+        unsigned char *table_of_contents = NULL, bool *toc_is_valid = NULL,
         int32_t *quality_indicator = NULL, int64_t *duration = NULL);
 
 // static
@@ -36,7 +36,7 @@
 
     if (!parse_xing_header(
                 source, first_frame_pos,
-                NULL, &seeker->mSizeBytes, seeker->mTableOfContents,
+                NULL, &seeker->mSizeBytes, seeker->mTOC, &seeker->mTOCValid,
                 NULL, &seeker->mDurationUs)) {
         return NULL;
     }
@@ -60,7 +60,7 @@
 }
 
 bool XINGSeeker::getOffsetForTime(int64_t *timeUs, off64_t *pos) {
-    if (mSizeBytes == 0 || mTableOfContents[0] <= 0 || mDurationUs < 0) {
+    if (mSizeBytes == 0 || !mTOCValid || mDurationUs < 0) {
         return false;
     }
 
@@ -76,10 +76,10 @@
         if ( a == 0 ) {
             fa = 0.0f;
         } else {
-            fa = (float)mTableOfContents[a-1];
+            fa = (float)mTOC[a-1];
         }
         if ( a < 99 ) {
-            fb = (float)mTableOfContents[a];
+            fb = (float)mTOC[a];
         } else {
             fb = 256.0f;
         }
@@ -94,7 +94,8 @@
 static bool parse_xing_header(
         const sp<DataSource> &source, off64_t first_frame_pos,
         int32_t *frame_number, int32_t *byte_number,
-        unsigned char *table_of_contents, int32_t *quality_indicator,
+        unsigned char *table_of_contents, bool *toc_valid,
+        int32_t *quality_indicator,
         int64_t *duration) {
     if (frame_number) {
         *frame_number = 0;
@@ -102,8 +103,8 @@
     if (byte_number) {
         *byte_number = 0;
     }
-    if (table_of_contents) {
-        table_of_contents[0] = 0;
+    if (toc_valid) {
+        *toc_valid = false;
     }
     if (quality_indicator) {
         *quality_indicator = 0;
@@ -199,10 +200,13 @@
         offset += 4;
     }
     if (flags & 0x0004) {  // TOC field is present
-       if (table_of_contents) {
+        if (table_of_contents) {
             if (source->readAt(offset + 1, table_of_contents, 99) < 99) {
                 return false;
             }
+            if (toc_valid) {
+                *toc_valid = true;
+            }
         }
         offset += 100;
     }
diff --git a/media/libstagefright/include/AwesomePlayer.h b/media/libstagefright/include/AwesomePlayer.h
index 4c7bfa6..06e9468 100644
--- a/media/libstagefright/include/AwesomePlayer.h
+++ b/media/libstagefright/include/AwesomePlayer.h
@@ -90,6 +90,7 @@
 
     status_t setParameter(int key, const Parcel &request);
     status_t getParameter(int key, Parcel *reply);
+    status_t invoke(const Parcel &request, Parcel *reply);
     status_t setCacheStatCollectFreq(const Parcel &request);
 
     status_t seekTo(int64_t timeUs);
@@ -100,8 +101,6 @@
     void postAudioEOS(int64_t delayUs = 0ll);
     void postAudioSeekComplete();
 
-    status_t setTimedTextTrackIndex(int32_t index);
-
     status_t dump(int fd, const Vector<String16> &args) const;
 
 private:
@@ -136,7 +135,7 @@
         INCOGNITO           = 0x8000,
 
         TEXT_RUNNING        = 0x10000,
-        TEXTPLAYER_STARTED  = 0x20000,
+        TEXTPLAYER_INITIALIZED  = 0x20000,
 
         SLOW_DECODER_HACK   = 0x40000,
     };
diff --git a/media/libstagefright/include/XINGSeeker.h b/media/libstagefright/include/XINGSeeker.h
index ec5bd9b..8510979 100644
--- a/media/libstagefright/include/XINGSeeker.h
+++ b/media/libstagefright/include/XINGSeeker.h
@@ -37,7 +37,8 @@
     int32_t mSizeBytes;
 
     // TOC entries in XING header. Skip the first one since it's always 0.
-    unsigned char mTableOfContents[99];
+    unsigned char mTOC[99];
+    bool mTOCValid;
 
     XINGSeeker();
 
diff --git a/media/libstagefright/timedtext/TimedText3GPPSource.cpp b/media/libstagefright/timedtext/TimedText3GPPSource.cpp
index 4a3bfd3..c423ef0 100644
--- a/media/libstagefright/timedtext/TimedText3GPPSource.cpp
+++ b/media/libstagefright/timedtext/TimedText3GPPSource.cpp
@@ -110,4 +110,8 @@
     return OK;
 }
 
+sp<MetaData> TimedText3GPPSource::getFormat() {
+    return mSource->getFormat();
+}
+
 }  // namespace android
diff --git a/media/libstagefright/timedtext/TimedText3GPPSource.h b/media/libstagefright/timedtext/TimedText3GPPSource.h
index dfc6418..4ec3d8a 100644
--- a/media/libstagefright/timedtext/TimedText3GPPSource.h
+++ b/media/libstagefright/timedtext/TimedText3GPPSource.h
@@ -37,6 +37,7 @@
             Parcel *parcel,
             const MediaSource::ReadOptions *options = NULL);
     virtual status_t extractGlobalDescriptions(Parcel *parcel);
+    virtual sp<MetaData> getFormat();
 
 protected:
     virtual ~TimedText3GPPSource();
diff --git a/media/libstagefright/timedtext/TimedTextDriver.cpp b/media/libstagefright/timedtext/TimedTextDriver.cpp
index c70870e..ed83894 100644
--- a/media/libstagefright/timedtext/TimedTextDriver.cpp
+++ b/media/libstagefright/timedtext/TimedTextDriver.cpp
@@ -20,10 +20,13 @@
 
 #include <binder/IPCThreadState.h>
 
+#include <media/mediaplayer.h>
 #include <media/MediaPlayerInterface.h>
+#include <media/stagefright/DataSource.h>
+#include <media/stagefright/MediaDefs.h>
 #include <media/stagefright/MediaErrors.h>
 #include <media/stagefright/MediaSource.h>
-#include <media/stagefright/DataSource.h>
+#include <media/stagefright/MetaData.h>
 #include <media/stagefright/Utils.h>
 #include <media/stagefright/foundation/ADebug.h>
 #include <media/stagefright/foundation/ALooper.h>
@@ -47,24 +50,22 @@
 }
 
 TimedTextDriver::~TimedTextDriver() {
-    mTextInBandVector.clear();
-    mTextOutOfBandVector.clear();
+    mTextSourceVector.clear();
     mLooper->stop();
 }
 
-status_t TimedTextDriver::setTimedTextTrackIndex_l(int32_t index) {
-    if (index >=
-            (int)(mTextInBandVector.size() + mTextOutOfBandVector.size())) {
+status_t TimedTextDriver::selectTrack_l(int32_t index) {
+    if (index >= (int)(mTextSourceVector.size())) {
         return BAD_VALUE;
     }
 
     sp<TimedTextSource> source;
-    if (index < mTextInBandVector.size()) {
-        source = mTextInBandVector.itemAt(index);
-    } else {
-        source = mTextOutOfBandVector.itemAt(index - mTextInBandVector.size());
-    }
+    source = mTextSourceVector.itemAt(index);
     mPlayer->setDataSource(source);
+    if (mState == UNINITIALIZED) {
+        mState = PAUSED;
+    }
+    mCurrentTrackIndex = index;
     return OK;
 }
 
@@ -73,13 +74,10 @@
     switch (mState) {
         case UNINITIALIZED:
             return INVALID_OPERATION;
-        case STOPPED:
-            mPlayer->start();
-            break;
         case PLAYING:
             return OK;
         case PAUSED:
-            mPlayer->resume();
+            mPlayer->start();
             break;
         default:
             TRESPASS();
@@ -88,10 +86,6 @@
     return OK;
 }
 
-status_t TimedTextDriver::stop() {
-    return pause();
-}
-
 // TODO: Test if pause() works properly.
 // Scenario 1: start - pause - resume
 // Scenario 2: start - seek
@@ -101,8 +95,6 @@
     switch (mState) {
         case UNINITIALIZED:
             return INVALID_OPERATION;
-        case STOPPED:
-            return OK;
         case PLAYING:
             mPlayer->pause();
             break;
@@ -115,45 +107,17 @@
     return OK;
 }
 
-status_t TimedTextDriver::resume() {
-    return start();
-}
-
-status_t TimedTextDriver::seekToAsync(int64_t timeUs) {
-    mPlayer->seekToAsync(timeUs);
-    return OK;
-}
-
-status_t TimedTextDriver::setTimedTextTrackIndex(int32_t index) {
-    // TODO: This is current implementation for MediaPlayer::disableTimedText().
-    // Find better way for readability.
-    if (index < 0) {
-        mPlayer->pause();
-        return OK;
-    }
-
+status_t TimedTextDriver::selectTrack(int32_t index) {
     status_t ret = OK;
     Mutex::Autolock autoLock(mLock);
     switch (mState) {
         case UNINITIALIZED:
-            ret = INVALID_OPERATION;
-            break;
         case PAUSED:
-            ret = setTimedTextTrackIndex_l(index);
+            ret = selectTrack_l(index);
             break;
         case PLAYING:
             mPlayer->pause();
-            ret = setTimedTextTrackIndex_l(index);
-            if (ret != OK) {
-                break;
-            }
-            mPlayer->start();
-            break;
-        case STOPPED:
-            // TODO: The only difference between STOPPED and PAUSED is this
-            // part. Revise the flow from "MediaPlayer::enableTimedText()" and
-            // remove one of the status, PAUSED and STOPPED, if possible.
-            ret = setTimedTextTrackIndex_l(index);
+            ret = selectTrack_l(index);
             if (ret != OK) {
                 break;
             }
@@ -165,6 +129,24 @@
     return ret;
 }
 
+status_t TimedTextDriver::unselectTrack(int32_t index) {
+    if (mCurrentTrackIndex != index) {
+        return INVALID_OPERATION;
+    }
+    status_t err = pause();
+    if (err != OK) {
+        return err;
+    }
+    Mutex::Autolock autoLock(mLock);
+    mState = UNINITIALIZED;
+    return OK;
+}
+
+status_t TimedTextDriver::seekToAsync(int64_t timeUs) {
+    mPlayer->seekToAsync(timeUs);
+    return OK;
+}
+
 status_t TimedTextDriver::addInBandTextSource(
         const sp<MediaSource>& mediaSource) {
     sp<TimedTextSource> source =
@@ -173,25 +155,17 @@
         return ERROR_UNSUPPORTED;
     }
     Mutex::Autolock autoLock(mLock);
-    mTextInBandVector.add(source);
-    if (mState == UNINITIALIZED) {
-        mState = STOPPED;
-    }
+    mTextSourceVector.add(source);
     return OK;
 }
 
 status_t TimedTextDriver::addOutOfBandTextSource(
-        const Parcel &request) {
+        const char *uri, const char *mimeType) {
     // TODO: Define "TimedTextSource::CreateFromURI(uri)"
     // and move below lines there..?
 
-    // String values written in Parcel are UTF-16 values.
-    const String16 uri16 = request.readString16();
-    String8 uri = String8(request.readString16());
-
-    uri.toLower();
     // To support local subtitle file only for now
-    if (strncasecmp("file://", uri.string(), 7)) {
+    if (strncasecmp("file://", uri, 7)) {
         return ERROR_UNSUPPORTED;
     }
     sp<DataSource> dataSource =
@@ -201,7 +175,7 @@
     }
 
     sp<TimedTextSource> source;
-    if (uri.getPathExtension() == String8(".srt")) {
+    if (strcasecmp(mimeType, MEDIA_MIMETYPE_TEXT_SUBRIP)) {
         source = TimedTextSource::CreateTimedTextSource(
                 dataSource, TimedTextSource::OUT_OF_BAND_FILE_SRT);
     }
@@ -211,12 +185,38 @@
     }
 
     Mutex::Autolock autoLock(mLock);
-
-    mTextOutOfBandVector.add(source);
-    if (mState == UNINITIALIZED) {
-        mState = STOPPED;
-    }
+    mTextSourceVector.add(source);
     return OK;
 }
 
+status_t TimedTextDriver::addOutOfBandTextSource(
+        int fd, off64_t offset, size_t length, const char *mimeType) {
+    // Not supported yet. This requires DataSource::sniff to detect various text
+    // formats such as srt/smi/ttml.
+    return ERROR_UNSUPPORTED;
+}
+
+void TimedTextDriver::getTrackInfo(Parcel *parcel) {
+    Mutex::Autolock autoLock(mLock);
+    Vector<sp<TimedTextSource> >::const_iterator iter;
+    parcel->writeInt32(mTextSourceVector.size());
+    for (iter = mTextSourceVector.begin();
+         iter != mTextSourceVector.end(); ++iter) {
+        sp<MetaData> meta = (*iter)->getFormat();
+        if (meta != NULL) {
+            // There are two fields.
+            parcel->writeInt32(2);
+
+            // track type.
+            parcel->writeInt32(MEDIA_TRACK_TYPE_TIMEDTEXT);
+
+            const char *lang = "und";
+            meta->findCString(kKeyMediaLanguage, &lang);
+            parcel->writeString16(String16(lang));
+        } else {
+            parcel->writeInt32(0);
+        }
+    }
+}
+
 }  // namespace android
diff --git a/media/libstagefright/timedtext/TimedTextPlayer.cpp b/media/libstagefright/timedtext/TimedTextPlayer.cpp
index bda7b46..8717914 100644
--- a/media/libstagefright/timedtext/TimedTextPlayer.cpp
+++ b/media/libstagefright/timedtext/TimedTextPlayer.cpp
@@ -56,10 +56,6 @@
     (new AMessage(kWhatPause, id()))->post();
 }
 
-void TimedTextPlayer::resume() {
-    start();
-}
-
 void TimedTextPlayer::seekToAsync(int64_t timeUs) {
     sp<AMessage> msg = new AMessage(kWhatSeek, id());
     msg->setInt64("seekTimeUs", timeUs);
@@ -104,9 +100,9 @@
             if (obj != NULL) {
                 sp<ParcelEvent> parcelEvent;
                 parcelEvent = static_cast<ParcelEvent*>(obj.get());
-                notifyListener(MEDIA_TIMED_TEXT, &(parcelEvent->parcel));
+                notifyListener(&(parcelEvent->parcel));
             } else {
-                notifyListener(MEDIA_TIMED_TEXT);
+                notifyListener();
             }
             doRead();
             break;
@@ -119,14 +115,18 @@
                 mSource->stop();
             }
             mSource = static_cast<TimedTextSource*>(obj.get());
-            mSource->start();
-            Parcel parcel;
-            if (mSource->extractGlobalDescriptions(&parcel) == OK &&
-                parcel.dataSize() > 0) {
-                notifyListener(MEDIA_TIMED_TEXT, &parcel);
-            } else {
-                notifyListener(MEDIA_TIMED_TEXT);
+            status_t err = mSource->start();
+            if (err != OK) {
+                notifyError(err);
+                break;
             }
+            Parcel parcel;
+            err = mSource->extractGlobalDescriptions(&parcel);
+            if (err != OK) {
+                notifyError(err);
+                break;
+            }
+            notifyListener(&parcel);
             break;
         }
     }
@@ -141,8 +141,12 @@
 void TimedTextPlayer::doRead(MediaSource::ReadOptions* options) {
     int64_t timeUs = 0;
     sp<ParcelEvent> parcelEvent = new ParcelEvent();
-    mSource->read(&timeUs, &(parcelEvent->parcel), options);
-    postTextEvent(parcelEvent, timeUs);
+    status_t err = mSource->read(&timeUs, &(parcelEvent->parcel), options);
+    if (err != OK) {
+        notifyError(err);
+    } else {
+        postTextEvent(parcelEvent, timeUs);
+    }
 }
 
 void TimedTextPlayer::postTextEvent(const sp<ParcelEvent>& parcel, int64_t timeUs) {
@@ -151,7 +155,7 @@
         int64_t positionUs, delayUs;
         int32_t positionMs = 0;
         listener->getCurrentPosition(&positionMs);
-        positionUs = positionMs * 1000;
+        positionUs = positionMs * 1000ll;
 
         if (timeUs <= positionUs + kAdjustmentProcessingTimeUs) {
             delayUs = 0;
@@ -167,13 +171,20 @@
     }
 }
 
-void TimedTextPlayer::notifyListener(int msg, const Parcel *parcel) {
+void TimedTextPlayer::notifyError(int error) {
+    sp<MediaPlayerBase> listener = mListener.promote();
+    if (listener != NULL) {
+        listener->sendEvent(MEDIA_INFO, MEDIA_INFO_TIMED_TEXT_ERROR, error);
+    }
+}
+
+void TimedTextPlayer::notifyListener(const Parcel *parcel) {
     sp<MediaPlayerBase> listener = mListener.promote();
     if (listener != NULL) {
         if (parcel != NULL && (parcel->dataSize() > 0)) {
-            listener->sendEvent(msg, 0, 0, parcel);
+            listener->sendEvent(MEDIA_TIMED_TEXT, 0, 0, parcel);
         } else {  // send an empty timed text to clear the screen
-            listener->sendEvent(msg);
+            listener->sendEvent(MEDIA_TIMED_TEXT);
         }
     }
 }
diff --git a/media/libstagefright/timedtext/TimedTextPlayer.h b/media/libstagefright/timedtext/TimedTextPlayer.h
index 837beeb..b869f18 100644
--- a/media/libstagefright/timedtext/TimedTextPlayer.h
+++ b/media/libstagefright/timedtext/TimedTextPlayer.h
@@ -40,7 +40,6 @@
 
     void start();
     void pause();
-    void resume();
     void seekToAsync(int64_t timeUs);
     void setDataSource(sp<TimedTextSource> source);
 
@@ -68,7 +67,8 @@
     void doRead(MediaSource::ReadOptions* options = NULL);
     void onTextEvent();
     void postTextEvent(const sp<ParcelEvent>& parcel = NULL, int64_t timeUs = -1);
-    void notifyListener(int msg, const Parcel *parcel = NULL);
+    void notifyError(int error = 0);
+    void notifyListener(const Parcel *parcel = NULL);
 
     DISALLOW_EVIL_CONSTRUCTORS(TimedTextPlayer);
 };
diff --git a/media/libstagefright/timedtext/TimedTextSRTSource.cpp b/media/libstagefright/timedtext/TimedTextSRTSource.cpp
index 3752d34..c44a99b 100644
--- a/media/libstagefright/timedtext/TimedTextSRTSource.cpp
+++ b/media/libstagefright/timedtext/TimedTextSRTSource.cpp
@@ -21,8 +21,10 @@
 #include <binder/Parcel.h>
 #include <media/stagefright/foundation/AString.h>
 #include <media/stagefright/DataSource.h>
+#include <media/stagefright/MediaDefs.h>  // for MEDIA_MIMETYPE_xxx
 #include <media/stagefright/MediaErrors.h>
 #include <media/stagefright/MediaSource.h>
+#include <media/stagefright/MetaData.h>
 
 #include "TimedTextSRTSource.h"
 #include "TextDescriptions.h"
@@ -31,6 +33,7 @@
 
 TimedTextSRTSource::TimedTextSRTSource(const sp<DataSource>& dataSource)
         : mSource(dataSource),
+          mMetaData(new MetaData),
           mIndex(0) {
 }
 
@@ -42,10 +45,14 @@
     if (err != OK) {
         reset();
     }
+    // TODO: Need to detect the language, because SRT doesn't give language
+    // information explicitly.
+    mMetaData->setCString(kKeyMediaLanguage, "");
     return err;
 }
 
 void TimedTextSRTSource::reset() {
+    mMetaData->clear();
     mTextVector.clear();
     mIndex = 0;
 }
@@ -272,4 +279,8 @@
     return OK;
 }
 
+sp<MetaData> TimedTextSRTSource::getFormat() {
+    return mMetaData;
+}
+
 }  // namespace android
diff --git a/media/libstagefright/timedtext/TimedTextSRTSource.h b/media/libstagefright/timedtext/TimedTextSRTSource.h
index acc01f9..62710a0 100644
--- a/media/libstagefright/timedtext/TimedTextSRTSource.h
+++ b/media/libstagefright/timedtext/TimedTextSRTSource.h
@@ -39,12 +39,14 @@
             int64_t *timeUs,
             Parcel *parcel,
             const MediaSource::ReadOptions *options = NULL);
+    virtual sp<MetaData> getFormat();
 
 protected:
     virtual ~TimedTextSRTSource();
 
 private:
     sp<DataSource> mSource;
+    sp<MetaData> mMetaData;
 
     struct TextInfo {
         int64_t endTimeUs;
diff --git a/media/libstagefright/timedtext/TimedTextSource.cpp b/media/libstagefright/timedtext/TimedTextSource.cpp
index ffbe1c3..953f7b5 100644
--- a/media/libstagefright/timedtext/TimedTextSource.cpp
+++ b/media/libstagefright/timedtext/TimedTextSource.cpp
@@ -59,4 +59,8 @@
     return NULL;
 }
 
+sp<MetaData> TimedTextSource::getFormat() {
+    return NULL;
+}
+
 }  // namespace android
diff --git a/media/libstagefright/timedtext/TimedTextSource.h b/media/libstagefright/timedtext/TimedTextSource.h
index 06bae71..9349342 100644
--- a/media/libstagefright/timedtext/TimedTextSource.h
+++ b/media/libstagefright/timedtext/TimedTextSource.h
@@ -25,6 +25,7 @@
 namespace android {
 
 class DataSource;
+class MetaData;
 class Parcel;
 
 class TimedTextSource : public RefBase {
@@ -48,6 +49,7 @@
   virtual status_t extractGlobalDescriptions(Parcel *parcel) {
       return INVALID_OPERATION;
   }
+  virtual sp<MetaData> getFormat();
 
  protected:
   virtual ~TimedTextSource() { }
diff --git a/services/java/com/android/server/wm/AppWindowToken.java b/services/java/com/android/server/wm/AppWindowToken.java
index b84fbdb..5ca09e7 100644
--- a/services/java/com/android/server/wm/AppWindowToken.java
+++ b/services/java/com/android/server/wm/AppWindowToken.java
@@ -27,6 +27,7 @@
 import android.view.IApplicationToken;
 import android.view.View;
 import android.view.WindowManager;
+import android.view.WindowManagerPolicy;
 import android.view.animation.Animation;
 import android.view.animation.Transformation;
 
@@ -37,7 +38,7 @@
  * Version of WindowToken that is specifically for a particular application (or
  * really activity) that is displaying windows.
  */
-class AppWindowToken extends WindowToken implements WindowManagerService.StepAnimator {
+class AppWindowToken extends WindowToken {
     // Non-null only for application tokens.
     final IApplicationToken appToken;
 
@@ -195,8 +196,8 @@
         }
     }
 
-    @Override
-    public boolean stepAnimation(long currentTime) {
+
+    private boolean stepAnimation(long currentTime) {
         if (animation == null) {
             return false;
         }
@@ -216,7 +217,7 @@
     }
 
     // This must be called while inside a transaction.
-    boolean startAndFinishAnimationLocked(long currentTime, int dw, int dh) {
+    boolean stepAnimationLocked(long currentTime, int dw, int dh) {
         if (!service.mDisplayFrozen && service.mPolicy.isScreenOnFully()) {
             // We will run animations as long as the display isn't frozen.
 
@@ -240,7 +241,7 @@
                     animating = true;
                 }
                 // we're done!
-                return true;
+                return stepAnimation(currentTime);
             }
         } else if (animation != null) {
             // If the display is frozen, and there is a pending animation,
@@ -255,6 +256,7 @@
             return false;
         }
 
+        service.mPendingLayoutChanges |= WindowManagerPolicy.FINISH_LAYOUT_REDO_ANIM;
         clearAnimation();
         animating = false;
         if (animLayerAdjustment != 0) {
diff --git a/services/java/com/android/server/wm/ScreenRotationAnimation.java b/services/java/com/android/server/wm/ScreenRotationAnimation.java
index 7b5bf08..58187b6 100644
--- a/services/java/com/android/server/wm/ScreenRotationAnimation.java
+++ b/services/java/com/android/server/wm/ScreenRotationAnimation.java
@@ -29,7 +29,7 @@
 import android.view.animation.AnimationUtils;
 import android.view.animation.Transformation;
 
-class ScreenRotationAnimation implements WindowManagerService.StepAnimator {
+class ScreenRotationAnimation {
     static final String TAG = "ScreenRotationAnimation";
     static final boolean DEBUG_STATE = false;
     static final boolean DEBUG_TRANSFORMS = false;
@@ -540,8 +540,7 @@
                 || mRotateFrameAnimation != null;
     }
 
-    @Override
-    public boolean stepAnimation(long now) {
+    private boolean stepAnimation(long now) {
 
         if (mFinishAnimReady && mFinishAnimStartTime < 0) {
             if (DEBUG_STATE) Slog.v(TAG, "Step: finish anim now ready");
@@ -725,7 +724,7 @@
         setSnapshotTransform(mSnapshotFinalMatrix, mExitTransformation.getAlpha());
     }
     
-    public boolean startAndFinishAnimationLocked(long now) {
+    public boolean stepAnimationLocked(long now) {
         if (!isAnimating()) {
             if (DEBUG_STATE) Slog.v(TAG, "Step: no animations running");
             mFinishAnimReady = false;
@@ -763,8 +762,8 @@
             }
             mAnimRunning = true;
         }
-        
-        return true;
+
+        return stepAnimation(now);
     }
 
     public Transformation getEnterTransformation() {
diff --git a/services/java/com/android/server/wm/WindowManagerService.java b/services/java/com/android/server/wm/WindowManagerService.java
index 22949f3..4f55217 100644
--- a/services/java/com/android/server/wm/WindowManagerService.java
+++ b/services/java/com/android/server/wm/WindowManagerService.java
@@ -461,6 +461,7 @@
             = new ArrayList<IRotationWatcher>();
     int mDeferredRotationPauseCount;
 
+    int mPendingLayoutChanges = 0;
     boolean mLayoutNeeded = true;
     boolean mTraversalScheduled = false;
     boolean mDisplayFrozen = false;
@@ -617,18 +618,6 @@
     final AnimationRunnable mAnimationRunnable = new AnimationRunnable();
     boolean mAnimationScheduled;
 
-    interface StepAnimator {
-        /**
-         * Continue the stepping of an ongoing animation. When the animation completes this method
-         * must disable the animation on the StepAnimator. 
-         * @param currentTime Animation time in milliseconds. Use SystemClock.uptimeMillis().
-         * @return True if the animation is still going on, false if the animation has completed
-         *      and stepAnimation has cleared the animation locally.
-         */
-        boolean stepAnimation(long currentTime);
-    }
-    final ArrayList<StepAnimator> mStepAnimators = new ArrayList<StepAnimator>();
-    
     final class DragInputEventReceiver extends InputEventReceiver {
         public DragInputEventReceiver(InputChannel inputChannel, Looper looper) {
             super(inputChannel, looper);
@@ -2995,15 +2984,27 @@
     }
 
     void applyEnterAnimationLocked(WindowState win) {
-        int transit = WindowManagerPolicy.TRANSIT_SHOW;
+        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.mLocalAnimating && win.mAnimationIsEntrance == isEntrance) {
@@ -7643,20 +7644,6 @@
     }
 
     /**
-     * Run through each of the animating objects saved in mStepAnimators.
-     */
-    private void stepAnimations() {
-        final long currentTime = SystemClock.uptimeMillis();
-        for (final StepAnimator stepAnimator : mStepAnimators) {
-            final boolean more = stepAnimator.stepAnimation(currentTime);
-            if (DEBUG_ANIM) {
-                Slog.v(TAG, "stepAnimations: " + currentTime + ": Stepped " + stepAnimator
-                        + (more ? " more" : " done"));
-            }
-        }
-    }
-    
-    /**
      * Extracted from {@link #performLayoutAndPlaceSurfacesLockedInner} to reduce size of method.
      * Update animations of all applications, including those associated with exiting/removed apps.
      *
@@ -7670,16 +7657,14 @@
         final int NAT = mAppTokens.size();
         for (i=0; i<NAT; i++) {
             final AppWindowToken appToken = mAppTokens.get(i);
-            if (appToken.startAndFinishAnimationLocked(currentTime, innerDw, innerDh)) {
-                mStepAnimators.add(appToken);
+            if (appToken.stepAnimationLocked(currentTime, innerDw, innerDh)) {
                 mInnerFields.mAnimating = true;
             }
         }
         final int NEAT = mExitingAppTokens.size();
         for (i=0; i<NEAT; i++) {
             final AppWindowToken appToken = mExitingAppTokens.get(i);
-            if (appToken.startAndFinishAnimationLocked(currentTime, innerDw, innerDh)) {
-                mStepAnimators.add(appToken);
+            if (appToken.stepAnimationLocked(currentTime, innerDw, innerDh)) {
                 mInnerFields.mAnimating = true;
             }
         }
@@ -7687,10 +7672,9 @@
         if (mScreenRotationAnimation != null) {
             if (mScreenRotationAnimation.isAnimating() ||
                     mScreenRotationAnimation.mFinishAnimReady) {
-                if (mScreenRotationAnimation.startAndFinishAnimationLocked(currentTime)) {
+                if (mScreenRotationAnimation.stepAnimationLocked(currentTime)) {
                     mInnerFields.mUpdateRotation = false;
                     mInnerFields.mAnimating = true;
-                    mStepAnimators.add(mScreenRotationAnimation);
                 } else {
                     mInnerFields.mUpdateRotation = true;
                     mScreenRotationAnimation.kill();
@@ -7711,9 +7695,7 @@
      */
     private int updateWindowsAndWallpaperLocked(final long currentTime, final int dw, final int dh,
                                                 final int innerDw, final int innerDh) {
-
-        mPolicy.beginAnimationLw(dw, dh);
-
+        int changes = 0;
         for (int i = mWindows.size() - 1; i >= 0; i--) {
             WindowState w = mWindows.get(i);
 
@@ -7727,6 +7709,7 @@
                         if (DEBUG_WALLPAPER) Slog.v(TAG,
                                 "First draw done in potential wallpaper target " + w);
                         mInnerFields.mWallpaperMayChange = true;
+                        changes |= WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
                     }
                 }
 
@@ -7749,13 +7732,7 @@
                 }
 
                 final boolean wasAnimating = w.mWasAnimating;
-                
-                
-                final boolean nowAnimating = w.startAndFinishAnimationLocked(currentTime);
-                if (nowAnimating) {
-                    mStepAnimators.add(w);
-                    mInnerFields.mAnimating = true;
-                }
+                final boolean nowAnimating = w.stepAnimationLocked(currentTime);
 
                 if (DEBUG_WALLPAPER) {
                     Slog.v(TAG, w + ": wasAnimating=" + wasAnimating +
@@ -7806,6 +7783,7 @@
 
                 if (wasAnimating && !w.mAnimating && mWallpaperTarget == w) {
                     mInnerFields.mWallpaperMayChange = true;
+                    changes |= WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
                 }
 
                 if (mPolicy.doesForceHide(w, attrs)) {
@@ -7814,6 +7792,7 @@
                                 "Animation started that could impact force hide: "
                                 + w);
                         mInnerFields.mWallpaperForceHidingChanged = true;
+                        changes |= WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
                         mFocusMayChange = true;
                     } else if (w.isReadyForDisplay() && w.mAnimation == null) {
                         mInnerFields.mForceHiding = true;
@@ -7852,10 +7831,9 @@
                     if (changed && (attrs.flags
                             & WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER) != 0) {
                         mInnerFields.mWallpaperMayChange = true;
+                        changes |= WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
                     }
                 }
-
-                mPolicy.animatingWindowLw(w, attrs);
             }
 
             final AppWindowToken atoken = w.mAppToken;
@@ -7900,10 +7878,11 @@
                 }
             } else if (w.mReadyToShow) {
                 w.performShowLocked();
+                changes |= WindowManagerPolicy.FINISH_LAYOUT_REDO_ANIM;
             }
         } // end forall windows
 
-        return mPolicy.finishAnimationLw();
+        return changes;
     }
 
     /**
@@ -8322,6 +8301,72 @@
         return changes;
     }
 
+    private void updateResizingWindows(final WindowState w) {
+        if (!w.mAppFreezing && w.mLayoutSeq == mLayoutSeq) {
+            w.mContentInsetsChanged |=
+                !w.mLastContentInsets.equals(w.mContentInsets);
+            w.mVisibleInsetsChanged |=
+                !w.mLastVisibleInsets.equals(w.mVisibleInsets);
+            boolean configChanged =
+                w.mConfiguration != mCurConfiguration
+                && (w.mConfiguration == null
+                        || mCurConfiguration.diff(w.mConfiguration) != 0);
+            if (DEBUG_CONFIGURATION && configChanged) {
+                Slog.v(TAG, "Win " + w + " config changed: "
+                        + mCurConfiguration);
+            }
+            if (localLOGV) Slog.v(TAG, "Resizing " + w
+                    + ": configChanged=" + configChanged
+                    + " last=" + w.mLastFrame + " frame=" + w.mFrame);
+            w.mLastFrame.set(w.mFrame);
+            if (w.mContentInsetsChanged
+                    || w.mVisibleInsetsChanged
+                    || w.mSurfaceResized
+                    || configChanged) {
+                if (DEBUG_RESIZE || DEBUG_ORIENTATION) {
+                    Slog.v(TAG, "Resize reasons: "
+                            + " contentInsetsChanged=" + w.mContentInsetsChanged
+                            + " visibleInsetsChanged=" + w.mVisibleInsetsChanged
+                            + " surfaceResized=" + w.mSurfaceResized
+                            + " configChanged=" + configChanged);
+                }
+
+                w.mLastContentInsets.set(w.mContentInsets);
+                w.mLastVisibleInsets.set(w.mVisibleInsets);
+                makeWindowFreezingScreenIfNeededLocked(w);
+                // If the orientation is changing, then we need to
+                // hold off on unfreezing the display until this
+                // window has been redrawn; to do that, we need
+                // to go through the process of getting informed
+                // by the application when it has finished drawing.
+                if (w.mOrientationChanging) {
+                    if (DEBUG_ORIENTATION) Slog.v(TAG,
+                            "Orientation start waiting for draw in "
+                            + w + ", surface " + w.mSurface);
+                    w.mDrawPending = true;
+                    w.mCommitDrawPending = false;
+                    w.mReadyToShow = false;
+                    if (w.mAppToken != null) {
+                        w.mAppToken.allDrawn = false;
+                    }
+                }
+                if (!mResizingWindows.contains(w)) {
+                    if (DEBUG_RESIZE || DEBUG_ORIENTATION) Slog.v(TAG,
+                            "Resizing window " + w + " to " + w.mSurfaceW
+                            + "x" + w.mSurfaceH);
+                    mResizingWindows.add(w);
+                }
+            } else if (w.mOrientationChanging) {
+                if (!w.mDrawPending && !w.mCommitDrawPending) {
+                    if (DEBUG_ORIENTATION) Slog.v(TAG,
+                            "Orientation not waiting for draw in "
+                            + w + ", surface " + w.mSurface);
+                    w.mOrientationChanging = false;
+                }
+            }
+        }
+    }
+
     /**
      * Extracted from {@link #performLayoutAndPlaceSurfacesLockedInner} to reduce size of method.
      *
@@ -8407,69 +8452,7 @@
             }
         }
 
-        if (!w.mAppFreezing && w.mLayoutSeq == mLayoutSeq) {
-            w.mContentInsetsChanged |=
-                !w.mLastContentInsets.equals(w.mContentInsets);
-            w.mVisibleInsetsChanged |=
-                !w.mLastVisibleInsets.equals(w.mVisibleInsets);
-            boolean configChanged =
-                w.mConfiguration != mCurConfiguration
-                && (w.mConfiguration == null
-                        || mCurConfiguration.diff(w.mConfiguration) != 0);
-            if (DEBUG_CONFIGURATION && configChanged) {
-                Slog.v(TAG, "Win " + w + " config changed: "
-                        + mCurConfiguration);
-            }
-            if (localLOGV) Slog.v(TAG, "Resizing " + w
-                    + ": configChanged=" + configChanged
-                    + " last=" + w.mLastFrame + " frame=" + w.mFrame);
-            w.mLastFrame.set(w.mFrame);
-            if (w.mContentInsetsChanged
-                    || w.mVisibleInsetsChanged
-                    || w.mSurfaceResized
-                    || configChanged) {
-                if (DEBUG_RESIZE || DEBUG_ORIENTATION) {
-                    Slog.v(TAG, "Resize reasons: "
-                            + " contentInsetsChanged=" + w.mContentInsetsChanged
-                            + " visibleInsetsChanged=" + w.mVisibleInsetsChanged
-                            + " surfaceResized=" + w.mSurfaceResized
-                            + " configChanged=" + configChanged);
-                }
-
-                w.mLastContentInsets.set(w.mContentInsets);
-                w.mLastVisibleInsets.set(w.mVisibleInsets);
-                makeWindowFreezingScreenIfNeededLocked(w);
-                // If the orientation is changing, then we need to
-                // hold off on unfreezing the display until this
-                // window has been redrawn; to do that, we need
-                // to go through the process of getting informed
-                // by the application when it has finished drawing.
-                if (w.mOrientationChanging) {
-                    if (DEBUG_ORIENTATION) Slog.v(TAG,
-                            "Orientation start waiting for draw in "
-                            + w + ", surface " + w.mSurface);
-                    w.mDrawPending = true;
-                    w.mCommitDrawPending = false;
-                    w.mReadyToShow = false;
-                    if (w.mAppToken != null) {
-                        w.mAppToken.allDrawn = false;
-                    }
-                }
-                if (!mResizingWindows.contains(w)) {
-                    if (DEBUG_RESIZE || DEBUG_ORIENTATION) Slog.v(TAG,
-                            "Resizing window " + w + " to " + w.mSurfaceW
-                            + "x" + w.mSurfaceH);
-                    mResizingWindows.add(w);
-                }
-            } else if (w.mOrientationChanging) {
-                if (!w.mDrawPending && !w.mCommitDrawPending) {
-                    if (DEBUG_ORIENTATION) Slog.v(TAG,
-                            "Orientation not waiting for draw in "
-                            + w + ", surface " + w.mSurface);
-                    w.mOrientationChanging = false;
-                }
-            }
-        }
+        updateResizingWindows(w);
 
         if (w.mAttachedHidden || !w.isReadyForDisplay()) {
             if (!w.mLastHidden) {
@@ -8678,6 +8661,67 @@
         }
     }
 
+    private final int performAnimationsLocked(long currentTime, int dw, int dh,
+            int innerDw, int innerDh) {
+        ++mTransactionSequence;
+
+        if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, "*** ANIM STEP: seq="
+                + mTransactionSequence + " mAnimating="
+                + mInnerFields.mAnimating);
+
+        mInnerFields.mTokenMayBeDrawn = false;
+        mInnerFields.mWallpaperMayChange = false;
+        mInnerFields.mForceHiding = false;
+        mInnerFields.mDetachedWallpaper = null;
+        mInnerFields.mWindowAnimationBackground = null;
+        mInnerFields.mWindowAnimationBackgroundColor = 0;
+
+        int changes = updateWindowsAndWallpaperLocked(currentTime, dw, dh, innerDw, innerDh);
+
+        if (mInnerFields.mTokenMayBeDrawn) {
+            changes |= testTokenMayBeDrawnLocked();
+        }
+
+        // If we are ready to perform an app transition, check through
+        // all of the app tokens to be shown and see if they are ready
+        // to go.
+        if (mAppTransitionReady) {
+            changes |= handleAppTransitionReadyLocked();
+        }
+
+        mInnerFields.mAdjResult = 0;
+
+        if (!mInnerFields.mAnimating && mAppTransitionRunning) {
+            // We have finished the animation of an app transition.  To do
+            // this, we have delayed a lot of operations like showing and
+            // hiding apps, moving apps in Z-order, etc.  The app token list
+            // reflects the correct Z-order, but the window list may now
+            // be out of sync with it.  So here we will just rebuild the
+            // entire app window list.  Fun!
+            changes |= handleAnimatingStoppedAndTransitionLocked();
+        }
+
+        if (mInnerFields.mWallpaperForceHidingChanged && changes == 0 && !mAppTransitionReady) {
+            // At this point, there was a window with a wallpaper that
+            // was force hiding other windows behind it, but now it
+            // is going away.  This may be simple -- just animate
+            // away the wallpaper and its window -- or it may be
+            // hard -- the wallpaper now needs to be shown behind
+            // something that was hidden.
+            changes |= animateAwayWallpaperLocked();
+        }
+
+        changes |= testWallpaperAndBackgroundLocked();
+
+        if (mLayoutNeeded) {
+            changes |= PhoneWindowManager.FINISH_LAYOUT_REDO_LAYOUT;
+        }
+
+        if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, "*** ANIM STEP: changes=0x"
+                + Integer.toHexString(changes));
+        return changes;
+    }
+
     // "Something has changed!  Let's make it correct now."
     private final void performLayoutAndPlaceSurfacesLockedInner(
             boolean recoveringMemory) {
@@ -8741,7 +8785,6 @@
         try {
             mInnerFields.mWallpaperForceHidingChanged = false;
             int repeats = 0;
-            int changes = 0;
             
             do {
                 repeats++;
@@ -8751,20 +8794,20 @@
                     break;
                 }
 
-                if ((changes & WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER) != 0) {
+                if ((mPendingLayoutChanges & WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER) != 0) {
                     if ((adjustWallpaperWindowsLocked()&ADJUST_WALLPAPER_LAYERS_CHANGED) != 0) {
                         assignLayersLocked();
                         mLayoutNeeded = true;
                     }
                 }
-                if ((changes & WindowManagerPolicy.FINISH_LAYOUT_REDO_CONFIG) != 0) {
+                if ((mPendingLayoutChanges & WindowManagerPolicy.FINISH_LAYOUT_REDO_CONFIG) != 0) {
                     if (DEBUG_LAYOUT) Slog.v(TAG, "Computing new config from layout");
                     if (updateOrientationFromAppTokensLocked(true)) {
                         mLayoutNeeded = true;
                         mH.sendEmptyMessage(H.SEND_NEW_CONFIGURATION);
                     }
                 }
-                if ((changes & WindowManagerPolicy.FINISH_LAYOUT_REDO_LAYOUT) != 0) {
+                if ((mPendingLayoutChanges & WindowManagerPolicy.FINISH_LAYOUT_REDO_LAYOUT) != 0) {
                     mLayoutNeeded = true;
                 }
 
@@ -8775,71 +8818,26 @@
                     Slog.w(TAG, "Layout repeat skipped after too many iterations");
                 }
 
-                ++mTransactionSequence;
-
-                if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, "*** ANIM STEP: seq="
-                        + mTransactionSequence + " mAnimating="
-                        + mInnerFields.mAnimating);
-
-                mInnerFields.mTokenMayBeDrawn = false;
-                mInnerFields.mWallpaperMayChange = false;
-                mInnerFields.mForceHiding = false;
-                mInnerFields.mDetachedWallpaper = null;
-                mInnerFields.mWindowAnimationBackground = null;
-                mInnerFields.mWindowAnimationBackgroundColor = 0;
-
-                mStepAnimators.clear();
-                changes = updateWindowsAndWallpaperLocked(currentTime, dw, dh, innerDw, innerDh);
-
-                if (mInnerFields.mTokenMayBeDrawn) {
-                    changes |= testTokenMayBeDrawnLocked();
+                // FIRST AND ONE HALF LOOP: Make WindowManagerPolicy think
+                // it is animating.
+                mPendingLayoutChanges = 0;
+                mPolicy.beginAnimationLw(dw, dh);
+                for (i = mWindows.size() - 1; i >= 0; i--) {
+                    WindowState w = mWindows.get(i);
+                    if (w.mSurface != null) {
+                        mPolicy.animatingWindowLw(w, w.mAttrs);
+                    }
                 }
-
-                // If we are ready to perform an app transition, check through
-                // all of the app tokens to be shown and see if they are ready
-                // to go.
-                if (mAppTransitionReady) {
-                    changes |= handleAppTransitionReadyLocked();
-                }
-
-                mInnerFields.mAdjResult = 0;
-
-                if (!mInnerFields.mAnimating && mAppTransitionRunning) {
-                    // We have finished the animation of an app transition.  To do
-                    // this, we have delayed a lot of operations like showing and
-                    // hiding apps, moving apps in Z-order, etc.  The app token list
-                    // reflects the correct Z-order, but the window list may now
-                    // be out of sync with it.  So here we will just rebuild the
-                    // entire app window list.  Fun!
-                    changes |= handleAnimatingStoppedAndTransitionLocked();
-                }
-
-                if (mInnerFields.mWallpaperForceHidingChanged && changes == 0 && !mAppTransitionReady) {
-                    // At this point, there was a window with a wallpaper that
-                    // was force hiding other windows behind it, but now it
-                    // is going away.  This may be simple -- just animate
-                    // away the wallpaper and its window -- or it may be
-                    // hard -- the wallpaper now needs to be shown behind
-                    // something that was hidden.
-                    changes |= animateAwayWallpaperLocked();
-                }
-
-                changes |= testWallpaperAndBackgroundLocked();
-
-                if (mLayoutNeeded) {
-                    changes |= PhoneWindowManager.FINISH_LAYOUT_REDO_LAYOUT;
-                }
-
-                if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, "*** ANIM STEP: changes=0x"
-                        + Integer.toHexString(changes));
-            } while (changes != 0);
+                mPendingLayoutChanges |= mPolicy.finishAnimationLw();
+                
+            } while (mPendingLayoutChanges != 0);
 
             // Update animations of all applications, including those
             // associated with exiting/removed apps
 
+            mPendingLayoutChanges = performAnimationsLocked(currentTime, dw, dh,
+                    innerDw, innerDh);
             updateWindowsAppsAndRotationAnimationsLocked(currentTime, innerDw, innerDh);
-            
-            stepAnimations();
 
             // THIRD LOOP: Update the surfaces of all windows.
 
@@ -9049,6 +9047,13 @@
         if (wallpaperDestroyed) {
             needRelayout = adjustWallpaperWindowsLocked() != 0;
         }
+        if ((mPendingLayoutChanges & (
+                WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER |
+                ADJUST_WALLPAPER_LAYERS_CHANGED |
+                WindowManagerPolicy.FINISH_LAYOUT_REDO_CONFIG |
+                WindowManagerPolicy.FINISH_LAYOUT_REDO_LAYOUT)) != 0) {
+            needRelayout = true;
+        }
         if (needRelayout) {
             requestTraversalLocked();
         } else if (mInnerFields.mAnimating) {
diff --git a/services/java/com/android/server/wm/WindowState.java b/services/java/com/android/server/wm/WindowState.java
index e11c87a..48788e7 100644
--- a/services/java/com/android/server/wm/WindowState.java
+++ b/services/java/com/android/server/wm/WindowState.java
@@ -54,8 +54,7 @@
 /**
  * A window in the window manager.
  */
-final class WindowState implements WindowManagerPolicy.WindowState,
-        WindowManagerService.StepAnimator {
+final class WindowState implements WindowManagerPolicy.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;
@@ -995,8 +994,7 @@
         return true;
     }
 
-    @Override
-    public boolean stepAnimation(long currentTime) {
+    private boolean stepAnimation(long currentTime) {
         if ((mAnimation == null) || !mLocalAnimating || (mAnimState != ANIM_STATE_RUNNING)) {
             return false;
         }
@@ -1013,7 +1011,7 @@
 
     // This must be called while inside a transaction.  Returns true if
     // there is more animation to run.
-    boolean startAndFinishAnimationLocked(long currentTime) {
+    boolean stepAnimationLocked(long currentTime) {
         // Save the animation state as it was before this step so WindowManagerService can tell if
         // we just started or just stopped animating by comparing mWasAnimating with isAnimating().
         mWasAnimating = mAnimating;
@@ -1038,7 +1036,7 @@
                 }
                 if ((mAnimation != null) && mLocalAnimating && 
                         (mAnimState != ANIM_STATE_STOPPING)) {
-                    return true;
+                    return stepAnimation(currentTime);
                 }
                 if (WindowManagerService.DEBUG_ANIM) Slog.v(
                     WindowManagerService.TAG, "Finished animation in " + this +
@@ -1133,6 +1131,7 @@
         }
 
         finishExit();
+        mService.mPendingLayoutChanges |= WindowManagerPolicy.FINISH_LAYOUT_REDO_ANIM;
 
         if (mAppToken != null) {
             mAppToken.updateReportedVisibilityLocked();
@@ -1608,6 +1607,7 @@
 
     boolean showLw(boolean doAnimation, boolean requestAnim) {
         if (mPolicyVisibility && mPolicyVisibilityAfterAnim) {
+            // Already showing.
             return false;
         }
         if (DEBUG_VISIBILITY) Slog.v(WindowManagerService.TAG, "Policy visibility true: " + this);
@@ -1647,6 +1647,7 @@
         boolean current = doAnimation ? mPolicyVisibilityAfterAnim
                 : mPolicyVisibility;
         if (!current) {
+            // Already hiding.
             return false;
         }
         if (doAnimation) {