Merge "Support mpeg1,2 audio and mpeg1,2,4 video content extraction from .ts streams."
diff --git a/Android.mk b/Android.mk
index e3406ea..c838600 100644
--- a/Android.mk
+++ b/Android.mk
@@ -437,6 +437,8 @@
 		            resources/samples/WiktionarySimple "Wiktionary (Simplified)" \
 		-samplecode $(sample_dir)/VoiceRecognitionService \
 		            resources/samples/VoiceRecognitionService "Voice Recognition Service" \
+		-samplecode $(sample_dir)/VoicemailProviderDemo \
+		            resources/samples/VoicemailProviderDemo "Voicemail Provider Demo" \
 		-samplecode $(sample_dir)/XmlAdapters \
 		            resources/samples/XmlAdapters "XML Adapters"
 
diff --git a/api/current.txt b/api/current.txt
index 6854965..2f5b9b4 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -19381,17 +19381,21 @@
   public class SuggestionSpan implements android.text.ParcelableSpan {
     ctor public SuggestionSpan(android.content.Context, java.lang.String[], int);
     ctor public SuggestionSpan(java.util.Locale, java.lang.String[], int);
-    ctor public SuggestionSpan(android.content.Context, java.util.Locale, java.lang.String[], int, java.lang.String);
+    ctor public SuggestionSpan(android.content.Context, java.util.Locale, java.lang.String[], int, java.lang.Class<?>);
     ctor public SuggestionSpan(android.os.Parcel);
     method public int describeContents();
     method public int getFlags();
     method public java.lang.String getLocale();
-    method public java.lang.String getOriginalString();
     method public int getSpanTypeId();
     method public java.lang.String[] getSuggestions();
     method public void writeToParcel(android.os.Parcel, int);
+    field public static final java.lang.String ACTION_SUGGESTION_PICKED = "android.text.style.SUGGESTION_PICKED";
     field public static final android.os.Parcelable.Creator CREATOR;
     field public static final int FLAG_VERBATIM = 1; // 0x1
+    field public static final int SUGGESTIONS_MAX_SIZE = 5; // 0x5
+    field public static final java.lang.String SUGGESTION_SPAN_PICKED_AFTER = "after";
+    field public static final java.lang.String SUGGESTION_SPAN_PICKED_BEFORE = "before";
+    field public static final java.lang.String SUGGESTION_SPAN_PICKED_HASHCODE = "hashcode";
   }
 
   public class SuperscriptSpan extends android.text.style.MetricAffectingSpan implements android.text.ParcelableSpan {
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index 36b9d72..51f1e3d 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -35,6 +35,7 @@
 import android.content.pm.IPackageManager;
 import android.content.pm.PackageManager;
 import android.content.res.AssetManager;
+import android.content.res.CompatibilityInfo;
 import android.content.res.Resources;
 import android.database.DatabaseErrorHandler;
 import android.database.sqlite.SQLiteDatabase;
@@ -78,6 +79,7 @@
 import android.util.AndroidRuntimeException;
 import android.util.Log;
 import android.view.ContextThemeWrapper;
+import android.view.Display;
 import android.view.WindowManagerImpl;
 import android.view.accessibility.AccessibilityManager;
 import android.view.inputmethod.InputMethodManager;
@@ -423,7 +425,11 @@
 
         registerService(WINDOW_SERVICE, new ServiceFetcher() {
                 public Object getService(ContextImpl ctx) {
-                    return WindowManagerImpl.getDefault();
+                    RuntimeException e = new RuntimeException("foo");
+                    e.fillInStackTrace();
+                    Log.i(TAG, "Getting window manager", e);
+                    CompatibilityInfo ci = ctx.mResources.getCompatibilityInfo();
+                    return WindowManagerImpl.getDefault(ci);
                 }});
     }
 
diff --git a/core/java/android/app/Instrumentation.java b/core/java/android/app/Instrumentation.java
index 7b02763..2952e6b 100644
--- a/core/java/android/app/Instrumentation.java
+++ b/core/java/android/app/Instrumentation.java
@@ -1044,7 +1044,7 @@
             }
         }
         
-        activity.onCreate(icicle);
+        activity.performCreate(icicle);
         
         if (mActivityMonitors != null) {
             synchronized (mSync) {
diff --git a/core/java/android/content/res/CompatibilityInfo.java b/core/java/android/content/res/CompatibilityInfo.java
index 8bcb005..854d410 100644
--- a/core/java/android/content/res/CompatibilityInfo.java
+++ b/core/java/android/content/res/CompatibilityInfo.java
@@ -125,14 +125,16 @@
         if ((appInfo.flags & ApplicationInfo.FLAG_SUPPORTS_XLARGE_SCREENS) != 0) {
             compatFlags |= XLARGE_SCREENS | EXPANDABLE;
         }
-        if (!forceCompat) {
+        if ((appInfo.flags & ApplicationInfo.FLAG_RESIZEABLE_FOR_SCREENS) != 0) {
+            compatFlags |= EXPANDABLE;
+        }
+
+        if (forceCompat) {
             // If we are forcing compatibility mode, then ignore an app that
             // just says it is resizable for screens.  We'll only have it fill
             // the screen if it explicitly says it supports the screen size we
             // are running in.
-            if ((appInfo.flags & ApplicationInfo.FLAG_RESIZEABLE_FOR_SCREENS) != 0) {
-                compatFlags |= EXPANDABLE;
-            }
+            compatFlags &= ~EXPANDABLE;
         }
 
         boolean supportsScreen = false;
@@ -155,12 +157,10 @@
                 break;
         }
 
-        if ((screenLayout&Configuration.SCREENLAYOUT_COMPAT_NEEDED) == 0) {
+        if ((screenLayout&Configuration.SCREENLAYOUT_COMPAT_NEEDED) != 0) {
             if ((compatFlags&EXPANDABLE) != 0) {
                 supportsScreen = true;
-            }
-            if ((compatFlags&EXPANDABLE) == 0 &&
-                    (appInfo.flags & ApplicationInfo.FLAG_RESIZEABLE_FOR_SCREENS) == 0) {
+            } else if ((appInfo.flags & ApplicationInfo.FLAG_RESIZEABLE_FOR_SCREENS) == 0) {
                 compatFlags |= ALWAYS_COMPAT;
             }
         }
@@ -382,6 +382,9 @@
             // This is a larger screen device and the app is not
             // compatible with large screens, so diddle it.
             CompatibilityInfo.updateCompatibleScreenFrame(inoutDm, null, inoutDm);
+        } else {
+            inoutDm.widthPixels = inoutDm.realWidthPixels;
+            inoutDm.heightPixels = inoutDm.realHeightPixels;
         }
 
         if (isScalingRequired()) {
diff --git a/core/java/android/content/res/Configuration.java b/core/java/android/content/res/Configuration.java
index 51a7115..d476997 100644
--- a/core/java/android/content/res/Configuration.java
+++ b/core/java/android/content/res/Configuration.java
@@ -316,10 +316,11 @@
         StringBuilder sb = new StringBuilder(128);
         sb.append("{");
         sb.append(fontScale);
-        sb.append("x imsi=");
+        sb.append(" ");
         sb.append(mcc);
-        sb.append("/");
+        sb.append("mcc");
         sb.append(mnc);
+        sb.append("mnc");
         if (locale != null) {
             sb.append(" ");
             sb.append(locale);
diff --git a/core/java/android/content/res/Resources.java b/core/java/android/content/res/Resources.java
index a072e94..e63e7eb 100755
--- a/core/java/android/content/res/Resources.java
+++ b/core/java/android/content/res/Resources.java
@@ -115,7 +115,6 @@
     private NativePluralRules mPluralRule;
     
     private CompatibilityInfo mCompatibilityInfo;
-    private Display mDefaultDisplay;
 
     private static final LongSparseArray<Object> EMPTY_ARRAY = new LongSparseArray<Object>(0) {
         @Override
@@ -1426,6 +1425,15 @@
             }
             if (metrics != null) {
                 mMetrics.setTo(metrics);
+                // NOTE: We should re-arrange this code to create a Display
+                // with the CompatibilityInfo that is used everywhere we deal
+                // with the display in relation to this app, rather than
+                // doing the conversion here.  This impl should be okay because
+                // we make sure to return a compatible display in the places
+                // where there are public APIs to retrieve the display...  but
+                // it would be cleaner and more maintainble to just be
+                // consistently dealing with a compatible display everywhere in
+                // the framework.
                 mCompatibilityInfo.applyToDisplayMetrics(mMetrics);
             }
             mMetrics.scaledDensity = mMetrics.density * mConfiguration.fontScale;
@@ -2121,24 +2129,6 @@
                 + Integer.toHexString(id));
     }
 
-    /**
-     * Returns the display adjusted for the Resources' metrics.
-     * @hide
-     */
-    public Display getDefaultDisplay(Display defaultDisplay) {
-        if (mDefaultDisplay == null) {
-            if (!mCompatibilityInfo.isScalingRequired() && mCompatibilityInfo.supportsScreen()) {
-                // the app supports the display. just use the default one.
-                mDefaultDisplay = defaultDisplay;
-            } else {
-                // display needs adjustment.
-                mDefaultDisplay = Display.createMetricsBasedDisplay(
-                        defaultDisplay.getDisplayId(), mMetrics);
-            }
-        }
-        return mDefaultDisplay;
-    }
-
     private TypedArray getCachedStyledAttributes(int len) {
         synchronized (mTmpValue) {
             TypedArray attrs = mCachedStyledAttributes;
diff --git a/core/java/android/os/Environment.java b/core/java/android/os/Environment.java
index 6b58877..11f9445 100644
--- a/core/java/android/os/Environment.java
+++ b/core/java/android/os/Environment.java
@@ -113,18 +113,18 @@
             = getDirectory("ANDROID_SECURE_DATA", "/data/secure");
 
     private static final File EXTERNAL_STORAGE_DIRECTORY
-            = getDirectory("EXTERNAL_STORAGE", "/sdcard");
+            = getDirectory("EXTERNAL_STORAGE", "/mnt/sdcard");
 
     private static final File EXTERNAL_STORAGE_ANDROID_DATA_DIRECTORY
-            = new File (new File(getDirectory("EXTERNAL_STORAGE", "/sdcard"),
+            = new File (new File(getDirectory("EXTERNAL_STORAGE", "/mnt/sdcard"),
                     "Android"), "data");
 
     private static final File EXTERNAL_STORAGE_ANDROID_MEDIA_DIRECTORY
-            = new File (new File(getDirectory("EXTERNAL_STORAGE", "/sdcard"),
+            = new File (new File(getDirectory("EXTERNAL_STORAGE", "/mnt/sdcard"),
                     "Android"), "media");
 
     private static final File EXTERNAL_STORAGE_ANDROID_OBB_DIRECTORY
-            = new File (new File(getDirectory("EXTERNAL_STORAGE", "/sdcard"),
+            = new File (new File(getDirectory("EXTERNAL_STORAGE", "/mnt/sdcard"),
                     "Android"), "obb");
 
     private static final File DOWNLOAD_CACHE_DIRECTORY
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 570b801..c78b935 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -2563,6 +2563,13 @@
             "lock_screen_owner_info_enabled";
 
         /**
+         * The saved value for WindowManagerService.setForcedDisplaySize().
+         * Two integers separated by a comma.  If unset, then use the real display size.
+         * @hide
+         */
+        public static final String DISPLAY_SIZE_FORCED = "display_size_forced";
+
+        /**
          * Whether assisted GPS should be enabled or not.
          * @hide
          */
@@ -3737,6 +3744,23 @@
                 "setup_prepaid_detection_redir_host";
 
         /**
+         * The user's preferred "dream" (interactive screensaver) component.
+         *
+         * This component will be launched by the PhoneWindowManager after the user's chosen idle
+         * timeout (specified by {@link #DREAM_TIMEOUT}).
+         * @hide
+         */
+        public static final String DREAM_COMPONENT =
+                "dream_component";
+
+        /**
+         * The delay before a "dream" is started (set to 0 to disable).
+         * @hide
+         */
+        public static final String DREAM_TIMEOUT =
+                "dream_timeout";
+
+        /**
          * @hide
          */
         public static final String[] SETTINGS_TO_BACKUP = {
diff --git a/core/java/android/text/style/SuggestionSpan.java b/core/java/android/text/style/SuggestionSpan.java
index dcb0898..240ad9b 100644
--- a/core/java/android/text/style/SuggestionSpan.java
+++ b/core/java/android/text/style/SuggestionSpan.java
@@ -19,6 +19,7 @@
 import android.content.Context;
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.os.SystemClock;
 import android.text.ParcelableSpan;
 import android.text.TextUtils;
 
@@ -29,14 +30,18 @@
  * Holds suggestion candidates of words under this span.
  */
 public class SuggestionSpan implements ParcelableSpan {
-
     /**
      * Flag for indicating that the input is verbatim. TextView refers to this flag to determine
      * how it displays a word with SuggestionSpan.
      */
     public static final int FLAG_VERBATIM = 0x0001;
 
-    private static final int SUGGESTIONS_MAX_SIZE = 5;
+    public static final String ACTION_SUGGESTION_PICKED = "android.text.style.SUGGESTION_PICKED";
+    public static final String SUGGESTION_SPAN_PICKED_AFTER = "after";
+    public static final String SUGGESTION_SPAN_PICKED_BEFORE = "before";
+    public static final String SUGGESTION_SPAN_PICKED_HASHCODE = "hashcode";
+
+    public static final int SUGGESTIONS_MAX_SIZE = 5;
 
     /*
      * TODO: Needs to check the validity and add a feature that TextView will change
@@ -48,7 +53,9 @@
     private final int mFlags;
     private final String[] mSuggestions;
     private final String mLocaleString;
-    private final String mOriginalString;
+    private final String mNotificationTargetClassName;
+    private final int mHashCode;
+
     /*
      * TODO: If switching IME is required, needs to add parameters for ids of InputMethodInfo
      * and InputMethodSubtype.
@@ -77,10 +84,11 @@
      * @param locale locale Locale of the suggestions
      * @param suggestions Suggestions for the string under the span
      * @param flags Additional flags indicating how this span is handled in TextView
-     * @param originalString originalString for suggestions
+     * @param notificationTargetClass if not null, this class will get notified when the user
+     * selects one of the suggestions.
      */
     public SuggestionSpan(Context context, Locale locale, String[] suggestions, int flags,
-            String originalString) {
+            Class<?> notificationTargetClass) {
         final int N = Math.min(SUGGESTIONS_MAX_SIZE, suggestions.length);
         mSuggestions = Arrays.copyOf(suggestions, N);
         mFlags = flags;
@@ -89,14 +97,21 @@
         } else {
             mLocaleString = locale.toString();
         }
-        mOriginalString = originalString;
+        if (notificationTargetClass != null) {
+            mNotificationTargetClassName = notificationTargetClass.getCanonicalName();
+        } else {
+            mNotificationTargetClassName = "";
+        }
+        mHashCode = hashCodeInternal(
+                mFlags, mSuggestions, mLocaleString, mNotificationTargetClassName);
     }
 
     public SuggestionSpan(Parcel src) {
         mSuggestions = src.readStringArray();
         mFlags = src.readInt();
         mLocaleString = src.readString();
-        mOriginalString = src.readString();
+        mNotificationTargetClassName = src.readString();
+        mHashCode = src.readInt();
     }
 
     /**
@@ -114,10 +129,16 @@
     }
 
     /**
-     * @return original string of suggestions
+     * @return The name of the class to notify. The class of the original IME package will receive
+     * a notification when the user selects one of the suggestions. The notification will include
+     * the original string, the suggested replacement string as well as the hashCode of this span.
+     * The class will get notified by an intent that has those information.
+     * This is an internal API because only the framework should know the class name.
+     *
+     * @hide
      */
-    public String getOriginalString() {
-        return mOriginalString;
+    public String getNotificationTargetClassName() {
+        return mNotificationTargetClassName;
     }
 
     public int getFlags() {
@@ -134,7 +155,8 @@
         dest.writeStringArray(mSuggestions);
         dest.writeInt(mFlags);
         dest.writeString(mLocaleString);
-        dest.writeString(mOriginalString);
+        dest.writeString(mNotificationTargetClassName);
+        dest.writeInt(mHashCode);
     }
 
     @Override
@@ -142,6 +164,25 @@
         return TextUtils.SUGGESTION_SPAN;
     }
 
+    @Override
+    public boolean equals(Object o) {
+        if (o instanceof SuggestionSpan) {
+            return ((SuggestionSpan)o).hashCode() == mHashCode;
+        }
+        return false;
+    }
+
+    @Override
+    public int hashCode() {
+        return mHashCode;
+    }
+
+    private static int hashCodeInternal(int flags, String[] suggestions,String locale,
+            String notificationTargetClassName) {
+        return Arrays.hashCode(new Object[] {SystemClock.uptimeMillis(), flags, suggestions, locale,
+                notificationTargetClassName});
+    }
+
     public static final Parcelable.Creator<SuggestionSpan> CREATOR =
             new Parcelable.Creator<SuggestionSpan>() {
         @Override
diff --git a/core/java/android/view/Display.java b/core/java/android/view/Display.java
index 1d60066..b5d36d9 100644
--- a/core/java/android/view/Display.java
+++ b/core/java/android/view/Display.java
@@ -16,6 +16,7 @@
 
 package android.view;
 
+import android.content.res.CompatibilityInfo;
 import android.graphics.Point;
 import android.graphics.Rect;
 import android.os.RemoteException;
@@ -37,7 +38,7 @@
      * Display gives you access to some information about a particular display
      * connected to the device.
      */
-    Display(int display) {
+    Display(int display, CompatibilityInfo compatInfo) {
         // initalize the statics when this class is first instansiated. This is
         // done here instead of in the static block because Zygote
         synchronized (sStaticInit) {
@@ -46,6 +47,12 @@
                 sInitialized = true;
             }
         }
+        if (compatInfo != null && (compatInfo.isScalingRequired()
+                || !compatInfo.supportsScreen())) {
+            mCompatibilityInfo = compatInfo;
+        } else {
+            mCompatibilityInfo = null;
+        }
         mDisplay = display;
         init(display);
     }
@@ -82,6 +89,16 @@
                 // system process before the window manager is up.
                 outSize.y = getRealHeight();
             }
+            if (mCompatibilityInfo != null) {
+                synchronized (mTmpMetrics) {
+                    mTmpMetrics.realWidthPixels = outSize.x;
+                    mTmpMetrics.realHeightPixels = outSize.y;
+                    mTmpMetrics.density = mDensity;
+                    mCompatibilityInfo.applyToDisplayMetrics(mTmpMetrics);
+                    outSize.x = mTmpMetrics.widthPixels;
+                    outSize.y = mTmpMetrics.heightPixels;
+                }
+            }
         } catch (RemoteException e) {
             Slog.w("Display", "Unable to get display size", e);
         }
@@ -206,6 +223,10 @@
             outMetrics.heightPixels = mTmpPoint.y;
         }
         getNonSizeMetrics(outMetrics);
+
+        if (mCompatibilityInfo != null) {
+            mCompatibilityInfo.applyToDisplayMetrics(outMetrics);
+        }
     }
 
     /**
@@ -249,7 +270,8 @@
     
     private native void init(int display);
 
-    private int         mDisplay;
+    private final CompatibilityInfo mCompatibilityInfo;
+    private final int   mDisplay;
     // Following fields are initialized from native code
     private int         mPixelFormat;
     private float       mRefreshRate;
@@ -258,6 +280,7 @@
     private float       mDpiY;
     
     private final Point mTmpPoint = new Point();
+    private final DisplayMetrics mTmpMetrics = new DisplayMetrics();
     private float mLastGetTime;
 
     private static final Object sStaticInit = new Object();
@@ -268,27 +291,8 @@
      * Returns a display object which uses the metric's width/height instead.
      * @hide
      */
-    public static Display createMetricsBasedDisplay(int displayId, DisplayMetrics metrics) {
-        return new CompatibleDisplay(displayId, metrics);
-    }
-
-    private static class CompatibleDisplay extends Display {
-        private final DisplayMetrics mMetrics;
-
-        private CompatibleDisplay(int displayId, DisplayMetrics metrics) {
-            super(displayId);
-            mMetrics = metrics;
-        }
-
-        @Override
-        public int getWidth() {
-            return mMetrics.widthPixels;
-        }
-
-        @Override
-        public int getHeight() {
-            return mMetrics.heightPixels;
-        }
+    public static Display createCompatibleDisplay(int displayId, CompatibilityInfo compat) {
+        return new Display(displayId, compat);
     }
 }
 
diff --git a/core/java/android/view/Surface.java b/core/java/android/view/Surface.java
index 83f9119..c913bb3 100644
--- a/core/java/android/view/Surface.java
+++ b/core/java/android/view/Surface.java
@@ -20,7 +20,6 @@
 import android.graphics.*;
 import android.os.Parcelable;
 import android.os.Parcel;
-import android.util.DisplayMetrics;
 import android.util.Log;
 
 /**
@@ -174,9 +173,9 @@
     private int mSurfaceGenerationId;
     private String mName;
 
-    // The display metrics used to provide the pseudo canvas size for applications
-    // running in compatibility mode. This is set to null for non compatibility mode.
-    private DisplayMetrics mCompatibleDisplayMetrics;
+    // The Translator for density compatibility mode.  This is used for scaling
+    // the canvas to perform the appropriate density transformation.
+    private Translator mCompatibilityTranslator;
 
     // A matrix to scale the matrix set by application. This is set to null for
     // non compatibility mode.
@@ -263,14 +262,20 @@
 
         @Override
         public int getWidth() {
-            return mCompatibleDisplayMetrics == null ?
-                    super.getWidth() : mCompatibleDisplayMetrics.widthPixels;
+            int w = super.getWidth();
+            if (mCompatibilityTranslator != null) {
+                w = (int)(w * mCompatibilityTranslator.applicationInvertedScale + .5f);
+            }
+            return w;
         }
 
         @Override
         public int getHeight() {
-            return mCompatibleDisplayMetrics == null ?
-                    super.getHeight() : mCompatibleDisplayMetrics.heightPixels;
+            int h = super.getHeight();
+            if (mCompatibilityTranslator != null) {
+                h = (int)(h * mCompatibilityTranslator.applicationInvertedScale + .5f);
+            }
+            return h;
         }
 
         @Override
@@ -297,10 +302,9 @@
     }
 
     /**
-     * Sets the display metrics used to provide canvas's width/height in compatibility mode.
+     * Sets the translator used to scale canvas's width/height in compatibility mode.
      */
-    void setCompatibleDisplayMetrics(DisplayMetrics metrics, Translator translator) {
-        mCompatibleDisplayMetrics = metrics;
+    void setCompatibilityTranslator(Translator translator) {
         if (translator != null) {
             float appScale = translator.applicationScale;
             mCompatibleMatrix = new Matrix();
diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java
index a98c669..764899f 100644
--- a/core/java/android/view/SurfaceView.java
+++ b/core/java/android/view/SurfaceView.java
@@ -21,7 +21,6 @@
 
 import android.content.Context;
 import android.content.res.Configuration;
-import android.content.res.Resources;
 import android.content.res.CompatibilityInfo.Translator;
 import android.graphics.Canvas;
 import android.graphics.PixelFormat;
@@ -432,9 +431,8 @@
             mTranslator = viewRoot.mTranslator;
         }
 
-        Resources res = getContext().getResources();
-        if (mTranslator != null || !res.getCompatibilityInfo().supportsScreen()) {
-            mSurface.setCompatibleDisplayMetrics(res.getDisplayMetrics(), mTranslator);
+        if (mTranslator != null) {
+            mSurface.setCompatibilityTranslator(mTranslator);
         }
         
         int myWidth = mRequestedWidth;
diff --git a/core/java/android/view/ViewAncestor.java b/core/java/android/view/ViewAncestor.java
index bf04502..bd33a6a 100644
--- a/core/java/android/view/ViewAncestor.java
+++ b/core/java/android/view/ViewAncestor.java
@@ -380,9 +380,8 @@
                 CompatibilityInfo compatibilityInfo = resources.getCompatibilityInfo();
                 mTranslator = compatibilityInfo.getTranslator();
 
-                if (mTranslator != null || !compatibilityInfo.supportsScreen()) {
-                    mSurface.setCompatibleDisplayMetrics(resources.getDisplayMetrics(),
-                            mTranslator);
+                if (mTranslator != null) {
+                    mSurface.setCompatibilityTranslator(mTranslator);
                 }
 
                 boolean restore = false;
diff --git a/core/java/android/view/Window.java b/core/java/android/view/Window.java
index 2095a93..5236a9e 100644
--- a/core/java/android/view/Window.java
+++ b/core/java/android/view/Window.java
@@ -17,6 +17,7 @@
 package android.view;
 
 import android.content.Context;
+import android.content.res.CompatibilityInfo;
 import android.content.res.Configuration;
 import android.content.res.TypedArray;
 import android.graphics.PixelFormat;
@@ -462,13 +463,11 @@
         mWindowManager = new LocalWindowManager(wm, hardwareAccelerated);
     }
 
-    private class LocalWindowManager implements WindowManager {
-        private boolean mHardwareAccelerated;
+    private class LocalWindowManager extends WindowManagerImpl.CompatModeWrapper {
+        private final boolean mHardwareAccelerated;
 
         LocalWindowManager(WindowManager wm, boolean hardwareAccelerated) {
-            mWindowManager = wm;
-            mDefaultDisplay = mContext.getResources().getDefaultDisplay(
-                    mWindowManager.getDefaultDisplay());
+            super(wm, mContext.getResources().getCompatibilityInfo());
             mHardwareAccelerated = hardwareAccelerated;
         }
 
@@ -523,28 +522,8 @@
             if (mHardwareAccelerated) {
                 wp.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
             }
-            mWindowManager.addView(view, params);
+            super.addView(view, params);
         }
-
-        public void updateViewLayout(View view, ViewGroup.LayoutParams params) {
-            mWindowManager.updateViewLayout(view, params);
-        }
-
-        public final void removeView(View view) {
-            mWindowManager.removeView(view);
-        }
-
-        public final void removeViewImmediate(View view) {
-            mWindowManager.removeViewImmediate(view);
-        }
-
-        public Display getDefaultDisplay() {
-            return mDefaultDisplay;
-        }
-        
-        private final WindowManager mWindowManager;
-
-        private final Display mDefaultDisplay;
     }
 
     /**
diff --git a/core/java/android/view/WindowManagerImpl.java b/core/java/android/view/WindowManagerImpl.java
index d7a3096..d18ae0e 100644
--- a/core/java/android/view/WindowManagerImpl.java
+++ b/core/java/android/view/WindowManagerImpl.java
@@ -16,6 +16,9 @@
 
 package android.view;
 
+import java.util.HashMap;
+
+import android.content.res.CompatibilityInfo;
 import android.graphics.PixelFormat;
 import android.os.IBinder;
 import android.util.AndroidRuntimeException;
@@ -74,9 +77,92 @@
     public static final int ADD_MULTIPLE_SINGLETON = -7;
     public static final int ADD_PERMISSION_DENIED = -8;
 
-    public static WindowManagerImpl getDefault()
-    {
-        return mWindowManager;
+    private View[] mViews;
+    private ViewAncestor[] mRoots;
+    private WindowManager.LayoutParams[] mParams;
+
+    private final static Object sLock = new Object();
+    private final static WindowManagerImpl sWindowManager = new WindowManagerImpl();
+    private final static HashMap<CompatibilityInfo, WindowManager> sCompatWindowManagers
+            = new HashMap<CompatibilityInfo, WindowManager>();
+
+    static class CompatModeWrapper implements WindowManager {
+        private final WindowManager mWindowManager;
+        private final Display mDefaultDisplay;
+
+        CompatModeWrapper(WindowManager wm, CompatibilityInfo ci) {
+            mWindowManager = wm;
+
+            // Use the original display if there is no compatibility mode
+            // to apply, or the underlying window manager is already a
+            // compatibility mode wrapper.  (We assume that if it is a
+            // wrapper, it is applying the same compatibility mode.)
+            if (ci == null || wm instanceof CompatModeWrapper
+                    || (!ci.isScalingRequired() && ci.supportsScreen())) {
+                mDefaultDisplay = mWindowManager.getDefaultDisplay();
+            } else {
+                //mDefaultDisplay = mWindowManager.getDefaultDisplay();
+                mDefaultDisplay = Display.createCompatibleDisplay(
+                        mWindowManager.getDefaultDisplay().getDisplayId(), ci);
+            }
+        }
+
+        @Override
+        public void addView(View view, android.view.ViewGroup.LayoutParams params) {
+            mWindowManager.addView(view, params);
+        }
+
+        @Override
+        public void updateViewLayout(View view, android.view.ViewGroup.LayoutParams params) {
+            mWindowManager.updateViewLayout(view, params);
+
+        }
+
+        @Override
+        public void removeView(View view) {
+            mWindowManager.removeView(view);
+        }
+
+        @Override
+        public Display getDefaultDisplay() {
+            return mDefaultDisplay;
+        }
+
+        @Override
+        public void removeViewImmediate(View view) {
+            mWindowManager.removeViewImmediate(view);
+        }
+
+        @Override
+        public boolean isHardwareAccelerated() {
+            return mWindowManager.isHardwareAccelerated();
+        }
+
+    }
+
+    public static WindowManagerImpl getDefault() {
+        return sWindowManager;
+    }
+
+    public static WindowManager getDefault(CompatibilityInfo compatInfo) {
+        if (compatInfo == null || (!compatInfo.isScalingRequired()
+                && compatInfo.supportsScreen())) {
+            return sWindowManager;
+        }
+
+        synchronized (sLock) {
+            // NOTE: It would be cleaner to move the implementation of
+            // WindowManagerImpl into a static inner class, and have this
+            // public impl just call into that.  Then we can make multiple
+            // instances of WindowManagerImpl for compat mode rather than
+            // having to make wrappers.
+            WindowManager wm = sCompatWindowManagers.get(compatInfo);
+            if (wm == null) {
+                wm = new CompatModeWrapper(sWindowManager, compatInfo);
+                sCompatWindowManagers.put(compatInfo, wm);
+            }
+            return wm;
+        }
     }
     
     public boolean isHardwareAccelerated() {
@@ -340,13 +426,9 @@
     }
     
     public Display getDefaultDisplay() {
-        return new Display(Display.DEFAULT_DISPLAY);
+        return new Display(Display.DEFAULT_DISPLAY, null);
     }
 
-    private View[] mViews;
-    private ViewAncestor[] mRoots;
-    private WindowManager.LayoutParams[] mParams;
-
     private static void removeItem(Object[] dst, Object[] src, int index)
     {
         if (dst.length > 0) {
@@ -375,6 +457,4 @@
             return -1;
         }
     }
-
-    private static WindowManagerImpl mWindowManager = new WindowManagerImpl();
 }
diff --git a/core/java/android/view/WindowManagerPolicy.java b/core/java/android/view/WindowManagerPolicy.java
index 4e52e40..64b1ac8 100644
--- a/core/java/android/view/WindowManagerPolicy.java
+++ b/core/java/android/view/WindowManagerPolicy.java
@@ -884,7 +884,13 @@
     public boolean performHapticFeedbackLw(WindowState win, int effectId, boolean always);
     
     /**
-     * Called when we have stopped keeping the screen on because a window
+     * Called when we have started keeping the screen on because a window
+     * requesting this has become visible.
+     */
+    public void screenOnStartedLw();
+
+    /**
+     * Called when we have stopped keeping the screen on because the last window
      * requesting this is no longer visible.
      */
     public void screenOnStoppedLw();
diff --git a/core/java/android/view/inputmethod/BaseInputConnection.java b/core/java/android/view/inputmethod/BaseInputConnection.java
index b4303f4..abe3c2c 100644
--- a/core/java/android/view/inputmethod/BaseInputConnection.java
+++ b/core/java/android/view/inputmethod/BaseInputConnection.java
@@ -49,8 +49,9 @@
     private static final boolean DEBUG = false;
     private static final String TAG = "BaseInputConnection";
     static final Object COMPOSING = new ComposingText();
-    
-    final InputMethodManager mIMM;
+
+    /** @hide */
+    protected final InputMethodManager mIMM;
     final View mTargetView;
     final boolean mDummyMode;
     
diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java
index 27cbaf7..ea66d67 100644
--- a/core/java/android/view/inputmethod/InputMethodManager.java
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -35,6 +35,7 @@
 import android.os.RemoteException;
 import android.os.ResultReceiver;
 import android.os.ServiceManager;
+import android.text.style.SuggestionSpan;
 import android.util.Log;
 import android.util.PrintWriterPrinter;
 import android.util.Printer;
@@ -550,7 +551,25 @@
     public void setFullscreenMode(boolean fullScreen) {
         mFullscreenMode = fullScreen;
     }
-    
+
+    /** @hide */
+    public void registerSuggestionSpansForNotification(SuggestionSpan[] spans) {
+        try {
+            mService.registerSuggestionSpansForNotification(spans);
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /** @hide */
+    public void notifySuggestionPicked(SuggestionSpan span, String originalString, int index) {
+        try {
+            mService.notifySuggestionPicked(span, originalString, index);
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
     /**
      * Allows you to discover whether the attached input method is running
      * in fullscreen mode.  Return true if it is fullscreen, entirely covering
diff --git a/core/java/com/android/internal/view/IInputMethodManager.aidl b/core/java/com/android/internal/view/IInputMethodManager.aidl
index 4ffa4e1..8039fda 100644
--- a/core/java/com/android/internal/view/IInputMethodManager.aidl
+++ b/core/java/com/android/internal/view/IInputMethodManager.aidl
@@ -17,6 +17,7 @@
 package com.android.internal.view;
 
 import android.os.ResultReceiver;
+import android.text.style.SuggestionSpan;
 import android.view.inputmethod.InputMethodInfo;
 import android.view.inputmethod.InputMethodSubtype;
 import android.view.inputmethod.EditorInfo;
@@ -61,6 +62,8 @@
     void showMySoftInput(in IBinder token, int flags);
     void updateStatusIcon(in IBinder token, String packageName, int iconId);
     void setImeWindowStatus(in IBinder token, int vis, int backDisposition);
+    void registerSuggestionSpansForNotification(in SuggestionSpan[] spans);
+    boolean notifySuggestionPicked(in SuggestionSpan span, String originalString, int index);
     InputMethodSubtype getCurrentInputMethodSubtype();
     boolean setCurrentInputMethodSubtype(in InputMethodSubtype subtype);
     boolean switchToLastInputMethod(in IBinder token);
diff --git a/core/java/com/android/internal/widget/EditableInputConnection.java b/core/java/com/android/internal/widget/EditableInputConnection.java
index 0d32d4ba..32e733b 100644
--- a/core/java/com/android/internal/widget/EditableInputConnection.java
+++ b/core/java/com/android/internal/widget/EditableInputConnection.java
@@ -17,9 +17,10 @@
 package com.android.internal.widget;
 
 import android.os.Bundle;
-import android.os.IBinder;
 import android.text.Editable;
+import android.text.Spanned;
 import android.text.method.KeyListener;
+import android.text.style.SuggestionSpan;
 import android.util.Log;
 import android.view.inputmethod.BaseInputConnection;
 import android.view.inputmethod.CompletionInfo;
@@ -138,6 +139,11 @@
         if (mTextView == null) {
             return super.commitText(text, newCursorPosition);
         }
+        if (text instanceof Spanned) {
+            Spanned spanned = ((Spanned) text);
+            SuggestionSpan[] spans = spanned.getSpans(0, text.length(), SuggestionSpan.class);
+            mIMM.registerSuggestionSpansForNotification(spans);
+        }
 
         mTextView.resetErrorChangedFlag();
         boolean success = super.commitText(text, newCursorPosition);
diff --git a/docs/html/resources/resources-data.js b/docs/html/resources/resources-data.js
index 7751c82..8d9b7b1 100644
--- a/docs/html/resources/resources-data.js
+++ b/docs/html/resources/resources-data.js
@@ -626,6 +626,16 @@
     }
   },
   {
+    tags: ['sample', 'data', 'new'],
+    path: 'samples/VoicemailProviderDemo/index.html',
+    title: {
+      en: 'Voicemail Provider Demo'
+    },
+    description: {
+      en: 'A sample application to demonstrate how to use voicemail content provider APIs.'
+    }
+  },
+  {
     tags: ['sample', 'ui', 'widgets'],
     path: 'samples/Wiktionary/index.html',
     title: {
diff --git a/docs/html/resources/samples/images/VoicemailProviderDemo.png b/docs/html/resources/samples/images/VoicemailProviderDemo.png
new file mode 100644
index 0000000..1a45d7a
--- /dev/null
+++ b/docs/html/resources/samples/images/VoicemailProviderDemo.png
Binary files differ
diff --git a/include/gui/SurfaceTexture.h b/include/gui/SurfaceTexture.h
index 43b2fa9..2b31462 100644
--- a/include/gui/SurfaceTexture.h
+++ b/include/gui/SurfaceTexture.h
@@ -197,8 +197,9 @@
               mEglDisplay(EGL_NO_DISPLAY),
               mBufferState(BufferSlot::FREE),
               mRequestBufferCalled(false),
-              mLastQueuedTransform(0),
-              mLastQueuedTimestamp(0) {
+              mTransform(0),
+              mTimestamp(0) {
+            mCrop.makeInvalid();
         }
 
         // mGraphicBuffer points to the buffer allocated for this slot or is NULL
@@ -211,32 +212,56 @@
         // mEglDisplay is the EGLDisplay used to create mEglImage.
         EGLDisplay mEglDisplay;
 
-        // mBufferState indicates whether the slot is currently accessible to a
-        // client and should not be used by the SurfaceTexture object. It gets
-        // set to true when dequeueBuffer returns the slot and is reset to false
-        // when the client calls either queueBuffer or cancelBuffer on the slot.
-        enum { DEQUEUED=-2, FREE=-1, QUEUED=0 };
-        int8_t mBufferState;
+        // BufferState represents the different states in which a buffer slot
+        // can be.
+        enum BufferState {
+            // FREE indicates that the buffer is not currently being used and
+            // will not be used in the future until it gets dequeued and
+            // subseqently queued by the client.
+            FREE = 0,
 
+            // DEQUEUED indicates that the buffer has been dequeued by the
+            // client, but has not yet been queued or canceled. The buffer is
+            // considered 'owned' by the client, and the server should not use
+            // it for anything.
+            //
+            // Note that when in synchronous-mode (mSynchronousMode == true),
+            // the buffer that's currently attached to the texture may be
+            // dequeued by the client.  That means that the current buffer can
+            // be in either the DEQUEUED or QUEUED state.  In asynchronous mode,
+            // however, the current buffer is always in the QUEUED state.
+            DEQUEUED = 1,
+
+            // QUEUED indicates that the buffer has been queued by the client,
+            // and has not since been made available for the client to dequeue.
+            // Attaching the buffer to the texture does NOT transition the
+            // buffer away from the QUEUED state. However, in Synchronous mode
+            // the current buffer may be dequeued by the client under some
+            // circumstances. See the note about the current buffer in the
+            // documentation for DEQUEUED.
+            QUEUED = 2,
+        };
+
+        // mBufferState is the current state of this buffer slot.
+        BufferState mBufferState;
 
         // mRequestBufferCalled is used for validating that the client did
         // call requestBuffer() when told to do so. Technically this is not
         // needed but useful for debugging and catching client bugs.
         bool mRequestBufferCalled;
 
-        // mLastQueuedCrop is the crop rectangle for the buffer that was most
-        // recently queued. This gets set to mNextCrop each time queueBuffer gets
-        // called.
-        Rect mLastQueuedCrop;
+        // mCrop is the current crop rectangle for this buffer slot. This gets
+        // set to mNextCrop each time queueBuffer gets called for this buffer.
+        Rect mCrop;
 
-        // mLastQueuedTransform is the transform identifier for the buffer that was
-        // most recently queued. This gets set to mNextTransform each time
-        // queueBuffer gets called.
-        uint32_t mLastQueuedTransform;
+        // mTransform is the current transform flags for this buffer slot. This
+        // gets set to mNextTransform each time queueBuffer gets called for this
+        // slot.
+        uint32_t mTransform;
 
-        // mLastQueuedTimestamp is the timestamp for the buffer that was most
-        // recently queued. This gets set by queueBuffer.
-        int64_t mLastQueuedTimestamp;
+        // mTimestamp is the current timestamp for this buffer slot. This gets
+        // to set by queueBuffer each time this slot is queued.
+        int64_t mTimestamp;
     };
 
     // mSlots is the array of buffer slots that must be mirrored on the client
diff --git a/include/media/mediaplayer.h b/include/media/mediaplayer.h
index cfa4cfd..50a378f 100644
--- a/include/media/mediaplayer.h
+++ b/include/media/mediaplayer.h
@@ -127,6 +127,7 @@
 
 enum media_set_parameter_keys {
     KEY_PARAMETER_TIMED_TEXT_TRACK_INDEX = 1000,
+    KEY_PARAMETER_TIMED_TEXT_ADD_OUT_OF_BAND_SOURCE = 1001,
 };
 // ----------------------------------------------------------------------------
 // ref-counted object for callbacks
diff --git a/libs/gui/SurfaceTexture.cpp b/libs/gui/SurfaceTexture.cpp
index b08a5a8..ee97dcf 100644
--- a/libs/gui/SurfaceTexture.cpp
+++ b/libs/gui/SurfaceTexture.cpp
@@ -285,15 +285,19 @@
             return -EINVAL;
         }
 
-        // make sure the client is not trying to dequeue more buffers
-        // than allowed.
-        const int avail = mBufferCount - (dequeuedCount+1);
-        if (avail < (MIN_UNDEQUEUED_BUFFERS-int(mSynchronousMode))) {
-            LOGE("dequeueBuffer: MIN_UNDEQUEUED_BUFFERS=%d exceeded (dequeued=%d)",
-                    MIN_UNDEQUEUED_BUFFERS-int(mSynchronousMode),
-                    dequeuedCount);
-            // TODO: Enable this error report after we fix issue 4435022
-            // return -EBUSY;
+        // See whether a buffer has been queued since the last setBufferCount so
+        // we know whether to perform the MIN_UNDEQUEUED_BUFFERS check below.
+        bool bufferHasBeenQueued = mCurrentTexture != INVALID_BUFFER_SLOT;
+        if (bufferHasBeenQueued) {
+            // make sure the client is not trying to dequeue more buffers
+            // than allowed.
+            const int avail = mBufferCount - (dequeuedCount+1);
+            if (avail < (MIN_UNDEQUEUED_BUFFERS-int(mSynchronousMode))) {
+                LOGE("dequeueBuffer: MIN_UNDEQUEUED_BUFFERS=%d exceeded (dequeued=%d)",
+                        MIN_UNDEQUEUED_BUFFERS-int(mSynchronousMode),
+                        dequeuedCount);
+                return -EBUSY;
+            }
         }
 
         // we're in synchronous mode and didn't find a buffer, we need to wait
@@ -390,49 +394,49 @@
     sp<FrameAvailableListener> listener;
 
     { // scope for the lock
-    Mutex::Autolock lock(mMutex);
-    if (buf < 0 || buf >= mBufferCount) {
-        LOGE("queueBuffer: slot index out of range [0, %d]: %d",
-                mBufferCount, buf);
-        return -EINVAL;
-    } else if (mSlots[buf].mBufferState != BufferSlot::DEQUEUED) {
-        LOGE("queueBuffer: slot %d is not owned by the client (state=%d)",
-                buf, mSlots[buf].mBufferState);
-        return -EINVAL;
-    } else if (buf == mCurrentTexture) {
-        LOGE("queueBuffer: slot %d is current!", buf);
-        return -EINVAL;
-    } else if (!mSlots[buf].mRequestBufferCalled) {
-        LOGE("queueBuffer: slot %d was enqueued without requesting a buffer",
-                buf);
-        return -EINVAL;
-    }
+        Mutex::Autolock lock(mMutex);
+        if (buf < 0 || buf >= mBufferCount) {
+            LOGE("queueBuffer: slot index out of range [0, %d]: %d",
+                    mBufferCount, buf);
+            return -EINVAL;
+        } else if (mSlots[buf].mBufferState != BufferSlot::DEQUEUED) {
+            LOGE("queueBuffer: slot %d is not owned by the client (state=%d)",
+                    buf, mSlots[buf].mBufferState);
+            return -EINVAL;
+        } else if (buf == mCurrentTexture) {
+            LOGE("queueBuffer: slot %d is current!", buf);
+            return -EINVAL;
+        } else if (!mSlots[buf].mRequestBufferCalled) {
+            LOGE("queueBuffer: slot %d was enqueued without requesting a "
+                    "buffer", buf);
+            return -EINVAL;
+        }
 
-    if (mQueue.empty()) {
-        listener = mFrameAvailableListener;
-    }
-
-    if (mSynchronousMode) {
-        // in synchronous mode we queue all buffers in a FIFO
-        mQueue.push_back(buf);
-    } else {
-        // in asynchronous mode we only keep the most recent buffer
         if (mQueue.empty()) {
+            listener = mFrameAvailableListener;
+        }
+
+        if (mSynchronousMode) {
+            // in synchronous mode we queue all buffers in a FIFO
             mQueue.push_back(buf);
         } else {
-            Fifo::iterator front(mQueue.begin());
-            // buffer currently queued is freed
-            mSlots[*front].mBufferState = BufferSlot::FREE;
-            // and we record the new buffer index in the queued list
-            *front = buf;
+            // in asynchronous mode we only keep the most recent buffer
+            if (mQueue.empty()) {
+                mQueue.push_back(buf);
+            } else {
+                Fifo::iterator front(mQueue.begin());
+                // buffer currently queued is freed
+                mSlots[*front].mBufferState = BufferSlot::FREE;
+                // and we record the new buffer index in the queued list
+                *front = buf;
+            }
         }
-    }
 
-    mSlots[buf].mBufferState = BufferSlot::QUEUED;
-    mSlots[buf].mLastQueuedCrop = mNextCrop;
-    mSlots[buf].mLastQueuedTransform = mNextTransform;
-    mSlots[buf].mLastQueuedTimestamp = timestamp;
-    mDequeueCondition.signal();
+        mSlots[buf].mBufferState = BufferSlot::QUEUED;
+        mSlots[buf].mCrop = mNextCrop;
+        mSlots[buf].mTransform = mNextTransform;
+        mSlots[buf].mTimestamp = timestamp;
+        mDequeueCondition.signal();
     } // scope for the lock
 
     // call back without lock held
@@ -540,9 +544,9 @@
         mCurrentTexture = buf;
         mCurrentTextureTarget = target;
         mCurrentTextureBuf = mSlots[buf].mGraphicBuffer;
-        mCurrentCrop = mSlots[buf].mLastQueuedCrop;
-        mCurrentTransform = mSlots[buf].mLastQueuedTransform;
-        mCurrentTimestamp = mSlots[buf].mLastQueuedTimestamp;
+        mCurrentCrop = mSlots[buf].mCrop;
+        mCurrentTransform = mSlots[buf].mTransform;
+        mCurrentTimestamp = mSlots[buf].mTimestamp;
         mDequeueCondition.signal();
     } else {
         // We always bind the texture even if we don't update its contents.
@@ -826,12 +830,10 @@
         const BufferSlot& slot(mSlots[i]);
         snprintf(buffer, SIZE,
                 "%s%s[%02d] state=%-8s, crop=[%d,%d,%d,%d], transform=0x%02x, "
-                "timestamp=%lld\n"
-                ,
+                "timestamp=%lld\n",
                 prefix, (i==mCurrentTexture)?">":" ", i, stateName(slot.mBufferState),
-                slot.mLastQueuedCrop.left, slot.mLastQueuedCrop.top,
-                slot.mLastQueuedCrop.right, slot.mLastQueuedCrop.bottom,
-                slot.mLastQueuedTransform, slot.mLastQueuedTimestamp
+                slot.mCrop.left, slot.mCrop.top, slot.mCrop.right, slot.mCrop.bottom,
+                slot.mTransform, slot.mTimestamp
         );
         result.append(buffer);
     }
diff --git a/libs/gui/SurfaceTextureClient.cpp b/libs/gui/SurfaceTextureClient.cpp
index 6f10320..c20fcf27 100644
--- a/libs/gui/SurfaceTextureClient.cpp
+++ b/libs/gui/SurfaceTextureClient.cpp
@@ -117,7 +117,8 @@
             mReqFormat, mReqUsage);
     if (result < 0) {
         LOGV("dequeueBuffer: ISurfaceTexture::dequeueBuffer(%d, %d, %d, %d)"
-             "failed: %d", result, mReqWidth, mReqHeight, mReqFormat, mReqUsage);
+             "failed: %d", mReqWidth, mReqHeight, mReqFormat, mReqUsage,
+             result);
         return result;
     }
     sp<GraphicBuffer>& gbuf(mSlots[buf]);
diff --git a/libs/gui/tests/SurfaceTextureClient_test.cpp b/libs/gui/tests/SurfaceTextureClient_test.cpp
index 59a4cc5..2f704c8 100644
--- a/libs/gui/tests/SurfaceTextureClient_test.cpp
+++ b/libs/gui/tests/SurfaceTextureClient_test.cpp
@@ -400,7 +400,9 @@
     EXPECT_EQ(st->getCurrentBuffer().get(), buf[2]);
 }
 
-TEST_F(SurfaceTextureClientTest, SurfaceTextureSyncModeDequeueCurrent) {
+// XXX: We currently have no hardware that properly handles dequeuing the
+// buffer that is currently bound to the texture.
+TEST_F(SurfaceTextureClientTest, DISABLED_SurfaceTextureSyncModeDequeueCurrent) {
     sp<ANativeWindow> anw(mSTC);
     sp<SurfaceTexture> st(mST);
     android_native_buffer_t* buf[3];
@@ -429,10 +431,13 @@
     android_native_buffer_t* buf[3];
     ASSERT_EQ(OK, st->setSynchronousMode(true));
     ASSERT_EQ(OK, native_window_set_buffer_count(anw.get(), 3));
+
+    // We should be able to dequeue all the buffers before we've queued any.
     EXPECT_EQ(OK, anw->dequeueBuffer(anw.get(), &buf[0]));
     EXPECT_EQ(OK, anw->dequeueBuffer(anw.get(), &buf[1]));
-    EXPECT_EQ(-EBUSY, anw->dequeueBuffer(anw.get(), &buf[2]));
+    EXPECT_EQ(OK, anw->dequeueBuffer(anw.get(), &buf[2]));
 
+    ASSERT_EQ(OK, anw->cancelBuffer(anw.get(), buf[2]));
     ASSERT_EQ(OK, anw->queueBuffer(anw.get(), buf[1]));
 
     EXPECT_EQ(OK, st->updateTexImage());
@@ -440,11 +445,17 @@
 
     EXPECT_EQ(OK, anw->dequeueBuffer(anw.get(), &buf[2]));
 
+    // Once we've queued a buffer, however we should not be able to dequeue more
+    // than (buffer-count - MIN_UNDEQUEUED_BUFFERS), which is 2 in this case.
+    EXPECT_EQ(-EBUSY, anw->dequeueBuffer(anw.get(), &buf[1]));
+
     ASSERT_EQ(OK, anw->cancelBuffer(anw.get(), buf[0]));
     ASSERT_EQ(OK, anw->cancelBuffer(anw.get(), buf[2]));
 }
 
-TEST_F(SurfaceTextureClientTest, SurfaceTextureSyncModeWaitRetire) {
+// XXX: This is not expected to pass until the synchronization hacks are removed
+// from the SurfaceTexture class.
+TEST_F(SurfaceTextureClientTest, DISABLED_SurfaceTextureSyncModeWaitRetire) {
     sp<ANativeWindow> anw(mSTC);
     sp<SurfaceTexture> st(mST);
 
diff --git a/media/java/android/media/AudioService.java b/media/java/android/media/AudioService.java
index 4e77fcb..0876bbf 100644
--- a/media/java/android/media/AudioService.java
+++ b/media/java/android/media/AudioService.java
@@ -1437,9 +1437,7 @@
                 if (mBluetoothHeadsetDevice != null) {
                     if (mBluetoothHeadset != null) {
                         if (!mBluetoothHeadset.stopVoiceRecognition(
-                                mBluetoothHeadsetDevice) ||
-                                !mBluetoothHeadset.stopScoUsingVirtualVoiceCall(
-                                        mBluetoothHeadsetDevice)) {
+                                mBluetoothHeadsetDevice)) {
                             sendMsg(mAudioHandler, MSG_BT_HEADSET_CNCT_FAILED, 0,
                                     SENDMSG_REPLACE, 0, 0, null, 0);
                         }
@@ -1506,8 +1504,6 @@
                             break;
                         case SCO_STATE_DEACTIVATE_EXT_REQ:
                             status = mBluetoothHeadset.stopVoiceRecognition(
-                                    mBluetoothHeadsetDevice) &&
-                                    mBluetoothHeadset.stopScoUsingVirtualVoiceCall(
                                     mBluetoothHeadsetDevice);
                         }
                     }
diff --git a/media/java/android/media/MediaPlayer.java b/media/java/android/media/MediaPlayer.java
index 0e161a8..8f7dd60 100644
--- a/media/java/android/media/MediaPlayer.java
+++ b/media/java/android/media/MediaPlayer.java
@@ -1253,14 +1253,26 @@
      */
     public native void attachAuxEffect(int effectId);
 
-    /* Do not change these values without updating their counterparts
-     * in include/media/mediaplayer.h!
+    /* 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
+     * 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;
 
     /**
      * Sets the parameter indicated by key.
diff --git a/media/libstagefright/Android.mk b/media/libstagefright/Android.mk
index f731dfb..8c4b274 100644
--- a/media/libstagefright/Android.mk
+++ b/media/libstagefright/Android.mk
@@ -50,7 +50,6 @@
         ThrottledSource.cpp               \
         TimeSource.cpp                    \
         TimedEventQueue.cpp               \
-        TimedTextPlayer.cpp               \
         Utils.cpp                         \
         VBRISeeker.cpp                    \
         WAVExtractor.cpp                  \
@@ -89,6 +88,7 @@
         libstagefright_avcenc \
         libstagefright_m4vh263enc \
         libstagefright_matroska \
+        libstagefright_timedtext \
         libvpx \
         libstagefright_mpeg2ts \
         libstagefright_httplive \
diff --git a/media/libstagefright/AwesomePlayer.cpp b/media/libstagefright/AwesomePlayer.cpp
index 70053ea..3d270f89 100644
--- a/media/libstagefright/AwesomePlayer.cpp
+++ b/media/libstagefright/AwesomePlayer.cpp
@@ -29,9 +29,10 @@
 #include "include/NuCachedSource2.h"
 #include "include/ThrottledSource.h"
 #include "include/MPEG2TSExtractor.h"
-#include "include/TimedTextPlayer.h"
 #include "include/WVMExtractor.h"
 
+#include "timedtext/TimedTextPlayer.h"
+
 #include <binder/IPCThreadState.h>
 #include <binder/IServiceManager.h>
 #include <media/IMediaPlayerService.h>
@@ -1282,6 +1283,7 @@
 }
 
 void AwesomePlayer::addTextSource(sp<MediaSource> source) {
+    Mutex::Autolock autoLock(mTimedTextLock);
     CHECK(source != NULL);
 
     if (mTextPlayer == NULL) {
@@ -2066,10 +2068,26 @@
 }
 
 status_t AwesomePlayer::setParameter(int key, const Parcel &request) {
-    if (key == KEY_PARAMETER_TIMED_TEXT_TRACK_INDEX) {
-        return setTimedTextTrackIndex(request.readInt32());
+    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 (mTextPlayer == NULL) {
+                mTextPlayer = new TimedTextPlayer(this, mListener, &mQueue);
+            }
+
+            return mTextPlayer->setParameter(key, request);
+        }
+        default:
+        {
+            return ERROR_UNSUPPORTED;
+        }
     }
-    return ERROR_UNSUPPORTED;
 }
 
 status_t AwesomePlayer::getParameter(int key, Parcel *reply) {
diff --git a/media/libstagefright/MPEG4Extractor.cpp b/media/libstagefright/MPEG4Extractor.cpp
index 6692809..98ac044 100644
--- a/media/libstagefright/MPEG4Extractor.cpp
+++ b/media/libstagefright/MPEG4Extractor.cpp
@@ -20,7 +20,7 @@
 #include "include/MPEG4Extractor.h"
 #include "include/SampleTable.h"
 #include "include/ESDS.h"
-#include "include/TimedTextPlayer.h"
+#include "timedtext/TimedTextPlayer.h"
 
 #include <arpa/inet.h>
 
diff --git a/media/libstagefright/TimedTextPlayer.cpp b/media/libstagefright/TimedTextPlayer.cpp
deleted file mode 100644
index 1ac22cb..0000000
--- a/media/libstagefright/TimedTextPlayer.cpp
+++ /dev/null
@@ -1,252 +0,0 @@
- /*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-//#define LOG_NDEBUG 0
-#define LOG_TAG "TimedTextPlayer"
-#include <utils/Log.h>
-
-#include <binder/IPCThreadState.h>
-#include <media/stagefright/MediaDebug.h>
-#include <media/stagefright/MediaDefs.h>
-#include <media/stagefright/MediaErrors.h>
-#include <media/stagefright/MediaSource.h>
-#include <media/stagefright/MetaData.h>
-#include <media/stagefright/MediaBuffer.h>
-#include <media/stagefright/Utils.h>
-#include "include/AwesomePlayer.h"
-#include "include/TimedTextPlayer.h"
-
-namespace android {
-
-struct TimedTextEvent : public TimedEventQueue::Event {
-    TimedTextEvent(
-            TimedTextPlayer *player,
-            void (TimedTextPlayer::*method)())
-        : mPlayer(player),
-          mMethod(method) {
-    }
-
-protected:
-    virtual ~TimedTextEvent() {}
-
-    virtual void fire(TimedEventQueue *queue, int64_t /* now_us */) {
-        (mPlayer->*mMethod)();
-    }
-
-private:
-    TimedTextPlayer *mPlayer;
-    void (TimedTextPlayer::*mMethod)();
-
-    TimedTextEvent(const TimedTextEvent &);
-    TimedTextEvent &operator=(const TimedTextEvent &);
-};
-
-TimedTextPlayer::TimedTextPlayer(
-        AwesomePlayer *observer,
-        const wp<MediaPlayerBase> &listener,
-        TimedEventQueue *queue)
-    : mSource(NULL),
-      mSeekTimeUs(0),
-      mStarted(false),
-      mTextEventPending(false),
-      mQueue(queue),
-      mListener(listener),
-      mObserver(observer),
-      mTextBuffer(NULL) {
-    mTextEvent = new TimedTextEvent(this, &TimedTextPlayer::onTextEvent);
-}
-
-TimedTextPlayer::~TimedTextPlayer() {
-    if (mStarted) {
-        reset();
-    }
-
-    mTextTrackVector.clear();
-}
-
-status_t TimedTextPlayer::start(uint8_t index) {
-    CHECK(!mStarted);
-
-    if (index >= mTextTrackVector.size()) {
-        LOGE("Incorrect text track index");
-        return BAD_VALUE;
-    }
-
-    mSource = mTextTrackVector.itemAt(index);
-
-    status_t err = mSource->start();
-
-    if (err != OK) {
-        return err;
-    }
-
-    int64_t positionUs;
-    mObserver->getPosition(&positionUs);
-    seekTo(positionUs);
-
-    postTextEvent();
-
-    mStarted = true;
-
-    return OK;
-}
-
-void TimedTextPlayer::pause() {
-    CHECK(mStarted);
-
-    cancelTextEvent();
-}
-
-void TimedTextPlayer::resume() {
-    CHECK(mStarted);
-
-    postTextEvent();
-}
-
-void TimedTextPlayer::reset() {
-    CHECK(mStarted);
-
-    // send an empty text to clear the screen
-    notifyListener(MEDIA_TIMED_TEXT);
-
-    cancelTextEvent();
-
-    mSeeking = false;
-    mStarted = false;
-
-    if (mTextBuffer != NULL) {
-        mTextBuffer->release();
-        mTextBuffer = NULL;
-    }
-
-    if (mSource != NULL) {
-        mSource->stop();
-        mSource.clear();
-        mSource = NULL;
-    }
-}
-
-status_t TimedTextPlayer::seekTo(int64_t time_us) {
-    Mutex::Autolock autoLock(mLock);
-
-    mSeeking = true;
-    mSeekTimeUs = time_us;
-
-    return OK;
-}
-
-status_t TimedTextPlayer::setTimedTextTrackIndex(int32_t index) {
-    if (index >= (int)(mTextTrackVector.size())) {
-        return BAD_VALUE;
-    }
-
-    if (mStarted) {
-        reset();
-    }
-
-    if (index >= 0) {
-        return start(index);
-    }
-    return OK;
-}
-
-void TimedTextPlayer::onTextEvent() {
-    Mutex::Autolock autoLock(mLock);
-
-    if (!mTextEventPending) {
-        return;
-    }
-    mTextEventPending = false;
-
-    MediaSource::ReadOptions options;
-    if (mSeeking) {
-        options.setSeekTo(mSeekTimeUs,
-                MediaSource::ReadOptions::SEEK_PREVIOUS_SYNC);
-        mSeeking = false;
-
-        if (mTextBuffer != NULL) {
-            mTextBuffer->release();
-            mTextBuffer = NULL;
-        }
-
-        notifyListener(MEDIA_TIMED_TEXT); //empty text to clear the screen
-    }
-
-    if (mTextBuffer != NULL) {
-        uint8_t *tmp = (uint8_t *)(mTextBuffer->data());
-        size_t len = (*tmp) << 8 | (*(tmp + 1));
-
-        notifyListener(MEDIA_TIMED_TEXT,
-                       tmp + 2,
-                       len);
-
-        mTextBuffer->release();
-        mTextBuffer = NULL;
-
-    }
-
-    if (mSource->read(&mTextBuffer, &options) != OK) {
-        return;
-    }
-
-    int64_t positionUs, timeUs;
-    mObserver->getPosition(&positionUs);
-    mTextBuffer->meta_data()->findInt64(kKeyTime, &timeUs);
-
-    //send the text now
-    if (timeUs <= positionUs + 100000ll) {
-        postTextEvent();
-    } else {
-        postTextEvent(timeUs - positionUs - 100000ll);
-    }
-}
-
-void TimedTextPlayer::postTextEvent(int64_t delayUs) {
-    if (mTextEventPending) {
-        return;
-    }
-
-    mTextEventPending = true;
-    mQueue->postEventWithDelay(mTextEvent, delayUs < 0 ? 10000 : delayUs);
-}
-
-void TimedTextPlayer::cancelTextEvent() {
-    mQueue->cancelEvent(mTextEvent->eventID());
-    mTextEventPending = false;
-}
-
-void TimedTextPlayer::addTextSource(sp<MediaSource> source) {
-    mTextTrackVector.add(source);
-}
-
-void TimedTextPlayer::notifyListener(
-        int msg, const void *data, size_t size) {
-    if (mListener != NULL) {
-        sp<MediaPlayerBase> listener = mListener.promote();
-
-        if (listener != NULL) {
-            if (size > 0) {
-                mData.freeData();
-                mData.write(data, size);
-
-                listener->sendEvent(msg, 0, 0, &mData);
-            } else { // send an empty timed text to clear the screen
-                listener->sendEvent(msg);
-            }
-        }
-    }
-}
-}
diff --git a/media/libstagefright/include/AwesomePlayer.h b/media/libstagefright/include/AwesomePlayer.h
index 3c9a121..a9e8e95 100644
--- a/media/libstagefright/include/AwesomePlayer.h
+++ b/media/libstagefright/include/AwesomePlayer.h
@@ -231,6 +231,7 @@
 
     int64_t mLastVideoTimeUs;
     TimedTextPlayer *mTextPlayer;
+    mutable Mutex mTimedTextLock;
 
     sp<WVMExtractor> mWVMExtractor;
 
diff --git a/media/libstagefright/timedtext/Android.mk b/media/libstagefright/timedtext/Android.mk
new file mode 100644
index 0000000..9a6062c
--- /dev/null
+++ b/media/libstagefright/timedtext/Android.mk
@@ -0,0 +1,16 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES:=                 \
+        TimedTextParser.cpp       \
+        TimedTextPlayer.cpp
+
+LOCAL_CFLAGS += -Wno-multichar
+LOCAL_C_INCLUDES:= \
+        $(JNI_H_INCLUDE) \
+        $(TOP)/frameworks/base/media/libstagefright \
+        $(TOP)/frameworks/base/include/media/stagefright/openmax
+
+LOCAL_MODULE:= libstagefright_timedtext
+
+include $(BUILD_STATIC_LIBRARY)
diff --git a/media/libstagefright/timedtext/TimedTextParser.cpp b/media/libstagefright/timedtext/TimedTextParser.cpp
new file mode 100644
index 0000000..0bada16
--- /dev/null
+++ b/media/libstagefright/timedtext/TimedTextParser.cpp
@@ -0,0 +1,257 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "TimedTextParser.h"
+#include <media/stagefright/DataSource.h>
+
+namespace android {
+
+TimedTextParser::TimedTextParser()
+    : mDataSource(NULL),
+      mOffset(0),
+      mIndex(0) {
+}
+
+TimedTextParser::~TimedTextParser() {
+    reset();
+}
+
+status_t TimedTextParser::init(
+        const sp<DataSource> &dataSource, FileType fileType) {
+    mDataSource = dataSource;
+    mFileType = fileType;
+
+    status_t err;
+    if ((err = scanFile()) != OK) {
+        reset();
+        return err;
+    }
+
+    return OK;
+}
+
+void TimedTextParser::reset() {
+    mDataSource.clear();
+    mTextVector.clear();
+    mOffset = 0;
+    mIndex = 0;
+}
+
+// scan the text file to get start/stop time and the
+// offset of each piece of text content
+status_t TimedTextParser::scanFile() {
+    if (mFileType != OUT_OF_BAND_FILE_SRT) {
+        return ERROR_UNSUPPORTED;
+    }
+
+    off64_t offset = 0;
+    int64_t startTimeUs;
+    bool endOfFile = false;
+
+    while (!endOfFile) {
+        TextInfo info;
+        status_t err = getNextInSrtFileFormat(&offset, &startTimeUs, &info);
+
+        if (err != OK) {
+            if (err == ERROR_END_OF_STREAM) {
+                endOfFile = true;
+            } else {
+                return err;
+            }
+        } else {
+            mTextVector.add(startTimeUs, info);
+        }
+    }
+
+    if (mTextVector.isEmpty()) {
+        return ERROR_MALFORMED;
+    }
+    return OK;
+}
+
+// read one line started from *offset and store it into data.
+status_t TimedTextParser::readNextLine(off64_t *offset, AString *data) {
+    char character;
+
+    data->clear();
+
+    while (true) {
+        ssize_t err;
+        if ((err = mDataSource->readAt(*offset, &character, 1)) < 1) {
+            if (err == 0) {
+                return ERROR_END_OF_STREAM;
+            }
+            return ERROR_IO;
+        }
+
+        (*offset) ++;
+
+        // a line could end with CR, LF or CR + LF
+        if (character == 10) {
+            break;
+        } else if (character == 13) {
+            if ((err = mDataSource->readAt(*offset, &character, 1)) < 1) {
+                if (err == 0) { // end of the stream
+                    return OK;
+                }
+                return ERROR_IO;
+            }
+
+            (*offset) ++;
+
+            if (character != 10) {
+                (*offset) --;
+            }
+            break;
+        }
+
+        data->append(character);
+    }
+
+    return OK;
+}
+
+/* SRT format:
+ *  Subtitle number
+ *  Start time --> End time
+ *  Text of subtitle (one or more lines)
+ *  Blank line
+ *
+ * .srt file example:
+ *  1
+ *  00:00:20,000 --> 00:00:24,400
+ *  Altocumulus clouds occur between six thousand
+ *
+ *  2
+ *  00:00:24,600 --> 00:00:27,800
+ *  and twenty thousand feet above ground level.
+ */
+status_t TimedTextParser::getNextInSrtFileFormat(
+        off64_t *offset, int64_t *startTimeUs, TextInfo *info) {
+    AString data;
+    status_t err;
+    if ((err = readNextLine(offset, &data)) != OK) {
+        return err;
+    }
+
+    // to skip the first line
+    if ((err = readNextLine(offset, &data)) != OK) {
+        return err;
+    }
+
+    int hour1, hour2, min1, min2, sec1, sec2, msec1, msec2;
+    // the start time format is: hours:minutes:seconds,milliseconds
+    // 00:00:24,600 --> 00:00:27,800
+    if (sscanf(data.c_str(), "%02d:%02d:%02d,%03d --> %02d:%02d:%02d,%03d",
+                &hour1, &min1, &sec1, &msec1, &hour2, &min2, &sec2, &msec2) != 8) {
+        return ERROR_MALFORMED;
+    }
+
+    *startTimeUs = ((hour1 * 3600 + min1 * 60 + sec1) * 1000 + msec1) * 1000ll;
+    info->endTimeUs = ((hour2 * 3600 + min2 * 60 + sec2) * 1000 + msec2) * 1000ll;
+    if (info->endTimeUs <= *startTimeUs) {
+        return ERROR_MALFORMED;
+    }
+
+    info->offset = *offset;
+
+    bool needMoreData = true;
+    while (needMoreData) {
+        if ((err = readNextLine(offset, &data)) != OK) {
+            if (err == ERROR_END_OF_STREAM) {
+                needMoreData = false;
+            } else {
+                return err;
+            }
+        }
+
+        if (needMoreData) {
+            data.trim();
+            if (data.empty()) {
+                // it's an empty line used to separate two subtitles
+                needMoreData = false;
+            }
+        }
+    }
+
+    info->textLen = *offset - info->offset;
+
+    return OK;
+}
+
+status_t TimedTextParser::getText(
+        AString *text, int64_t *startTimeUs, int64_t *endTimeUs,
+        const MediaSource::ReadOptions *options) {
+    Mutex::Autolock autoLock(mLock);
+
+    text->clear();
+
+    int64_t seekTimeUs;
+    MediaSource::ReadOptions::SeekMode mode;
+    if (options && options->getSeekTo(&seekTimeUs, &mode)) {
+        int64_t lastEndTimeUs = mTextVector.valueAt(mTextVector.size() - 1).endTimeUs;
+        int64_t firstStartTimeUs = mTextVector.keyAt(0);
+
+        if (seekTimeUs < 0 || seekTimeUs > lastEndTimeUs) {
+            return ERROR_OUT_OF_RANGE;
+        } else if (seekTimeUs < firstStartTimeUs) {
+            mIndex = 0;
+        } else {
+            // binary search
+            ssize_t low = 0;
+            ssize_t high = mTextVector.size() - 1;
+            ssize_t mid = 0;
+            int64_t currTimeUs;
+
+            while (low <= high) {
+                mid = low + (high - low)/2;
+                currTimeUs = mTextVector.keyAt(mid);
+                const int diff = currTimeUs - seekTimeUs;
+
+                if (diff == 0) {
+                    break;
+                } else if (diff < 0) {
+                    low = mid + 1;
+                } else {
+                    if ((high == mid + 1)
+                            && (seekTimeUs < mTextVector.keyAt(high))) {
+                        break;
+                    }
+                    high = mid - 1;
+                }
+            }
+
+            mIndex = mid;
+        }
+    }
+
+    TextInfo info = mTextVector.valueAt(mIndex);
+    *startTimeUs = mTextVector.keyAt(mIndex);
+    *endTimeUs = info.endTimeUs;
+    mIndex ++;
+
+    char *str = new char[info.textLen];
+    if (mDataSource->readAt(info.offset, str, info.textLen) < info.textLen) {
+        delete[] str;
+        return ERROR_IO;
+    }
+
+    text->append(str, info.textLen);
+    delete[] str;
+    return OK;
+}
+
+}  // namespace android
diff --git a/media/libstagefright/timedtext/TimedTextParser.h b/media/libstagefright/timedtext/TimedTextParser.h
new file mode 100644
index 0000000..44774c2
--- /dev/null
+++ b/media/libstagefright/timedtext/TimedTextParser.h
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef TIMED_TEXT_PARSER_H_
+
+#define TIMED_TEXT_PARSER_H_
+
+#include <media/MediaPlayerInterface.h>
+#include <media/stagefright/foundation/ABase.h>
+#include <media/stagefright/foundation/AString.h>
+#include <media/stagefright/MediaSource.h>
+
+namespace android {
+
+class DataSource;
+
+class TimedTextParser : public RefBase {
+public:
+    TimedTextParser();
+    virtual ~TimedTextParser();
+
+    enum FileType {
+        OUT_OF_BAND_FILE_SRT = 1,
+    };
+
+    status_t getText(AString *text, int64_t *startTimeUs, int64_t *endTimeUs,
+                     const MediaSource::ReadOptions *options = NULL);
+    status_t init(const sp<DataSource> &dataSource, FileType fileType);
+    void reset();
+
+private:
+    Mutex mLock;
+
+    sp<DataSource> mDataSource;
+    off64_t mOffset;
+
+    struct TextInfo {
+        int64_t endTimeUs;
+        // the offset of the text in the original file
+        off64_t offset;
+        int textLen;
+    };
+
+    int mIndex;
+    FileType mFileType;
+
+    // the key indicated the start time of the text
+    KeyedVector<int64_t, TextInfo> mTextVector;
+
+    status_t getNextInSrtFileFormat(
+            off64_t *offset, int64_t *startTimeUs, TextInfo *info);
+    status_t readNextLine(off64_t *offset, AString *data);
+
+    status_t scanFile();
+
+    DISALLOW_EVIL_CONSTRUCTORS(TimedTextParser);
+};
+
+}  // namespace android
+
+#endif  // TIMED_TEXT_PARSER_H_
+
diff --git a/media/libstagefright/timedtext/TimedTextPlayer.cpp b/media/libstagefright/timedtext/TimedTextPlayer.cpp
new file mode 100644
index 0000000..50bb16d
--- /dev/null
+++ b/media/libstagefright/timedtext/TimedTextPlayer.cpp
@@ -0,0 +1,347 @@
+ /*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "TimedTextPlayer"
+#include <utils/Log.h>
+
+#include <binder/IPCThreadState.h>
+#include <media/stagefright/MediaDebug.h>
+#include <media/stagefright/MediaDefs.h>
+#include <media/stagefright/MediaErrors.h>
+#include <media/stagefright/MediaSource.h>
+#include <media/stagefright/MetaData.h>
+#include <media/stagefright/MediaBuffer.h>
+#include <media/stagefright/FileSource.h>
+#include <media/stagefright/Utils.h>
+#include "include/AwesomePlayer.h"
+#include "TimedTextPlayer.h"
+#include "TimedTextParser.h"
+
+namespace android {
+
+struct TimedTextEvent : public TimedEventQueue::Event {
+    TimedTextEvent(
+            TimedTextPlayer *player,
+            void (TimedTextPlayer::*method)())
+        : mPlayer(player),
+          mMethod(method) {
+    }
+
+protected:
+    virtual ~TimedTextEvent() {}
+
+    virtual void fire(TimedEventQueue *queue, int64_t /* now_us */) {
+        (mPlayer->*mMethod)();
+    }
+
+private:
+    TimedTextPlayer *mPlayer;
+    void (TimedTextPlayer::*mMethod)();
+
+    TimedTextEvent(const TimedTextEvent &);
+    TimedTextEvent &operator=(const TimedTextEvent &);
+};
+
+TimedTextPlayer::TimedTextPlayer(
+        AwesomePlayer *observer,
+        const wp<MediaPlayerBase> &listener,
+        TimedEventQueue *queue)
+    : mSource(NULL),
+      mOutOfBandSource(NULL),
+      mSeekTimeUs(0),
+      mStarted(false),
+      mTextEventPending(false),
+      mQueue(queue),
+      mListener(listener),
+      mObserver(observer),
+      mTextBuffer(NULL),
+      mTextParser(NULL),
+      mTextType(kNoText) {
+    mTextEvent = new TimedTextEvent(this, &TimedTextPlayer::onTextEvent);
+}
+
+TimedTextPlayer::~TimedTextPlayer() {
+    if (mStarted) {
+        reset();
+    }
+
+    mTextTrackVector.clear();
+    mTextOutOfBandVector.clear();
+}
+
+status_t TimedTextPlayer::start(uint8_t index) {
+    CHECK(!mStarted);
+
+    if (index >=
+            mTextTrackVector.size() + mTextOutOfBandVector.size()) {
+        LOGE("Incorrect text track index: %d", index);
+        return BAD_VALUE;
+    }
+
+    if (index < mTextTrackVector.size()) { // start an in-band text
+        mSource = mTextTrackVector.itemAt(index);
+
+        status_t err = mSource->start();
+
+        if (err != OK) {
+            return err;
+        }
+        mTextType = kInBandText;
+    } else { // start an out-of-band text
+        OutOfBandText text =
+            mTextOutOfBandVector.itemAt(index - mTextTrackVector.size());
+
+        mOutOfBandSource = text.source;
+        TimedTextParser::FileType fileType = text.type;
+
+        if (mTextParser == NULL) {
+            mTextParser = new TimedTextParser();
+        }
+
+        status_t err;
+        if ((err = mTextParser->init(mOutOfBandSource, fileType)) != OK) {
+            return err;
+        }
+        mTextType = kOutOfBandText;
+    }
+
+    int64_t positionUs;
+    mObserver->getPosition(&positionUs);
+    seekTo(positionUs);
+
+    postTextEvent();
+
+    mStarted = true;
+
+    return OK;
+}
+
+void TimedTextPlayer::pause() {
+    CHECK(mStarted);
+
+    cancelTextEvent();
+}
+
+void TimedTextPlayer::resume() {
+    CHECK(mStarted);
+
+    postTextEvent();
+}
+
+void TimedTextPlayer::reset() {
+    CHECK(mStarted);
+
+    // send an empty text to clear the screen
+    notifyListener(MEDIA_TIMED_TEXT);
+
+    cancelTextEvent();
+
+    mSeeking = false;
+    mStarted = false;
+
+    if (mTextType == kInBandText) {
+        if (mTextBuffer != NULL) {
+            mTextBuffer->release();
+            mTextBuffer = NULL;
+        }
+
+        if (mSource != NULL) {
+            mSource->stop();
+            mSource.clear();
+            mSource = NULL;
+        }
+    } else {
+        if (mTextParser != NULL) {
+            mTextParser.clear();
+            mTextParser = NULL;
+        }
+        if (mOutOfBandSource != NULL) {
+            mOutOfBandSource.clear();
+            mOutOfBandSource = NULL;
+        }
+    }
+}
+
+status_t TimedTextPlayer::seekTo(int64_t time_us) {
+    Mutex::Autolock autoLock(mLock);
+
+    mSeeking = true;
+    mSeekTimeUs = time_us;
+
+    postTextEvent();
+
+    return OK;
+}
+
+status_t TimedTextPlayer::setTimedTextTrackIndex(int32_t index) {
+    if (index >=
+            (int)(mTextTrackVector.size() + mTextOutOfBandVector.size())) {
+        return BAD_VALUE;
+    }
+
+    if (mStarted) {
+        reset();
+    }
+
+    if (index >= 0) {
+        return start(index);
+    }
+    return OK;
+}
+
+void TimedTextPlayer::onTextEvent() {
+    Mutex::Autolock autoLock(mLock);
+
+    if (!mTextEventPending) {
+        return;
+    }
+    mTextEventPending = false;
+
+    MediaSource::ReadOptions options;
+    if (mSeeking) {
+        options.setSeekTo(mSeekTimeUs,
+                MediaSource::ReadOptions::SEEK_PREVIOUS_SYNC);
+        mSeeking = false;
+
+        if (mTextType == kInBandText) {
+            if (mTextBuffer != NULL) {
+                mTextBuffer->release();
+                mTextBuffer = NULL;
+            }
+        } else {
+            mText.clear();
+        }
+
+        notifyListener(MEDIA_TIMED_TEXT); //empty text to clear the screen
+    }
+
+    int64_t positionUs, timeUs;
+    mObserver->getPosition(&positionUs);
+
+    if (mTextType == kInBandText) {
+        if (mTextBuffer != NULL) {
+            uint8_t *tmp = (uint8_t *)(mTextBuffer->data());
+            size_t len = (*tmp) << 8 | (*(tmp + 1));
+
+            notifyListener(MEDIA_TIMED_TEXT,
+                           tmp + 2,
+                           len);
+
+            mTextBuffer->release();
+            mTextBuffer = NULL;
+
+        }
+
+        if (mSource->read(&mTextBuffer, &options) != OK) {
+            return;
+        }
+
+        mTextBuffer->meta_data()->findInt64(kKeyTime, &timeUs);
+    } else {
+        if (mText.size() > 0) {
+            notifyListener(MEDIA_TIMED_TEXT,
+                           mText.c_str(),
+                           mText.size());
+            mText.clear();
+        }
+
+        int64_t endTimeUs;
+        if (mTextParser->getText(
+                    &mText, &timeUs, &endTimeUs, &options) != OK) {
+            return;
+        }
+    }
+
+    //send the text now
+    if (timeUs <= positionUs + 100000ll) {
+        postTextEvent();
+    } else {
+        postTextEvent(timeUs - positionUs - 100000ll);
+    }
+}
+
+void TimedTextPlayer::postTextEvent(int64_t delayUs) {
+    if (mTextEventPending) {
+        return;
+    }
+
+    mTextEventPending = true;
+    mQueue->postEventWithDelay(mTextEvent, delayUs < 0 ? 10000 : delayUs);
+}
+
+void TimedTextPlayer::cancelTextEvent() {
+    mQueue->cancelEvent(mTextEvent->eventID());
+    mTextEventPending = false;
+}
+
+void TimedTextPlayer::addTextSource(sp<MediaSource> source) {
+    Mutex::Autolock autoLock(mLock);
+    mTextTrackVector.add(source);
+}
+
+status_t TimedTextPlayer::setParameter(int key, const Parcel &request) {
+    Mutex::Autolock autoLock(mLock);
+
+    if (key == KEY_PARAMETER_TIMED_TEXT_ADD_OUT_OF_BAND_SOURCE) {
+        String8 uri = request.readString8();
+        KeyedVector<String8, String8> headers;
+
+        // To support local subtitle file only for now
+        if (strncasecmp("file://", uri.string(), 7)) {
+            return INVALID_OPERATION;
+        }
+        sp<DataSource> dataSource =
+            DataSource::CreateFromURI(uri, &headers);
+        status_t err = dataSource->initCheck();
+
+        if (err != OK) {
+            return err;
+        }
+
+        OutOfBandText text;
+        text.source = dataSource;
+        if (uri.getPathExtension() == String8(".srt")) {
+            text.type = TimedTextParser::OUT_OF_BAND_FILE_SRT;
+        } else {
+            return ERROR_UNSUPPORTED;
+        }
+
+        mTextOutOfBandVector.add(text);
+
+        return OK;
+    }
+    return INVALID_OPERATION;
+}
+
+void TimedTextPlayer::notifyListener(
+        int msg, const void *data, size_t size) {
+    if (mListener != NULL) {
+        sp<MediaPlayerBase> listener = mListener.promote();
+
+        if (listener != NULL) {
+            if (size > 0) {
+                mData.freeData();
+                mData.write(data, size);
+
+                listener->sendEvent(msg, 0, 0, &mData);
+            } else { // send an empty timed text to clear the screen
+                listener->sendEvent(msg);
+            }
+        }
+    }
+}
+}
diff --git a/media/libstagefright/include/TimedTextPlayer.h b/media/libstagefright/timedtext/TimedTextPlayer.h
similarity index 77%
rename from media/libstagefright/include/TimedTextPlayer.h
rename to media/libstagefright/timedtext/TimedTextPlayer.h
index ac41b4f..590760b 100644
--- a/media/libstagefright/include/TimedTextPlayer.h
+++ b/media/libstagefright/timedtext/TimedTextPlayer.h
@@ -20,8 +20,10 @@
 
 #include <media/MediaPlayerInterface.h>
 #include <media/stagefright/foundation/ABase.h>
+#include <media/stagefright/foundation/AString.h>
 
 #include "include/TimedEventQueue.h"
+#include "TimedTextParser.h"
 
 namespace android {
 
@@ -50,11 +52,19 @@
     void addTextSource(sp<MediaSource> source);
 
     status_t setTimedTextTrackIndex(int32_t index);
+    status_t setParameter(int key, const Parcel &request);
 
 private:
+    enum TextType {
+        kNoText        = 0,
+        kInBandText    = 1,
+        kOutOfBandText = 2,
+    };
+
     Mutex mLock;
 
     sp<MediaSource> mSource;
+    sp<DataSource> mOutOfBandSource;
 
     bool mSeeking;
     int64_t mSeekTimeUs;
@@ -72,8 +82,21 @@
     MediaBuffer *mTextBuffer;
     Parcel mData;
 
+    // for in-band timed text
     Vector<sp<MediaSource> > mTextTrackVector;
 
+    // for out-of-band timed text
+    struct OutOfBandText {
+        TimedTextParser::FileType type;
+        sp<DataSource> source;
+    };
+    Vector<OutOfBandText > mTextOutOfBandVector;
+
+    sp<TimedTextParser> mTextParser;
+    AString mText;
+
+    TextType mTextType;
+
     void reset();
 
     void onTextEvent();
diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
index e5abeac..e48dbe9 100755
--- a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
+++ b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
@@ -23,6 +23,7 @@
 import android.app.UiModeManager;
 import android.content.ActivityNotFoundException;
 import android.content.BroadcastReceiver;
+import android.content.ComponentName;
 import android.content.ContentResolver;
 import android.content.Context;
 import android.content.Intent;
@@ -344,6 +345,10 @@
     int mLockScreenTimeout;
     boolean mLockScreenTimerActive;
 
+    // visual screen saver support
+    int mScreenSaverTimeout;
+    boolean mScreenSaverEnabled = false;
+
     // Behavior of ENDCALL Button.  (See Settings.System.END_BUTTON_BEHAVIOR.)
     int mEndcallBehavior;
 
@@ -399,6 +404,8 @@
                     Settings.Secure.DEFAULT_INPUT_METHOD), false, this);
             resolver.registerContentObserver(Settings.System.getUriFor(
                     "fancy_rotation_anim"), false, this);
+            resolver.registerContentObserver(Settings.System.getUriFor(
+                    Settings.Secure.DREAM_TIMEOUT), false, this);
             updateSettings();
         }
 
@@ -832,6 +839,11 @@
                 mHasSoftInput = hasSoftInput;
                 updateRotation = true;
             }
+
+            mScreenSaverTimeout = Settings.System.getInt(resolver,
+                    Settings.Secure.DREAM_TIMEOUT, 0);
+            mScreenSaverEnabled = true;
+            updateScreenSaverTimeoutLocked();
         }
         if (updateRotation) {
             updateRotation(0);
@@ -2603,6 +2615,7 @@
             mScreenOn = false;
             updateOrientationListenerLp();
             updateLockScreenTimeout();
+            updateScreenSaverTimeoutLocked();
         }
     }
 
@@ -2614,6 +2627,7 @@
             mScreenOn = true;
             updateOrientationListenerLp();
             updateLockScreenTimeout();
+            updateScreenSaverTimeoutLocked();
         }
     }
 
@@ -2894,6 +2908,63 @@
                 mStatusBarService.userActivity();
             } catch (RemoteException ex) {}
         }
+
+        synchronized (mLock) {
+            updateScreenSaverTimeoutLocked();
+        }
+    }
+
+    Runnable mScreenSaverActivator = new Runnable() {
+        public void run() {
+            synchronized (this) {
+                if (!(mScreenSaverEnabled && mScreenOn)) {
+                    Log.w(TAG, "mScreenSaverActivator ran, but the screensaver should not be showing. Who's driving this thing?");
+                    return;
+                }
+
+                if (localLOGV) Log.v(TAG, "mScreenSaverActivator entering dreamland");
+                try {
+                    String component = Settings.System.getString(
+                            mContext.getContentResolver(), Settings.Secure.DREAM_COMPONENT);
+                    if (component != null) {
+                        ComponentName cn = ComponentName.unflattenFromString(component);
+                        Intent intent = new Intent(Intent.ACTION_MAIN)
+                            .setComponent(cn)
+                            .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK
+                                | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS
+                                | Intent.FLAG_ACTIVITY_NO_USER_ACTION
+                                | Intent.FLAG_ACTIVITY_SINGLE_TOP);
+                        mContext.startActivity(intent);
+                    } else {
+                        Log.e(TAG, "Couldn't start screen saver: none selected");
+                    }
+                } catch (android.content.ActivityNotFoundException exc) {
+                    // no screensaver? give up
+                    Log.e(TAG, "Couldn't start screen saver: none installed");
+                }
+            }
+        }
+    };
+
+    // Must call while holding mLock
+    private void updateScreenSaverTimeoutLocked() {
+        synchronized (mScreenSaverActivator) {
+            mHandler.removeCallbacks(mScreenSaverActivator);
+            if (mScreenSaverEnabled && mScreenOn && mScreenSaverTimeout > 0) {
+                if (localLOGV)
+                    Log.v(TAG, "scheduling screensaver for " + mScreenSaverTimeout + "ms from now");
+                mHandler.postDelayed(mScreenSaverActivator, mScreenSaverTimeout);
+            } else {
+                if (localLOGV) {
+                    if (mScreenSaverTimeout == 0)
+                        Log.v(TAG, "screen saver disabled by user");
+                    else if (!mScreenOn)
+                        Log.v(TAG, "screen saver disabled while screen off");
+                    else
+                        Log.v(TAG, "screen saver disabled by wakelock");
+                }
+            }
+        }
     }
 
     Runnable mScreenLockTimeout = new Runnable() {
@@ -3089,10 +3160,27 @@
         return true;
     }
     
+    public void screenOnStartedLw() {
+        // The window manager has just grabbed a wake lock. This is our cue to disable the screen
+        // saver.
+        synchronized (mLock) {
+            mScreenSaverEnabled = false;
+        }
+    }
+
     public void screenOnStoppedLw() {
-        if (!mKeyguardMediator.isShowingAndNotHidden() && mPowerManager.isScreenOn()) {
-            long curTime = SystemClock.uptimeMillis();
-            mPowerManager.userActivity(curTime, false, LocalPowerManager.OTHER_EVENT);
+        if (mPowerManager.isScreenOn()) {
+            if (!mKeyguardMediator.isShowingAndNotHidden()) {
+                long curTime = SystemClock.uptimeMillis();
+                mPowerManager.userActivity(curTime, false, LocalPowerManager.OTHER_EVENT);
+            }
+
+            synchronized (mLock) {
+                // even if the keyguard is up, now that all the wakelocks have been released, we
+                // should re-enable the screen saver
+                mScreenSaverEnabled = true;
+                updateScreenSaverTimeoutLocked();
+            }
         }
     }
 
diff --git a/services/java/com/android/server/DevicePolicyManagerService.java b/services/java/com/android/server/DevicePolicyManagerService.java
index df2cd1b..92d76be 100644
--- a/services/java/com/android/server/DevicePolicyManagerService.java
+++ b/services/java/com/android/server/DevicePolicyManagerService.java
@@ -134,18 +134,43 @@
         final DeviceAdminInfo info;
 
         int passwordQuality = DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED;
-        int minimumPasswordLength = 0;
-        int passwordHistoryLength = 0;
-        int minimumPasswordUpperCase = 0;
-        int minimumPasswordLowerCase = 0;
-        int minimumPasswordLetters = 1;
-        int minimumPasswordNumeric = 1;
-        int minimumPasswordSymbols = 1;
-        int minimumPasswordNonLetter = 0;
-        long maximumTimeToUnlock = 0;
-        int maximumFailedPasswordsForWipe = 0;
-        long passwordExpirationTimeout = 0L;
-        long passwordExpirationDate = 0L;
+
+        static final int DEF_MINIMUM_PASSWORD_LENGTH = 0;
+        int minimumPasswordLength = DEF_MINIMUM_PASSWORD_LENGTH;
+
+        static final int DEF_PASSWORD_HISTORY_LENGTH = 0;
+        int passwordHistoryLength = DEF_PASSWORD_HISTORY_LENGTH;
+
+        static final int DEF_MINIMUM_PASSWORD_UPPER_CASE = 0;
+        int minimumPasswordUpperCase = DEF_MINIMUM_PASSWORD_UPPER_CASE;
+
+        static final int DEF_MINIMUM_PASSWORD_LOWER_CASE = 0;
+        int minimumPasswordLowerCase = DEF_MINIMUM_PASSWORD_LOWER_CASE;
+
+        static final int DEF_MINIMUM_PASSWORD_LETTERS = 1;
+        int minimumPasswordLetters = DEF_MINIMUM_PASSWORD_LOWER_CASE;
+
+        static final int DEF_MINIMUM_PASSWORD_NUMERIC = 1;
+        int minimumPasswordNumeric = DEF_MINIMUM_PASSWORD_NUMERIC;
+
+        static final int DEF_MINIMUM_PASSWORD_SYMBOLS = 1;
+        int minimumPasswordSymbols = DEF_MINIMUM_PASSWORD_SYMBOLS;
+
+        static final int DEF_MINIMUM_PASSWORD_NON_LETTER = 0;
+        int minimumPasswordNonLetter = DEF_MINIMUM_PASSWORD_NON_LETTER;
+
+        static final long DEF_MAXIMUM_TIME_TO_UNLOCK = 0;
+        long maximumTimeToUnlock = DEF_MAXIMUM_TIME_TO_UNLOCK;
+
+        static final int DEF_MAXIMUM_FAILED_PASSWORDS_FOR_WIPE = 0;
+        int maximumFailedPasswordsForWipe = DEF_MAXIMUM_FAILED_PASSWORDS_FOR_WIPE;
+
+        static final long DEF_PASSWORD_EXPIRATION_TIMEOUT = 0;
+        long passwordExpirationTimeout = DEF_PASSWORD_EXPIRATION_TIMEOUT;
+
+        static final long DEF_PASSWORD_EXPIRATION_DATE = 0;
+        long passwordExpirationDate = DEF_PASSWORD_EXPIRATION_DATE;
+
         boolean encryptionRequested = false;
 
         // TODO: review implementation decisions with frameworks team
@@ -168,53 +193,53 @@
                 out.startTag(null, "password-quality");
                 out.attribute(null, "value", Integer.toString(passwordQuality));
                 out.endTag(null, "password-quality");
-                if (minimumPasswordLength > 0) {
+                if (minimumPasswordLength != DEF_MINIMUM_PASSWORD_LENGTH) {
                     out.startTag(null, "min-password-length");
                     out.attribute(null, "value", Integer.toString(minimumPasswordLength));
                     out.endTag(null, "min-password-length");
                 }
-                if(passwordHistoryLength > 0) {
+                if(passwordHistoryLength != DEF_PASSWORD_HISTORY_LENGTH) {
                     out.startTag(null, "password-history-length");
                     out.attribute(null, "value", Integer.toString(passwordHistoryLength));
                     out.endTag(null, "password-history-length");
                 }
-                if (minimumPasswordUpperCase > 0) {
+                if (minimumPasswordUpperCase != DEF_MINIMUM_PASSWORD_UPPER_CASE) {
                     out.startTag(null, "min-password-uppercase");
                     out.attribute(null, "value", Integer.toString(minimumPasswordUpperCase));
                     out.endTag(null, "min-password-uppercase");
                 }
-                if (minimumPasswordLowerCase > 0) {
+                if (minimumPasswordLowerCase != DEF_MINIMUM_PASSWORD_LOWER_CASE) {
                     out.startTag(null, "min-password-lowercase");
                     out.attribute(null, "value", Integer.toString(minimumPasswordLowerCase));
                     out.endTag(null, "min-password-lowercase");
                 }
-                if (minimumPasswordLetters > 0) {
+                if (minimumPasswordLetters != DEF_MINIMUM_PASSWORD_LETTERS) {
                     out.startTag(null, "min-password-letters");
                     out.attribute(null, "value", Integer.toString(minimumPasswordLetters));
                     out.endTag(null, "min-password-letters");
                 }
-                if (minimumPasswordNumeric > 0) {
+                if (minimumPasswordNumeric != DEF_MINIMUM_PASSWORD_NUMERIC) {
                     out.startTag(null, "min-password-numeric");
                     out.attribute(null, "value", Integer.toString(minimumPasswordNumeric));
                     out.endTag(null, "min-password-numeric");
                 }
-                if (minimumPasswordSymbols > 0) {
+                if (minimumPasswordSymbols != DEF_MINIMUM_PASSWORD_SYMBOLS) {
                     out.startTag(null, "min-password-symbols");
                     out.attribute(null, "value", Integer.toString(minimumPasswordSymbols));
                     out.endTag(null, "min-password-symbols");
                 }
-                if (minimumPasswordNonLetter > 0) {
+                if (minimumPasswordNonLetter > DEF_MINIMUM_PASSWORD_NON_LETTER) {
                     out.startTag(null, "min-password-nonletter");
                     out.attribute(null, "value", Integer.toString(minimumPasswordNonLetter));
                     out.endTag(null, "min-password-nonletter");
                 }
             }
-            if (maximumTimeToUnlock != 0) {
+            if (maximumTimeToUnlock != DEF_MAXIMUM_TIME_TO_UNLOCK) {
                 out.startTag(null, "max-time-to-unlock");
                 out.attribute(null, "value", Long.toString(maximumTimeToUnlock));
                 out.endTag(null, "max-time-to-unlock");
             }
-            if (maximumFailedPasswordsForWipe != 0) {
+            if (maximumFailedPasswordsForWipe != DEF_MAXIMUM_FAILED_PASSWORDS_FOR_WIPE) {
                 out.startTag(null, "max-failed-password-wipe");
                 out.attribute(null, "value", Integer.toString(maximumFailedPasswordsForWipe));
                 out.endTag(null, "max-failed-password-wipe");
@@ -234,12 +259,12 @@
                     out.endTag(null, "global-proxy-exclusion-list");
                 }
             }
-            if (passwordExpirationTimeout != 0L) {
+            if (passwordExpirationTimeout != DEF_PASSWORD_EXPIRATION_TIMEOUT) {
                 out.startTag(null, "password-expiration-timeout");
                 out.attribute(null, "value", Long.toString(passwordExpirationTimeout));
                 out.endTag(null, "password-expiration-timeout");
             }
-            if (passwordExpirationDate != 0L) {
+            if (passwordExpirationDate != DEF_PASSWORD_EXPIRATION_DATE) {
                 out.startTag(null, "password-expiration-date");
                 out.attribute(null, "value", Long.toString(passwordExpirationDate));
                 out.endTag(null, "password-expiration-date");
@@ -399,6 +424,7 @@
                 }
                 if (removed) {
                     validatePasswordOwnerLocked();
+                    saveSettingsLocked();
                 }
             }
         }
@@ -509,12 +535,21 @@
     }
 
     void sendAdminCommandLocked(ActiveAdmin admin, String action) {
+        sendAdminCommandLocked(admin, action, null);
+    }
+
+    void sendAdminCommandLocked(ActiveAdmin admin, String action, BroadcastReceiver result) {
         Intent intent = new Intent(action);
         intent.setComponent(admin.info.getComponent());
         if (action.equals(DeviceAdminReceiver.ACTION_PASSWORD_EXPIRING)) {
             intent.putExtra("expiration", admin.passwordExpirationDate);
         }
-        mContext.sendBroadcast(intent);
+        if (result != null) {
+            mContext.sendOrderedBroadcast(intent, null, result, mHandler,
+                    Activity.RESULT_OK, null, null);
+        } else {
+            mContext.sendBroadcast(intent);
+        }
     }
 
     void sendAdminCommandLocked(String action, int reqPolicy) {
@@ -529,20 +564,27 @@
         }
     }
 
-    void removeActiveAdminLocked(ComponentName adminReceiver) {
-        ActiveAdmin admin = getActiveAdminUncheckedLocked(adminReceiver);
+    void removeActiveAdminLocked(final ComponentName adminReceiver) {
+        final ActiveAdmin admin = getActiveAdminUncheckedLocked(adminReceiver);
         if (admin != null) {
-            boolean doProxyCleanup =
-                admin.info.usesPolicy(DeviceAdminInfo.USES_POLICY_SETS_GLOBAL_PROXY);
             sendAdminCommandLocked(admin,
-                    DeviceAdminReceiver.ACTION_DEVICE_ADMIN_DISABLED);
-            // XXX need to wait for it to complete.
-            mAdminList.remove(admin);
-            mAdminMap.remove(adminReceiver);
-            validatePasswordOwnerLocked();
-            if (doProxyCleanup) {
-                resetGlobalProxy();
-            }
+                    DeviceAdminReceiver.ACTION_DEVICE_ADMIN_DISABLED,
+                    new BroadcastReceiver() {
+                        @Override
+                        public void onReceive(Context context, Intent intent) {
+                            synchronized (this) {
+                                boolean doProxyCleanup = admin.info.usesPolicy(
+                                        DeviceAdminInfo.USES_POLICY_SETS_GLOBAL_PROXY);
+                                mAdminList.remove(admin);
+                                mAdminMap.remove(adminReceiver);
+                                validatePasswordOwnerLocked();
+                                if (doProxyCleanup) {
+                                    resetGlobalProxy();
+                                }
+                                saveSettingsLocked();
+                            }
+                        }
+            });
         }
     }
 
@@ -1749,7 +1791,6 @@
 
             // Scan through active admins and find if anyone has already
             // set the global proxy.
-            final int N = mAdminList.size();
             Set<ComponentName> compSet = mAdminMap.keySet();
             for  (ComponentName component : compSet) {
                 ActiveAdmin ap = mAdminMap.get(component);
diff --git a/services/java/com/android/server/InputMethodManagerService.java b/services/java/com/android/server/InputMethodManagerService.java
index c365af5..465fd2e 100644
--- a/services/java/com/android/server/InputMethodManagerService.java
+++ b/services/java/com/android/server/InputMethodManagerService.java
@@ -64,7 +64,9 @@
 import android.provider.Settings.Secure;
 import android.provider.Settings.SettingNotFoundException;
 import android.text.TextUtils;
+import android.text.style.SuggestionSpan;
 import android.util.EventLog;
+import android.util.LruCache;
 import android.util.Pair;
 import android.util.Slog;
 import android.util.PrintWriterPrinter;
@@ -117,6 +119,8 @@
 
     static final long TIME_TO_RECONNECT = 10*1000;
 
+    static final int SECURE_SUGGESTION_SPANS_MAX_SIZE = 20;
+
     private static final int NOT_A_SUBTYPE_ID = -1;
     private static final String NOT_A_SUBTYPE_ID_STR = String.valueOf(NOT_A_SUBTYPE_ID);
     private static final String SUBTYPE_MODE_KEYBOARD = "keyboard";
@@ -141,6 +145,8 @@
     // lock for this class.
     final ArrayList<InputMethodInfo> mMethodList = new ArrayList<InputMethodInfo>();
     final HashMap<String, InputMethodInfo> mMethodMap = new HashMap<String, InputMethodInfo>();
+    private final LruCache<SuggestionSpan, InputMethodInfo> mSecureSuggestionSpans =
+            new LruCache<SuggestionSpan, InputMethodInfo>(SECURE_SUGGESTION_SPANS_MAX_SIZE);
 
     class SessionState {
         final ClientState client;
@@ -965,6 +971,7 @@
         }
     }
 
+    @Override
     public void updateStatusIcon(IBinder token, String packageName, int iconId) {
         int uid = Binder.getCallingUid();
         long ident = Binder.clearCallingIdentity();
@@ -989,6 +996,7 @@
         }
     }
 
+    @Override
     public void setImeWindowStatus(IBinder token, int vis, int backDisposition) {
         int uid = Binder.getCallingUid();
         long ident = Binder.clearCallingIdentity();
@@ -1008,6 +1016,42 @@
         }
     }
 
+    public void registerSuggestionSpansForNotification(SuggestionSpan[] spans) {
+        synchronized (mMethodMap) {
+            final InputMethodInfo currentImi = mMethodMap.get(mCurMethodId);
+            for (int i = 0; i < spans.length; ++i) {
+                SuggestionSpan ss = spans[i];
+                if (!TextUtils.isEmpty(ss.getNotificationTargetClassName())) {
+                    mSecureSuggestionSpans.put(ss, currentImi);
+                    final InputMethodInfo targetImi = mSecureSuggestionSpans.get(ss);
+                }
+            }
+        }
+    }
+
+    public boolean notifySuggestionPicked(SuggestionSpan span, String originalString, int index) {
+        synchronized (mMethodMap) {
+            final InputMethodInfo targetImi = mSecureSuggestionSpans.get(span);
+            // TODO: Do not send the intent if the process of the targetImi is already dead.
+            if (targetImi != null) {
+                final String[] suggestions = span.getSuggestions();
+                if (index < 0 || index >= suggestions.length) return false;
+                final String className = span.getNotificationTargetClassName();
+                final Intent intent = new Intent();
+                // Ensures that only a class in the original IME package will receive the
+                // notification.
+                intent.setClassName(targetImi.getPackageName(), className);
+                intent.setAction(SuggestionSpan.ACTION_SUGGESTION_PICKED);
+                intent.putExtra(SuggestionSpan.SUGGESTION_SPAN_PICKED_BEFORE, originalString);
+                intent.putExtra(SuggestionSpan.SUGGESTION_SPAN_PICKED_AFTER, suggestions[index]);
+                intent.putExtra(SuggestionSpan.SUGGESTION_SPAN_PICKED_HASHCODE, span.hashCode());
+                mContext.sendBroadcast(intent);
+                return true;
+            }
+        }
+        return false;
+    }
+
     void updateFromSettingsLocked() {
         // We are assuming that whoever is changing DEFAULT_INPUT_METHOD and
         // ENABLED_INPUT_METHODS is taking care of keeping them correctly in
@@ -1102,6 +1146,7 @@
         }
     }
 
+    @Override
     public boolean showSoftInput(IInputMethodClient client, int flags,
             ResultReceiver resultReceiver) {
         int uid = Binder.getCallingUid();
@@ -1167,6 +1212,7 @@
         return res;
     }
 
+    @Override
     public boolean hideSoftInput(IInputMethodClient client, int flags,
             ResultReceiver resultReceiver) {
         int uid = Binder.getCallingUid();
@@ -1229,6 +1275,7 @@
         return res;
     }
 
+    @Override
     public void windowGainedFocus(IInputMethodClient client, IBinder windowToken,
             boolean viewHasFocus, boolean isTextEditor, int softInputMode,
             boolean first, int windowFlags) {
@@ -1330,6 +1377,7 @@
         }
     }
 
+    @Override
     public void showInputMethodPickerFromClient(IInputMethodClient client) {
         synchronized (mMethodMap) {
             if (mCurClient == null || client == null
@@ -1344,10 +1392,12 @@
         }
     }
 
+    @Override
     public void setInputMethod(IBinder token, String id) {
         setInputMethodWithSubtypeId(token, id, NOT_A_SUBTYPE_ID);
     }
 
+    @Override
     public void setInputMethodAndSubtype(IBinder token, String id, InputMethodSubtype subtype) {
         synchronized (mMethodMap) {
             if (subtype != null) {
@@ -1359,6 +1409,7 @@
         }
     }
 
+    @Override
     public void showInputMethodAndSubtypeEnablerFromClient(
             IInputMethodClient client, String inputMethodId) {
         synchronized (mMethodMap) {
@@ -1481,6 +1532,7 @@
         }
     }
 
+    @Override
     public void hideMySoftInput(IBinder token, int flags) {
         synchronized (mMethodMap) {
             if (token == null || mCurToken != token) {
@@ -1497,6 +1549,7 @@
         }
     }
 
+    @Override
     public void showMySoftInput(IBinder token, int flags) {
         synchronized (mMethodMap) {
             if (token == null || mCurToken != token) {
@@ -1533,6 +1586,7 @@
         }
     }
 
+    @Override
     public boolean handleMessage(Message msg) {
         HandlerCaller.SomeArgs args;
         switch (msg.what) {
@@ -1876,6 +1930,7 @@
             }
 
             AlertDialog.OnClickListener adocl = new AlertDialog.OnClickListener() {
+                @Override
                 public void onClick(DialogInterface dialog, int which) {
                     hideInputMethodMenu();
                 }
@@ -1887,6 +1942,7 @@
             mDialogBuilder = new AlertDialog.Builder(context)
                     .setTitle(com.android.internal.R.string.select_input_method)
                     .setOnCancelListener(new OnCancelListener() {
+                        @Override
                         public void onCancel(DialogInterface dialog) {
                             hideInputMethodMenu();
                         }
@@ -1897,6 +1953,7 @@
 
             mDialogBuilder.setSingleChoiceItems(mItems, checkedItem,
                     new AlertDialog.OnClickListener() {
+                        @Override
                         public void onClick(DialogInterface dialog, int which) {
                             synchronized (mMethodMap) {
                                 if (mIms == null || mIms.length <= which
@@ -1921,6 +1978,7 @@
                 mDialogBuilder.setPositiveButton(
                         com.android.internal.R.string.configure_input_methods,
                         new DialogInterface.OnClickListener() {
+                            @Override
                             public void onClick(DialogInterface dialog, int whichButton) {
                                 showConfigureInputMethods();
                             }
@@ -1956,6 +2014,7 @@
 
     // ----------------------------------------------------------------------
 
+    @Override
     public boolean setInputMethodEnabled(String id, boolean enabled) {
         synchronized (mMethodMap) {
             if (mContext.checkCallingOrSelfPermission(
@@ -2286,6 +2345,7 @@
     /**
      * @return Return the current subtype of this input method.
      */
+    @Override
     public InputMethodSubtype getCurrentInputMethodSubtype() {
         boolean subtypeIsSelected = false;
         try {
@@ -2368,6 +2428,7 @@
         }
     }
 
+    @Override
     public boolean setCurrentInputMethodSubtype(InputMethodSubtype subtype) {
         synchronized (mMethodMap) {
             if (subtype != null && mCurMethodId != null) {
diff --git a/services/java/com/android/server/wm/WindowManagerService.java b/services/java/com/android/server/wm/WindowManagerService.java
index 92c490e..08fea1b 100644
--- a/services/java/com/android/server/wm/WindowManagerService.java
+++ b/services/java/com/android/server/wm/WindowManagerService.java
@@ -6000,8 +6000,12 @@
             mActivityManager.updateConfiguration(null);
         } catch (RemoteException e) {
         }
-        
+
         mPolicy.systemReady();
+
+        synchronized (mWindowMap) {
+            readForcedDisplaySizeLocked();
+        }
     }
 
     // This is an animation that does nothing: it just immediately finishes
@@ -6517,6 +6521,8 @@
                         ? shortDimen : mInitialDisplayHeight;
             }
             setForcedDisplaySizeLocked(width, height);
+            Settings.Secure.putString(mContext.getContentResolver(),
+                    Settings.Secure.DISPLAY_SIZE_FORCED, width + "," + height);
         }
     }
 
@@ -6563,7 +6569,29 @@
         }
     }
 
+    private void readForcedDisplaySizeLocked() {
+        final String str = Settings.Secure.getString(mContext.getContentResolver(),
+                Settings.Secure.DISPLAY_SIZE_FORCED);
+        if (str == null || str.length() == 0) {
+            return;
+        }
+        final int pos = str.indexOf(',');
+        if (pos <= 0 || str.lastIndexOf(',') != pos) {
+            return;
+        }
+        int width, height;
+        try {
+            width = Integer.parseInt(str.substring(0, pos));
+            height = Integer.parseInt(str.substring(pos+1));
+        } catch (NumberFormatException ex) {
+            return;
+        }
+        setForcedDisplaySizeLocked(width, height);
+    }
+
     private void setForcedDisplaySizeLocked(int width, int height) {
+        Slog.i(TAG, "Using new display size: " + width + "x" + height);
+
         mBaseDisplayWidth = width;
         mBaseDisplayHeight = height;
         mPolicy.setInitialDisplaySize(mBaseDisplayWidth, mBaseDisplayHeight);
@@ -6593,6 +6621,8 @@
     public void clearForcedDisplaySize() {
         synchronized(mWindowMap) {
             setForcedDisplaySizeLocked(mInitialDisplayWidth, mInitialDisplayHeight);
+            Settings.Secure.putString(mContext.getContentResolver(),
+                    Settings.Secure.DISPLAY_SIZE_FORCED, "");
         }
     }
 
@@ -8267,6 +8297,7 @@
         boolean state = mHoldingScreenWakeLock.isHeld();
         if (holding != state) {
             if (holding) {
+                mPolicy.screenOnStartedLw();
                 mHoldingScreenWakeLock.acquire();
             } else {
                 mPolicy.screenOnStoppedLw();
diff --git a/tests/HwAccelerationTest/AndroidManifest.xml b/tests/HwAccelerationTest/AndroidManifest.xml
index c1aa215..35f2b9b 100644
--- a/tests/HwAccelerationTest/AndroidManifest.xml
+++ b/tests/HwAccelerationTest/AndroidManifest.xml
@@ -376,7 +376,7 @@
 
         <activity
                 android:name="ListActivity"
-                android:label="_List">
+                android:label="__List">
             <intent-filter>
                 <action android:name="android.intent.action.MAIN" />
                 <category android:name="android.intent.category.LAUNCHER" />