Merge "Fix "dumpsys activity provider""
diff --git a/api/16.txt b/api/16.txt
index 8e07844..02a5104 100644
--- a/api/16.txt
+++ b/api/16.txt
@@ -15052,7 +15052,7 @@
     method public static final deprecated boolean supportsProcesses();
     field public static final int BLUETOOTH_GID = 2000; // 0x7d0
     field public static final int FIRST_APPLICATION_UID = 10000; // 0x2710
-    field public static final int LAST_APPLICATION_UID = 99999; // 0x1869f
+    field public static final int LAST_APPLICATION_UID = 89999; // 0x1869f
     field public static final int PHONE_UID = 1001; // 0x3e9
     field public static final int SIGNAL_KILL = 9; // 0x9
     field public static final int SIGNAL_QUIT = 3; // 0x3
diff --git a/api/current.txt b/api/current.txt
index 574af6b..bb05bfb 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -562,6 +562,7 @@
     field public static final int isRepeatable = 16843336; // 0x1010248
     field public static final int isScrollContainer = 16843342; // 0x101024e
     field public static final int isSticky = 16843335; // 0x1010247
+    field public static final int isolatedProcess = 16843687; // 0x10103a7
     field public static final int itemBackground = 16843056; // 0x1010130
     field public static final int itemIconDisabledAlpha = 16843057; // 0x1010131
     field public static final int itemPadding = 16843565; // 0x101032d
@@ -6469,6 +6470,7 @@
     method public int describeContents();
     method public void dump(android.util.Printer, java.lang.String);
     field public static final android.os.Parcelable.Creator CREATOR;
+    field public static final int FLAG_ISOLATED_PROCESS = 2; // 0x2
     field public static final int FLAG_STOP_WITH_TASK = 1; // 0x1
     field public int flags;
     field public java.lang.String permission;
@@ -15178,7 +15180,7 @@
     method public static final deprecated boolean supportsProcesses();
     field public static final int BLUETOOTH_GID = 2000; // 0x7d0
     field public static final int FIRST_APPLICATION_UID = 10000; // 0x2710
-    field public static final int LAST_APPLICATION_UID = 99999; // 0x1869f
+    field public static final int LAST_APPLICATION_UID = 89999; // 0x15f8f
     field public static final int PHONE_UID = 1001; // 0x3e9
     field public static final int SIGNAL_KILL = 9; // 0x9
     field public static final int SIGNAL_QUIT = 3; // 0x3
@@ -24890,6 +24892,7 @@
     method public void showSoftInputFromInputMethod(android.os.IBinder, int);
     method public void showStatusIcon(android.os.IBinder, java.lang.String, int);
     method public boolean switchToLastInputMethod(android.os.IBinder);
+    method public boolean switchToNextInputMethod(android.os.IBinder, boolean);
     method public void toggleSoftInput(int, int);
     method public void toggleSoftInputFromWindow(android.os.IBinder, int, int);
     method public void updateCursor(android.view.View, int, int, int, int);
diff --git a/cmds/servicemanager/service_manager.c b/cmds/servicemanager/service_manager.c
index 4ed2489..cfc2d16 100644
--- a/cmds/servicemanager/service_manager.c
+++ b/cmds/servicemanager/service_manager.c
@@ -90,6 +90,7 @@
     struct svcinfo *next;
     void *ptr;
     struct binder_death death;
+    int allow_isolated;
     unsigned len;
     uint16_t name[0];
 };
@@ -125,13 +126,21 @@
 };
   
 
-void *do_find_service(struct binder_state *bs, uint16_t *s, unsigned len)
+void *do_find_service(struct binder_state *bs, uint16_t *s, unsigned len, unsigned uid)
 {
     struct svcinfo *si;
     si = find_svc(s, len);
 
 //    ALOGI("check_service('%s') ptr = %p\n", str8(s), si ? si->ptr : 0);
     if (si && si->ptr) {
+        if (!si->allow_isolated) {
+            // If this service doesn't allow access from isolated processes,
+            // then check the uid to see if it is isolated.
+            unsigned appid = uid % AID_USER;
+            if (appid >= AID_ISOLATED_START && appid <= AID_ISOLATED_END) {
+                return 0;
+            }
+        }
         return si->ptr;
     } else {
         return 0;
@@ -140,10 +149,11 @@
 
 int do_add_service(struct binder_state *bs,
                    uint16_t *s, unsigned len,
-                   void *ptr, unsigned uid)
+                   void *ptr, unsigned uid, int allow_isolated)
 {
     struct svcinfo *si;
-//    ALOGI("add_service('%s',%p) uid=%d\n", str8(s), ptr, uid);
+    //ALOGI("add_service('%s',%p,%s) uid=%d\n", str8(s), ptr,
+    //        allow_isolated ? "allow_isolated" : "!allow_isolated", uid);
 
     if (!ptr || (len == 0) || (len > 127))
         return -1;
@@ -175,6 +185,7 @@
         si->name[len] = '\0';
         si->death.func = svcinfo_death;
         si->death.ptr = si;
+        si->allow_isolated = allow_isolated;
         si->next = svclist;
         svclist = si;
     }
@@ -194,6 +205,7 @@
     unsigned len;
     void *ptr;
     uint32_t strict_policy;
+    int allow_isolated;
 
 //    ALOGI("target=%p code=%d pid=%d uid=%d\n",
 //         txn->target, txn->code, txn->sender_pid, txn->sender_euid);
@@ -217,7 +229,7 @@
     case SVC_MGR_GET_SERVICE:
     case SVC_MGR_CHECK_SERVICE:
         s = bio_get_string16(msg, &len);
-        ptr = do_find_service(bs, s, len);
+        ptr = do_find_service(bs, s, len, txn->sender_euid);
         if (!ptr)
             break;
         bio_put_ref(reply, ptr);
@@ -226,7 +238,8 @@
     case SVC_MGR_ADD_SERVICE:
         s = bio_get_string16(msg, &len);
         ptr = bio_get_ref(msg);
-        if (do_add_service(bs, s, len, ptr, txn->sender_euid))
+        allow_isolated = bio_get_uint32(msg) ? 1 : 0;
+        if (do_add_service(bs, s, len, ptr, txn->sender_euid, allow_isolated))
             return -1;
         break;
 
diff --git a/cmds/surfaceflinger/main_surfaceflinger.cpp b/cmds/surfaceflinger/main_surfaceflinger.cpp
index 78b1007..6dbcf5c 100644
--- a/cmds/surfaceflinger/main_surfaceflinger.cpp
+++ b/cmds/surfaceflinger/main_surfaceflinger.cpp
@@ -20,6 +20,6 @@
 using namespace android;
 
 int main(int argc, char** argv) {
-    SurfaceFlinger::publishAndJoinThreadPool();
+    SurfaceFlinger::publishAndJoinThreadPool(true);
     return 0;
 }
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index e4cfc99..bf632a9 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -134,7 +134,7 @@
     private static final boolean DEBUG_RESULTS = false;
     private static final boolean DEBUG_BACKUP = true;
     private static final boolean DEBUG_CONFIGURATION = false;
-    private static final boolean DEBUG_SERVICE = true;
+    private static final boolean DEBUG_SERVICE = false;
     private static final long MIN_TIME_BETWEEN_GCS = 5*1000;
     private static final Pattern PATTERN_SEMICOLON = Pattern.compile(";");
     private static final int SQLITE_MEM_RELEASED_EVENT_LOG_TAG = 75003;
@@ -3764,13 +3764,17 @@
     }
 
     private void setupGraphicsSupport(LoadedApk info) {
+        if (Process.isIsolated()) {
+            // Isolated processes aren't going to do UI.
+            return;
+        }
         try {
             int uid = Process.myUid();
             String[] packages = getPackageManager().getPackagesForUid(uid);
 
             // If there are several packages in this application we won't
             // initialize the graphics disk caches 
-            if (packages.length == 1) {
+            if (packages != null && packages.length == 1) {
                 ContextImpl appContext = new ContextImpl();
                 appContext.init(info, null, this);
 
diff --git a/core/java/android/app/LoadedApk.java b/core/java/android/app/LoadedApk.java
index fcbcd81..d9bbb4a 100644
--- a/core/java/android/app/LoadedApk.java
+++ b/core/java/android/app/LoadedApk.java
@@ -119,7 +119,7 @@
         final int myUid = Process.myUid();
         mResDir = aInfo.uid == myUid ? aInfo.sourceDir
                 : aInfo.publicSourceDir;
-        if (!UserId.isSameUser(aInfo.uid, myUid)) {
+        if (!UserId.isSameUser(aInfo.uid, myUid) && !Process.isIsolated()) {
             aInfo.dataDir = PackageManager.getDataDirForUser(UserId.getUserId(myUid),
                     mPackageName);
         }
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index faee873..2023f82 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -2581,6 +2581,11 @@
                 false)) {
             s.info.flags |= ServiceInfo.FLAG_STOP_WITH_TASK;
         }
+        if (sa.getBoolean(
+                com.android.internal.R.styleable.AndroidManifestService_isolatedProcess,
+                false)) {
+            s.info.flags |= ServiceInfo.FLAG_ISOLATED_PROCESS;
+        }
 
         sa.recycle();
 
diff --git a/core/java/android/content/pm/ServiceInfo.java b/core/java/android/content/pm/ServiceInfo.java
index 612e345..7ee84ab 100644
--- a/core/java/android/content/pm/ServiceInfo.java
+++ b/core/java/android/content/pm/ServiceInfo.java
@@ -42,10 +42,17 @@
     public static final int FLAG_STOP_WITH_TASK = 0x0001;
 
     /**
+     * Bit in {@link #flags}: If set, the service will run in its own
+     * isolated process.  Set from the
+     * {@link android.R.attr#isolatedProcess} attribute.
+     */
+    public static final int FLAG_ISOLATED_PROCESS = 0x0002;
+
+    /**
      * Options that have been set in the service declaration in the
      * manifest.
      * These include:
-     * {@link #FLAG_STOP_WITH_TASK}
+     * {@link #FLAG_STOP_WITH_TASK}, {@link #FLAG_ISOLATED_PROCESS}.
      */
     public int flags;
 
diff --git a/core/java/android/hardware/SensorEvent.java b/core/java/android/hardware/SensorEvent.java
index 784bcc5..51a17c1 100644
--- a/core/java/android/hardware/SensorEvent.java
+++ b/core/java/android/hardware/SensorEvent.java
@@ -294,7 +294,7 @@
      * <li>X is defined as the vector product <b>Y.Z</b> (It is tangential to
      * the ground at the device's current location and roughly points East).</li>
      * <li>Y is tangential to the ground at the device's current location and
-     * points towards the magnetic North Pole.</li>
+     * points towards magnetic north.</li>
      * <li>Z points towards the sky and is perpendicular to the ground.</li>
      * </ul>
      *
diff --git a/core/java/android/os/IServiceManager.java b/core/java/android/os/IServiceManager.java
index 9a5ff47..7b11c28 100644
--- a/core/java/android/os/IServiceManager.java
+++ b/core/java/android/os/IServiceManager.java
@@ -45,7 +45,8 @@
      * Place a new @a service called @a name into the service
      * manager.
      */
-    public void addService(String name, IBinder service) throws RemoteException;
+    public void addService(String name, IBinder service, boolean allowIsolated)
+                throws RemoteException;
 
     /**
      * Return a list of all currently running services.
diff --git a/core/java/android/os/Process.java b/core/java/android/os/Process.java
index cdf235d..0746af8 100644
--- a/core/java/android/os/Process.java
+++ b/core/java/android/os/Process.java
@@ -119,7 +119,19 @@
      * Last of application-specific UIDs starting at
      * {@link #FIRST_APPLICATION_UID}.
      */
-    public static final int LAST_APPLICATION_UID = 99999;
+    public static final int LAST_APPLICATION_UID = 89999;
+
+    /**
+     * First uid used for fully isolated sandboxed processes (with no permissions of their own)
+     * @hide
+     */
+    public static final int FIRST_ISOLATED_UID = 99000;
+
+    /**
+     * Last uid used for fully isolated sandboxed processes (with no permissions of their own)
+     * @hide
+     */
+    public static final int LAST_ISOLATED_UID = 99999;
 
     /**
      * Defines a secondary group id for access to the bluetooth hardware.
@@ -576,6 +588,15 @@
     public static final native int myUid();
 
     /**
+     * Returns whether the current process is in an isolated sandbox.
+     * @hide
+     */
+    public static final boolean isIsolated() {
+        int uid = UserId.getAppId(myUid());
+        return uid >= FIRST_ISOLATED_UID && uid <= LAST_ISOLATED_UID;
+    }
+
+    /**
      * Returns the UID assigned to a particular user name, or -1 if there is
      * none.  If the given string consists of only numbers, it is converted
      * directly to a uid.
diff --git a/core/java/android/os/ServiceManager.java b/core/java/android/os/ServiceManager.java
index 1af24f4a..13b8b66 100644
--- a/core/java/android/os/ServiceManager.java
+++ b/core/java/android/os/ServiceManager.java
@@ -69,7 +69,24 @@
      */
     public static void addService(String name, IBinder service) {
         try {
-            getIServiceManager().addService(name, service);
+            getIServiceManager().addService(name, service, false);
+        } catch (RemoteException e) {
+            Log.e(TAG, "error in addService", e);
+        }
+    }
+
+    /**
+     * Place a new @a service called @a name into the service
+     * manager.
+     * 
+     * @param name the name of the new service
+     * @param service the service object
+     * @param allowIsolated set to true to allow isolated sandboxed processes
+     * to access this service
+     */
+    public static void addService(String name, IBinder service, boolean allowIsolated) {
+        try {
+            getIServiceManager().addService(name, service, allowIsolated);
         } catch (RemoteException e) {
             Log.e(TAG, "error in addService", e);
         }
diff --git a/core/java/android/os/ServiceManagerNative.java b/core/java/android/os/ServiceManagerNative.java
index 2aab0e6..43b5128 100644
--- a/core/java/android/os/ServiceManagerNative.java
+++ b/core/java/android/os/ServiceManagerNative.java
@@ -71,7 +71,8 @@
                 data.enforceInterface(IServiceManager.descriptor);
                 String name = data.readString();
                 IBinder service = data.readStrongBinder();
-                addService(name, service);
+                boolean allowIsolated = data.readInt() != 0;
+                addService(name, service, allowIsolated);
                 return true;
             }
     
@@ -136,13 +137,14 @@
         return binder;
     }
 
-    public void addService(String name, IBinder service)
+    public void addService(String name, IBinder service, boolean allowIsolated)
             throws RemoteException {
         Parcel data = Parcel.obtain();
         Parcel reply = Parcel.obtain();
         data.writeInterfaceToken(IServiceManager.descriptor);
         data.writeString(name);
         data.writeStrongBinder(service);
+        data.writeInt(allowIsolated ? 1 : 0);
         mRemote.transact(ADD_SERVICE_TRANSACTION, data, reply, 0);
         reply.recycle();
         data.recycle();
diff --git a/core/java/android/os/UserId.java b/core/java/android/os/UserId.java
index 4124d51..286b674 100644
--- a/core/java/android/os/UserId.java
+++ b/core/java/android/os/UserId.java
@@ -56,6 +56,11 @@
         return getAppId(uid1) == getAppId(uid2);
     }
 
+    public static final boolean isIsolated(int uid) {
+        uid = getAppId(uid);
+        return uid >= Process.FIRST_ISOLATED_UID && uid <= Process.LAST_ISOLATED_UID;
+    }
+
     /**
      * Returns the user id for a given uid.
      * @hide
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 375e5e4..4e147c2 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -1412,7 +1412,7 @@
         public static final int SCREEN_BRIGHTNESS_MODE_MANUAL = 0;
 
         /**
-         * SCREEN_BRIGHTNESS_MODE value for manual mode.
+         * SCREEN_BRIGHTNESS_MODE value for automatic mode.
          */
         public static final int SCREEN_BRIGHTNESS_MODE_AUTOMATIC = 1;
 
diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java
index 0985e14..7171b58 100644
--- a/core/java/android/view/inputmethod/InputMethodManager.java
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -1593,6 +1593,27 @@
     }
 
     /**
+     * Force switch to the next input method and subtype. If there is no IME enabled except
+     * current IME and subtype, do nothing.
+     * @param imeToken Supplies the identifying token given to an input method when it was started,
+     * which allows it to perform this operation on itself.
+     * @param onlyCurrentIme if true, the framework will find the next subtype which
+     * belongs to the current IME
+     * @return true if the current input method and subtype was successfully switched to the next
+     * input method and subtype.
+     */
+    public boolean switchToNextInputMethod(IBinder imeToken, boolean onlyCurrentIme) {
+        synchronized (mH) {
+            try {
+                return mService.switchToNextInputMethod(imeToken, onlyCurrentIme);
+            } catch (RemoteException e) {
+                Log.w(TAG, "IME died: " + mCurId, e);
+                return false;
+            }
+        }
+    }
+
+    /**
      * Set additional input method subtypes. Only a process which shares the same uid with the IME
      * can add additional input method subtypes to the IME.
      * Please note that a subtype's status is stored in the system.
diff --git a/core/java/android/webkit/WebSettings.java b/core/java/android/webkit/WebSettings.java
index b4c38db..c463b40 100644
--- a/core/java/android/webkit/WebSettings.java
+++ b/core/java/android/webkit/WebSettings.java
@@ -1156,8 +1156,13 @@
     }
 
     /**
-     * Tell the WebView to load image resources automatically.
-     * @param flag True if the WebView should load images automatically.
+     * Sets whether the WebView should load image resources. Note that this method
+     * controls loading of all images, including those embedded using the data
+     * URI scheme. Use {@link #setBlockNetworkImage} to control loading only
+     * of images specified using network URI schemes. Note that if the value of this
+     * setting is changed from false to true, all images resources referenced
+     * by content currently displayed by the WebView are loaded automatically.
+     * @param flag Whether the WebView should load image resources.
      */
     public synchronized void setLoadsImagesAutomatically(boolean flag) {
         if (mLoadsImagesAutomatically != flag) {
@@ -1167,20 +1172,26 @@
     }
 
     /**
-     * Return true if the WebView will load image resources automatically.
-     * The default is true.
-     * @return True if the WebView loads images automatically.
+     * Returns true if the WebView loads image resources. This includes
+     * images embedded using the data URI scheme. The default is true.
+     * @return True if the WebView loads image resources.
      */
     public synchronized boolean getLoadsImagesAutomatically() {
         return mLoadsImagesAutomatically;
     }
 
     /**
-     * Tell the WebView to block network images. This is only checked when
-     * {@link #getLoadsImagesAutomatically} is true. If you set the value to
-     * false, images will automatically be loaded. Use this api to reduce
-     * bandwidth only. Use {@link #setBlockNetworkLoads} if possible.
-     * @param flag True if the WebView should block network images.
+     * Sets whether the WebView should not load image resources from the
+     * network (resources accessed via http and https URI schemes).  Note
+     * that this method has no effect unless
+     * {@link #getLoadsImagesAutomatically} returns true. Also note that
+     * disabling all network loads using {@link #setBlockNetworkLoads}
+     * will also prevent network images from loading, even if this flag is set
+     * to false. When the value of this setting is changed from true to false,
+     * network images resources referenced by content currently displayed by
+     * the WebView are fetched automatically.
+     * @param flag Whether the WebView should not load image resources from
+     * the network.
      * @see #setBlockNetworkLoads
      */
     public synchronized void setBlockNetworkImage(boolean flag) {
@@ -1191,20 +1202,27 @@
     }
 
     /**
-     * Return true if the WebView will block network images. The default is
-     * false.
-     * @return True if the WebView blocks network images.
+     * Returns true if the WebView does not load image resources from the network.
+     * The default is false.
+     * @return True if the WebView does not load image resources from the network.
      */
     public synchronized boolean getBlockNetworkImage() {
         return mBlockNetworkImage;
     }
 
     /**
-     * Tell the WebView to block all network load requests. If you set the
-     * value to false, you must call {@link android.webkit.WebView#reload} to
-     * fetch remote resources. This flag supercedes the value passed to
-     * {@link #setBlockNetworkImage}.
-     * @param flag True if the WebView should block all network loads.
+     * Sets whether the WebView should not load resources from the network.
+     * Use {@link #setBlockNetworkImage} to only avoid loading
+     * image resources. Note that if the value of this setting is
+     * changed from true to false, network resources referenced by content
+     * currently displayed by the WebView are not fetched until
+     * {@link android.webkit.WebView#reload} is called.
+     * If the application does not have the
+     * {@link android.Manifest.permission#INTERNET} permission, attempts to set
+     * a value of false will cause a {@link java.lang.SecurityException}
+     * to be thrown.
+     * @param flag Whether the WebView should not load any resources
+     * from the network.
      * @see android.webkit.WebView#reload
      */
     public synchronized void setBlockNetworkLoads(boolean flag) {
@@ -1216,9 +1234,11 @@
     }
 
     /**
-     * Return true if the WebView will block all network loads. The default is
-     * false.
-     * @return True if the WebView blocks all network loads.
+     * Returns true if the WebView does not load any resources from the network.
+     * The default value is false if the application has the
+     * {@link android.Manifest.permission#INTERNET} permission, otherwise it is
+     * true.
+     * @return True if the WebView does not load any resources from the network.
      */
     public synchronized boolean getBlockNetworkLoads() {
         return mBlockNetworkLoads;
diff --git a/core/java/com/android/internal/view/IInputMethodManager.aidl b/core/java/com/android/internal/view/IInputMethodManager.aidl
index 683aca5..5d80b79 100644
--- a/core/java/com/android/internal/view/IInputMethodManager.aidl
+++ b/core/java/com/android/internal/view/IInputMethodManager.aidl
@@ -67,6 +67,7 @@
     InputMethodSubtype getCurrentInputMethodSubtype();
     boolean setCurrentInputMethodSubtype(in InputMethodSubtype subtype);
     boolean switchToLastInputMethod(in IBinder token);
+    boolean switchToNextInputMethod(in IBinder token, boolean onlyCurrentIme);
     boolean setInputMethodEnabled(String id, boolean enabled);
     oneway void setAdditionalInputMethodSubtypes(String id, in InputMethodSubtype[] subtypes);
 }
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 97658a1..1199cf7 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -717,6 +717,13 @@
         android:label="@string/permlab_removeTasks"
         android:description="@string/permdesc_removeTasks" />
 
+    <!-- @hide Change the screen compatibility mode of applications -->
+    <permission android:name="android.permission.SET_SCREEN_COMPATIBILITY"
+        android:permissionGroup="android.permission-group.SYSTEM_TOOLS"
+        android:protectionLevel="signature"
+        android:label="@string/permlab_setScreenCompatibility"
+        android:description="@string/permdesc_setScreenCompatibility" />
+
     <!-- Allows an application to modify the current configuration, such
          as locale. -->
     <permission android:name="android.permission.CHANGE_CONFIGURATION"
diff --git a/core/res/res/values-fi/strings.xml b/core/res/res/values-fi/strings.xml
index 0a60adb..5a4d2f0 100644
--- a/core/res/res/values-fi/strings.xml
+++ b/core/res/res/values-fi/strings.xml
@@ -776,7 +776,7 @@
     <string name="searchview_description_query" msgid="5911778593125355124">"Hakulauseke"</string>
     <string name="searchview_description_clear" msgid="1330281990951833033">"Tyhjennä kysely"</string>
     <string name="searchview_description_submit" msgid="2688450133297983542">"Lähetä kysely"</string>
-    <string name="searchview_description_voice" msgid="2453203695674994440">"Äänihaku"</string>
+    <string name="searchview_description_voice" msgid="2453203695674994440">"Puhehaku"</string>
     <string name="oneMonthDurationPast" msgid="7396384508953779925">"kuukausi sitten"</string>
     <string name="beforeOneMonthDurationPast" msgid="909134546836499826">"Yli kuukausi sitten"</string>
   <plurals name="num_seconds_ago">
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index dbd49fb..92c59ab 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -1235,6 +1235,10 @@
              when the user remove a task rooted in an activity owned by
              the application.  The default is false. -->
         <attr name="stopWithTask" format="boolean" />
+        <!-- If set to true, this service will run under a special process
+             that is isolated from the rest of the system.  The only communication
+             with it is through the Service API (binding and starting). -->
+        <attr name="isolatedProcess" format="boolean" />
     </declare-styleable>
     
     <!-- The <code>receiver</code> tag declares an
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index e3c2bd8..1b91643 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -3482,4 +3482,10 @@
   <public type="color" name="holo_orange_dark" id="0x01060019" />
   <public type="color" name="holo_purple" id="0x0106001a" />
   <public type="color" name="holo_blue_bright" id="0x0106001b" />
+
+<!-- ===============================================================
+     Resources added in version 16 of the platform (Jelly Bean)
+     =============================================================== -->
+  <public type="attr" name="isolatedProcess" id="0x010103a7" />
+
 </resources>
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index dc45c408..701782c 100755
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -532,6 +532,13 @@
         tasks and kill their apps. Malicious apps may disrupt
         the behavior of other apps.</string>
 
+    <!-- Title of an application permission, allowing control of app screen compatibility mode -->
+    <string name="permlab_setScreenCompatibility">set screen compatibility</string>
+    <!-- Description of an application permission, allowing control of app screen compatibility mode -->
+    <string name="permdesc_setScreenCompatibility">Allows the app to control the
+        screen compatibility mode of other applications.  Malicious applications may
+        break the behavior of other applications.</string>
+
     <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
     <string name="permlab_setDebugApp">enable app debugging</string>
     <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
diff --git a/docs/html/guide/developing/devices/managing-avds.jd b/docs/html/guide/developing/devices/managing-avds.jd
index 1817ce7..e70a0bb 100644
--- a/docs/html/guide/developing/devices/managing-avds.jd
+++ b/docs/html/guide/developing/devices/managing-avds.jd
@@ -233,13 +233,5 @@
 
       <td>hw.lcd.density</td>
     </tr>
-
-    <tr>
-      <td>Trackball support</td>
-
-      <td>Whether there is a trackball present.</td>
-
-      <td>hw.trackBall</td>
-    </tr>
   </table>
 
diff --git a/docs/html/guide/developing/projects/index.jd b/docs/html/guide/developing/projects/index.jd
index ac8a1a5..63e67cd 100644
--- a/docs/html/guide/developing/projects/index.jd
+++ b/docs/html/guide/developing/projects/index.jd
@@ -179,8 +179,9 @@
 
     <dd>Customizable computer-specific properties for the build system. If you use Ant to build
     the project, this contains the path to the SDK installation. Because the content of the file
-    is specific to the local installation of the SDK, maintained it in a source
-    revision control system. If you use Eclipse, this file is not used.</dd>
+    is specific to the local installation of the SDK, the <code>local.properties</code> should not
+be maintained in a source revision control system. If you use Eclipse, this file is not
+used.</dd>
 
     <dt><code>ant.properties</code></dt>
 
diff --git a/docs/html/guide/topics/fundamentals.jd b/docs/html/guide/topics/fundamentals.jd
index 661f5cb..d1a3786 100644
--- a/docs/html/guide/topics/fundamentals.jd
+++ b/docs/html/guide/topics/fundamentals.jd
@@ -243,7 +243,7 @@
 android.content.ContentResolver} object. This leaves a layer of abstraction between the content
 provider and the component requesting information (for security).</p>
 
-<p>There are separate methods for activiting each type of component:</p>
+<p>There are separate methods for activating each type of component:</p>
 <ul>
   <li>You can start an activity (or give it something new to do) by
 passing an {@link android.content.Intent} to {@link android.content.Context#startActivity
@@ -400,7 +400,7 @@
 requirements in your manifest file. That way, devices that do <em>not</em> have a camera and have an
 Android version <em>lower</em> than 2.1 cannot install your application from Android Market.</p>
 
-<p>However, you can also declare that your applicaiton uses the camera, but does not
+<p>However, you can also declare that your application uses the camera, but does not
 <em>require</em> it. In that case, your application must perform a check at runtime to determine
 if the device has a camera and disable any features that use the camera if one is not available.</p>
 
diff --git a/docs/html/guide/topics/fundamentals/loaders.jd b/docs/html/guide/topics/fundamentals/loaders.jd
index 3aad204..ddd513b 100644
--- a/docs/html/guide/topics/fundamentals/loaders.jd
+++ b/docs/html/guide/topics/fundamentals/loaders.jd
@@ -491,7 +491,7 @@
 LoaderCursor</a> &#8212; A complete version of the
 snippet shown above.</li>
   <li><a href="{@docRoot}resources/samples/ApiDemos/src/com/example/android/apis/app/LoaderThrottle.html"> LoaderThrottle</a> &#8212; An example of how to use throttling to
-reduce the number of queries a content provider does then its data changes.</li>
+reduce the number of queries a content provider does when its data changes.</li>
 </ul>
 
 <p>For information on downloading and installing the SDK samples, see <a
diff --git a/docs/html/guide/topics/manifest/activity-element.jd b/docs/html/guide/topics/manifest/activity-element.jd
index 4d9603f..f44901b 100644
--- a/docs/html/guide/topics/manifest/activity-element.jd
+++ b/docs/html/guide/topics/manifest/activity-element.jd
@@ -272,10 +272,11 @@
 </p></dd>
 
 <dt><a name="exclude"></a>{@code android:excludeFromRecents}</dt>
-<dd>Whether or not the activity should be excluded from the list of recently 
-launched activities that can be displayed to users &mdash; "{@code true}" if 
-it should be excluded, and "{@code false}" if it should be included.  
-The default value is "{@code false}".
+<dd>Whether or not the task initiated by this activity should be excluded from the list of recently
+used applications ("recent apps"). That is, when this activity is the root activity of a new task,
+this attribute determines whether the task should not appear in the list of recent apps. "{@code
+true}" if the task should be <em>excluded</em> from the list; "{@code false}" if it should be
+<em>included</em>. The default value is "{@code false}".
 </p></dd>
 
 <dt><a name="exported"></a>{@code android:exported}</dt>
diff --git a/docs/html/guide/topics/resources/drawable-resource.jd b/docs/html/guide/topics/resources/drawable-resource.jd
index 80de9f9..a34ed6c 100644
--- a/docs/html/guide/topics/resources/drawable-resource.jd
+++ b/docs/html/guide/topics/resources/drawable-resource.jd
@@ -1248,7 +1248,6 @@
     android:drawable="@drawable/android"
     android:clipOrientation="horizontal"
     android:gravity="left" /&gt;
-&lt;/clip>
 </pre>
     <p>The following layout XML applies the clip drawable to a View:</p>
 <pre>
diff --git a/docs/html/guide/topics/resources/more-resources.jd b/docs/html/guide/topics/resources/more-resources.jd
index 972eab9..d37b9f8 100644
--- a/docs/html/guide/topics/resources/more-resources.jd
+++ b/docs/html/guide/topics/resources/more-resources.jd
@@ -216,27 +216,29 @@
 For example: 10px, 2in, 5sp. The following units of measure are supported by Android:</p>
 <dl>
   <dt>{@code dp}</dt>
-    <dd>Density-independent Pixels - an abstract unit that is based on the physical density of the
-screen. These units are relative to a 160 dpi (dots per inch) screen, so <em>{@code 160dp} is
-always one inch</em> regardless of the screen density. The ratio of dp-to-pixel will change with the
-screen density, but not necessarily in direct proportion. You should use these units when specifying
-view dimensions in your layout, so the UI properly scales to render at the same actual size on
-different screens. (The compiler accepts both "dip" and "dp", though "dp" is more consistent with
-"sp".)</dd>
+    <dd>Density-independent Pixels - An abstract unit that is based on the physical density of the
+screen. These units are relative to a 160 dpi (dots per inch) screen, on which 1dp is roughly equal
+to 1px. When running on a higher density screen, the number of pixels used to draw 1dp is scaled up
+by a factor appropriate for the screen's dpi. Likewise, when on a lower density screen, the number
+of pixels used for 1dp is scaled down. The ratio of dp-to-pixel will change with the screen density,
+but not necessarily in direct proportion. Using dp units (instead of px units) is a simple solution
+to making the view dimensions in your layout resize properly for different screen densities. In
+other words, it provides consistency for the real-world sizes of your UI elements across different
+devices.</dd>
   <dt>{@code sp}</dt>
-    <dd>Scale-independent Pixels - this is like the dp unit, but it is also scaled by the user's font
+    <dd>Scale-independent Pixels - This is like the dp unit, but it is also scaled by the user's font
     size preference. It is recommend you use this unit when specifying font sizes, so they will be adjusted
     for both the screen density and the user's preference.</dd>
   <dt>{@code pt}</dt>
     <dd>Points - 1/72 of an inch based on the physical size of the screen.</dd>
   <dt>{@code px}</dt>
-    <dd>Pixels - corresponds to actual pixels on the screen. This unit of measure is not recommended because
+    <dd>Pixels - Corresponds to actual pixels on the screen. This unit of measure is not recommended because
     the actual representation can vary across devices; each devices may have a different number of pixels
     per inch and may have more or fewer total pixels available on the screen.</dd>
   <dt>{@code mm}</dt>
-    <dd>Millimeters - based on the physical size of the screen.</dd>
+    <dd>Millimeters - Based on the physical size of the screen.</dd>
   <dt>{@code in}</dt>
-    <dd>Inches - based on the physical size of the screen.</dd>
+    <dd>Inches - Based on the physical size of the screen.</dd>
 </dl>
 
 <p class="note"><strong>Note:</strong> A dimension is a simple resource that is referenced
diff --git a/docs/html/resources/articles/faster-screen-orientation-change.jd b/docs/html/resources/articles/faster-screen-orientation-change.jd
index 52531bb..e7b73bf 100644
--- a/docs/html/resources/articles/faster-screen-orientation-change.jd
+++ b/docs/html/resources/articles/faster-screen-orientation-change.jd
@@ -58,7 +58,7 @@
 
 <p>When your application displays a lot of data, or data that is expensive to fetch,
 the automatic destruction/creation of the activities can be lead to a
-painful user experience. Take the example of <a href="http://code.google.com/p/apps-for-android/source/browse/trunk/Photostream/">Photostream</a>,
+painful user experience. Take the example of <a href="http://code.google.com/p/apps-for-android/source/browse/#git%2FPhotostream%2Fsrc%2Fcom%2Fgoogle%2Fandroid%2Fphotostream">Photostream</a>,
 a simple Flickr browsing application. After you launch the application and choose a Flickr account, the
 application downloads a set of 6 photos (on a T-Mobile G1) from the
 Flickr servers and displays them on screen. To improve the user
@@ -80,9 +80,9 @@
 
 <p>The Activity class has a special method called 
 {@link android.app.Activity#onRetainNonConfigurationInstance()}. This method 
-can be used to pass an arbitrary object <em>your future self</em> and Android 
+can be used to pass an arbitrary object to <em>your future self</em> and Android 
 is smart enough to call this method only when needed. In the case of Photostream, 
-the application <a href="http://code.google.com/p/apps-for-android/source/browse/trunk/Photostream/src/com/google/android/photostream/PhotostreamActivity.java#226">used this method</a>
+the application used this method
 to pass the downloaded images to the future activity on orientation change. 
 The implementation can be summarized like so:</p>
 
@@ -96,7 +96,7 @@
 
 <p>In the new activity, in <code>onCreate()</code>, all you have to do to 
 get your object back is to call {@link android.app.Activity#getLastNonConfigurationInstance()}. 
-In Photostream, <a href="http://code.google.com/p/apps-for-android/source/browse/trunk/Photostream/src/com/google/android/photostream/PhotostreamActivity.java#251">this method is invoked</a> 
+In Photostream, this method is invoked 
 and if the returned value is not null, the grid is loaded with the list of 
 photos from the previous activity:</p>
 
@@ -128,3 +128,6 @@
 <code>onRetainNonConfigurationChange()</code> should be used only to retain 
 data that is expensive to load. Otherwise, keep it simple and let Android 
 do everything.</p>
+
+<p>Also read the guide to <a href="{@docRoot}guide/topics/resources/runtime-changes.html">Handling Runtime
+Changes</a>.</p>
diff --git a/docs/html/resources/faq/troubleshooting.jd b/docs/html/resources/faq/troubleshooting.jd
index 05a7ddab..f19f5ec 100644
--- a/docs/html/resources/faq/troubleshooting.jd
+++ b/docs/html/resources/faq/troubleshooting.jd
@@ -222,8 +222,8 @@
 
 <ol>
   <li>In a terminal, change to the tools directory of the SDK.</li>
-  <li>If no emulator instance is running, start an emulator using using the command <code>emulator &</code>.</li>
-  <li>Uninstall the preinstalled app using the command <code>adb uninstall com.android.samples</code>.</li>
+  <li>If no emulator instance is running, start an emulator using using the command <code>emulator</code>.</li>
+  <li>Uninstall the preinstalled app using the command <code>adb uninstall com.example.android.apis</code>.</li>
   <li>Reinstall the app using the command <code>adb install &lt;path to the ApiDemos.apk&gt;</code>. If you are 
   working in Eclipse/ADT, you can just compile and run the app in the normal way. </li>
 </ol>
diff --git a/docs/html/resources/tutorials/notepad/notepad-ex2.jd b/docs/html/resources/tutorials/notepad/notepad-ex2.jd
index 7e3288f1..ed06778 100644
--- a/docs/html/resources/tutorials/notepad/notepad-ex2.jd
+++ b/docs/html/resources/tutorials/notepad/notepad-ex2.jd
@@ -87,8 +87,7 @@
     menu callback used for the options menu. Here, we add just one line, which will add a menu item
     to delete a note. Call <code>menu.add()</code> like so:
       <pre>
-public void onCreateContextMenu(Menu menu, View v,
-        ContextMenu.ContextMenuInfo menuInfo) {
+public void onCreateContextMenu(Menu menu, View v, ContextMenuInfo menuInfo) {
     super.onCreateContextMenu(menu, v, menuInfo);
     menu.add(0, DELETE_ID, 0, R.string.menu_delete);
 }</pre>
diff --git a/docs/html/resources/tutorials/opengl/opengl-es10.jd b/docs/html/resources/tutorials/opengl/opengl-es10.jd
index 3570766..2b44620 100644
--- a/docs/html/resources/tutorials/opengl/opengl-es10.jd
+++ b/docs/html/resources/tutorials/opengl/opengl-es10.jd
@@ -58,7 +58,7 @@
 needs. For more information, see
 <a href="{@docRoot}guide/topics/graphics/opengl.html#choosing-version">Choosing an OpenGL API
 Version</a>. If you would prefer to use OpenGL ES 2.0, see the <a
-href="{@docRoot}resources/tutorials/opengl/opengl-es20.jd">OpenGL ES 2.0 tutorial</a>.</p>
+href="{@docRoot}resources/tutorials/opengl/opengl-es20.html">OpenGL ES 2.0 tutorial</a>.</p>
 
 <p>Before you start, you should understand how to create a basic Android application. If you do not
 know how to create an app, follow the <a href="{@docRoot}resources/tutorials/hello-world.html">Hello
diff --git a/docs/html/resources/tutorials/opengl/opengl-es20.jd b/docs/html/resources/tutorials/opengl/opengl-es20.jd
index 889dd50..dd23dbf 100644
--- a/docs/html/resources/tutorials/opengl/opengl-es20.jd
+++ b/docs/html/resources/tutorials/opengl/opengl-es20.jd
@@ -57,7 +57,7 @@
 needs. For more information, see
 <a href="{@docRoot}guide/topics/graphics/opengl.html#choosing-version">Choosing an OpenGL API
 Version</a>. If you would prefer to use OpenGL ES 1.0, see the <a
-href="{@docRoot}resources/tutorials/opengl/opengl-es10.jd">OpenGL ES 1.0 tutorial</a>.</p>
+href="{@docRoot}resources/tutorials/opengl/opengl-es10.html">OpenGL ES 1.0 tutorial</a>.</p>
 
 <p>Before you start, you should understand how to create a basic Android application. If you do not
 know how to create an app, follow the <a href="{@docRoot}resources/tutorials/hello-world.html">Hello
diff --git a/docs/html/resources/tutorials/views/hello-mapview.jd b/docs/html/resources/tutorials/views/hello-mapview.jd
index 836d22c..ac5e826 100644
--- a/docs/html/resources/tutorials/views/hello-mapview.jd
+++ b/docs/html/resources/tutorials/views/hello-mapview.jd
@@ -255,7 +255,7 @@
 <pre>
 List&lt;Overlay> mapOverlays = mapView.getOverlays();
 Drawable drawable = this.getResources().getDrawable(R.drawable.androidmarker);
-HelloItemizedOverlay itemizedoverlay = new HelloItemizedOverlay(drawable);</pre>
+HelloItemizedOverlay itemizedoverlay = new HelloItemizedOverlay(drawable, this);</pre>
 
     <p>All overlay elements on a map are held by the {@code MapView}, so when you want to add some,
     you have to get a list from the <code>getOverlays()</code> method. Then instantiate the {@link
diff --git a/docs/html/sdk/download.jd b/docs/html/sdk/download.jd
index 44fe5e4..af25609 100644
--- a/docs/html/sdk/download.jd
+++ b/docs/html/sdk/download.jd
@@ -52,7 +52,7 @@
   <script language="javascript">
     var loc = window.location.href;
     if (loc.indexOf('?v=') != -1) {
-      var filename = loc.substring(loc.indexOf('=')+1,loc.length);
+      var filename = loc.substring(loc.indexOf('=')+1,loc.length).replace(/</g,"&lt;").replace(/>/g,"&gt;");
       document.write("File: " + filename);
     }
   </script>
diff --git a/graphics/java/android/graphics/BitmapFactory.java b/graphics/java/android/graphics/BitmapFactory.java
index 8d17561..bff2a76 100644
--- a/graphics/java/android/graphics/BitmapFactory.java
+++ b/graphics/java/android/graphics/BitmapFactory.java
@@ -518,15 +518,16 @@
         byte[] np = bm.getNinePatchChunk();
         final boolean isNinePatch = np != null && NinePatch.isNinePatchChunk(np);
         if (opts.inScaled || isNinePatch) {
-            float scale = targetDensity / (float)density;
-            // TODO: This is very inefficient and should be done in native by Skia
-            final Bitmap oldBitmap = bm;
-            bm = Bitmap.createScaledBitmap(oldBitmap, (int) (bm.getWidth() * scale + 0.5f),
-                    (int) (bm.getHeight() * scale + 0.5f), true);
-            oldBitmap.recycle();
+            float scale = targetDensity / (float) density;
+            if (scale != 1.0f) {
+                final Bitmap oldBitmap = bm;
+                bm = Bitmap.createScaledBitmap(oldBitmap, (int) (bm.getWidth() * scale + 0.5f),
+                        (int) (bm.getHeight() * scale + 0.5f), true);
+                if (bm != oldBitmap) oldBitmap.recycle();
+            }
 
             if (isNinePatch) {
-                np = nativeScaleNinePatch(np, scale, outPadding);
+                if (scale != 1.0f) np = nativeScaleNinePatch(np, scale, outPadding);
                 bm.setNinePatchChunk(np);
             }
             bm.setDensity(targetDensity);
diff --git a/include/binder/BinderService.h b/include/binder/BinderService.h
index 2316fef..ca594d3 100644
--- a/include/binder/BinderService.h
+++ b/include/binder/BinderService.h
@@ -34,15 +34,15 @@
 class BinderService
 {
 public:
-    static status_t publish() {
+    static status_t publish(bool allowIsolated = false) {
         sp<IServiceManager> sm(defaultServiceManager());
-        return sm->addService(String16(SERVICE::getServiceName()), new SERVICE());
+        return sm->addService(String16(SERVICE::getServiceName()), new SERVICE(), allowIsolated);
     }
 
-    static void publishAndJoinThreadPool() {
+    static void publishAndJoinThreadPool(bool allowIsolated = false) {
         sp<ProcessState> proc(ProcessState::self());
         sp<IServiceManager> sm(defaultServiceManager());
-        sm->addService(String16(SERVICE::getServiceName()), new SERVICE());
+        sm->addService(String16(SERVICE::getServiceName()), new SERVICE(), allowIsolated);
         ProcessState::self()->startThreadPool();
         IPCThreadState::self()->joinThreadPool();
     }
diff --git a/include/binder/IServiceManager.h b/include/binder/IServiceManager.h
index 24e9e99..2c297d6 100644
--- a/include/binder/IServiceManager.h
+++ b/include/binder/IServiceManager.h
@@ -47,7 +47,8 @@
      * Register a service.
      */
     virtual status_t            addService( const String16& name,
-                                            const sp<IBinder>& service) = 0;
+                                            const sp<IBinder>& service,
+                                            bool allowIsolated = false) = 0;
 
     /**
      * Return list of all existing services.
diff --git a/include/gui/BufferQueue.h b/include/gui/BufferQueue.h
new file mode 100644
index 0000000..991a181
--- /dev/null
+++ b/include/gui/BufferQueue.h
@@ -0,0 +1,343 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_GUI_BUFFERQUEUE_H
+#define ANDROID_GUI_BUFFERQUEUE_H
+
+#include <EGL/egl.h>
+
+#include <gui/ISurfaceTexture.h>
+
+#include <surfaceflinger/IGraphicBufferAlloc.h>
+#include <ui/GraphicBuffer.h>
+
+#include <utils/String8.h>
+#include <utils/Vector.h>
+#include <utils/threads.h>
+
+namespace android {
+// ----------------------------------------------------------------------------
+
+class BufferQueue : public BnSurfaceTexture {
+public:
+    enum { MIN_UNDEQUEUED_BUFFERS = 2 };
+    enum {
+        MIN_ASYNC_BUFFER_SLOTS = MIN_UNDEQUEUED_BUFFERS + 1,
+        MIN_SYNC_BUFFER_SLOTS  = MIN_UNDEQUEUED_BUFFERS
+    };
+    enum { NUM_BUFFER_SLOTS = 32 };
+    enum { NO_CONNECTED_API = 0 };
+
+    struct FrameAvailableListener : public virtual RefBase {
+        // onFrameAvailable() is called from queueBuffer() each time an
+        // additional frame becomes available for consumption. This means that
+        // frames that are queued while in asynchronous mode only trigger the
+        // callback if no previous frames are pending. Frames queued while in
+        // synchronous mode always trigger the callback.
+        //
+        // This is called without any lock held and can be called concurrently
+        // by multiple threads.
+        virtual void onFrameAvailable() = 0;
+    };
+
+    // BufferQueue manages a pool of gralloc memory slots to be used
+    // by producers and consumers.
+    // allowSynchronousMode specifies whether or not synchronous mode can be
+    // enabled.
+    BufferQueue(bool allowSynchronousMode = true);
+    virtual ~BufferQueue();
+
+    // setBufferCount updates the number of available buffer slots.  After
+    // calling this all buffer slots are both unallocated and owned by the
+    // BufferQueue object (i.e. they are not owned by the client).
+    virtual status_t setBufferCount(int bufferCount);
+
+    virtual status_t requestBuffer(int slot, sp<GraphicBuffer>* buf);
+
+    // dequeueBuffer gets the next buffer slot index for the client to use. If a
+    // buffer slot is available then that slot index is written to the location
+    // pointed to by the buf argument and a status of OK is returned.  If no
+    // slot is available then a status of -EBUSY is returned and buf is
+    // unmodified.
+    // The width and height parameters must be no greater than the minimum of
+    // GL_MAX_VIEWPORT_DIMS and GL_MAX_TEXTURE_SIZE (see: glGetIntegerv).
+    // An error due to invalid dimensions might not be reported until
+    // updateTexImage() is called.
+    virtual status_t dequeueBuffer(int *buf, uint32_t width, uint32_t height,
+            uint32_t format, uint32_t usage);
+
+    // queueBuffer returns a filled buffer to the BufferQueue. In addition, a
+    // timestamp must be provided for the buffer. The timestamp is in
+    // nanoseconds, and must be monotonically increasing. Its other semantics
+    // (zero point, etc) are client-dependent and should be documented by the
+    // client.
+    virtual status_t queueBuffer(int buf, int64_t timestamp,
+            uint32_t* outWidth, uint32_t* outHeight, uint32_t* outTransform);
+    virtual void cancelBuffer(int buf);
+    virtual status_t setCrop(const Rect& reg);
+    virtual status_t setTransform(uint32_t transform);
+    virtual status_t setScalingMode(int mode);
+
+    // setSynchronousMode set whether dequeueBuffer is synchronous or
+    // asynchronous. In synchronous mode, dequeueBuffer blocks until
+    // a buffer is available, the currently bound buffer can be dequeued and
+    // queued buffers will be retired in order.
+    // The default mode is asynchronous.
+    virtual status_t setSynchronousMode(bool enabled);
+
+    // connect attempts to connect a producer client API to the BufferQueue.
+    // This must be called before any other ISurfaceTexture methods are called
+    // except for getAllocator.
+    //
+    // This method will fail if the connect was previously called on the
+    // BufferQueue and no corresponding disconnect call was made.
+    virtual status_t connect(int api,
+            uint32_t* outWidth, uint32_t* outHeight, uint32_t* outTransform);
+
+    // disconnect attempts to disconnect a producer client API from the
+    // BufferQueue. Calling this method will cause any subsequent calls to other
+    // ISurfaceTexture methods to fail except for getAllocator and connect.
+    // Successfully calling connect after this will allow the other methods to
+    // succeed again.
+    //
+    // This method will fail if the the BufferQueue is not currently
+    // connected to the specified client API.
+    virtual status_t disconnect(int api);
+
+protected:
+
+    // freeBufferLocked frees the resources (both GraphicBuffer and EGLImage)
+    // for the given slot.
+    void freeBufferLocked(int index);
+
+    // freeAllBuffersLocked frees the resources (both GraphicBuffer and
+    // EGLImage) for all slots.
+    void freeAllBuffersLocked();
+
+    // freeAllBuffersExceptHeadLocked frees the resources (both GraphicBuffer
+    // and EGLImage) for all slots except the head of mQueue
+    void freeAllBuffersExceptHeadLocked();
+
+    // drainQueueLocked drains the buffer queue if we're in synchronous mode
+    // returns immediately otherwise. It returns NO_INIT if the BufferQueue
+    // became abandoned or disconnected during this call.
+    status_t drainQueueLocked();
+
+    // drainQueueAndFreeBuffersLocked drains the buffer queue if we're in
+    // synchronous mode and free all buffers. In asynchronous mode, all buffers
+    // are freed except the current buffer.
+    status_t drainQueueAndFreeBuffersLocked();
+
+    status_t setBufferCountServerLocked(int bufferCount);
+
+    enum { INVALID_BUFFER_SLOT = -1 };
+
+    struct BufferSlot {
+
+        BufferSlot()
+        : mEglImage(EGL_NO_IMAGE_KHR),
+          mEglDisplay(EGL_NO_DISPLAY),
+          mBufferState(BufferSlot::FREE),
+          mRequestBufferCalled(false),
+          mTransform(0),
+          mScalingMode(NATIVE_WINDOW_SCALING_MODE_FREEZE),
+          mTimestamp(0),
+          mFrameNumber(0),
+          mFence(EGL_NO_SYNC_KHR) {
+            mCrop.makeInvalid();
+        }
+
+        // mGraphicBuffer points to the buffer allocated for this slot or is NULL
+        // if no buffer has been allocated.
+        sp<GraphicBuffer> mGraphicBuffer;
+
+        // mEglImage is the EGLImage created from mGraphicBuffer.
+        EGLImageKHR mEglImage;
+
+        // mEglDisplay is the EGLDisplay used to create mEglImage.
+        EGLDisplay mEglDisplay;
+
+        // 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
+            // subsequently 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;
+
+        // 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;
+
+        // 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;
+
+        // mScalingMode is the current scaling mode for this buffer slot. This
+        // gets set to mNextScalingMode each time queueBuffer gets called for
+        // this slot.
+        uint32_t mScalingMode;
+
+        // mTimestamp is the current timestamp for this buffer slot. This gets
+        // to set by queueBuffer each time this slot is queued.
+        int64_t mTimestamp;
+
+        // mFrameNumber is the number of the queued frame for this slot.
+        uint64_t mFrameNumber;
+
+        // mFence is the EGL sync object that must signal before the buffer
+        // associated with this buffer slot may be dequeued. It is initialized
+        // to EGL_NO_SYNC_KHR when the buffer is created and (optionally, based
+        // on a compile-time option) set to a new sync object in updateTexImage.
+        EGLSyncKHR mFence;
+    };
+
+    // mSlots is the array of buffer slots that must be mirrored on the client
+    // side. This allows buffer ownership to be transferred between the client
+    // and server without sending a GraphicBuffer over binder. The entire array
+    // is initialized to NULL at construction time, and buffers are allocated
+    // for a slot when requestBuffer is called with that slot's index.
+    BufferSlot mSlots[NUM_BUFFER_SLOTS];
+
+
+    // mDefaultWidth holds the default width of allocated buffers. It is used
+    // in requestBuffers() if a width and height of zero is specified.
+    uint32_t mDefaultWidth;
+
+    // mDefaultHeight holds the default height of allocated buffers. It is used
+    // in requestBuffers() if a width and height of zero is specified.
+    uint32_t mDefaultHeight;
+
+    // mPixelFormat holds the pixel format of allocated buffers. It is used
+    // in requestBuffers() if a format of zero is specified.
+    uint32_t mPixelFormat;
+
+    // mBufferCount is the number of buffer slots that the client and server
+    // must maintain. It defaults to MIN_ASYNC_BUFFER_SLOTS and can be changed
+    // by calling setBufferCount or setBufferCountServer
+    int mBufferCount;
+
+    // mClientBufferCount is the number of buffer slots requested by the client.
+    // The default is zero, which means the client doesn't care how many buffers
+    // there is.
+    int mClientBufferCount;
+
+    // mServerBufferCount buffer count requested by the server-side
+    int mServerBufferCount;
+
+    // mCurrentTexture is the buffer slot index of the buffer that is currently
+    // bound to the OpenGL texture. It is initialized to INVALID_BUFFER_SLOT,
+    // indicating that no buffer slot is currently bound to the texture. Note,
+    // however, that a value of INVALID_BUFFER_SLOT does not necessarily mean
+    // that no buffer is bound to the texture. A call to setBufferCount will
+    // reset mCurrentTexture to INVALID_BUFFER_SLOT.
+    int mCurrentTexture;
+
+    // mNextCrop is the crop rectangle that will be used for the next buffer
+    // that gets queued. It is set by calling setCrop.
+    Rect mNextCrop;
+
+    // mNextTransform is the transform identifier that will be used for the next
+    // buffer that gets queued. It is set by calling setTransform.
+    uint32_t mNextTransform;
+
+    // mNextScalingMode is the scaling mode that will be used for the next
+    // buffers that get queued. It is set by calling setScalingMode.
+    int mNextScalingMode;
+
+    // mGraphicBufferAlloc is the connection to SurfaceFlinger that is used to
+    // allocate new GraphicBuffer objects.
+    sp<IGraphicBufferAlloc> mGraphicBufferAlloc;
+
+    // mFrameAvailableListener is the listener object that will be called when a
+    // new frame becomes available. If it is not NULL it will be called from
+    // queueBuffer.
+    sp<FrameAvailableListener> mFrameAvailableListener;
+
+    // mSynchronousMode whether we're in synchronous mode or not
+    bool mSynchronousMode;
+
+    // mAllowSynchronousMode whether we allow synchronous mode or not
+    const bool mAllowSynchronousMode;
+
+    // mConnectedApi indicates the API that is currently connected to this
+    // BufferQueue.  It defaults to NO_CONNECTED_API (= 0), and gets updated
+    // by the connect and disconnect methods.
+    int mConnectedApi;
+
+    // mDequeueCondition condition used for dequeueBuffer in synchronous mode
+    mutable Condition mDequeueCondition;
+
+    // mQueue is a FIFO of queued buffers used in synchronous mode
+    typedef Vector<int> Fifo;
+    Fifo mQueue;
+
+    // mAbandoned indicates that the BufferQueue will no longer be used to
+    // consume images buffers pushed to it using the ISurfaceTexture interface.
+    // It is initialized to false, and set to true in the abandon method.  A
+    // BufferQueue that has been abandoned will return the NO_INIT error from
+    // all ISurfaceTexture methods capable of returning an error.
+    bool mAbandoned;
+
+    // mName is a string used to identify the BufferQueue in log messages.
+    // It is set by the setName method.
+    String8 mName;
+
+    // mMutex is the mutex used to prevent concurrent access to the member
+    // variables of BufferQueue objects. It must be locked whenever the
+    // member variables are accessed.
+    mutable Mutex mMutex;
+
+    // mFrameCounter is the free running counter, incremented for every buffer queued
+    // with the surface Texture.
+    uint64_t mFrameCounter;
+};
+
+// ----------------------------------------------------------------------------
+}; // namespace android
+
+#endif // ANDROID_GUI_BUFFERQUEUE_H
diff --git a/include/gui/SurfaceTexture.h b/include/gui/SurfaceTexture.h
index a8c7672..4318f0f 100644
--- a/include/gui/SurfaceTexture.h
+++ b/include/gui/SurfaceTexture.h
@@ -23,6 +23,7 @@
 #include <GLES2/gl2ext.h>
 
 #include <gui/ISurfaceTexture.h>
+#include <gui/BufferQueue.h>
 
 #include <ui/GraphicBuffer.h>
 
@@ -35,30 +36,11 @@
 namespace android {
 // ----------------------------------------------------------------------------
 
-class IGraphicBufferAlloc;
+
 class String8;
 
-class SurfaceTexture : public BnSurfaceTexture {
+class SurfaceTexture : public BufferQueue {
 public:
-    enum { MIN_UNDEQUEUED_BUFFERS = 2 };
-    enum {
-        MIN_ASYNC_BUFFER_SLOTS = MIN_UNDEQUEUED_BUFFERS + 1,
-        MIN_SYNC_BUFFER_SLOTS  = MIN_UNDEQUEUED_BUFFERS
-    };
-    enum { NUM_BUFFER_SLOTS = 32 };
-    enum { NO_CONNECTED_API = 0 };
-
-    struct FrameAvailableListener : public virtual RefBase {
-        // onFrameAvailable() is called from queueBuffer() each time an
-        // additional frame becomes available for consumption. This means that
-        // frames that are queued while in asynchronous mode only trigger the
-        // callback if no previous frames are pending. Frames queued while in
-        // synchronous mode always trigger the callback.
-        //
-        // This is called without any lock held and can be called concurrently
-        // by multiple threads.
-        virtual void onFrameAvailable() = 0;
-    };
 
     // SurfaceTexture constructs a new SurfaceTexture object. tex indicates the
     // name of the OpenGL ES texture to which images are to be streamed. This
@@ -73,65 +55,8 @@
 
     virtual ~SurfaceTexture();
 
-    // setBufferCount updates the number of available buffer slots.  After
-    // calling this all buffer slots are both unallocated and owned by the
-    // SurfaceTexture object (i.e. they are not owned by the client).
-    virtual status_t setBufferCount(int bufferCount);
-
-    virtual status_t requestBuffer(int slot, sp<GraphicBuffer>* buf);
-
-    // dequeueBuffer gets the next buffer slot index for the client to use. If a
-    // buffer slot is available then that slot index is written to the location
-    // pointed to by the buf argument and a status of OK is returned.  If no
-    // slot is available then a status of -EBUSY is returned and buf is
-    // unmodified.
-    // The width and height parameters must be no greater than the minimum of
-    // GL_MAX_VIEWPORT_DIMS and GL_MAX_TEXTURE_SIZE (see: glGetIntegerv).
-    // An error due to invalid dimensions might not be reported until
-    // updateTexImage() is called.
-    virtual status_t dequeueBuffer(int *buf, uint32_t width, uint32_t height,
-            uint32_t format, uint32_t usage);
-
-    // queueBuffer returns a filled buffer to the SurfaceTexture. In addition, a
-    // timestamp must be provided for the buffer. The timestamp is in
-    // nanoseconds, and must be monotonically increasing. Its other semantics
-    // (zero point, etc) are client-dependent and should be documented by the
-    // client.
-    virtual status_t queueBuffer(int buf, int64_t timestamp,
-            uint32_t* outWidth, uint32_t* outHeight, uint32_t* outTransform);
-    virtual void cancelBuffer(int buf);
-    virtual status_t setCrop(const Rect& reg);
-    virtual status_t setTransform(uint32_t transform);
-    virtual status_t setScalingMode(int mode);
-
     virtual int query(int what, int* value);
 
-    // setSynchronousMode set whether dequeueBuffer is synchronous or
-    // asynchronous. In synchronous mode, dequeueBuffer blocks until
-    // a buffer is available, the currently bound buffer can be dequeued and
-    // queued buffers will be retired in order.
-    // The default mode is asynchronous.
-    virtual status_t setSynchronousMode(bool enabled);
-
-    // connect attempts to connect a client API to the SurfaceTexture.  This
-    // must be called before any other ISurfaceTexture methods are called except
-    // for getAllocator.
-    //
-    // This method will fail if the connect was previously called on the
-    // SurfaceTexture and no corresponding disconnect call was made.
-    virtual status_t connect(int api,
-            uint32_t* outWidth, uint32_t* outHeight, uint32_t* outTransform);
-
-    // disconnect attempts to disconnect a client API from the SurfaceTexture.
-    // Calling this method will cause any subsequent calls to other
-    // ISurfaceTexture methods to fail except for getAllocator and connect.
-    // Successfully calling connect after this will allow the other methods to
-    // succeed again.
-    //
-    // This method will fail if the the SurfaceTexture is not currently
-    // connected to the specified client API.
-    virtual status_t disconnect(int api);
-
     // updateTexImage sets the image contents of the target texture to that of
     // the most recently queued buffer.
     //
@@ -233,28 +158,6 @@
 
 protected:
 
-    // freeBufferLocked frees the resources (both GraphicBuffer and EGLImage)
-    // for the given slot.
-    void freeBufferLocked(int index);
-
-    // freeAllBuffersLocked frees the resources (both GraphicBuffer and
-    // EGLImage) for all slots.
-    void freeAllBuffersLocked();
-
-    // freeAllBuffersExceptHeadLocked frees the resources (both GraphicBuffer
-    // and EGLImage) for all slots except the head of mQueue
-    void freeAllBuffersExceptHeadLocked();
-
-    // drainQueueLocked drains the buffer queue if we're in synchronous mode
-    // returns immediately otherwise. return NO_INIT if SurfaceTexture
-    // became abandoned or disconnected during this call.
-    status_t drainQueueLocked();
-
-    // drainQueueAndFreeBuffersLocked drains the buffer queue if we're in
-    // synchronous mode and free all buffers. In asynchronous mode, all buffers
-    // are freed except the current buffer.
-    status_t drainQueueAndFreeBuffersLocked();
-
     static bool isExternalFormat(uint32_t format);
 
 private:
@@ -263,146 +166,11 @@
     EGLImageKHR createImage(EGLDisplay dpy,
             const sp<GraphicBuffer>& graphicBuffer);
 
-    status_t setBufferCountServerLocked(int bufferCount);
-
     // computeCurrentTransformMatrix computes the transform matrix for the
     // current texture.  It uses mCurrentTransform and the current GraphicBuffer
     // to compute this matrix and stores it in mCurrentTransformMatrix.
     void computeCurrentTransformMatrix();
 
-    enum { INVALID_BUFFER_SLOT = -1 };
-
-    struct BufferSlot {
-
-        BufferSlot()
-            : mEglImage(EGL_NO_IMAGE_KHR),
-              mEglDisplay(EGL_NO_DISPLAY),
-              mBufferState(BufferSlot::FREE),
-              mRequestBufferCalled(false),
-              mTransform(0),
-              mScalingMode(NATIVE_WINDOW_SCALING_MODE_FREEZE),
-              mTimestamp(0),
-              mFrameNumber(0),
-              mFence(EGL_NO_SYNC_KHR) {
-            mCrop.makeInvalid();
-        }
-
-        // mGraphicBuffer points to the buffer allocated for this slot or is NULL
-        // if no buffer has been allocated.
-        sp<GraphicBuffer> mGraphicBuffer;
-
-        // mEglImage is the EGLImage created from mGraphicBuffer.
-        EGLImageKHR mEglImage;
-
-        // mEglDisplay is the EGLDisplay used to create mEglImage.
-        EGLDisplay mEglDisplay;
-
-        // 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
-            // subsequently 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;
-
-        // 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;
-
-        // 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;
-
-        // mScalingMode is the current scaling mode for this buffer slot. This
-        // gets set to mNextScalingMode each time queueBuffer gets called for
-        // this slot.
-        uint32_t mScalingMode;
-
-        // mTimestamp is the current timestamp for this buffer slot. This gets
-        // to set by queueBuffer each time this slot is queued.
-        int64_t mTimestamp;
-
-        // mFrameNumber is the number of the queued frame for this slot.
-        uint64_t mFrameNumber;
-
-        // mFence is the EGL sync object that must signal before the buffer
-        // associated with this buffer slot may be dequeued. It is initialized
-        // to EGL_NO_SYNC_KHR when the buffer is created and (optionally, based
-        // on a compile-time option) set to a new sync object in updateTexImage.
-        EGLSyncKHR mFence;
-    };
-
-    // mSlots is the array of buffer slots that must be mirrored on the client
-    // side. This allows buffer ownership to be transferred between the client
-    // and server without sending a GraphicBuffer over binder. The entire array
-    // is initialized to NULL at construction time, and buffers are allocated
-    // for a slot when requestBuffer is called with that slot's index.
-    BufferSlot mSlots[NUM_BUFFER_SLOTS];
-
-    // mDefaultWidth holds the default width of allocated buffers. It is used
-    // in requestBuffers() if a width and height of zero is specified.
-    uint32_t mDefaultWidth;
-
-    // mDefaultHeight holds the default height of allocated buffers. It is used
-    // in requestBuffers() if a width and height of zero is specified.
-    uint32_t mDefaultHeight;
-
-    // mPixelFormat holds the pixel format of allocated buffers. It is used
-    // in requestBuffers() if a format of zero is specified.
-    uint32_t mPixelFormat;
-
-    // mBufferCount is the number of buffer slots that the client and server
-    // must maintain. It defaults to MIN_ASYNC_BUFFER_SLOTS and can be changed
-    // by calling setBufferCount or setBufferCountServer
-    int mBufferCount;
-
-    // mClientBufferCount is the number of buffer slots requested by the client.
-    // The default is zero, which means the client doesn't care how many buffers
-    // there is.
-    int mClientBufferCount;
-
-    // mServerBufferCount buffer count requested by the server-side
-    int mServerBufferCount;
-
-    // mCurrentTexture is the buffer slot index of the buffer that is currently
-    // bound to the OpenGL texture. It is initialized to INVALID_BUFFER_SLOT,
-    // indicating that no buffer slot is currently bound to the texture. Note,
-    // however, that a value of INVALID_BUFFER_SLOT does not necessarily mean
-    // that no buffer is bound to the texture. A call to setBufferCount will
-    // reset mCurrentTexture to INVALID_BUFFER_SLOT.
-    int mCurrentTexture;
-
     // mCurrentTextureBuf is the graphic buffer of the current texture. It's
     // possible that this buffer is not associated with any buffer slot, so we
     // must track it separately in order to support the getCurrentBuffer method.
@@ -429,72 +197,17 @@
     // gets set each time updateTexImage is called.
     int64_t mCurrentTimestamp;
 
-    // mNextCrop is the crop rectangle that will be used for the next buffer
-    // that gets queued. It is set by calling setCrop.
-    Rect mNextCrop;
-
-    // mNextTransform is the transform identifier that will be used for the next
-    // buffer that gets queued. It is set by calling setTransform.
-    uint32_t mNextTransform;
-
-    // mNextScalingMode is the scaling mode that will be used for the next
-    // buffers that get queued. It is set by calling setScalingMode.
-    int mNextScalingMode;
-
     // mTexName is the name of the OpenGL texture to which streamed images will
     // be bound when updateTexImage is called. It is set at construction time
     // changed with a call to setTexName.
     const GLuint mTexName;
 
-    // mGraphicBufferAlloc is the connection to SurfaceFlinger that is used to
-    // allocate new GraphicBuffer objects.
-    sp<IGraphicBufferAlloc> mGraphicBufferAlloc;
-
-    // mFrameAvailableListener is the listener object that will be called when a
-    // new frame becomes available. If it is not NULL it will be called from
-    // queueBuffer.
-    sp<FrameAvailableListener> mFrameAvailableListener;
-
-    // mSynchronousMode whether we're in synchronous mode or not
-    bool mSynchronousMode;
-
-    // mAllowSynchronousMode whether we allow synchronous mode or not
-    const bool mAllowSynchronousMode;
-
-    // mConnectedApi indicates the API that is currently connected to this
-    // SurfaceTexture.  It defaults to NO_CONNECTED_API (= 0), and gets updated
-    // by the connect and disconnect methods.
-    int mConnectedApi;
-
-    // mDequeueCondition condition used for dequeueBuffer in synchronous mode
-    mutable Condition mDequeueCondition;
-
-    // mQueue is a FIFO of queued buffers used in synchronous mode
-    typedef Vector<int> Fifo;
-    Fifo mQueue;
-
-    // mAbandoned indicates that the SurfaceTexture will no longer be used to
-    // consume images buffers pushed to it using the ISurfaceTexture interface.
-    // It is initialized to false, and set to true in the abandon method.  A
-    // SurfaceTexture that has been abandoned will return the NO_INIT error from
-    // all ISurfaceTexture methods capable of returning an error.
-    bool mAbandoned;
-
-    // mName is a string used to identify the SurfaceTexture in log messages.
-    // It is set by the setName method.
-    String8 mName;
-
     // mUseFenceSync indicates whether creation of the EGL_KHR_fence_sync
     // extension should be used to prevent buffers from being dequeued before
     // it's safe for them to be written. It gets set at construction time and
     // never changes.
     const bool mUseFenceSync;
 
-    // mMutex is the mutex used to prevent concurrent access to the member
-    // variables of SurfaceTexture objects. It must be locked whenever the
-    // member variables are accessed.
-    mutable Mutex mMutex;
-
     // mTexTarget is the GL texture target with which the GL texture object is
     // associated.  It is set in the constructor and never changed.  It is
     // almost always GL_TEXTURE_EXTERNAL_OES except for one use case in Android
@@ -504,11 +217,6 @@
     // browser's tile cache exceeds.
     const GLenum mTexTarget;
 
-    // mFrameCounter is the free running counter, incremented for every buffer queued
-    // with the surface Texture.
-    uint64_t mFrameCounter;
-
-
 };
 
 // ----------------------------------------------------------------------------
diff --git a/libs/binder/IServiceManager.cpp b/libs/binder/IServiceManager.cpp
index 33b305d..1750640 100644
--- a/libs/binder/IServiceManager.cpp
+++ b/libs/binder/IServiceManager.cpp
@@ -151,12 +151,14 @@
         return reply.readStrongBinder();
     }
 
-    virtual status_t addService(const String16& name, const sp<IBinder>& service)
+    virtual status_t addService(const String16& name, const sp<IBinder>& service,
+            bool allowIsolated)
     {
         Parcel data, reply;
         data.writeInterfaceToken(IServiceManager::getInterfaceDescriptor());
         data.writeString16(name);
         data.writeStrongBinder(service);
+        data.writeInt32(allowIsolated ? 1 : 0);
         status_t err = remote()->transact(ADD_SERVICE_TRANSACTION, data, &reply);
         return err == NO_ERROR ? reply.readExceptionCode() : err;
     }
diff --git a/libs/gui/Android.mk b/libs/gui/Android.mk
index b6f5b9e..2f4ac62 100644
--- a/libs/gui/Android.mk
+++ b/libs/gui/Android.mk
@@ -3,6 +3,7 @@
 
 LOCAL_SRC_FILES:= \
 	BitTube.cpp \
+	BufferQueue.cpp \
 	DisplayEventReceiver.cpp \
 	IDisplayEventConnection.cpp \
 	ISensorEventConnection.cpp \
diff --git a/libs/gui/BufferQueue.cpp b/libs/gui/BufferQueue.cpp
new file mode 100644
index 0000000..c7e2c0f
--- /dev/null
+++ b/libs/gui/BufferQueue.cpp
@@ -0,0 +1,722 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "BufferQueue"
+
+#define GL_GLEXT_PROTOTYPES
+#define EGL_EGLEXT_PROTOTYPES
+
+#include <EGL/egl.h>
+#include <EGL/eglext.h>
+
+#include <gui/BufferQueue.h>
+#include <private/gui/ComposerService.h>
+#include <surfaceflinger/ISurfaceComposer.h>
+
+#include <utils/Log.h>
+
+// This compile option causes SurfaceTexture to return the buffer that is currently
+// attached to the GL texture from dequeueBuffer when no other buffers are
+// available.  It requires the drivers (Gralloc, GL, OMX IL, and Camera) to do
+// implicit cross-process synchronization to prevent the buffer from being
+// written to before the buffer has (a) been detached from the GL texture and
+// (b) all GL reads from the buffer have completed.
+#ifdef ALLOW_DEQUEUE_CURRENT_BUFFER
+#define FLAG_ALLOW_DEQUEUE_CURRENT_BUFFER    true
+#warning "ALLOW_DEQUEUE_CURRENT_BUFFER enabled"
+#else
+#define FLAG_ALLOW_DEQUEUE_CURRENT_BUFFER    false
+#endif
+
+// Macros for including the BufferQueue name in log messages
+#define ST_LOGV(x, ...) ALOGV("[%s] "x, mName.string(), ##__VA_ARGS__)
+#define ST_LOGD(x, ...) ALOGD("[%s] "x, mName.string(), ##__VA_ARGS__)
+#define ST_LOGI(x, ...) ALOGI("[%s] "x, mName.string(), ##__VA_ARGS__)
+#define ST_LOGW(x, ...) ALOGW("[%s] "x, mName.string(), ##__VA_ARGS__)
+#define ST_LOGE(x, ...) ALOGE("[%s] "x, mName.string(), ##__VA_ARGS__)
+
+namespace android {
+
+// Get an ID that's unique within this process.
+static int32_t createProcessUniqueId() {
+    static volatile int32_t globalCounter = 0;
+    return android_atomic_inc(&globalCounter);
+}
+
+BufferQueue::BufferQueue( bool allowSynchronousMode ) :
+    mDefaultWidth(1),
+    mDefaultHeight(1),
+    mPixelFormat(PIXEL_FORMAT_RGBA_8888),
+    mBufferCount(MIN_ASYNC_BUFFER_SLOTS),
+    mClientBufferCount(0),
+    mServerBufferCount(MIN_ASYNC_BUFFER_SLOTS),
+    mCurrentTexture(INVALID_BUFFER_SLOT),
+    mNextTransform(0),
+    mNextScalingMode(NATIVE_WINDOW_SCALING_MODE_FREEZE),
+    mSynchronousMode(false),
+    mAllowSynchronousMode(allowSynchronousMode),
+    mConnectedApi(NO_CONNECTED_API),
+    mAbandoned(false),
+    mFrameCounter(0)
+{
+    // Choose a name using the PID and a process-unique ID.
+    mName = String8::format("unnamed-%d-%d", getpid(), createProcessUniqueId());
+
+    ST_LOGV("BufferQueue");
+    sp<ISurfaceComposer> composer(ComposerService::getComposerService());
+    mGraphicBufferAlloc = composer->createGraphicBufferAlloc();
+    mNextCrop.makeInvalid();
+}
+
+BufferQueue::~BufferQueue() {
+    ST_LOGV("~BufferQueue");
+}
+
+status_t BufferQueue::setBufferCountServerLocked(int bufferCount) {
+    if (bufferCount > NUM_BUFFER_SLOTS)
+        return BAD_VALUE;
+
+    // special-case, nothing to do
+    if (bufferCount == mBufferCount)
+        return OK;
+
+    if (!mClientBufferCount &&
+        bufferCount >= mBufferCount) {
+        // easy, we just have more buffers
+        mBufferCount = bufferCount;
+        mServerBufferCount = bufferCount;
+        mDequeueCondition.signal();
+    } else {
+        // we're here because we're either
+        // - reducing the number of available buffers
+        // - or there is a client-buffer-count in effect
+
+        // less than 2 buffers is never allowed
+        if (bufferCount < 2)
+            return BAD_VALUE;
+
+        // when there is non client-buffer-count in effect, the client is not
+        // allowed to dequeue more than one buffer at a time,
+        // so the next time they dequeue a buffer, we know that they don't
+        // own one. the actual resizing will happen during the next
+        // dequeueBuffer.
+
+        mServerBufferCount = bufferCount;
+    }
+    return OK;
+}
+
+status_t BufferQueue::setBufferCount(int bufferCount) {
+    ST_LOGV("setBufferCount: count=%d", bufferCount);
+    Mutex::Autolock lock(mMutex);
+
+    if (mAbandoned) {
+        ST_LOGE("setBufferCount: SurfaceTexture has been abandoned!");
+        return NO_INIT;
+    }
+    if (bufferCount > NUM_BUFFER_SLOTS) {
+        ST_LOGE("setBufferCount: bufferCount larger than slots available");
+        return BAD_VALUE;
+    }
+
+    // Error out if the user has dequeued buffers
+    for (int i=0 ; i<mBufferCount ; i++) {
+        if (mSlots[i].mBufferState == BufferSlot::DEQUEUED) {
+            ST_LOGE("setBufferCount: client owns some buffers");
+            return -EINVAL;
+        }
+    }
+
+    const int minBufferSlots = mSynchronousMode ?
+            MIN_SYNC_BUFFER_SLOTS : MIN_ASYNC_BUFFER_SLOTS;
+    if (bufferCount == 0) {
+        mClientBufferCount = 0;
+        bufferCount = (mServerBufferCount >= minBufferSlots) ?
+                mServerBufferCount : minBufferSlots;
+        return setBufferCountServerLocked(bufferCount);
+    }
+
+    if (bufferCount < minBufferSlots) {
+        ST_LOGE("setBufferCount: requested buffer count (%d) is less than "
+                "minimum (%d)", bufferCount, minBufferSlots);
+        return BAD_VALUE;
+    }
+
+    // here we're guaranteed that the client doesn't have dequeued buffers
+    // and will release all of its buffer references.
+    freeAllBuffersLocked();
+    mBufferCount = bufferCount;
+    mClientBufferCount = bufferCount;
+    mCurrentTexture = INVALID_BUFFER_SLOT;
+    mQueue.clear();
+    mDequeueCondition.signal();
+    return OK;
+}
+
+status_t BufferQueue::requestBuffer(int slot, sp<GraphicBuffer>* buf) {
+    ST_LOGV("requestBuffer: slot=%d", slot);
+    Mutex::Autolock lock(mMutex);
+    if (mAbandoned) {
+        ST_LOGE("requestBuffer: SurfaceTexture has been abandoned!");
+        return NO_INIT;
+    }
+    if (slot < 0 || mBufferCount <= slot) {
+        ST_LOGE("requestBuffer: slot index out of range [0, %d]: %d",
+                mBufferCount, slot);
+        return BAD_VALUE;
+    }
+    mSlots[slot].mRequestBufferCalled = true;
+    *buf = mSlots[slot].mGraphicBuffer;
+    return NO_ERROR;
+}
+
+status_t BufferQueue::dequeueBuffer(int *outBuf, uint32_t w, uint32_t h,
+        uint32_t format, uint32_t usage) {
+    ST_LOGV("dequeueBuffer: w=%d h=%d fmt=%#x usage=%#x", w, h, format, usage);
+
+    if ((w && !h) || (!w && h)) {
+        ST_LOGE("dequeueBuffer: invalid size: w=%u, h=%u", w, h);
+        return BAD_VALUE;
+    }
+
+    status_t returnFlags(OK);
+    EGLDisplay dpy = EGL_NO_DISPLAY;
+    EGLSyncKHR fence = EGL_NO_SYNC_KHR;
+
+    { // Scope for the lock
+        Mutex::Autolock lock(mMutex);
+
+        int found = -1;
+        int foundSync = -1;
+        int dequeuedCount = 0;
+        bool tryAgain = true;
+        while (tryAgain) {
+            if (mAbandoned) {
+                ST_LOGE("dequeueBuffer: SurfaceTexture has been abandoned!");
+                return NO_INIT;
+            }
+
+            // We need to wait for the FIFO to drain if the number of buffer
+            // needs to change.
+            //
+            // The condition "number of buffers needs to change" is true if
+            // - the client doesn't care about how many buffers there are
+            // - AND the actual number of buffer is different from what was
+            //   set in the last setBufferCountServer()
+            //                         - OR -
+            //   setBufferCountServer() was set to a value incompatible with
+            //   the synchronization mode (for instance because the sync mode
+            //   changed since)
+            //
+            // As long as this condition is true AND the FIFO is not empty, we
+            // wait on mDequeueCondition.
+
+            const int minBufferCountNeeded = mSynchronousMode ?
+                    MIN_SYNC_BUFFER_SLOTS : MIN_ASYNC_BUFFER_SLOTS;
+
+            const bool numberOfBuffersNeedsToChange = !mClientBufferCount &&
+                    ((mServerBufferCount != mBufferCount) ||
+                            (mServerBufferCount < minBufferCountNeeded));
+
+            if (!mQueue.isEmpty() && numberOfBuffersNeedsToChange) {
+                // wait for the FIFO to drain
+                mDequeueCondition.wait(mMutex);
+                // NOTE: we continue here because we need to reevaluate our
+                // whole state (eg: we could be abandoned or disconnected)
+                continue;
+            }
+
+            if (numberOfBuffersNeedsToChange) {
+                // here we're guaranteed that mQueue is empty
+                freeAllBuffersLocked();
+                mBufferCount = mServerBufferCount;
+                if (mBufferCount < minBufferCountNeeded)
+                    mBufferCount = minBufferCountNeeded;
+                mCurrentTexture = INVALID_BUFFER_SLOT;
+                returnFlags |= ISurfaceTexture::RELEASE_ALL_BUFFERS;
+            }
+
+            // look for a free buffer to give to the client
+            found = INVALID_BUFFER_SLOT;
+            foundSync = INVALID_BUFFER_SLOT;
+            dequeuedCount = 0;
+            for (int i = 0; i < mBufferCount; i++) {
+                const int state = mSlots[i].mBufferState;
+                if (state == BufferSlot::DEQUEUED) {
+                    dequeuedCount++;
+                }
+
+                // if buffer is FREE it CANNOT be current
+                ALOGW_IF((state == BufferSlot::FREE) && (mCurrentTexture==i),
+                        "dequeueBuffer: buffer %d is both FREE and current!",
+                        i);
+
+                if (FLAG_ALLOW_DEQUEUE_CURRENT_BUFFER) {
+                    if (state == BufferSlot::FREE || i == mCurrentTexture) {
+                        foundSync = i;
+                        if (i != mCurrentTexture) {
+                            found = i;
+                            break;
+                        }
+                    }
+                } else {
+                    if (state == BufferSlot::FREE) {
+                        /* We return the oldest of the free buffers to avoid
+                         * stalling the producer if possible.  This is because
+                         * the consumer may still have pending reads of the
+                         * buffers in flight.
+                         */
+                        bool isOlder = mSlots[i].mFrameNumber <
+                                mSlots[found].mFrameNumber;
+                        if (found < 0 || isOlder) {
+                            foundSync = i;
+                            found = i;
+                        }
+                    }
+                }
+            }
+
+            // clients are not allowed to dequeue more than one buffer
+            // if they didn't set a buffer count.
+            if (!mClientBufferCount && dequeuedCount) {
+                ST_LOGE("dequeueBuffer: can't dequeue multiple buffers without "
+                        "setting the buffer count");
+                return -EINVAL;
+            }
+
+            // 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))) {
+                    ST_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 for some buffers to be consumed
+            tryAgain = mSynchronousMode && (foundSync == INVALID_BUFFER_SLOT);
+            if (tryAgain) {
+                mDequeueCondition.wait(mMutex);
+            }
+        }
+
+        if (mSynchronousMode && found == INVALID_BUFFER_SLOT) {
+            // foundSync guaranteed to be != INVALID_BUFFER_SLOT
+            found = foundSync;
+        }
+
+        if (found == INVALID_BUFFER_SLOT) {
+            // This should not happen.
+            ST_LOGE("dequeueBuffer: no available buffer slots");
+            return -EBUSY;
+        }
+
+        const int buf = found;
+        *outBuf = found;
+
+        const bool useDefaultSize = !w && !h;
+        if (useDefaultSize) {
+            // use the default size
+            w = mDefaultWidth;
+            h = mDefaultHeight;
+        }
+
+        const bool updateFormat = (format != 0);
+        if (!updateFormat) {
+            // keep the current (or default) format
+            format = mPixelFormat;
+        }
+
+        // buffer is now in DEQUEUED (but can also be current at the same time,
+        // if we're in synchronous mode)
+        mSlots[buf].mBufferState = BufferSlot::DEQUEUED;
+
+        const sp<GraphicBuffer>& buffer(mSlots[buf].mGraphicBuffer);
+        if ((buffer == NULL) ||
+            (uint32_t(buffer->width)  != w) ||
+            (uint32_t(buffer->height) != h) ||
+            (uint32_t(buffer->format) != format) ||
+            ((uint32_t(buffer->usage) & usage) != usage))
+        {
+            usage |= GraphicBuffer::USAGE_HW_TEXTURE;
+            status_t error;
+            sp<GraphicBuffer> graphicBuffer(
+                    mGraphicBufferAlloc->createGraphicBuffer(
+                            w, h, format, usage, &error));
+            if (graphicBuffer == 0) {
+                ST_LOGE("dequeueBuffer: SurfaceComposer::createGraphicBuffer "
+                        "failed");
+                return error;
+            }
+            if (updateFormat) {
+                mPixelFormat = format;
+            }
+            mSlots[buf].mGraphicBuffer = graphicBuffer;
+            mSlots[buf].mRequestBufferCalled = false;
+            mSlots[buf].mFence = EGL_NO_SYNC_KHR;
+            if (mSlots[buf].mEglImage != EGL_NO_IMAGE_KHR) {
+                eglDestroyImageKHR(mSlots[buf].mEglDisplay,
+                        mSlots[buf].mEglImage);
+                mSlots[buf].mEglImage = EGL_NO_IMAGE_KHR;
+                mSlots[buf].mEglDisplay = EGL_NO_DISPLAY;
+            }
+            if (mCurrentTexture == buf) {
+                // The current texture no longer references the buffer in this slot
+                // since we just allocated a new buffer.
+                mCurrentTexture = INVALID_BUFFER_SLOT;
+            }
+            returnFlags |= ISurfaceTexture::BUFFER_NEEDS_REALLOCATION;
+        }
+
+        dpy = mSlots[buf].mEglDisplay;
+        fence = mSlots[buf].mFence;
+        mSlots[buf].mFence = EGL_NO_SYNC_KHR;
+    }
+
+    if (fence != EGL_NO_SYNC_KHR) {
+        EGLint result = eglClientWaitSyncKHR(dpy, fence, 0, 1000000000);
+        // If something goes wrong, log the error, but return the buffer without
+        // synchronizing access to it.  It's too late at this point to abort the
+        // dequeue operation.
+        if (result == EGL_FALSE) {
+            ALOGE("dequeueBuffer: error waiting for fence: %#x", eglGetError());
+        } else if (result == EGL_TIMEOUT_EXPIRED_KHR) {
+            ALOGE("dequeueBuffer: timeout waiting for fence");
+        }
+        eglDestroySyncKHR(dpy, fence);
+    }
+
+    ST_LOGV("dequeueBuffer: returning slot=%d buf=%p flags=%#x", *outBuf,
+            mSlots[*outBuf].mGraphicBuffer->handle, returnFlags);
+
+    return returnFlags;
+}
+
+status_t BufferQueue::setSynchronousMode(bool enabled) {
+    ST_LOGV("setSynchronousMode: enabled=%d", enabled);
+    Mutex::Autolock lock(mMutex);
+
+    if (mAbandoned) {
+        ST_LOGE("setSynchronousMode: SurfaceTexture has been abandoned!");
+        return NO_INIT;
+    }
+
+    status_t err = OK;
+    if (!mAllowSynchronousMode && enabled)
+        return err;
+
+    if (!enabled) {
+        // going to asynchronous mode, drain the queue
+        err = drainQueueLocked();
+        if (err != NO_ERROR)
+            return err;
+    }
+
+    if (mSynchronousMode != enabled) {
+        // - if we're going to asynchronous mode, the queue is guaranteed to be
+        // empty here
+        // - if the client set the number of buffers, we're guaranteed that
+        // we have at least 3 (because we don't allow less)
+        mSynchronousMode = enabled;
+        mDequeueCondition.signal();
+    }
+    return err;
+}
+
+status_t BufferQueue::queueBuffer(int buf, int64_t timestamp,
+        uint32_t* outWidth, uint32_t* outHeight, uint32_t* outTransform) {
+    ST_LOGV("queueBuffer: slot=%d time=%lld", buf, timestamp);
+
+    sp<FrameAvailableListener> listener;
+
+    { // scope for the lock
+        Mutex::Autolock lock(mMutex);
+        if (mAbandoned) {
+            ST_LOGE("queueBuffer: SurfaceTexture has been abandoned!");
+            return NO_INIT;
+        }
+        if (buf < 0 || buf >= mBufferCount) {
+            ST_LOGE("queueBuffer: slot index out of range [0, %d]: %d",
+                    mBufferCount, buf);
+            return -EINVAL;
+        } else if (mSlots[buf].mBufferState != BufferSlot::DEQUEUED) {
+            ST_LOGE("queueBuffer: slot %d is not owned by the client "
+                    "(state=%d)", buf, mSlots[buf].mBufferState);
+            return -EINVAL;
+        } else if (buf == mCurrentTexture) {
+            ST_LOGE("queueBuffer: slot %d is current!", buf);
+            return -EINVAL;
+        } else if (!mSlots[buf].mRequestBufferCalled) {
+            ST_LOGE("queueBuffer: slot %d was enqueued without requesting a "
+                    "buffer", buf);
+            return -EINVAL;
+        }
+
+        if (mSynchronousMode) {
+            // In synchronous mode we queue all buffers in a FIFO.
+            mQueue.push_back(buf);
+
+            // Synchronous mode always signals that an additional frame should
+            // be consumed.
+            listener = mFrameAvailableListener;
+        } else {
+            // In asynchronous mode we only keep the most recent buffer.
+            if (mQueue.empty()) {
+                mQueue.push_back(buf);
+
+                // Asynchronous mode only signals that a frame should be
+                // consumed if no previous frame was pending. If a frame were
+                // pending then the consumer would have already been notified.
+                listener = mFrameAvailableListener;
+            } 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].mCrop = mNextCrop;
+        mSlots[buf].mTransform = mNextTransform;
+        mSlots[buf].mScalingMode = mNextScalingMode;
+        mSlots[buf].mTimestamp = timestamp;
+        mFrameCounter++;
+        mSlots[buf].mFrameNumber = mFrameCounter;
+
+        mDequeueCondition.signal();
+
+        *outWidth = mDefaultWidth;
+        *outHeight = mDefaultHeight;
+        *outTransform = 0;
+    } // scope for the lock
+
+    // call back without lock held
+    if (listener != 0) {
+        listener->onFrameAvailable();
+    }
+    return OK;
+}
+
+void BufferQueue::cancelBuffer(int buf) {
+    ST_LOGV("cancelBuffer: slot=%d", buf);
+    Mutex::Autolock lock(mMutex);
+
+    if (mAbandoned) {
+        ST_LOGW("cancelBuffer: BufferQueue has been abandoned!");
+        return;
+    }
+
+    if (buf < 0 || buf >= mBufferCount) {
+        ST_LOGE("cancelBuffer: slot index out of range [0, %d]: %d",
+                mBufferCount, buf);
+        return;
+    } else if (mSlots[buf].mBufferState != BufferSlot::DEQUEUED) {
+        ST_LOGE("cancelBuffer: slot %d is not owned by the client (state=%d)",
+                buf, mSlots[buf].mBufferState);
+        return;
+    }
+    mSlots[buf].mBufferState = BufferSlot::FREE;
+    mSlots[buf].mFrameNumber = 0;
+    mDequeueCondition.signal();
+}
+
+status_t BufferQueue::setCrop(const Rect& crop) {
+    ST_LOGV("setCrop: crop=[%d,%d,%d,%d]", crop.left, crop.top, crop.right,
+            crop.bottom);
+
+    Mutex::Autolock lock(mMutex);
+    if (mAbandoned) {
+        ST_LOGE("setCrop: BufferQueue has been abandoned!");
+        return NO_INIT;
+    }
+    mNextCrop = crop;
+    return OK;
+}
+
+status_t BufferQueue::setTransform(uint32_t transform) {
+    ST_LOGV("setTransform: xform=%#x", transform);
+    Mutex::Autolock lock(mMutex);
+    if (mAbandoned) {
+        ST_LOGE("setTransform: BufferQueue has been abandoned!");
+        return NO_INIT;
+    }
+    mNextTransform = transform;
+    return OK;
+}
+
+status_t BufferQueue::setScalingMode(int mode) {
+    ST_LOGV("setScalingMode: mode=%d", mode);
+
+    switch (mode) {
+        case NATIVE_WINDOW_SCALING_MODE_FREEZE:
+        case NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW:
+            break;
+        default:
+            ST_LOGE("unknown scaling mode: %d", mode);
+            return BAD_VALUE;
+    }
+
+    Mutex::Autolock lock(mMutex);
+    mNextScalingMode = mode;
+    return OK;
+}
+
+status_t BufferQueue::connect(int api,
+        uint32_t* outWidth, uint32_t* outHeight, uint32_t* outTransform) {
+    ST_LOGV("connect: api=%d", api);
+    Mutex::Autolock lock(mMutex);
+
+    if (mAbandoned) {
+        ST_LOGE("connect: BufferQueue has been abandoned!");
+        return NO_INIT;
+    }
+
+    int err = NO_ERROR;
+    switch (api) {
+        case NATIVE_WINDOW_API_EGL:
+        case NATIVE_WINDOW_API_CPU:
+        case NATIVE_WINDOW_API_MEDIA:
+        case NATIVE_WINDOW_API_CAMERA:
+            if (mConnectedApi != NO_CONNECTED_API) {
+                ST_LOGE("connect: already connected (cur=%d, req=%d)",
+                        mConnectedApi, api);
+                err = -EINVAL;
+            } else {
+                mConnectedApi = api;
+                *outWidth = mDefaultWidth;
+                *outHeight = mDefaultHeight;
+                *outTransform = 0;
+            }
+            break;
+        default:
+            err = -EINVAL;
+            break;
+    }
+    return err;
+}
+
+status_t BufferQueue::disconnect(int api) {
+    ST_LOGV("disconnect: api=%d", api);
+    Mutex::Autolock lock(mMutex);
+
+    if (mAbandoned) {
+        // it is not really an error to disconnect after the surface
+        // has been abandoned, it should just be a no-op.
+        return NO_ERROR;
+    }
+
+    int err = NO_ERROR;
+    switch (api) {
+        case NATIVE_WINDOW_API_EGL:
+        case NATIVE_WINDOW_API_CPU:
+        case NATIVE_WINDOW_API_MEDIA:
+        case NATIVE_WINDOW_API_CAMERA:
+            if (mConnectedApi == api) {
+                drainQueueAndFreeBuffersLocked();
+                mConnectedApi = NO_CONNECTED_API;
+                mNextCrop.makeInvalid();
+                mNextScalingMode = NATIVE_WINDOW_SCALING_MODE_FREEZE;
+                mNextTransform = 0;
+                mDequeueCondition.signal();
+            } else {
+                ST_LOGE("disconnect: connected to another api (cur=%d, req=%d)",
+                        mConnectedApi, api);
+                err = -EINVAL;
+            }
+            break;
+        default:
+            ST_LOGE("disconnect: unknown API %d", api);
+            err = -EINVAL;
+            break;
+    }
+    return err;
+}
+
+void BufferQueue::freeBufferLocked(int i) {
+    mSlots[i].mGraphicBuffer = 0;
+    mSlots[i].mBufferState = BufferSlot::FREE;
+    mSlots[i].mFrameNumber = 0;
+    if (mSlots[i].mEglImage != EGL_NO_IMAGE_KHR) {
+        eglDestroyImageKHR(mSlots[i].mEglDisplay, mSlots[i].mEglImage);
+        mSlots[i].mEglImage = EGL_NO_IMAGE_KHR;
+        mSlots[i].mEglDisplay = EGL_NO_DISPLAY;
+    }
+}
+
+void BufferQueue::freeAllBuffersLocked() {
+    ALOGW_IF(!mQueue.isEmpty(),
+            "freeAllBuffersLocked called but mQueue is not empty");
+    mCurrentTexture = INVALID_BUFFER_SLOT;
+    for (int i = 0; i < NUM_BUFFER_SLOTS; i++) {
+        freeBufferLocked(i);
+    }
+}
+
+void BufferQueue::freeAllBuffersExceptHeadLocked() {
+    ALOGW_IF(!mQueue.isEmpty(),
+            "freeAllBuffersExceptCurrentLocked called but mQueue is not empty");
+    int head = -1;
+    if (!mQueue.empty()) {
+        Fifo::iterator front(mQueue.begin());
+        head = *front;
+    }
+    mCurrentTexture = INVALID_BUFFER_SLOT;
+    for (int i = 0; i < NUM_BUFFER_SLOTS; i++) {
+        if (i != head) {
+            freeBufferLocked(i);
+        }
+    }
+}
+
+status_t BufferQueue::drainQueueLocked() {
+    while (mSynchronousMode && !mQueue.isEmpty()) {
+        mDequeueCondition.wait(mMutex);
+        if (mAbandoned) {
+            ST_LOGE("drainQueueLocked: BufferQueue has been abandoned!");
+            return NO_INIT;
+        }
+        if (mConnectedApi == NO_CONNECTED_API) {
+            ST_LOGE("drainQueueLocked: BufferQueue is not connected!");
+            return NO_INIT;
+        }
+    }
+    return NO_ERROR;
+}
+
+status_t BufferQueue::drainQueueAndFreeBuffersLocked() {
+    status_t err = drainQueueLocked();
+    if (err == NO_ERROR) {
+        if (mSynchronousMode) {
+            freeAllBuffersLocked();
+        } else {
+            freeAllBuffersExceptHeadLocked();
+        }
+    }
+    return err;
+}
+
+}; // namespace android
diff --git a/libs/gui/SurfaceTexture.cpp b/libs/gui/SurfaceTexture.cpp
index 3abe84a..be1bcd1 100644
--- a/libs/gui/SurfaceTexture.cpp
+++ b/libs/gui/SurfaceTexture.cpp
@@ -38,19 +38,6 @@
 #include <utils/Log.h>
 #include <utils/String8.h>
 
-// This compile option causes SurfaceTexture to return the buffer that is currently
-// attached to the GL texture from dequeueBuffer when no other buffers are
-// available.  It requires the drivers (Gralloc, GL, OMX IL, and Camera) to do
-// implicit cross-process synchronization to prevent the buffer from being
-// written to before the buffer has (a) been detached from the GL texture and
-// (b) all GL reads from the buffer have completed.
-#ifdef ALLOW_DEQUEUE_CURRENT_BUFFER
-#define FLAG_ALLOW_DEQUEUE_CURRENT_BUFFER    true
-#warning "ALLOW_DEQUEUE_CURRENT_BUFFER enabled"
-#else
-#define FLAG_ALLOW_DEQUEUE_CURRENT_BUFFER    false
-#endif
-
 // This compile option makes SurfaceTexture use the EGL_KHR_fence_sync extension
 // to synchronize access to the buffers.  It will cause dequeueBuffer to stall,
 // waiting for the GL reads for the buffer being dequeued to complete before
@@ -110,44 +97,22 @@
 
 static void mtxMul(float out[16], const float a[16], const float b[16]);
 
-// Get an ID that's unique within this process.
-static int32_t createProcessUniqueId() {
-    static volatile int32_t globalCounter = 0;
-    return android_atomic_inc(&globalCounter);
-}
 
 SurfaceTexture::SurfaceTexture(GLuint tex, bool allowSynchronousMode,
         GLenum texTarget, bool useFenceSync) :
-    mDefaultWidth(1),
-    mDefaultHeight(1),
-    mPixelFormat(PIXEL_FORMAT_RGBA_8888),
-    mBufferCount(MIN_ASYNC_BUFFER_SLOTS),
-    mClientBufferCount(0),
-    mServerBufferCount(MIN_ASYNC_BUFFER_SLOTS),
-    mCurrentTexture(INVALID_BUFFER_SLOT),
+    BufferQueue(allowSynchronousMode),
     mCurrentTransform(0),
     mCurrentTimestamp(0),
-    mNextTransform(0),
-    mNextScalingMode(NATIVE_WINDOW_SCALING_MODE_FREEZE),
     mTexName(tex),
-    mSynchronousMode(false),
-    mAllowSynchronousMode(allowSynchronousMode),
-    mConnectedApi(NO_CONNECTED_API),
-    mAbandoned(false),
 #ifdef USE_FENCE_SYNC
     mUseFenceSync(useFenceSync),
 #else
     mUseFenceSync(false),
 #endif
-    mTexTarget(texTarget),
-    mFrameCounter(0) {
-    // Choose a name using the PID and a process-unique ID.
-    mName = String8::format("unnamed-%d-%d", getpid(), createProcessUniqueId());
+    mTexTarget(texTarget)
+{
 
     ST_LOGV("SurfaceTexture");
-    sp<ISurfaceComposer> composer(ComposerService::getComposerService());
-    mGraphicBufferAlloc = composer->createGraphicBufferAlloc();
-    mNextCrop.makeInvalid();
     memcpy(mCurrentTransformMatrix, mtxIdentity,
             sizeof(mCurrentTransformMatrix));
 }
@@ -157,91 +122,11 @@
     freeAllBuffersLocked();
 }
 
-status_t SurfaceTexture::setBufferCountServerLocked(int bufferCount) {
-    if (bufferCount > NUM_BUFFER_SLOTS)
-        return BAD_VALUE;
-
-    // special-case, nothing to do
-    if (bufferCount == mBufferCount)
-        return OK;
-
-    if (!mClientBufferCount &&
-        bufferCount >= mBufferCount) {
-        // easy, we just have more buffers
-        mBufferCount = bufferCount;
-        mServerBufferCount = bufferCount;
-        mDequeueCondition.signal();
-    } else {
-        // we're here because we're either
-        // - reducing the number of available buffers
-        // - or there is a client-buffer-count in effect
-
-        // less than 2 buffers is never allowed
-        if (bufferCount < 2)
-            return BAD_VALUE;
-
-        // when there is non client-buffer-count in effect, the client is not
-        // allowed to dequeue more than one buffer at a time,
-        // so the next time they dequeue a buffer, we know that they don't
-        // own one. the actual resizing will happen during the next
-        // dequeueBuffer.
-
-        mServerBufferCount = bufferCount;
-    }
-    return OK;
-}
-
 status_t SurfaceTexture::setBufferCountServer(int bufferCount) {
     Mutex::Autolock lock(mMutex);
     return setBufferCountServerLocked(bufferCount);
 }
 
-status_t SurfaceTexture::setBufferCount(int bufferCount) {
-    ST_LOGV("setBufferCount: count=%d", bufferCount);
-    Mutex::Autolock lock(mMutex);
-
-    if (mAbandoned) {
-        ST_LOGE("setBufferCount: SurfaceTexture has been abandoned!");
-        return NO_INIT;
-    }
-    if (bufferCount > NUM_BUFFER_SLOTS) {
-        ST_LOGE("setBufferCount: bufferCount larger than slots available");
-        return BAD_VALUE;
-    }
-
-    // Error out if the user has dequeued buffers
-    for (int i=0 ; i<mBufferCount ; i++) {
-        if (mSlots[i].mBufferState == BufferSlot::DEQUEUED) {
-            ST_LOGE("setBufferCount: client owns some buffers");
-            return -EINVAL;
-        }
-    }
-
-    const int minBufferSlots = mSynchronousMode ?
-            MIN_SYNC_BUFFER_SLOTS : MIN_ASYNC_BUFFER_SLOTS;
-    if (bufferCount == 0) {
-        mClientBufferCount = 0;
-        bufferCount = (mServerBufferCount >= minBufferSlots) ?
-                mServerBufferCount : minBufferSlots;
-        return setBufferCountServerLocked(bufferCount);
-    }
-
-    if (bufferCount < minBufferSlots) {
-        ST_LOGE("setBufferCount: requested buffer count (%d) is less than "
-                "minimum (%d)", bufferCount, minBufferSlots);
-        return BAD_VALUE;
-    }
-
-    // here we're guaranteed that the client doesn't have dequeued buffers
-    // and will release all of its buffer references.
-    freeAllBuffersLocked();
-    mBufferCount = bufferCount;
-    mClientBufferCount = bufferCount;
-    mCurrentTexture = INVALID_BUFFER_SLOT;
-    mQueue.clear();
-    mDequeueCondition.signal();
-    return OK;
-}
 
 status_t SurfaceTexture::setDefaultBufferSize(uint32_t w, uint32_t h)
 {
@@ -258,496 +143,6 @@
     return OK;
 }
 
-status_t SurfaceTexture::requestBuffer(int slot, sp<GraphicBuffer>* buf) {
-    ST_LOGV("requestBuffer: slot=%d", slot);
-    Mutex::Autolock lock(mMutex);
-    if (mAbandoned) {
-        ST_LOGE("requestBuffer: SurfaceTexture has been abandoned!");
-        return NO_INIT;
-    }
-    if (slot < 0 || mBufferCount <= slot) {
-        ST_LOGE("requestBuffer: slot index out of range [0, %d]: %d",
-                mBufferCount, slot);
-        return BAD_VALUE;
-    }
-    mSlots[slot].mRequestBufferCalled = true;
-    *buf = mSlots[slot].mGraphicBuffer;
-    return NO_ERROR;
-}
-
-status_t SurfaceTexture::dequeueBuffer(int *outBuf, uint32_t w, uint32_t h,
-        uint32_t format, uint32_t usage) {
-    ST_LOGV("dequeueBuffer: w=%d h=%d fmt=%#x usage=%#x", w, h, format, usage);
-
-    if ((w && !h) || (!w && h)) {
-        ST_LOGE("dequeueBuffer: invalid size: w=%u, h=%u", w, h);
-        return BAD_VALUE;
-    }
-
-    status_t returnFlags(OK);
-    EGLDisplay dpy = EGL_NO_DISPLAY;
-    EGLSyncKHR fence = EGL_NO_SYNC_KHR;
-
-    { // Scope for the lock
-        Mutex::Autolock lock(mMutex);
-
-        int found = -1;
-        int foundSync = -1;
-        int dequeuedCount = 0;
-        bool tryAgain = true;
-        while (tryAgain) {
-            if (mAbandoned) {
-                ST_LOGE("dequeueBuffer: SurfaceTexture has been abandoned!");
-                return NO_INIT;
-            }
-
-            // We need to wait for the FIFO to drain if the number of buffer
-            // needs to change.
-            //
-            // The condition "number of buffers needs to change" is true if
-            // - the client doesn't care about how many buffers there are
-            // - AND the actual number of buffer is different from what was
-            //   set in the last setBufferCountServer()
-            //                         - OR -
-            //   setBufferCountServer() was set to a value incompatible with
-            //   the synchronization mode (for instance because the sync mode
-            //   changed since)
-            //
-            // As long as this condition is true AND the FIFO is not empty, we
-            // wait on mDequeueCondition.
-
-            const int minBufferCountNeeded = mSynchronousMode ?
-                    MIN_SYNC_BUFFER_SLOTS : MIN_ASYNC_BUFFER_SLOTS;
-
-            const bool numberOfBuffersNeedsToChange = !mClientBufferCount &&
-                    ((mServerBufferCount != mBufferCount) ||
-                            (mServerBufferCount < minBufferCountNeeded));
-
-            if (!mQueue.isEmpty() && numberOfBuffersNeedsToChange) {
-                // wait for the FIFO to drain
-                mDequeueCondition.wait(mMutex);
-                // NOTE: we continue here because we need to reevaluate our
-                // whole state (eg: we could be abandoned or disconnected)
-                continue;
-            }
-
-            if (numberOfBuffersNeedsToChange) {
-                // here we're guaranteed that mQueue is empty
-                freeAllBuffersLocked();
-                mBufferCount = mServerBufferCount;
-                if (mBufferCount < minBufferCountNeeded)
-                    mBufferCount = minBufferCountNeeded;
-                mCurrentTexture = INVALID_BUFFER_SLOT;
-                returnFlags |= ISurfaceTexture::RELEASE_ALL_BUFFERS;
-            }
-
-            // look for a free buffer to give to the client
-            found = INVALID_BUFFER_SLOT;
-            foundSync = INVALID_BUFFER_SLOT;
-            dequeuedCount = 0;
-            for (int i = 0; i < mBufferCount; i++) {
-                const int state = mSlots[i].mBufferState;
-                if (state == BufferSlot::DEQUEUED) {
-                    dequeuedCount++;
-                }
-
-                // if buffer is FREE it CANNOT be current
-                ALOGW_IF((state == BufferSlot::FREE) && (mCurrentTexture==i),
-                        "dequeueBuffer: buffer %d is both FREE and current!",
-                        i);
-
-                if (FLAG_ALLOW_DEQUEUE_CURRENT_BUFFER) {
-                    if (state == BufferSlot::FREE || i == mCurrentTexture) {
-                        foundSync = i;
-                        if (i != mCurrentTexture) {
-                            found = i;
-                            break;
-                        }
-                    }
-                } else {
-                    if (state == BufferSlot::FREE) {
-                        /* We return the oldest of the free buffers to avoid
-                         * stalling the producer if possible.  This is because
-                         * the consumer may still have pending reads of the
-                         * buffers in flight.
-                         */
-                        bool isOlder = mSlots[i].mFrameNumber <
-                                mSlots[found].mFrameNumber;
-                        if (found < 0 || isOlder) {
-                            foundSync = i;
-                            found = i;
-                        }
-                    }
-                }
-            }
-
-            // clients are not allowed to dequeue more than one buffer
-            // if they didn't set a buffer count.
-            if (!mClientBufferCount && dequeuedCount) {
-                ST_LOGE("dequeueBuffer: can't dequeue multiple buffers without "
-                        "setting the buffer count");
-                return -EINVAL;
-            }
-
-            // 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))) {
-                    ST_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 for some buffers to be consumed
-            tryAgain = mSynchronousMode && (foundSync == INVALID_BUFFER_SLOT);
-            if (tryAgain) {
-                mDequeueCondition.wait(mMutex);
-            }
-        }
-
-        if (mSynchronousMode && found == INVALID_BUFFER_SLOT) {
-            // foundSync guaranteed to be != INVALID_BUFFER_SLOT
-            found = foundSync;
-        }
-
-        if (found == INVALID_BUFFER_SLOT) {
-            // This should not happen.
-            ST_LOGE("dequeueBuffer: no available buffer slots");
-            return -EBUSY;
-        }
-
-        const int buf = found;
-        *outBuf = found;
-
-        const bool useDefaultSize = !w && !h;
-        if (useDefaultSize) {
-            // use the default size
-            w = mDefaultWidth;
-            h = mDefaultHeight;
-        }
-
-        const bool updateFormat = (format != 0);
-        if (!updateFormat) {
-            // keep the current (or default) format
-            format = mPixelFormat;
-        }
-
-        // buffer is now in DEQUEUED (but can also be current at the same time,
-        // if we're in synchronous mode)
-        mSlots[buf].mBufferState = BufferSlot::DEQUEUED;
-
-        const sp<GraphicBuffer>& buffer(mSlots[buf].mGraphicBuffer);
-        if ((buffer == NULL) ||
-            (uint32_t(buffer->width)  != w) ||
-            (uint32_t(buffer->height) != h) ||
-            (uint32_t(buffer->format) != format) ||
-            ((uint32_t(buffer->usage) & usage) != usage))
-        {
-            usage |= GraphicBuffer::USAGE_HW_TEXTURE;
-            status_t error;
-            sp<GraphicBuffer> graphicBuffer(
-                    mGraphicBufferAlloc->createGraphicBuffer(
-                            w, h, format, usage, &error));
-            if (graphicBuffer == 0) {
-                ST_LOGE("dequeueBuffer: SurfaceComposer::createGraphicBuffer "
-                        "failed");
-                return error;
-            }
-            if (updateFormat) {
-                mPixelFormat = format;
-            }
-            mSlots[buf].mGraphicBuffer = graphicBuffer;
-            mSlots[buf].mRequestBufferCalled = false;
-            mSlots[buf].mFence = EGL_NO_SYNC_KHR;
-            if (mSlots[buf].mEglImage != EGL_NO_IMAGE_KHR) {
-                eglDestroyImageKHR(mSlots[buf].mEglDisplay,
-                        mSlots[buf].mEglImage);
-                mSlots[buf].mEglImage = EGL_NO_IMAGE_KHR;
-                mSlots[buf].mEglDisplay = EGL_NO_DISPLAY;
-            }
-            if (mCurrentTexture == buf) {
-                // The current texture no longer references the buffer in this slot
-                // since we just allocated a new buffer.
-                mCurrentTexture = INVALID_BUFFER_SLOT;
-            }
-            returnFlags |= ISurfaceTexture::BUFFER_NEEDS_REALLOCATION;
-        }
-
-        dpy = mSlots[buf].mEglDisplay;
-        fence = mSlots[buf].mFence;
-        mSlots[buf].mFence = EGL_NO_SYNC_KHR;
-    }
-
-    if (fence != EGL_NO_SYNC_KHR) {
-        EGLint result = eglClientWaitSyncKHR(dpy, fence, 0, 1000000000);
-        // If something goes wrong, log the error, but return the buffer without
-        // synchronizing access to it.  It's too late at this point to abort the
-        // dequeue operation.
-        if (result == EGL_FALSE) {
-            ALOGE("dequeueBuffer: error waiting for fence: %#x", eglGetError());
-        } else if (result == EGL_TIMEOUT_EXPIRED_KHR) {
-            ALOGE("dequeueBuffer: timeout waiting for fence");
-        }
-        eglDestroySyncKHR(dpy, fence);
-    }
-
-    ST_LOGV("dequeueBuffer: returning slot=%d buf=%p flags=%#x", *outBuf,
-            mSlots[*outBuf].mGraphicBuffer->handle, returnFlags);
-
-    return returnFlags;
-}
-
-status_t SurfaceTexture::setSynchronousMode(bool enabled) {
-    ST_LOGV("setSynchronousMode: enabled=%d", enabled);
-    Mutex::Autolock lock(mMutex);
-
-    if (mAbandoned) {
-        ST_LOGE("setSynchronousMode: SurfaceTexture has been abandoned!");
-        return NO_INIT;
-    }
-
-    status_t err = OK;
-    if (!mAllowSynchronousMode && enabled)
-        return err;
-
-    if (!enabled) {
-        // going to asynchronous mode, drain the queue
-        err = drainQueueLocked();
-        if (err != NO_ERROR)
-            return err;
-    }
-
-    if (mSynchronousMode != enabled) {
-        // - if we're going to asynchronous mode, the queue is guaranteed to be
-        // empty here
-        // - if the client set the number of buffers, we're guaranteed that
-        // we have at least 3 (because we don't allow less)
-        mSynchronousMode = enabled;
-        mDequeueCondition.signal();
-    }
-    return err;
-}
-
-status_t SurfaceTexture::queueBuffer(int buf, int64_t timestamp,
-        uint32_t* outWidth, uint32_t* outHeight, uint32_t* outTransform) {
-    ST_LOGV("queueBuffer: slot=%d time=%lld", buf, timestamp);
-
-    sp<FrameAvailableListener> listener;
-
-    { // scope for the lock
-        Mutex::Autolock lock(mMutex);
-        if (mAbandoned) {
-            ST_LOGE("queueBuffer: SurfaceTexture has been abandoned!");
-            return NO_INIT;
-        }
-        if (buf < 0 || buf >= mBufferCount) {
-            ST_LOGE("queueBuffer: slot index out of range [0, %d]: %d",
-                    mBufferCount, buf);
-            return -EINVAL;
-        } else if (mSlots[buf].mBufferState != BufferSlot::DEQUEUED) {
-            ST_LOGE("queueBuffer: slot %d is not owned by the client "
-                    "(state=%d)", buf, mSlots[buf].mBufferState);
-            return -EINVAL;
-        } else if (buf == mCurrentTexture) {
-            ST_LOGE("queueBuffer: slot %d is current!", buf);
-            return -EINVAL;
-        } else if (!mSlots[buf].mRequestBufferCalled) {
-            ST_LOGE("queueBuffer: slot %d was enqueued without requesting a "
-                    "buffer", buf);
-            return -EINVAL;
-        }
-
-        if (mSynchronousMode) {
-            // In synchronous mode we queue all buffers in a FIFO.
-            mQueue.push_back(buf);
-
-            // Synchronous mode always signals that an additional frame should
-            // be consumed.
-            listener = mFrameAvailableListener;
-        } else {
-            // In asynchronous mode we only keep the most recent buffer.
-            if (mQueue.empty()) {
-                mQueue.push_back(buf);
-
-                // Asynchronous mode only signals that a frame should be
-                // consumed if no previous frame was pending. If a frame were
-                // pending then the consumer would have already been notified.
-                listener = mFrameAvailableListener;
-            } 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].mCrop = mNextCrop;
-        mSlots[buf].mTransform = mNextTransform;
-        mSlots[buf].mScalingMode = mNextScalingMode;
-        mSlots[buf].mTimestamp = timestamp;
-        mFrameCounter++;
-        mSlots[buf].mFrameNumber = mFrameCounter;
-
-        mDequeueCondition.signal();
-
-        *outWidth = mDefaultWidth;
-        *outHeight = mDefaultHeight;
-        *outTransform = 0;
-    } // scope for the lock
-
-    // call back without lock held
-    if (listener != 0) {
-        listener->onFrameAvailable();
-    }
-    return OK;
-}
-
-void SurfaceTexture::cancelBuffer(int buf) {
-    ST_LOGV("cancelBuffer: slot=%d", buf);
-    Mutex::Autolock lock(mMutex);
-
-    if (mAbandoned) {
-        ST_LOGW("cancelBuffer: SurfaceTexture has been abandoned!");
-        return;
-    }
-
-    if (buf < 0 || buf >= mBufferCount) {
-        ST_LOGE("cancelBuffer: slot index out of range [0, %d]: %d",
-                mBufferCount, buf);
-        return;
-    } else if (mSlots[buf].mBufferState != BufferSlot::DEQUEUED) {
-        ST_LOGE("cancelBuffer: slot %d is not owned by the client (state=%d)",
-                buf, mSlots[buf].mBufferState);
-        return;
-    }
-    mSlots[buf].mBufferState = BufferSlot::FREE;
-    mSlots[buf].mFrameNumber = 0;
-    mDequeueCondition.signal();
-}
-
-status_t SurfaceTexture::setCrop(const Rect& crop) {
-    ST_LOGV("setCrop: crop=[%d,%d,%d,%d]", crop.left, crop.top, crop.right,
-            crop.bottom);
-
-    Mutex::Autolock lock(mMutex);
-    if (mAbandoned) {
-        ST_LOGE("setCrop: SurfaceTexture has been abandoned!");
-        return NO_INIT;
-    }
-    mNextCrop = crop;
-    return OK;
-}
-
-status_t SurfaceTexture::setTransform(uint32_t transform) {
-    ST_LOGV("setTransform: xform=%#x", transform);
-    Mutex::Autolock lock(mMutex);
-    if (mAbandoned) {
-        ST_LOGE("setTransform: SurfaceTexture has been abandoned!");
-        return NO_INIT;
-    }
-    mNextTransform = transform;
-    return OK;
-}
-
-status_t SurfaceTexture::connect(int api,
-        uint32_t* outWidth, uint32_t* outHeight, uint32_t* outTransform) {
-    ST_LOGV("connect: api=%d", api);
-    Mutex::Autolock lock(mMutex);
-
-    if (mAbandoned) {
-        ST_LOGE("connect: SurfaceTexture has been abandoned!");
-        return NO_INIT;
-    }
-
-    int err = NO_ERROR;
-    switch (api) {
-        case NATIVE_WINDOW_API_EGL:
-        case NATIVE_WINDOW_API_CPU:
-        case NATIVE_WINDOW_API_MEDIA:
-        case NATIVE_WINDOW_API_CAMERA:
-            if (mConnectedApi != NO_CONNECTED_API) {
-                ST_LOGE("connect: already connected (cur=%d, req=%d)",
-                        mConnectedApi, api);
-                err = -EINVAL;
-            } else {
-                mConnectedApi = api;
-                *outWidth = mDefaultWidth;
-                *outHeight = mDefaultHeight;
-                *outTransform = 0;
-            }
-            break;
-        default:
-            err = -EINVAL;
-            break;
-    }
-    return err;
-}
-
-status_t SurfaceTexture::disconnect(int api) {
-    ST_LOGV("disconnect: api=%d", api);
-    Mutex::Autolock lock(mMutex);
-
-    if (mAbandoned) {
-        // it is not really an error to disconnect after the surface
-        // has been abandoned, it should just be a no-op.
-        return NO_ERROR;
-    }
-
-    int err = NO_ERROR;
-    switch (api) {
-        case NATIVE_WINDOW_API_EGL:
-        case NATIVE_WINDOW_API_CPU:
-        case NATIVE_WINDOW_API_MEDIA:
-        case NATIVE_WINDOW_API_CAMERA:
-            if (mConnectedApi == api) {
-                drainQueueAndFreeBuffersLocked();
-                mConnectedApi = NO_CONNECTED_API;
-                mNextCrop.makeInvalid();
-                mNextScalingMode = NATIVE_WINDOW_SCALING_MODE_FREEZE;
-                mNextTransform = 0;
-                mDequeueCondition.signal();
-            } else {
-                ST_LOGE("disconnect: connected to another api (cur=%d, req=%d)",
-                        mConnectedApi, api);
-                err = -EINVAL;
-            }
-            break;
-        default:
-            ST_LOGE("disconnect: unknown API %d", api);
-            err = -EINVAL;
-            break;
-    }
-    return err;
-}
-
-status_t SurfaceTexture::setScalingMode(int mode) {
-    ST_LOGV("setScalingMode: mode=%d", mode);
-
-    switch (mode) {
-        case NATIVE_WINDOW_SCALING_MODE_FREEZE:
-        case NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW:
-            break;
-        default:
-            ST_LOGE("unknown scaling mode: %d", mode);
-            return BAD_VALUE;
-    }
-
-    Mutex::Autolock lock(mMutex);
-    mNextScalingMode = mode;
-    return OK;
-}
-
 status_t SurfaceTexture::updateTexImage() {
     ST_LOGV("updateTexImage");
     Mutex::Autolock lock(mMutex);
@@ -980,69 +375,6 @@
     mFrameAvailableListener = listener;
 }
 
-void SurfaceTexture::freeBufferLocked(int i) {
-    mSlots[i].mGraphicBuffer = 0;
-    mSlots[i].mBufferState = BufferSlot::FREE;
-    mSlots[i].mFrameNumber = 0;
-    if (mSlots[i].mEglImage != EGL_NO_IMAGE_KHR) {
-        eglDestroyImageKHR(mSlots[i].mEglDisplay, mSlots[i].mEglImage);
-        mSlots[i].mEglImage = EGL_NO_IMAGE_KHR;
-        mSlots[i].mEglDisplay = EGL_NO_DISPLAY;
-    }
-}
-
-void SurfaceTexture::freeAllBuffersLocked() {
-    ALOGW_IF(!mQueue.isEmpty(),
-            "freeAllBuffersLocked called but mQueue is not empty");
-    mCurrentTexture = INVALID_BUFFER_SLOT;
-    for (int i = 0; i < NUM_BUFFER_SLOTS; i++) {
-        freeBufferLocked(i);
-    }
-}
-
-void SurfaceTexture::freeAllBuffersExceptHeadLocked() {
-    ALOGW_IF(!mQueue.isEmpty(),
-            "freeAllBuffersExceptCurrentLocked called but mQueue is not empty");
-    int head = -1;
-    if (!mQueue.empty()) {
-        Fifo::iterator front(mQueue.begin());
-        head = *front;
-    }
-    mCurrentTexture = INVALID_BUFFER_SLOT;
-    for (int i = 0; i < NUM_BUFFER_SLOTS; i++) {
-        if (i != head) {
-            freeBufferLocked(i);
-        }
-    }
-}
-
-status_t SurfaceTexture::drainQueueLocked() {
-    while (mSynchronousMode && !mQueue.isEmpty()) {
-        mDequeueCondition.wait(mMutex);
-        if (mAbandoned) {
-            ST_LOGE("drainQueueLocked: SurfaceTexture has been abandoned!");
-            return NO_INIT;
-        }
-        if (mConnectedApi == NO_CONNECTED_API) {
-            ST_LOGE("drainQueueLocked: SurfaceTexture is not connected!");
-            return NO_INIT;
-        }
-    }
-    return NO_ERROR;
-}
-
-status_t SurfaceTexture::drainQueueAndFreeBuffersLocked() {
-    status_t err = drainQueueLocked();
-    if (err == NO_ERROR) {
-        if (mSynchronousMode) {
-            freeAllBuffersLocked();
-        } else {
-            freeAllBuffersExceptHeadLocked();
-        }
-    }
-    return err;
-}
-
 EGLImageKHR SurfaceTexture::createImage(EGLDisplay dpy,
         const sp<GraphicBuffer>& graphicBuffer) {
     EGLClientBuffer cbuf = (EGLClientBuffer)graphicBuffer->getNativeBuffer();
diff --git a/libs/rs/rsContext.cpp b/libs/rs/rsContext.cpp
index 04284dd..1a34bd5 100644
--- a/libs/rs/rsContext.cpp
+++ b/libs/rs/rsContext.cpp
@@ -248,7 +248,7 @@
     rsc->mRunning = true;
     if (!rsc->mIsGraphicsContext) {
         while (!rsc->mExit) {
-            rsc->mIO.playCoreCommands(rsc, true, -1);
+            rsc->mIO.playCoreCommands(rsc, -1);
         }
     } else {
 #ifndef ANDROID_RS_SERIALIZE
@@ -268,14 +268,14 @@
                 vsyncRate = targetRate;
             }
             if (targetRate) {
-                drawOnce |= rsc->mIO.playCoreCommands(rsc, true, displayEvent.getFd());
+                drawOnce |= rsc->mIO.playCoreCommands(rsc, displayEvent.getFd());
                 while (displayEvent.getEvents(eventBuffer, 1) != 0) {
                     //ALOGE("vs2 time past %lld", (rsc->getTime() - eventBuffer[0].header.timestamp) / 1000000);
                 }
             } else
 #endif
             {
-                drawOnce |= rsc->mIO.playCoreCommands(rsc, true, -1);
+                drawOnce |= rsc->mIO.playCoreCommands(rsc, -1);
             }
 
             if ((rsc->mRootScript.get() != NULL) && rsc->mHasSurface &&
diff --git a/libs/rs/rsThreadIO.cpp b/libs/rs/rsThreadIO.cpp
index 8e4b988..4f30573 100644
--- a/libs/rs/rsThreadIO.cpp
+++ b/libs/rs/rsThreadIO.cpp
@@ -89,7 +89,7 @@
     //mToCore.setTimeoutCallback(cb, dat, timeout);
 }
 
-bool ThreadIO::playCoreCommands(Context *con, bool waitForCommand, int waitFd) {
+bool ThreadIO::playCoreCommands(Context *con, int waitFd) {
     bool ret = false;
 
     uint8_t buf[2 * 1024];
@@ -132,7 +132,6 @@
             if (con->props.mLogTimes) {
                 con->timerSet(Context::RS_TIMER_INTERNAL);
             }
-            waitForCommand = false;
             //ALOGV("playCoreCommands 3 %i %i", cmd->cmdID, cmd->bytes);
 
             if (cmd->cmdID >= (sizeof(gPlaybackFuncs) / sizeof(void *))) {
diff --git a/libs/rs/rsThreadIO.h b/libs/rs/rsThreadIO.h
index d56a1c9..62e3e33 100644
--- a/libs/rs/rsThreadIO.h
+++ b/libs/rs/rsThreadIO.h
@@ -36,7 +36,7 @@
 
     // Plays back commands from the client.
     // Returns true if any commands were processed.
-    bool playCoreCommands(Context *con, bool waitForCommand, int waitFd);
+    bool playCoreCommands(Context *con, int waitFd);
 
     void setTimeoutCallback(void (*)(void *), void *, uint64_t timeout);
 
diff --git a/media/java/android/media/CamcorderProfile.java b/media/java/android/media/CamcorderProfile.java
index 7d60c55..e94bddc 100644
--- a/media/java/android/media/CamcorderProfile.java
+++ b/media/java/android/media/CamcorderProfile.java
@@ -20,12 +20,12 @@
 import android.hardware.Camera.CameraInfo;
 
 /**
- * The CamcorderProfile class is used to retrieve the
+ * Retrieves the
  * predefined camcorder profile settings for camcorder applications.
  * These settings are read-only.
  *
- * The compressed output from a recording session with a given
- * CamcorderProfile contains two tracks: one for auido and one for video.
+ * <p>The compressed output from a recording session with a given
+ * CamcorderProfile contains two tracks: one for audio and one for video.
  *
  * <p>Each profile specifies the following set of parameters:
  * <ul>
diff --git a/media/jni/mediaeditor/Android.mk b/media/jni/mediaeditor/Android.mk
index e44dc7c..b17ae45 100755
--- a/media/jni/mediaeditor/Android.mk
+++ b/media/jni/mediaeditor/Android.mk
@@ -57,6 +57,7 @@
     libaudioflinger \
     libbinder \
     libstagefright \
+    libstagefright_foundation \
     libstagefright_omx \
     libgui \
     libvideoeditorplayer
diff --git a/media/libstagefright/AwesomePlayer.cpp b/media/libstagefright/AwesomePlayer.cpp
index 8073af8..aacfbdd 100644
--- a/media/libstagefright/AwesomePlayer.cpp
+++ b/media/libstagefright/AwesomePlayer.cpp
@@ -1609,7 +1609,7 @@
                     mSeekTimeUs,
                     mSeeking == SEEK_VIDEO_ONLY
                         ? MediaSource::ReadOptions::SEEK_NEXT_SYNC
-                        : MediaSource::ReadOptions::SEEK_CLOSEST_SYNC);
+                        : MediaSource::ReadOptions::SEEK_CLOSEST);
         }
         for (;;) {
             status_t err = mVideoSource->read(&mVideoBuffer, &options);
diff --git a/media/libstagefright/MPEG4Writer.cpp b/media/libstagefright/MPEG4Writer.cpp
index c7a8b32..7ebbe1d 100755
--- a/media/libstagefright/MPEG4Writer.cpp
+++ b/media/libstagefright/MPEG4Writer.cpp
@@ -23,10 +23,10 @@
 #include <pthread.h>
 #include <sys/prctl.h>
 
+#include <media/stagefright/foundation/ADebug.h>
 #include <media/stagefright/MPEG4Writer.h>
 #include <media/stagefright/MediaBuffer.h>
 #include <media/stagefright/MetaData.h>
-#include <media/stagefright/MediaDebug.h>
 #include <media/stagefright/MediaDefs.h>
 #include <media/stagefright/MediaErrors.h>
 #include <media/stagefright/MediaSource.h>
@@ -141,7 +141,7 @@
             : sampleCount(count), sampleDuration(timescaledDur) {}
 
         uint32_t sampleCount;
-        int32_t sampleDuration;  // time scale based
+        uint32_t sampleDuration;  // time scale based
     };
     size_t        mNumCttsTableEntries;
     List<CttsTableEntry> mCttsTableEntries;
@@ -478,7 +478,7 @@
         !param->findInt32(kKeyTimeScale, &mTimeScale)) {
         mTimeScale = 1000;
     }
-    CHECK(mTimeScale > 0);
+    CHECK_GT(mTimeScale, 0);
     ALOGV("movie time scale: %d", mTimeScale);
 
     mStreamableFile = true;
@@ -497,7 +497,7 @@
         }
         mEstimatedMoovBoxSize = estimateMoovBoxSize(bitRate);
     }
-    CHECK(mEstimatedMoovBoxSize >= 8);
+    CHECK_GE(mEstimatedMoovBoxSize, 8);
     lseek64(mFd, mFreeBoxOffset, SEEK_SET);
     writeInt32(mEstimatedMoovBoxSize);
     write("free", 4);
@@ -691,7 +691,7 @@
 
     mWriteMoovBoxToMemory = false;
     if (mStreamableFile) {
-        CHECK(mMoovBoxBufferOffset + 8 <= mEstimatedMoovBoxSize);
+        CHECK_LE(mMoovBoxBufferOffset + 8, mEstimatedMoovBoxSize);
 
         // Moov box
         lseek64(mFd, mFreeBoxOffset, SEEK_SET);
@@ -863,7 +863,7 @@
 
         mOffset += length + 4;
     } else {
-        CHECK(length < 65536);
+        CHECK_LT(length, 65536);
 
         uint8_t x = length >> 8;
         ::write(mFd, &x, 1);
@@ -1092,7 +1092,7 @@
 
 void MPEG4Writer::setStartTimestampUs(int64_t timeUs) {
     ALOGI("setStartTimestampUs: %lld", timeUs);
-    CHECK(timeUs >= 0);
+    CHECK_GE(timeUs, 0ll);
     Mutex::Autolock autoLock(mLock);
     if (mStartTimestampUs < 0 || mStartTimestampUs > timeUs) {
         mStartTimestampUs = timeUs;
@@ -1222,7 +1222,7 @@
         mTimeScale = timeScale;
     }
 
-    CHECK(mTimeScale > 0);
+    CHECK_GT(mTimeScale, 0);
 }
 
 void MPEG4Writer::Track::getCodecSpecificDataFromInputFormatIfPossible() {
@@ -1303,7 +1303,7 @@
         }
     }
 
-    CHECK("Received a chunk for a unknown track" == 0);
+    CHECK(!"Received a chunk for a unknown track");
 }
 
 void MPEG4Writer::writeChunkToFile(Chunk* chunk) {
@@ -1846,7 +1846,7 @@
     int64_t cttsOffsetTimeUs = 0;
     int64_t currCttsOffsetTimeTicks = 0;   // Timescale based ticks
     int64_t lastCttsOffsetTimeTicks = -1;  // Timescale based ticks
-    int32_t cttsSampleCount = 1;           // Sample count in the current ctts table entry
+    int32_t cttsSampleCount = 0;           // Sample count in the current ctts table entry
 
     if (mIsAudio) {
         prctl(PR_SET_NAME, (unsigned long)"AudioTrackEncoding", 0, 0, 0);
@@ -1889,7 +1889,7 @@
                         (const uint8_t *)buffer->data()
                             + buffer->range_offset(),
                         buffer->range_length());
-                CHECK_EQ(OK, err);
+                CHECK_EQ((status_t)OK, err);
             } else if (mIsMPEG4) {
                 mCodecSpecificDataSize = buffer->range_length();
                 mCodecSpecificData = malloc(mCodecSpecificDataSize);
@@ -1955,15 +1955,15 @@
 
         if (mResumed) {
             int64_t durExcludingEarlierPausesUs = timestampUs - previousPausedDurationUs;
-            CHECK(durExcludingEarlierPausesUs >= 0);
+            CHECK_GE(durExcludingEarlierPausesUs, 0ll);
             int64_t pausedDurationUs = durExcludingEarlierPausesUs - mTrackDurationUs;
-            CHECK(pausedDurationUs >= lastDurationUs);
+            CHECK_GE(pausedDurationUs, lastDurationUs);
             previousPausedDurationUs += pausedDurationUs - lastDurationUs;
             mResumed = false;
         }
 
         timestampUs -= previousPausedDurationUs;
-        CHECK(timestampUs >= 0);
+        CHECK_GE(timestampUs, 0ll);
         if (!mIsAudio) {
             /*
              * Composition time: timestampUs
@@ -1975,7 +1975,7 @@
             decodingTimeUs -= previousPausedDurationUs;
             cttsOffsetTimeUs =
                     timestampUs + kMaxCttsOffsetTimeUs - decodingTimeUs;
-            CHECK(cttsOffsetTimeUs >= 0);
+            CHECK_GE(cttsOffsetTimeUs, 0ll);
             timestampUs = decodingTimeUs;
             ALOGV("decoding time: %lld and ctts offset time: %lld",
                 timestampUs, cttsOffsetTimeUs);
@@ -1983,16 +1983,23 @@
             // Update ctts box table if necessary
             currCttsOffsetTimeTicks =
                     (cttsOffsetTimeUs * mTimeScale + 500000LL) / 1000000LL;
-            CHECK(currCttsOffsetTimeTicks <= 0x7FFFFFFFLL);
-#if 0
-            // FIXME:
-            // Optimize to reduce the number of ctts table entries.
-            // Also, make sure that the very first ctts table entry contains
-            // only a single sample.
-#else
-            addOneCttsTableEntry(1, currCttsOffsetTimeTicks);
-#endif
-            lastCttsOffsetTimeTicks = currCttsOffsetTimeTicks;
+            CHECK_LE(currCttsOffsetTimeTicks, 0x0FFFFFFFFLL);
+            if (mNumSamples == 0) {
+                // Force the first ctts table entry to have one single entry
+                // so that we can do adjustment for the initial track start
+                // time offset easily in writeCttsBox().
+                lastCttsOffsetTimeTicks = currCttsOffsetTimeTicks;
+                addOneCttsTableEntry(1, currCttsOffsetTimeTicks);
+                cttsSampleCount = 0;      // No sample in ctts box is pending
+            } else {
+                if (currCttsOffsetTimeTicks != lastCttsOffsetTimeTicks) {
+                    addOneCttsTableEntry(cttsSampleCount, lastCttsOffsetTimeTicks);
+                    lastCttsOffsetTimeTicks = currCttsOffsetTimeTicks;
+                    cttsSampleCount = 1;  // One sample in ctts box is pending
+                } else {
+                    ++cttsSampleCount;
+                }
+            }
 
             // Update ctts time offset range
             if (mNumSamples == 0) {
@@ -2014,7 +2021,7 @@
             }
         }
 
-        CHECK(timestampUs >= 0);
+        CHECK_GE(timestampUs, 0ll);
         ALOGV("%s media time stamp: %lld and previous paused duration %lld",
                 mIsAudio? "Audio": "Video", timestampUs, previousPausedDurationUs);
         if (timestampUs > mTrackDurationUs) {
@@ -2029,7 +2036,7 @@
         currDurationTicks =
             ((timestampUs * mTimeScale + 500000LL) / 1000000LL -
                 (lastTimestampUs * mTimeScale + 500000LL) / 1000000LL);
-        CHECK(currDurationTicks >= 0);
+        CHECK_GE(currDurationTicks, 0ll);
 
         mSampleSizes.push_back(sampleSize);
         ++mNumSamples;
@@ -2127,7 +2134,6 @@
         lastDurationTicks = 0;
     } else {
         ++sampleCount;  // Count for the last sample
-        ++cttsSampleCount;
     }
 
     if (mNumSamples <= 2) {
@@ -2139,6 +2145,14 @@
         addOneSttsTableEntry(sampleCount, lastDurationTicks);
     }
 
+    // The last ctts box may not have been written yet, and this
+    // is to make sure that we write out the last ctts box.
+    if (currCttsOffsetTimeTicks == lastCttsOffsetTimeTicks) {
+        if (cttsSampleCount > 0) {
+            addOneCttsTableEntry(cttsSampleCount, lastCttsOffsetTimeTicks);
+        }
+    }
+
     mTrackDurationUs += lastDurationUs;
     mReachedEOS = true;
 
@@ -2404,7 +2418,7 @@
     mOwner->writeInt16(0x18);        // depth
     mOwner->writeInt16(-1);          // predefined
 
-    CHECK(23 + mCodecSpecificDataSize < 128);
+    CHECK_LT(23 + mCodecSpecificDataSize, 128);
 
     if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_MPEG4, mime)) {
         writeMp4vEsdsBox();
@@ -2463,10 +2477,10 @@
 void MPEG4Writer::Track::writeMp4aEsdsBox() {
     mOwner->beginBox("esds");
     CHECK(mCodecSpecificData);
-    CHECK(mCodecSpecificDataSize > 0);
+    CHECK_GT(mCodecSpecificDataSize, 0);
 
     // Make sure all sizes encode to a single byte.
-    CHECK(mCodecSpecificDataSize + 23 < 128);
+    CHECK_LT(mCodecSpecificDataSize + 23, 128);
 
     mOwner->writeInt32(0);     // version=0, flags=0
     mOwner->writeInt8(0x03);   // ES_DescrTag
@@ -2500,7 +2514,7 @@
 
 void MPEG4Writer::Track::writeMp4vEsdsBox() {
     CHECK(mCodecSpecificData);
-    CHECK(mCodecSpecificDataSize > 0);
+    CHECK_GT(mCodecSpecificDataSize, 0);
     mOwner->beginBox("esds");
 
     mOwner->writeInt32(0);    // version=0, flags=0
@@ -2660,7 +2674,7 @@
 
 void MPEG4Writer::Track::writeAvccBox() {
     CHECK(mCodecSpecificData);
-    CHECK(mCodecSpecificDataSize >= 5);
+    CHECK_GE(mCodecSpecificDataSize, 5);
 
     // Patch avcc's lengthSize field to match the number
     // of bytes we use to indicate the size of a nal unit.
@@ -2692,7 +2706,7 @@
     int64_t trackStartTimeOffsetUs = 0;
     int64_t moovStartTimeUs = mOwner->getStartTimestampUs();
     if (mStartTimestampUs != moovStartTimeUs) {
-        CHECK(mStartTimestampUs > moovStartTimeUs);
+        CHECK_GT(mStartTimestampUs, moovStartTimeUs);
         trackStartTimeOffsetUs = mStartTimestampUs - moovStartTimeUs;
     }
     return (trackStartTimeOffsetUs *  mTimeScale + 500000LL) / 1000000LL;
@@ -2715,7 +2729,7 @@
         mOwner->writeInt32(it->sampleDuration);
         totalCount += it->sampleCount;
     }
-    CHECK(totalCount == mNumSamples);
+    CHECK_EQ(totalCount, mNumSamples);
     mOwner->endBox();  // stts
 }
 
@@ -2758,7 +2772,7 @@
         mOwner->writeInt32(it->sampleDuration - mMinCttsOffsetTimeUs);
         totalCount += it->sampleCount;
     }
-    CHECK(totalCount == mNumSamples);
+    CHECK_EQ(totalCount, mNumSamples);
     mOwner->endBox();  // ctts
 }
 
diff --git a/media/libstagefright/matroska/MatroskaExtractor.cpp b/media/libstagefright/matroska/MatroskaExtractor.cpp
index a1644d2..a0db719 100644
--- a/media/libstagefright/matroska/MatroskaExtractor.cpp
+++ b/media/libstagefright/matroska/MatroskaExtractor.cpp
@@ -93,7 +93,10 @@
 
     void advance();
     void reset();
-    void seek(int64_t seekTimeUs, bool seekToKeyFrame);
+
+    void seek(
+            int64_t seekTimeUs, bool seekToKeyFrame,
+            int64_t *actualFrameTimeUs);
 
     const mkvparser::Block *block() const;
     int64_t blockTimeUs() const;
@@ -303,22 +306,52 @@
     } while (!eos() && block()->GetTrackNumber() != mTrackNum);
 }
 
-void BlockIterator::seek(int64_t seekTimeUs, bool seekToKeyFrame) {
+void BlockIterator::seek(
+        int64_t seekTimeUs, bool seekToKeyFrame,
+        int64_t *actualFrameTimeUs) {
     Mutex::Autolock autoLock(mExtractor->mLock);
 
-    mCluster = mExtractor->mSegment->FindCluster(seekTimeUs * 1000ll);
+    *actualFrameTimeUs = -1ll;
+
+    int64_t seekTimeNs = seekTimeUs * 1000ll;
+
+    mCluster = mExtractor->mSegment->FindCluster(seekTimeNs);
     mBlockEntry = NULL;
     mBlockEntryIndex = 0;
 
-    do {
-        advance_l();
-    }
-    while (!eos() && block()->GetTrackNumber() != mTrackNum);
+    long prevKeyFrameBlockEntryIndex = -1;
 
-    if (seekToKeyFrame) {
-        while (!eos() && !mBlockEntry->GetBlock()->IsKey()) {
-            advance_l();
+    for (;;) {
+        advance_l();
+
+        if (eos()) {
+            break;
         }
+
+        if (block()->GetTrackNumber() != mTrackNum) {
+            continue;
+        }
+
+        if (block()->IsKey()) {
+            prevKeyFrameBlockEntryIndex = mBlockEntryIndex - 1;
+        }
+
+        int64_t timeNs = block()->GetTime(mCluster);
+
+        if (timeNs >= seekTimeNs) {
+            *actualFrameTimeUs = (timeNs + 500ll) / 1000ll;
+            break;
+        }
+    }
+
+    if (eos()) {
+        return;
+    }
+
+    if (seekToKeyFrame && !block()->IsKey()) {
+        CHECK_GE(prevKeyFrameBlockEntryIndex, 0);
+        mBlockEntryIndex = prevKeyFrameBlockEntryIndex;
+        advance_l();
     }
 }
 
@@ -397,6 +430,8 @@
         MediaBuffer **out, const ReadOptions *options) {
     *out = NULL;
 
+    int64_t targetSampleTimeUs = -1ll;
+
     int64_t seekTimeUs;
     ReadOptions::SeekMode mode;
     if (options && options->getSeekTo(&seekTimeUs, &mode)
@@ -406,10 +441,14 @@
         // Apparently keyframe indication in audio tracks is unreliable,
         // fortunately in all our currently supported audio encodings every
         // frame is effectively a keyframe.
-        mBlockIter.seek(seekTimeUs, !mIsAudio);
+        int64_t actualFrameTimeUs;
+        mBlockIter.seek(seekTimeUs, !mIsAudio, &actualFrameTimeUs);
+
+        if (mode == ReadOptions::SEEK_CLOSEST) {
+            targetSampleTimeUs = actualFrameTimeUs;
+        }
     }
 
-again:
     while (mPendingFrames.empty()) {
         status_t err = readBlock();
 
@@ -424,6 +463,11 @@
     mPendingFrames.erase(mPendingFrames.begin());
 
     if (mType != AVC) {
+        if (targetSampleTimeUs >= 0ll) {
+            frame->meta_data()->setInt64(
+                    kKeyTargetTime, targetSampleTimeUs);
+        }
+
         *out = frame;
 
         return OK;
@@ -506,6 +550,11 @@
     frame->release();
     frame = NULL;
 
+    if (targetSampleTimeUs >= 0ll) {
+        buffer->meta_data()->setInt64(
+                kKeyTargetTime, targetSampleTimeUs);
+    }
+
     *out = buffer;
 
     return OK;
diff --git a/packages/SystemUI/res/values-et/strings.xml b/packages/SystemUI/res/values-et/strings.xml
index a5bfb4a..16bb040 100644
--- a/packages/SystemUI/res/values-et/strings.xml
+++ b/packages/SystemUI/res/values-et/strings.xml
@@ -65,7 +65,7 @@
     <string name="screenshot_saving_ticker" msgid="7403652894056693515">"Kuvatõmmise salvestamine ..."</string>
     <string name="screenshot_saving_title" msgid="8242282144535555697">"Kuvatõmmise salvestamine ..."</string>
     <string name="screenshot_saving_text" msgid="2419718443411738818">"Kuvatõmmist salvestatakse."</string>
-    <string name="screenshot_saved_title" msgid="6461865960961414961">"Kuvatõmmis on jäädvustatud."</string>
+    <string name="screenshot_saved_title" msgid="6461865960961414961">"Ekraanipilt on jäädvustatud."</string>
     <string name="screenshot_saved_text" msgid="1152839647677558815">"Puudutage kuvatõmmise vaatamiseks."</string>
     <string name="screenshot_failed_title" msgid="705781116746922771">"Kuvatõmmist ei saanud jäädvustada."</string>
     <string name="screenshot_failed_text" msgid="8134011269572415402">"Kuvatõmmist ei saa salvestada. Mäluseade võib olla kasutuses."</string>
diff --git a/packages/SystemUI/res/values-zu/strings.xml b/packages/SystemUI/res/values-zu/strings.xml
index 39b428a..0249e89 100644
--- a/packages/SystemUI/res/values-zu/strings.xml
+++ b/packages/SystemUI/res/values-zu/strings.xml
@@ -36,7 +36,7 @@
     <string name="status_bar_latest_events_title" msgid="6594767438577593172">"Izaziso"</string>
     <string name="battery_low_title" msgid="2783104807551211639">"Xhuma ishaja."</string>
     <string name="battery_low_subtitle" msgid="1752040062087829196">"Ibhetri iya ngokuphela."</string>
-    <string name="battery_low_percent_format" msgid="1077244949318261761">"okusele okungu-<xliff:g id="NUMBER">%d%%</xliff:g>"</string>
+    <string name="battery_low_percent_format" msgid="1077244949318261761">"<xliff:g id="NUMBER">%d%%</xliff:g> okusele"</string>
     <string name="invalid_charger" msgid="4549105996740522523">"Ukushaja i-USB akusekelwe."\n"Sebenzisa kuphela ishaja enikeziwe."</string>
     <string name="battery_low_why" msgid="7279169609518386372">"Ukusebenzisa ibhetri"</string>
     <string name="status_bar_settings_settings_button" msgid="3023889916699270224">"Izilungiselelo"</string>
diff --git a/services/java/com/android/server/InputMethodManagerService.java b/services/java/com/android/server/InputMethodManagerService.java
index aba1bc6..86669f8 100644
--- a/services/java/com/android/server/InputMethodManagerService.java
+++ b/services/java/com/android/server/InputMethodManagerService.java
@@ -101,6 +101,7 @@
 import java.io.IOException;
 import java.io.PrintWriter;
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.Comparator;
 import java.util.HashMap;
 import java.util.HashSet;
@@ -154,6 +155,7 @@
     final IWindowManager mIWindowManager;
     final HandlerCaller mCaller;
     private final InputMethodFileManager mFileManager;
+    private final InputMethodAndSubtypeListManager mImListManager;
 
     final InputBindResult mNoBinding = new InputBindResult(null, null, -1);
 
@@ -555,6 +557,7 @@
         synchronized (mMethodMap) {
             mFileManager = new InputMethodFileManager(mMethodMap);
         }
+        mImListManager = new InputMethodAndSubtypeListManager(context, this);
 
         (new MyPackageMonitor()).register(mContext, true);
 
@@ -1693,6 +1696,19 @@
     }
 
     @Override
+    public boolean switchToNextInputMethod(IBinder token, boolean onlyCurrentIme) {
+        synchronized (mMethodMap) {
+            final ImeSubtypeListItem nextSubtype = mImListManager.getNextInputMethod(
+                    onlyCurrentIme, mMethodMap.get(mCurMethodId), mCurrentSubtype);
+            if (nextSubtype == null) {
+                return false;
+            }
+            setInputMethodWithSubtypeId(token, nextSubtype.mImi.getId(), nextSubtype.mSubtypeId);
+            return true;
+        }
+    }
+
+    @Override
     public InputMethodSubtype getLastInputMethodSubtype() {
         synchronized (mMethodMap) {
             final Pair<String, String> lastIme = mSettings.getLastInputMethodAndSubtypeLocked();
@@ -2109,62 +2125,9 @@
 
             hideInputMethodMenuLocked();
 
-            final TreeMap<InputMethodInfo, List<InputMethodSubtype>> sortedImmis =
-                    new TreeMap<InputMethodInfo, List<InputMethodSubtype>>(
-                            new Comparator<InputMethodInfo>() {
-                                @Override
-                                public int compare(InputMethodInfo imi1, InputMethodInfo imi2) {
-                                    if (imi2 == null) return 0;
-                                    if (imi1 == null) return 1;
-                                    if (pm == null) {
-                                        return imi1.getId().compareTo(imi2.getId());
-                                    }
-                                    CharSequence imiId1 = imi1.loadLabel(pm) + "/" + imi1.getId();
-                                    CharSequence imiId2 = imi2.loadLabel(pm) + "/" + imi2.getId();
-                                    return imiId1.toString().compareTo(imiId2.toString());
-                                }
-                            });
-
-            sortedImmis.putAll(immis);
-
-            final ArrayList<ImeSubtypeListItem> imList = new ArrayList<ImeSubtypeListItem>();
-
-            for (InputMethodInfo imi : sortedImmis.keySet()) {
-                if (imi == null) continue;
-                List<InputMethodSubtype> explicitlyOrImplicitlyEnabledSubtypeList = immis.get(imi);
-                HashSet<String> enabledSubtypeSet = new HashSet<String>();
-                for (InputMethodSubtype subtype: explicitlyOrImplicitlyEnabledSubtypeList) {
-                    enabledSubtypeSet.add(String.valueOf(subtype.hashCode()));
-                }
-                ArrayList<InputMethodSubtype> subtypes = getSubtypes(imi);
-                final CharSequence imeLabel = imi.loadLabel(pm);
-                if (showSubtypes && enabledSubtypeSet.size() > 0) {
-                    final int subtypeCount = imi.getSubtypeCount();
-                    if (DEBUG) {
-                        Slog.v(TAG, "Add subtypes: " + subtypeCount + ", " + imi.getId());
-                    }
-                    for (int j = 0; j < subtypeCount; ++j) {
-                        final InputMethodSubtype subtype = imi.getSubtypeAt(j);
-                        final String subtypeHashCode = String.valueOf(subtype.hashCode());
-                        // We show all enabled IMEs and subtypes when an IME is shown.
-                        if (enabledSubtypeSet.contains(subtypeHashCode)
-                                && ((mInputShown && !isScreenLocked) || !subtype.isAuxiliary())) {
-                            final CharSequence subtypeLabel =
-                                    subtype.overridesImplicitlyEnabledSubtype() ? null
-                                            : subtype.getDisplayName(context, imi.getPackageName(),
-                                                    imi.getServiceInfo().applicationInfo);
-                            imList.add(new ImeSubtypeListItem(imeLabel, subtypeLabel, imi, j));
-
-                            // Removing this subtype from enabledSubtypeSet because we no longer
-                            // need to add an entry of this subtype to imList to avoid duplicated
-                            // entries.
-                            enabledSubtypeSet.remove(subtypeHashCode);
-                        }
-                    }
-                } else {
-                    imList.add(new ImeSubtypeListItem(imeLabel, null, imi, NOT_A_SUBTYPE_ID));
-                }
-            }
+            final List<ImeSubtypeListItem> imList =
+                    mImListManager.getSortedInputMethodAndSubtypeList(
+                            showSubtypes, mInputShown, isScreenLocked);
 
             if (lastInputMethodSubtypeId == NOT_A_SUBTYPE_ID) {
                 final InputMethodSubtype currentSubtype = getCurrentInputMethodSubtype();
@@ -2786,6 +2749,117 @@
         }
     }
 
+    private static class InputMethodAndSubtypeListManager {
+        private final Context mContext;
+        private final PackageManager mPm;
+        private final InputMethodManagerService mImms;
+        public InputMethodAndSubtypeListManager(Context context, InputMethodManagerService imms) {
+            mContext = context;
+            mPm = context.getPackageManager();
+            mImms = imms;
+        }
+
+        private final TreeMap<InputMethodInfo, List<InputMethodSubtype>> mSortedImmis =
+                new TreeMap<InputMethodInfo, List<InputMethodSubtype>>(
+                        new Comparator<InputMethodInfo>() {
+                            @Override
+                            public int compare(InputMethodInfo imi1, InputMethodInfo imi2) {
+                                if (imi2 == null) return 0;
+                                if (imi1 == null) return 1;
+                                if (mPm == null) {
+                                    return imi1.getId().compareTo(imi2.getId());
+                                }
+                                CharSequence imiId1 = imi1.loadLabel(mPm) + "/" + imi1.getId();
+                                CharSequence imiId2 = imi2.loadLabel(mPm) + "/" + imi2.getId();
+                                return imiId1.toString().compareTo(imiId2.toString());
+                            }
+                        });
+
+        public ImeSubtypeListItem getNextInputMethod(
+                boolean onlyCurrentIme, InputMethodInfo imi, InputMethodSubtype subtype) {
+            if (imi == null) {
+                return null;
+            }
+            final List<ImeSubtypeListItem> imList = getSortedInputMethodAndSubtypeList();
+            if (imList.size() <= 1) {
+                return null;
+            }
+            final int N = imList.size();
+            final int currentSubtypeId = subtype != null
+                    ? mImms.getSubtypeIdFromHashCode(imi, subtype.hashCode())
+                    : NOT_A_SUBTYPE_ID;
+            for (int i = 0; i < N; ++i) {
+                final ImeSubtypeListItem isli = imList.get(i);
+                if (isli.mImi.equals(imi) && isli.mSubtypeId == currentSubtypeId) {
+                    if (!onlyCurrentIme) {
+                        return imList.get((i + 1) % N);
+                    }
+                    for (int j = 0; j < N - 1; ++j) {
+                        final ImeSubtypeListItem candidate = imList.get((i + j + 1) % N);
+                        if (candidate.mImi.equals(imi)) {
+                            return candidate;
+                        }
+                    }
+                    return null;
+                }
+            }
+            return null;
+        }
+
+        public List<ImeSubtypeListItem> getSortedInputMethodAndSubtypeList() {
+            return getSortedInputMethodAndSubtypeList(true, false, false);
+        }
+
+        public List<ImeSubtypeListItem> getSortedInputMethodAndSubtypeList(boolean showSubtypes,
+                boolean inputShown, boolean isScreenLocked) {
+            final ArrayList<ImeSubtypeListItem> imList = new ArrayList<ImeSubtypeListItem>();
+            final HashMap<InputMethodInfo, List<InputMethodSubtype>> immis =
+                    mImms.getExplicitlyOrImplicitlyEnabledInputMethodsAndSubtypeListLocked();
+            if (immis == null || immis.size() == 0) {
+                return Collections.emptyList();
+            }
+            mSortedImmis.clear();
+            mSortedImmis.putAll(immis);
+            for (InputMethodInfo imi : mSortedImmis.keySet()) {
+                if (imi == null) continue;
+                List<InputMethodSubtype> explicitlyOrImplicitlyEnabledSubtypeList = immis.get(imi);
+                HashSet<String> enabledSubtypeSet = new HashSet<String>();
+                for (InputMethodSubtype subtype: explicitlyOrImplicitlyEnabledSubtypeList) {
+                    enabledSubtypeSet.add(String.valueOf(subtype.hashCode()));
+                }
+                ArrayList<InputMethodSubtype> subtypes = getSubtypes(imi);
+                final CharSequence imeLabel = imi.loadLabel(mPm);
+                if (showSubtypes && enabledSubtypeSet.size() > 0) {
+                    final int subtypeCount = imi.getSubtypeCount();
+                    if (DEBUG) {
+                        Slog.v(TAG, "Add subtypes: " + subtypeCount + ", " + imi.getId());
+                    }
+                    for (int j = 0; j < subtypeCount; ++j) {
+                        final InputMethodSubtype subtype = imi.getSubtypeAt(j);
+                        final String subtypeHashCode = String.valueOf(subtype.hashCode());
+                        // We show all enabled IMEs and subtypes when an IME is shown.
+                        if (enabledSubtypeSet.contains(subtypeHashCode)
+                                && ((inputShown && !isScreenLocked) || !subtype.isAuxiliary())) {
+                            final CharSequence subtypeLabel =
+                                    subtype.overridesImplicitlyEnabledSubtype() ? null
+                                            : subtype.getDisplayName(mContext, imi.getPackageName(),
+                                                    imi.getServiceInfo().applicationInfo);
+                            imList.add(new ImeSubtypeListItem(imeLabel, subtypeLabel, imi, j));
+
+                            // Removing this subtype from enabledSubtypeSet because we no longer
+                            // need to add an entry of this subtype to imList to avoid duplicated
+                            // entries.
+                            enabledSubtypeSet.remove(subtypeHashCode);
+                        }
+                    }
+                } else {
+                    imList.add(new ImeSubtypeListItem(imeLabel, null, imi, NOT_A_SUBTYPE_ID));
+                }
+            }
+            return imList;
+        }
+    }
+
     /**
      * Utility class for putting and getting settings for InputMethod
      * TODO: Move all putters and getters of settings to this class.
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index a089fcf..f98b668 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -835,7 +835,7 @@
                         info.activityInfo.applicationInfo, true,
                         r.intent.getFlags() | Intent.FLAG_FROM_BACKGROUND,
                         "broadcast", r.curComponent,
-                        (r.intent.getFlags()&Intent.FLAG_RECEIVER_BOOT_UPGRADE) != 0))
+                        (r.intent.getFlags()&Intent.FLAG_RECEIVER_BOOT_UPGRADE) != 0, false))
                                 == null) {
                     // Ah, this recipient is unavailable.  Finish it if necessary,
                     // and mark the broadcast record as ready for the next.
@@ -1136,6 +1136,17 @@
     final ProcessMap<ProcessRecord> mProcessNames = new ProcessMap<ProcessRecord>();
 
     /**
+     * The currently running isolated processes.
+     */
+    final SparseArray<ProcessRecord> mIsolatedProcesses = new SparseArray<ProcessRecord>();
+
+    /**
+     * Counter for assigning isolated process uids, to avoid frequently reusing the
+     * same ones.
+     */
+    int mNextIsolatedProcessUid = 0;
+
+    /**
      * The currently running heavy-weight process, if any.
      */
     ProcessRecord mHeavyWeightProcess = null;
@@ -2082,7 +2093,7 @@
         try {
             ActivityManagerService m = mSelf;
             
-            ServiceManager.addService("activity", m);
+            ServiceManager.addService("activity", m, true);
             ServiceManager.addService("meminfo", new MemBinder(m));
             ServiceManager.addService("gfxinfo", new GraphicsBinder(m));
             ServiceManager.addService("dbinfo", new DbBinder(m));
@@ -2099,11 +2110,11 @@
             synchronized (mSelf) {
                 ProcessRecord app = mSelf.newProcessRecordLocked(
                         mSystemThread.getApplicationThread(), info,
-                        info.processName);
+                        info.processName, false);
                 app.persistent = true;
                 app.pid = MY_PID;
                 app.maxAdj = ProcessList.SYSTEM_ADJ;
-                mSelf.mProcessNames.put(app.processName, app.info.uid, app);
+                mSelf.mProcessNames.put(app.processName, app.uid, app);
                 synchronized (mSelf.mPidsSelfLocked) {
                     mSelf.mPidsSelfLocked.put(app.pid, app);
                 }
@@ -2621,8 +2632,15 @@
     
     final ProcessRecord startProcessLocked(String processName,
             ApplicationInfo info, boolean knownToBeDead, int intentFlags,
-            String hostingType, ComponentName hostingName, boolean allowWhileBooting) {
-        ProcessRecord app = getProcessRecordLocked(processName, info.uid);
+            String hostingType, ComponentName hostingName, boolean allowWhileBooting,
+            boolean isolated) {
+        ProcessRecord app;
+        if (!isolated) {
+            app = getProcessRecordLocked(processName, info.uid);
+        } else {
+            // If this is an isolated process, it can't re-use an existing process.
+            app = null;
+        }
         // We don't have to do anything more if:
         // (1) There is an existing application record; and
         // (2) The caller doesn't think it is dead, OR there is no thread
@@ -2651,36 +2669,46 @@
 
         String hostingNameStr = hostingName != null
                 ? hostingName.flattenToShortString() : null;
-        
-        if ((intentFlags&Intent.FLAG_FROM_BACKGROUND) != 0) {
-            // If we are in the background, then check to see if this process
-            // is bad.  If so, we will just silently fail.
-            if (mBadProcesses.get(info.processName, info.uid) != null) {
-                if (DEBUG_PROCESSES) Slog.v(TAG, "Bad process: " + info.uid
+
+        if (!isolated) {
+            if ((intentFlags&Intent.FLAG_FROM_BACKGROUND) != 0) {
+                // If we are in the background, then check to see if this process
+                // is bad.  If so, we will just silently fail.
+                if (mBadProcesses.get(info.processName, info.uid) != null) {
+                    if (DEBUG_PROCESSES) Slog.v(TAG, "Bad process: " + info.uid
+                            + "/" + info.processName);
+                    return null;
+                }
+            } else {
+                // When the user is explicitly starting a process, then clear its
+                // crash count so that we won't make it bad until they see at
+                // least one crash dialog again, and make the process good again
+                // if it had been bad.
+                if (DEBUG_PROCESSES) Slog.v(TAG, "Clearing bad process: " + info.uid
                         + "/" + info.processName);
-                return null;
-            }
-        } else {
-            // When the user is explicitly starting a process, then clear its
-            // crash count so that we won't make it bad until they see at
-            // least one crash dialog again, and make the process good again
-            // if it had been bad.
-            if (DEBUG_PROCESSES) Slog.v(TAG, "Clearing bad process: " + info.uid
-                    + "/" + info.processName);
-            mProcessCrashTimes.remove(info.processName, info.uid);
-            if (mBadProcesses.get(info.processName, info.uid) != null) {
-                EventLog.writeEvent(EventLogTags.AM_PROC_GOOD, info.uid,
-                        info.processName);
-                mBadProcesses.remove(info.processName, info.uid);
-                if (app != null) {
-                    app.bad = false;
+                mProcessCrashTimes.remove(info.processName, info.uid);
+                if (mBadProcesses.get(info.processName, info.uid) != null) {
+                    EventLog.writeEvent(EventLogTags.AM_PROC_GOOD, info.uid,
+                            info.processName);
+                    mBadProcesses.remove(info.processName, info.uid);
+                    if (app != null) {
+                        app.bad = false;
+                    }
                 }
             }
         }
-        
+
         if (app == null) {
-            app = newProcessRecordLocked(null, info, processName);
-            mProcessNames.put(processName, info.uid, app);
+            app = newProcessRecordLocked(null, info, processName, isolated);
+            if (app == null) {
+                Slog.w(TAG, "Failed making new process record for "
+                        + processName + "/" + info.uid + " isolated=" + isolated);
+                return null;
+            }
+            mProcessNames.put(processName, app.uid, app);
+            if (isolated) {
+                mIsolatedProcesses.put(app.uid, app);
+            }
         } else {
             // If this is a new package in the process, add the package to the list
             app.addPackage(info.packageName);
@@ -2726,14 +2754,16 @@
         mProcDeaths[0] = 0;
         
         try {
-            int uid = app.info.uid;
+            int uid = app.uid;
 
             int[] gids = null;
-            try {
-                gids = mContext.getPackageManager().getPackageGids(
-                        app.info.packageName);
-            } catch (PackageManager.NameNotFoundException e) {
-                Slog.w(TAG, "Unable to retrieve gids", e);
+            if (!app.isolated) {
+                try {
+                    gids = mContext.getPackageManager().getPackageGids(
+                            app.info.packageName);
+                } catch (PackageManager.NameNotFoundException e) {
+                    Slog.w(TAG, "Unable to retrieve gids", e);
+                }
             }
             if (mFactoryTest != SystemServer.FACTORY_TEST_OFF) {
                 if (mFactoryTest == SystemServer.FACTORY_TEST_LOW_LEVEL
@@ -2934,37 +2964,52 @@
         return mCompatModePackages.compatibilityInfoForPackageLocked(ai);
     }
 
+    void enforceNotIsolatedCaller(String caller) {
+        if (UserId.isIsolated(Binder.getCallingUid())) {
+            throw new SecurityException("Isolated process not allowed to call " + caller);
+        }
+    }
+
     public int getFrontActivityScreenCompatMode() {
+        enforceNotIsolatedCaller("getFrontActivityScreenCompatMode");
         synchronized (this) {
             return mCompatModePackages.getFrontActivityScreenCompatModeLocked();
         }
     }
 
     public void setFrontActivityScreenCompatMode(int mode) {
+        enforceCallingPermission(android.Manifest.permission.SET_SCREEN_COMPATIBILITY,
+                "setFrontActivityScreenCompatMode");
         synchronized (this) {
             mCompatModePackages.setFrontActivityScreenCompatModeLocked(mode);
         }
     }
 
     public int getPackageScreenCompatMode(String packageName) {
+        enforceNotIsolatedCaller("getPackageScreenCompatMode");
         synchronized (this) {
             return mCompatModePackages.getPackageScreenCompatModeLocked(packageName);
         }
     }
 
     public void setPackageScreenCompatMode(String packageName, int mode) {
+        enforceCallingPermission(android.Manifest.permission.SET_SCREEN_COMPATIBILITY,
+                "setPackageScreenCompatMode");
         synchronized (this) {
             mCompatModePackages.setPackageScreenCompatModeLocked(packageName, mode);
         }
     }
 
     public boolean getPackageAskScreenCompat(String packageName) {
+        enforceNotIsolatedCaller("getPackageAskScreenCompat");
         synchronized (this) {
             return mCompatModePackages.getPackageAskCompatModeLocked(packageName);
         }
     }
 
     public void setPackageAskScreenCompat(String packageName, boolean ask) {
+        enforceCallingPermission(android.Manifest.permission.SET_SCREEN_COMPATIBILITY,
+                "setPackageAskScreenCompat");
         synchronized (this) {
             mCompatModePackages.setPackageAskCompatModeLocked(packageName, ask);
         }
@@ -3039,6 +3084,7 @@
             int grantedMode, IBinder resultTo,
             String resultWho, int requestCode, boolean onlyIfNeeded, boolean debug,
             String profileFile, ParcelFileDescriptor profileFd, boolean autoStopProfiler) {
+        enforceNotIsolatedCaller("startActivity");
         int userId = 0;
         if (intent.getCategories() != null && intent.getCategories().contains(Intent.CATEGORY_HOME)) {
             // Requesting home, set the identity to the current user
@@ -3063,6 +3109,7 @@
             int grantedMode, IBinder resultTo,
             String resultWho, int requestCode, boolean onlyIfNeeded, boolean debug,
             String profileFile, ParcelFileDescriptor profileFd, boolean autoStopProfiler) {
+        enforceNotIsolatedCaller("startActivityAndWait");
         WaitResult res = new WaitResult();
         int userId = Binder.getOrigCallingUser();
         mMainStack.startActivityMayWait(caller, -1, intent, resolvedType,
@@ -3077,6 +3124,7 @@
             int grantedMode, IBinder resultTo,
             String resultWho, int requestCode, boolean onlyIfNeeded,
             boolean debug, Configuration config) {
+        enforceNotIsolatedCaller("startActivityWithConfig");
         int ret = mMainStack.startActivityMayWait(caller, -1, intent, resolvedType,
                 grantedUriPermissions, grantedMode, resultTo, resultWho,
                 requestCode, onlyIfNeeded,
@@ -3088,6 +3136,7 @@
             IntentSender intent, Intent fillInIntent, String resolvedType,
             IBinder resultTo, String resultWho, int requestCode,
             int flagsMask, int flagsValues) {
+        enforceNotIsolatedCaller("startActivityIntentSender");
         // Refuse possible leaked file descriptors
         if (fillInIntent != null && fillInIntent.hasFileDescriptors()) {
             throw new IllegalArgumentException("File descriptors passed in Intent");
@@ -3229,6 +3278,7 @@
 
     public final int startActivities(IApplicationThread caller,
             Intent[] intents, String[] resolvedTypes, IBinder resultTo) {
+        enforceNotIsolatedCaller("startActivities");
         int ret = mMainStack.startActivities(caller, -1, intents, resolvedTypes, resultTo,
                 Binder.getOrigCallingUser());
         return ret;
@@ -3405,7 +3455,7 @@
             synchronized (mPidsSelfLocked) {
                 for (int i=0; i<mPidsSelfLocked.size(); i++) {
                     ProcessRecord p = mPidsSelfLocked.valueAt(i);
-                    if (p.info.uid != uid) {
+                    if (p.uid != uid) {
                         continue;
                     }
                     if (p.pid == initialPid) {
@@ -3990,6 +4040,7 @@
     
     public boolean clearApplicationUserData(final String packageName,
             final IPackageDataObserver observer, final int userId) {
+        enforceNotIsolatedCaller("clearApplicationUserData");
         int uid = Binder.getCallingUid();
         int pid = Binder.getCallingPid();
         long callingId = Binder.clearCallingIdentity();
@@ -4178,6 +4229,7 @@
     }
 
     public void closeSystemDialogs(String reason) {
+        enforceNotIsolatedCaller("closeSystemDialogs");
         Intent intent = new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
         intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
         if (reason != null) {
@@ -4218,6 +4270,7 @@
     
     public Debug.MemoryInfo[] getProcessMemoryInfo(int[] pids)
             throws RemoteException {
+        enforceNotIsolatedCaller("getProcessMemoryInfo");
         Debug.MemoryInfo[] infos = new Debug.MemoryInfo[pids.length];
         for (int i=pids.length-1; i>=0; i--) {
             infos[i] = new Debug.MemoryInfo();
@@ -4227,6 +4280,7 @@
     }
 
     public long[] getProcessPss(int[] pids) throws RemoteException {
+        enforceNotIsolatedCaller("getProcessPss");
         long[] pss = new long[pids.length];
         for (int i=pids.length-1; i>=0; i--) {
             pss[i] = Debug.getPss(pids[i]);
@@ -4389,6 +4443,7 @@
                     service.app.removed = true;
                 }
                 service.app = null;
+                service.isolatedProc = null;
                 services.add(service);
             }
         }
@@ -4434,12 +4489,13 @@
     private final boolean removeProcessLocked(ProcessRecord app,
             boolean callerWillRestart, boolean allowRestart, String reason) {
         final String name = app.processName;
-        final int uid = app.info.uid;
+        final int uid = app.uid;
         if (DEBUG_PROCESSES) Slog.d(
             TAG, "Force removing proc " + app.toShortString() + " (" + name
             + "/" + uid + ")");
 
         mProcessNames.remove(name, uid);
+        mIsolatedProcesses.remove(app.uid);
         if (mHeavyWeightProcess == app) {
             mHeavyWeightProcess = null;
             mHandler.sendEmptyMessage(CANCEL_HEAVY_NOTIFICATION_MSG);
@@ -4456,9 +4512,9 @@
             mLruProcesses.remove(app);
             Process.killProcessQuiet(pid);
             
-            if (app.persistent) {
+            if (app.persistent && !app.isolated) {
                 if (!callerWillRestart) {
-                    addAppLocked(app.info);
+                    addAppLocked(app.info, false);
                 } else {
                     needRestart = true;
                 }
@@ -4483,9 +4539,10 @@
         
         if (gone) {
             Slog.w(TAG, "Process " + app + " failed to attach");
-            EventLog.writeEvent(EventLogTags.AM_PROCESS_START_TIMEOUT, pid, app.info.uid,
+            EventLog.writeEvent(EventLogTags.AM_PROCESS_START_TIMEOUT, pid, app.uid,
                     app.processName);
-            mProcessNames.remove(app.processName, app.info.uid);
+            mProcessNames.remove(app.processName, app.uid);
+            mIsolatedProcesses.remove(app.uid);
             if (mHeavyWeightProcess == app) {
                 mHeavyWeightProcess = null;
                 mHandler.sendEmptyMessage(CANCEL_HEAVY_NOTIFICATION_MSG);
@@ -4495,9 +4552,11 @@
             // Take care of any services that are waiting for the process.
             for (int i=0; i<mPendingServices.size(); i++) {
                 ServiceRecord sr = mPendingServices.get(i);
-                if (app.info.uid == sr.appInfo.uid
-                        && app.processName.equals(sr.processName)) {
+                if ((app.uid == sr.appInfo.uid
+                        && app.processName.equals(sr.processName))
+                        || sr.isolatedProc == app) {
                     Slog.w(TAG, "Forcing bringing down service: " + sr);
+                    sr.isolatedProc = null;
                     mPendingServices.remove(i);
                     i--;
                     bringDownServiceLocked(sr, true);
@@ -4678,7 +4737,7 @@
         // See if the top visible activity is waiting to run in this process...
         ActivityRecord hr = mMainStack.topRunningActivityLocked(null);
         if (hr != null && normalMode) {
-            if (hr.app == null && app.info.uid == hr.info.applicationInfo.uid
+            if (hr.app == null && app.uid == hr.info.applicationInfo.uid
                     && processName.equals(hr.processName)) {
                 try {
                     if (mMainStack.realStartActivityLocked(hr, app, true, true)) {
@@ -4700,8 +4759,8 @@
             try {
                 for (int i=0; i<mPendingServices.size(); i++) {
                     sr = mPendingServices.get(i);
-                    if (app.info.uid != sr.appInfo.uid
-                            || !processName.equals(sr.processName)) {
+                    if (app != sr.isolatedProc && (app.uid != sr.appInfo.uid
+                            || !processName.equals(sr.processName))) {
                         continue;
                     }
 
@@ -4728,7 +4787,7 @@
         }
 
         // Check whether the next backup agent is in this process...
-        if (!badApp && mBackupTarget != null && mBackupTarget.appInfo.uid == app.info.uid) {
+        if (!badApp && mBackupTarget != null && mBackupTarget.appInfo.uid == app.uid) {
             if (DEBUG_BACKUP) Slog.v(TAG, "New app is backup target, launching agent for " + app);
             ensurePackageDexOpt(mBackupTarget.appInfo.packageName);
             try {
@@ -4790,10 +4849,12 @@
     }
 
     public void showBootMessage(final CharSequence msg, final boolean always) {
+        enforceNotIsolatedCaller("showBootMessage");
         mWindowManager.showBootMessage(msg, always);
     }
 
     public void dismissKeyguardOnNextActivity() {
+        enforceNotIsolatedCaller("dismissKeyguardOnNextActivity");
         synchronized (this) {
             mMainStack.dismissKeyguardOnNextActivityLocked();
         }
@@ -4956,6 +5017,7 @@
     public IIntentSender getIntentSender(int type,
             String packageName, IBinder token, String resultWho,
             int requestCode, Intent[] intents, String[] resolvedTypes, int flags) {
+        enforceNotIsolatedCaller("getIntentSender");
         // Refuse possible leaked file descriptors
         if (intents != null) {
             if (intents.length < 1) {
@@ -5183,7 +5245,7 @@
             
             synchronized (mPidsSelfLocked) {
                 ProcessRecord pr = mPidsSelfLocked.get(pid);
-                if (pr == null) {
+                if (pr == null && isForeground) {
                     Slog.w(TAG, "setProcessForeground called on unknown pid: " + pid);
                     return;
                 }
@@ -5191,7 +5253,9 @@
                 if (oldToken != null) {
                     oldToken.token.unlinkToDeath(oldToken, 0);
                     mForegroundProcesses.remove(pid);
-                    pr.forcingToForeground = null;
+                    if (pr != null) {
+                        pr.forcingToForeground = null;
+                    }
                     changed = true;
                 }
                 if (isForeground && token != null) {
@@ -5256,6 +5320,10 @@
         if (uid == 0 || uid == Process.SYSTEM_UID || pid == MY_PID) {
             return PackageManager.PERMISSION_GRANTED;
         }
+        // Isolated processes don't get any permissions.
+        if (UserId.isIsolated(uid)) {
+            return PackageManager.PERMISSION_DENIED;
+        }
         // If there is a uid that owns whatever is being accessed, it has
         // blanket access to it regardless of the permissions it requires.
         if (owningUid >= 0 && UserId.isSameApp(uid, owningUid)) {
@@ -5408,6 +5476,8 @@
     }
 
     public int checkUriPermission(Uri uri, int pid, int uid, int modeFlags) {
+        enforceNotIsolatedCaller("checkUriPermission");
+
         // Another redirected-binder-call permissions check as in
         // {@link checkComponentPermission}.
         Identity tlsIdentity = sCallerIdentity.get();
@@ -5558,6 +5628,7 @@
 
     public int checkGrantUriPermission(int callingUid, String targetPkg,
             Uri uri, int modeFlags) {
+        enforceNotIsolatedCaller("checkGrantUriPermission");
         synchronized(this) {
             return checkGrantUriPermissionLocked(callingUid, targetPkg, uri, modeFlags);
         }
@@ -5666,6 +5737,7 @@
 
     public void grantUriPermission(IApplicationThread caller, String targetPkg,
             Uri uri, int modeFlags) {
+        enforceNotIsolatedCaller("grantUriPermission");
         synchronized(this) {
             final ProcessRecord r = getRecordForAppLocked(caller);
             if (r == null) {
@@ -5680,7 +5752,7 @@
                 throw new IllegalArgumentException("null uri");
             }
 
-            grantUriPermissionLocked(r.info.uid, targetPkg, uri, modeFlags,
+            grantUriPermissionLocked(r.uid, targetPkg, uri, modeFlags,
                     null);
         }
     }
@@ -5789,6 +5861,7 @@
 
     public void revokeUriPermission(IApplicationThread caller, Uri uri,
             int modeFlags) {
+        enforceNotIsolatedCaller("revokeUriPermission");
         synchronized(this) {
             final ProcessRecord r = getRecordForAppLocked(caller);
             if (r == null) {
@@ -5811,8 +5884,7 @@
 
             final String authority = uri.getAuthority();
             ProviderInfo pi = null;
-            ContentProviderRecord cpr = mProviderMap.getProviderByName(authority,
-                    UserId.getUserId(r.info.uid));
+            ContentProviderRecord cpr = mProviderMap.getProviderByName(authority, r.userId);
             if (cpr != null) {
                 pi = cpr.info;
             } else {
@@ -5828,12 +5900,13 @@
                 return;
             }
 
-            revokeUriPermissionLocked(r.info.uid, uri, modeFlags);
+            revokeUriPermissionLocked(r.uid, uri, modeFlags);
         }
     }
 
     @Override
     public IBinder newUriPermissionOwner(String name) {
+        enforceNotIsolatedCaller("newUriPermissionOwner");
         synchronized(this) {
             UriPermissionOwner owner = new UriPermissionOwner(this, name);
             return owner.getExternalTokenLocked();
@@ -6370,6 +6443,7 @@
      * @return Returns true if the move completed, false if not.
      */
     public boolean moveActivityTaskToBack(IBinder token, boolean nonRoot) {
+        enforceNotIsolatedCaller("moveActivityTaskToBack");
         synchronized(this) {
             final long origId = Binder.clearCallingIdentity();
             int taskId = getTaskForActivityLocked(token, !nonRoot);
@@ -6424,6 +6498,7 @@
     }
 
     public void finishOtherInstances(IBinder token, ComponentName className) {
+        enforceNotIsolatedCaller("finishOtherInstances");
         synchronized(this) {
             final long origId = Binder.clearCallingIdentity();
 
@@ -6532,13 +6607,13 @@
         List<ProviderInfo> providers = null;
         try {
             providers = AppGlobals.getPackageManager().
-                queryContentProviders(app.processName, app.info.uid,
+                queryContentProviders(app.processName, app.uid,
                         STOCK_PM_FLAGS | PackageManager.GET_URI_PERMISSION_PATTERNS);
         } catch (RemoteException ex) {
         }
         if (DEBUG_MU)
-            Slog.v(TAG_MU, "generateApplicationProvidersLocked, app.info.uid = " + app.info.uid);
-        int userId = UserId.getUserId(app.info.uid);
+            Slog.v(TAG_MU, "generateApplicationProvidersLocked, app.info.uid = " + app.uid);
+        int userId = app.userId;
         if (providers != null) {
             final int N = providers.size();
             for (int i=0; i<N; i++) {
@@ -6564,7 +6639,7 @@
     private final String checkContentProviderPermissionLocked(
             ProviderInfo cpi, ProcessRecord r) {
         final int callingPid = (r != null) ? r.pid : Binder.getCallingPid();
-        final int callingUid = (r != null) ? r.info.uid : Binder.getCallingUid();
+        final int callingUid = (r != null) ? r.uid : Binder.getCallingUid();
         if (checkComponentPermission(cpi.readPermission, callingPid, callingUid,
                 cpi.applicationInfo.uid, cpi.exported)
                 == PackageManager.PERMISSION_GRANTED) {
@@ -6680,7 +6755,7 @@
             }
 
             // First check if this content provider has been published...
-            int userId = UserId.getUserId(r != null ? r.info.uid : Binder.getCallingUid());
+            int userId = UserId.getUserId(r != null ? r.uid : Binder.getCallingUid());
             cpr = mProviderMap.getProviderByName(name, userId);
             boolean providerRunning = cpr != null;
             if (providerRunning) {
@@ -6815,7 +6890,7 @@
 
                 if (DEBUG_PROVIDER) {
                     RuntimeException e = new RuntimeException("here");
-                    Slog.w(TAG, "LAUNCHING REMOTE PROVIDER (myuid " + r.info.uid
+                    Slog.w(TAG, "LAUNCHING REMOTE PROVIDER (myuid " + r.uid
                           + " pruid " + cpr.appInfo.uid + "): " + cpr.info.name, e);
                 }
 
@@ -6849,7 +6924,7 @@
                         ProcessRecord proc = startProcessLocked(cpi.processName,
                                 cpr.appInfo, false, 0, "content provider",
                                 new ComponentName(cpi.applicationInfo.packageName,
-                                        cpi.name), false);
+                                        cpi.name), false, false);
                         if (proc == null) {
                             Slog.w(TAG, "Unable to launch app "
                                     + cpi.applicationInfo.packageName + "/"
@@ -6902,6 +6977,7 @@
 
     public final ContentProviderHolder getContentProvider(
             IApplicationThread caller, String name) {
+        enforceNotIsolatedCaller("getContentProvider");
         if (caller == null) {
             String msg = "null IApplicationThread when getting content provider "
                     + name;
@@ -6922,6 +6998,7 @@
      * @param cpr
      */
     public void removeContentProvider(IApplicationThread caller, String name) {
+        enforceNotIsolatedCaller("removeContentProvider");
         synchronized (this) {
             int userId = UserId.getUserId(Binder.getCallingUid());
             ContentProviderRecord cpr = mProviderMap.getProviderByName(name, userId);
@@ -6984,10 +7061,11 @@
             return;
         }
 
+        enforceNotIsolatedCaller("publishContentProviders");
         synchronized(this) {
             final ProcessRecord r = getRecordForAppLocked(caller);
             if (DEBUG_MU)
-                Slog.v(TAG_MU, "ProcessRecord uid = " + r.info.uid);
+                Slog.v(TAG_MU, "ProcessRecord uid = " + r.uid);
             if (r == null) {
                 throw new SecurityException(
                         "Unable to find app for caller " + caller
@@ -7071,6 +7149,7 @@
      *     src/com/android/cts/usespermissiondiffcertapp/AccessPermissionWithDiffSigTest.java
      */
     public String getProviderMimeType(Uri uri) {
+        enforceNotIsolatedCaller("getProviderMimeType");
         final String name = uri.getAuthority();
         final long ident = Binder.clearCallingIdentity();
         ContentProviderHolder holder = null;
@@ -7098,22 +7177,52 @@
     // =========================================================
 
     final ProcessRecord newProcessRecordLocked(IApplicationThread thread,
-            ApplicationInfo info, String customProcess) {
+            ApplicationInfo info, String customProcess, boolean isolated) {
         String proc = customProcess != null ? customProcess : info.processName;
         BatteryStatsImpl.Uid.Proc ps = null;
         BatteryStatsImpl stats = mBatteryStatsService.getActiveStatistics();
+        int uid = info.uid;
+        if (isolated) {
+            int userId = UserId.getUserId(uid);
+            int stepsLeft = Process.LAST_ISOLATED_UID - Process.FIRST_ISOLATED_UID + 1;
+            uid = 0;
+            while (true) {
+                if (mNextIsolatedProcessUid < Process.FIRST_ISOLATED_UID
+                        || mNextIsolatedProcessUid > Process.LAST_ISOLATED_UID) {
+                    mNextIsolatedProcessUid = Process.FIRST_ISOLATED_UID;
+                }
+                uid = UserId.getUid(userId, mNextIsolatedProcessUid);
+                mNextIsolatedProcessUid++;
+                if (mIsolatedProcesses.indexOfKey(uid) < 0) {
+                    // No process for this uid, use it.
+                    break;
+                }
+                stepsLeft--;
+                if (stepsLeft <= 0) {
+                    return null;
+                }
+            }
+        }
         synchronized (stats) {
             ps = stats.getProcessStatsLocked(info.uid, proc);
         }
-        return new ProcessRecord(ps, thread, info, proc);
+        return new ProcessRecord(ps, thread, info, proc, uid);
     }
 
-    final ProcessRecord addAppLocked(ApplicationInfo info) {
-        ProcessRecord app = getProcessRecordLocked(info.processName, info.uid);
+    final ProcessRecord addAppLocked(ApplicationInfo info, boolean isolated) {
+        ProcessRecord app;
+        if (!isolated) {
+            app = getProcessRecordLocked(info.processName, info.uid);
+        } else {
+            app = null;
+        }
 
         if (app == null) {
-            app = newProcessRecordLocked(null, info, null);
-            mProcessNames.put(info.processName, info.uid, app);
+            app = newProcessRecordLocked(null, info, null, isolated);
+            mProcessNames.put(info.processName, app.uid, app);
+            if (isolated) {
+                mIsolatedProcesses.put(app.uid, app);
+            }
             updateLruProcessLocked(app, true, true);
         }
 
@@ -7158,6 +7267,7 @@
     }
 
     public ParcelFileDescriptor openContentUri(Uri uri) throws RemoteException {
+        enforceNotIsolatedCaller("openContentUri");
         String name = uri.getAuthority();
         ContentProviderHolder cph = getContentProviderExternal(name);
         ParcelFileDescriptor pfd = null;
@@ -7411,6 +7521,7 @@
     }
     
     public void registerActivityWatcher(IActivityWatcher watcher) {
+        enforceNotIsolatedCaller("registerActivityWatcher");
         synchronized (this) {
             mWatchers.register(watcher);
         }
@@ -7423,6 +7534,7 @@
     }
 
     public void registerProcessObserver(IProcessObserver observer) {
+        enforceNotIsolatedCaller("registerProcessObserver");
         mProcessObservers.register(observer);
     }
 
@@ -7451,6 +7563,7 @@
     }
 
     public boolean isTopActivityImmersive() {
+        enforceNotIsolatedCaller("startActivity");
         synchronized (this) {
             ActivityRecord r = mMainStack.topRunningActivityLocked(null);
             return (r != null) ? r.immersive : false;
@@ -7862,7 +7975,7 @@
                                 = (ApplicationInfo)apps.get(i);
                             if (info != null &&
                                     !info.packageName.equals("android")) {
-                                addAppLocked(info);
+                                addAppLocked(info, false);
                             }
                         }
                     }
@@ -7961,14 +8074,18 @@
     private boolean handleAppCrashLocked(ProcessRecord app) {
         long now = SystemClock.uptimeMillis();
 
-        Long crashTime = mProcessCrashTimes.get(app.info.processName,
-                app.info.uid);
+        Long crashTime;
+        if (!app.isolated) {
+            crashTime = mProcessCrashTimes.get(app.info.processName, app.uid);
+        } else {
+            crashTime = null;
+        }
         if (crashTime != null && now < crashTime+ProcessList.MIN_CRASH_INTERVAL) {
             // This process loses!
             Slog.w(TAG, "Process " + app.info.processName
                     + " has crashed too many times: killing!");
             EventLog.writeEvent(EventLogTags.AM_PROCESS_CRASHED_TOO_MUCH,
-                    app.info.processName, app.info.uid);
+                    app.info.processName, app.uid);
             for (int i=mMainStack.mHistory.size()-1; i>=0; i--) {
                 ActivityRecord r = (ActivityRecord)mMainStack.mHistory.get(i);
                 if (r.app == app) {
@@ -7982,11 +8099,15 @@
                 // explicitly does so...  but for persistent process, we really
                 // need to keep it running.  If a persistent process is actually
                 // repeatedly crashing, then badness for everyone.
-                EventLog.writeEvent(EventLogTags.AM_PROC_BAD, app.info.uid,
+                EventLog.writeEvent(EventLogTags.AM_PROC_BAD, app.uid,
                         app.info.processName);
-                mBadProcesses.put(app.info.processName, app.info.uid, now);
+                if (!app.isolated) {
+                    // XXX We don't have a way to mark isolated processes
+                    // as bad, since they don't have a peristent identity.
+                    mBadProcesses.put(app.info.processName, app.uid, now);
+                    mProcessCrashTimes.remove(app.info.processName, app.uid);
+                }
                 app.bad = true;
-                mProcessCrashTimes.remove(app.info.processName, app.info.uid);
                 app.removed = true;
                 // Don't let services in this process be restarted and potentially
                 // annoy the user repeatedly.  Unless it is persistent, since those
@@ -8058,7 +8179,12 @@
             }
         }
 
-        mProcessCrashTimes.put(app.info.processName, app.info.uid, now);
+        if (!app.isolated) {
+            // XXX Can't keep track of crash times for isolated processes,
+            // because they don't have a perisistent identity.
+            mProcessCrashTimes.put(app.info.processName, app.uid, now);
+        }
+
         return true;
     }
 
@@ -8556,8 +8682,10 @@
 
         Intent appErrorIntent = null;
         synchronized (this) {
-            if (r != null) {
-                mProcessCrashTimes.put(r.info.processName, r.info.uid,
+            if (r != null && !r.isolated) {
+                // XXX Can't keep track of crash time for isolated processes,
+                // since they don't have a persistent identity.
+                mProcessCrashTimes.put(r.info.processName, r.uid,
                         SystemClock.uptimeMillis());
             }
             if (res == AppErrorDialog.FORCE_QUIT_AND_REPORT) {
@@ -8620,6 +8748,7 @@
     }
 
     public List<ActivityManager.ProcessErrorStateInfo> getProcessesInErrorState() {
+        enforceNotIsolatedCaller("getProcessesInErrorState");
         // assume our apps are happy - lazy create the list
         List<ActivityManager.ProcessErrorStateInfo> errList = null;
 
@@ -8682,6 +8811,7 @@
     }
 
     public List<ActivityManager.RunningAppProcessInfo> getRunningAppProcesses() {
+        enforceNotIsolatedCaller("getRunningAppProcesses");
         // Lazy instantiation of list
         List<ActivityManager.RunningAppProcessInfo> runList = null;
         synchronized (this) {
@@ -8727,6 +8857,7 @@
     }
 
     public List<ApplicationInfo> getRunningExternalApplications() {
+        enforceNotIsolatedCaller("getRunningExternalApplications");
         List<ActivityManager.RunningAppProcessInfo> runningApps = getRunningAppProcesses();
         List<ApplicationInfo> retList = new ArrayList<ApplicationInfo>();
         if (runningApps != null && runningApps.size() > 0) {
@@ -9095,7 +9226,21 @@
                 }
             }
         }
-        
+
+        if (mIsolatedProcesses.size() > 0) {
+            if (needSep) pw.println(" ");
+            needSep = true;
+            pw.println("  Isolated process list (sorted by uid):");
+            for (int i=0; i<mIsolatedProcesses.size(); i++) {
+                ProcessRecord r = mIsolatedProcesses.valueAt(i);
+                if (dumpPackage != null && !dumpPackage.equals(r.info.packageName)) {
+                    continue;
+                }
+                pw.println(String.format("%sIsolated #%2d: %s",
+                        "    ", i, r.toString()));
+            }
+        }
+
         if (mLruProcesses.size() > 0) {
             if (needSep) pw.println(" ");
             needSep = true;
@@ -10789,6 +10934,7 @@
                     sr.stats.stopLaunchedLocked();
                 }
                 sr.app = null;
+                sr.isolatedProc = null;
                 sr.executeNesting = 0;
                 if (mStoppingServices.remove(sr)) {
                     if (DEBUG_SERVICE) Slog.v(TAG, "killServices remove stopping " + sr);
@@ -11024,10 +11170,11 @@
             return;
         }
 
-        if (!app.persistent) {
+        if (!app.persistent || app.isolated) {
             if (DEBUG_PROCESSES) Slog.v(TAG,
                     "Removing non-persistent process during cleanup: " + app);
-            mProcessNames.remove(app.processName, app.info.uid);
+            mProcessNames.remove(app.processName, app.uid);
+            mIsolatedProcesses.remove(app.uid);
             if (mHeavyWeightProcess == app) {
                 mHeavyWeightProcess = null;
                 mHandler.sendEmptyMessage(CANCEL_HEAVY_NOTIFICATION_MSG);
@@ -11052,10 +11199,10 @@
             mPreviousProcess = null;
         }
 
-        if (restart) {
+        if (restart && !app.isolated) {
             // We have components that still need to be running in the
             // process, so re-launch it.
-            mProcessNames.put(app.processName, app.info.uid, app);
+            mProcessNames.put(app.processName, app.uid, app);
             startProcessLocked(app, "restart", app.processName);
         } else if (app.pid > 0 && app.pid != MY_PID) {
             // Goodbye!
@@ -11135,6 +11282,7 @@
     
     public List<ActivityManager.RunningServiceInfo> getServices(int maxNum,
             int flags) {
+        enforceNotIsolatedCaller("getServices");
         synchronized (this) {
             ArrayList<ActivityManager.RunningServiceInfo> res
                     = new ArrayList<ActivityManager.RunningServiceInfo>();
@@ -11161,6 +11309,7 @@
     }
 
     public PendingIntent getRunningServiceControlPanel(ComponentName name) {
+        enforceNotIsolatedCaller("getRunningServiceControlPanel");
         synchronized (this) {
             int userId = UserId.getUserId(Binder.getCallingUid());
             ServiceRecord r = mServiceMap.getServiceByName(name, userId);
@@ -11457,7 +11606,7 @@
         }
         if (DEBUG_MU)
             Slog.v(TAG_MU, "realStartServiceLocked, ServiceRecord.uid = " + r.appInfo.uid
-                    + ", ProcessRecord.uid = " + app.info.uid);
+                    + ", ProcessRecord.uid = " + app.uid);
         r.app = app;
         r.restartTime = r.lastActivity = SystemClock.uptimeMillis();
 
@@ -11652,35 +11801,53 @@
                     + r.packageName + ": " + e);
         }
 
+        final boolean isolated = (r.serviceInfo.flags&ServiceInfo.FLAG_ISOLATED_PROCESS) != 0;
         final String appName = r.processName;
-        ProcessRecord app = getProcessRecordLocked(appName, r.appInfo.uid);
-        if (DEBUG_MU)
-            Slog.v(TAG_MU, "bringUpServiceLocked: appInfo.uid=" + r.appInfo.uid + " app=" + app);
-        if (app != null && app.thread != null) {
-            try {
-                app.addPackage(r.appInfo.packageName);
-                realStartServiceLocked(r, app);
-                return true;
-            } catch (RemoteException e) {
-                Slog.w(TAG, "Exception when starting service " + r.shortName, e);
-            }
+        ProcessRecord app;
 
-            // If a dead object exception was thrown -- fall through to
-            // restart the application.
+        if (!isolated) {
+            app = getProcessRecordLocked(appName, r.appInfo.uid);
+            if (DEBUG_MU)
+                Slog.v(TAG_MU, "bringUpServiceLocked: appInfo.uid=" + r.appInfo.uid + " app=" + app);
+            if (app != null && app.thread != null) {
+                try {
+                    app.addPackage(r.appInfo.packageName);
+                    realStartServiceLocked(r, app);
+                    return true;
+                } catch (RemoteException e) {
+                    Slog.w(TAG, "Exception when starting service " + r.shortName, e);
+                }
+
+                // If a dead object exception was thrown -- fall through to
+                // restart the application.
+            }
+        } else {
+            // If this service runs in an isolated process, then each time
+            // we call startProcessLocked() we will get a new isolated
+            // process, starting another process if we are currently waiting
+            // for a previous process to come up.  To deal with this, we store
+            // in the service any current isolated process it is running in or
+            // waiting to have come up.
+            app = r.isolatedProc;
         }
 
         // Not running -- get it started, and enqueue this service record
         // to be executed when the app comes up.
-        if (startProcessLocked(appName, r.appInfo, true, intentFlags,
-                "service", r.name, false) == null) {
-            Slog.w(TAG, "Unable to launch app "
-                    + r.appInfo.packageName + "/"
-                    + r.appInfo.uid + " for service "
-                    + r.intent.getIntent() + ": process is bad");
-            bringDownServiceLocked(r, true);
-            return false;
+        if (app == null) {
+            if ((app=startProcessLocked(appName, r.appInfo, true, intentFlags,
+                    "service", r.name, false, isolated)) == null) {
+                Slog.w(TAG, "Unable to launch app "
+                        + r.appInfo.packageName + "/"
+                        + r.appInfo.uid + " for service "
+                        + r.intent.getIntent() + ": process is bad");
+                bringDownServiceLocked(r, true);
+                return false;
+            }
+            if (isolated) {
+                r.isolatedProc = app;
+            }
         }
-        
+
         if (!mPendingServices.contains(r)) {
             mPendingServices.add(r);
         }
@@ -11870,6 +12037,7 @@
 
     public ComponentName startService(IApplicationThread caller, Intent service,
             String resolvedType) {
+        enforceNotIsolatedCaller("startService");
         // Refuse possible leaked file descriptors
         if (service != null && service.hasFileDescriptors() == true) {
             throw new IllegalArgumentException("File descriptors passed in Intent");
@@ -11912,6 +12080,7 @@
 
     public int stopService(IApplicationThread caller, Intent service,
             String resolvedType) {
+        enforceNotIsolatedCaller("stopService");
         // Refuse possible leaked file descriptors
         if (service != null && service.hasFileDescriptors() == true) {
             throw new IllegalArgumentException("File descriptors passed in Intent");
@@ -11949,6 +12118,7 @@
     }
 
     public IBinder peekService(Intent service, String resolvedType) {
+        enforceNotIsolatedCaller("peekService");
         // Refuse possible leaked file descriptors
         if (service != null && service.hasFileDescriptors() == true) {
             throw new IllegalArgumentException("File descriptors passed in Intent");
@@ -12087,6 +12257,7 @@
     public int bindService(IApplicationThread caller, IBinder token,
             Intent service, String resolvedType,
             IServiceConnection connection, int flags) {
+        enforceNotIsolatedCaller("bindService");
         // Refuse possible leaked file descriptors
         if (service != null && service.hasFileDescriptors() == true) {
             throw new IllegalArgumentException("File descriptors passed in Intent");
@@ -12596,7 +12767,7 @@
                     : new ComponentName("android", "FullBackupAgent");
             // startProcessLocked() returns existing proc's record if it's already running
             ProcessRecord proc = startProcessLocked(app.processName, app,
-                    false, 0, "backup", hostingName, false);
+                    false, 0, "backup", hostingName, false, false);
             if (proc == null) {
                 Slog.e(TAG, "Unable to start backup agent process " + r);
                 return false;
@@ -12743,6 +12914,7 @@
 
     public Intent registerReceiver(IApplicationThread caller, String callerPackage,
             IIntentReceiver receiver, IntentFilter filter, String permission) {
+        enforceNotIsolatedCaller("registerReceiver");
         synchronized(this) {
             ProcessRecord callerApp = null;
             if (caller != null) {
@@ -13252,6 +13424,7 @@
             Intent intent, String resolvedType, IIntentReceiver resultTo,
             int resultCode, String resultData, Bundle map,
             String requiredPermission, boolean serialized, boolean sticky, int userId) {
+        enforceNotIsolatedCaller("broadcastIntent");
         synchronized(this) {
             intent = verifyBroadcastLocked(intent);
             
@@ -13498,6 +13671,7 @@
     public boolean startInstrumentation(ComponentName className,
             String profileFile, int flags, Bundle arguments,
             IInstrumentationWatcher watcher) {
+        enforceNotIsolatedCaller("startInstrumentation");
         // Refuse possible leaked file descriptors
         if (arguments != null && arguments.hasFileDescriptors()) {
             throw new IllegalArgumentException("File descriptors passed in Bundle");
@@ -13541,7 +13715,7 @@
             final long origId = Binder.clearCallingIdentity();
             // Instrumentation can kill and relaunch even persistent processes
             forceStopPackageLocked(ii.targetPackage, -1, true, false, true, true);
-            ProcessRecord app = addAppLocked(ai);
+            ProcessRecord app = addAppLocked(ai, false);
             app.instrumentationClass = className;
             app.instrumentationInfo = ai;
             app.instrumentationProfileFile = profileFile;
@@ -13695,7 +13869,7 @@
      * configuration.
      * @param persistent TODO
      */
-    public boolean updateConfigurationLocked(Configuration values,
+    boolean updateConfigurationLocked(Configuration values,
             ActivityRecord starting, boolean persistent, boolean initLocale) {
         int changes = 0;
         
@@ -14754,6 +14928,20 @@
                         Process.killProcessQuiet(app.pid);
                     }
                 }
+                if (!app.killedBackground && app.isolated && app.services.size() <= 0) {
+                    // If this is an isolated process, and there are no
+                    // services running in it, then the process is no longer
+                    // needed.  We agressively kill these because we can by
+                    // definition not re-use the same process again, and it is
+                    // good to avoid having whatever code was running in them
+                    // left sitting around after no longer needed.
+                    Slog.i(TAG, "Isolated process " + app.processName
+                            + " (pid " + app.pid + ") no longer needed");
+                    EventLog.writeEvent(EventLogTags.AM_KILL, app.pid,
+                            app.processName, app.setAdj, "isolated not needed");
+                    app.killedBackground = true;
+                    Process.killProcessQuiet(app.pid);
+                }
             }
         }
 
@@ -14892,7 +15080,7 @@
                     
                     if (app.persistent) {
                         if (app.persistent) {
-                            addAppLocked(app.info);
+                            addAppLocked(app.info, false);
                         }
                     }
                 }
@@ -15101,7 +15289,7 @@
         synchronized (this) { }
     }
 
-    public void onCoreSettingsChange(Bundle settings) {
+    void onCoreSettingsChange(Bundle settings) {
         for (int i = mLruProcesses.size() - 1; i >= 0; i--) {
             ProcessRecord processRecord = mLruProcesses.get(i);
             try {
diff --git a/services/java/com/android/server/am/ActivityStack.java b/services/java/com/android/server/am/ActivityStack.java
index 0d21760..f59f0c1 100644
--- a/services/java/com/android/server/am/ActivityStack.java
+++ b/services/java/com/android/server/am/ActivityStack.java
@@ -761,7 +761,7 @@
         }
 
         mService.startProcessLocked(r.processName, r.info.applicationInfo, true, 0,
-                "activity", r.intent.getComponent(), false);
+                "activity", r.intent.getComponent(), false, false);
     }
     
     void stopIfSleepingLocked() {
diff --git a/services/java/com/android/server/am/ProcessRecord.java b/services/java/com/android/server/am/ProcessRecord.java
index 72292be..b64261d 100644
--- a/services/java/com/android/server/am/ProcessRecord.java
+++ b/services/java/com/android/server/am/ProcessRecord.java
@@ -28,7 +28,9 @@
 import android.content.res.CompatibilityInfo;
 import android.os.Bundle;
 import android.os.IBinder;
+import android.os.Process;
 import android.os.SystemClock;
+import android.os.UserId;
 import android.util.PrintWriterPrinter;
 import android.util.TimeUtils;
 
@@ -44,6 +46,9 @@
 class ProcessRecord {
     final BatteryStatsImpl.Uid.Proc batteryStats; // where to collect runtime statistics
     final ApplicationInfo info; // all about the first app in the process
+    final boolean isolated;     // true if this is a special isolated process
+    final int uid;              // uid of process; may be different from 'info' if isolated
+    final int userId;           // user of process.
     final String processName;   // name of the process
     // List of packages running in the process
     final HashSet<String> pkgList = new HashSet<String>();
@@ -147,6 +152,12 @@
     void dump(PrintWriter pw, String prefix) {
         final long now = SystemClock.uptimeMillis();
 
+        pw.print(prefix); pw.print("user #"); pw.print(userId);
+                pw.print(" uid="); pw.print(info.uid);
+        if (uid != info.uid) {
+            pw.print(" ISOLATED uid="); pw.print(uid);
+        }
+        pw.println();
         if (info.className != null) {
             pw.print(prefix); pw.print("class="); pw.println(info.className);
         }
@@ -267,9 +278,12 @@
     }
     
     ProcessRecord(BatteryStatsImpl.Uid.Proc _batteryStats, IApplicationThread _thread,
-            ApplicationInfo _info, String _processName) {
+            ApplicationInfo _info, String _processName, int _uid) {
         batteryStats = _batteryStats;
         info = _info;
+        isolated = _info.uid != _uid;
+        uid = _uid;
+        userId = UserId.getUserId(_uid);
         processName = _processName;
         pkgList.add(_info.packageName);
         thread = _thread;
@@ -343,7 +357,18 @@
         sb.append(':');
         sb.append(processName);
         sb.append('/');
-        sb.append(info.uid);
+        if (info.uid < Process.FIRST_APPLICATION_UID) {
+            sb.append(uid);
+        } else {
+            sb.append('u');
+            sb.append(userId);
+            sb.append('a');
+            sb.append(info.uid%Process.FIRST_APPLICATION_UID);
+            if (uid != info.uid) {
+                sb.append('i');
+                sb.append(UserId.getAppId(uid) - Process.FIRST_ISOLATED_UID);
+            }
+        }
     }
     
     public String toString() {
diff --git a/services/java/com/android/server/am/ServiceRecord.java b/services/java/com/android/server/am/ServiceRecord.java
index 75ba9474..daa3653 100644
--- a/services/java/com/android/server/am/ServiceRecord.java
+++ b/services/java/com/android/server/am/ServiceRecord.java
@@ -80,6 +80,7 @@
                             // IBinder -> ConnectionRecord of all bound clients
 
     ProcessRecord app;      // where this service is running or null.
+    ProcessRecord isolatedProc; // keep track of isolated process, if requested
     boolean isForeground;   // is service currently in foreground mode?
     int foregroundId;       // Notification ID of last foreground req.
     Notification foregroundNoti; // Notification record of foreground state.
@@ -210,6 +211,9 @@
         }
         pw.print(prefix); pw.print("dataDir="); pw.println(dataDir);
         pw.print(prefix); pw.print("app="); pw.println(app);
+        if (isolatedProc != null) {
+            pw.print(prefix); pw.print("isolatedProc="); pw.println(isolatedProc);
+        }
         if (isForeground || foregroundId != 0) {
             pw.print(prefix); pw.print("isForeground="); pw.print(isForeground);
                     pw.print(" foregroundId="); pw.print(foregroundId);
diff --git a/tests/BiDiTests/res/layout/grid_layout_code.xml b/tests/BiDiTests/res/layout/grid_layout_code.xml
new file mode 100644
index 0000000..87a0ec0
--- /dev/null
+++ b/tests/BiDiTests/res/layout/grid_layout_code.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2012 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+        android:id="@+id/grid_layout_code"
+        android:layout_width="fill_parent"
+        android:layout_height="fill_parent">
+
+</FrameLayout>
\ No newline at end of file
diff --git a/tests/BiDiTests/src/com/android/bidi/BiDiTestActivity.java b/tests/BiDiTests/src/com/android/bidi/BiDiTestActivity.java
index 4d7ace1..c5a1235 100644
--- a/tests/BiDiTests/src/com/android/bidi/BiDiTestActivity.java
+++ b/tests/BiDiTests/src/com/android/bidi/BiDiTestActivity.java
@@ -111,6 +111,8 @@
         addItem(result, "Grid LTR", BiDiTestGridLayoutLtr.class, R.id.grid_layout_ltr);
         addItem(result, "Grid RTL", BiDiTestGridLayoutRtl.class, R.id.grid_layout_rtl);
         addItem(result, "Grid LOC", BiDiTestGridLayoutLocale.class, R.id.grid_layout_locale);
+        addItem(result, "Grid C-LTR", BiDiTestGridLayoutCodeLtr.class, R.id.grid_layout_code);
+        addItem(result, "Grid C-RTL", BiDiTestGridLayoutCodeRtl.class, R.id.grid_layout_code);
 
         addItem(result, "Frame LTR", BiDiTestFrameLayoutLtr.class, R.id.frame_layout_ltr);
         addItem(result, "Frame RTL", BiDiTestFrameLayoutRtl.class, R.id.frame_layout_rtl);
diff --git a/tests/BiDiTests/src/com/android/bidi/BiDiTestGridLayoutCodeLtr.java b/tests/BiDiTests/src/com/android/bidi/BiDiTestGridLayoutCodeLtr.java
new file mode 100644
index 0000000..859b8fb
--- /dev/null
+++ b/tests/BiDiTests/src/com/android/bidi/BiDiTestGridLayoutCodeLtr.java
@@ -0,0 +1,123 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.bidi;
+
+import android.app.Fragment;
+import android.content.Context;
+import android.os.Bundle;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.GridLayout;
+
+import android.widget.*;
+
+import static android.text.InputType.*;
+import static android.widget.GridLayout.*;
+
+public class BiDiTestGridLayoutCodeLtr extends Fragment {
+
+    private FrameLayout currentView;
+
+    @Override
+    public View onCreateView(LayoutInflater inflater, ViewGroup container,
+            Bundle savedInstanceState) {
+        currentView = (FrameLayout) inflater.inflate(R.layout.grid_layout_code, container, false);
+        return currentView;
+    }
+
+    @Override
+    public void onViewCreated(View view, Bundle savedInstanceState) {
+        super.onViewCreated(view, savedInstanceState);
+        currentView.addView(create(currentView.getContext()));
+    }
+
+    public static View create(Context context) {
+        GridLayout layout = new GridLayout(context);
+        layout.setUseDefaultMargins(true);
+        layout.setAlignmentMode(ALIGN_BOUNDS);
+        layout.setRowOrderPreserved(false);
+        layout.setLayoutDirection(View.LAYOUT_DIRECTION_LTR);
+
+        Spec row1 = spec(0);
+        Spec row2 = spec(1);
+        Spec row3 = spec(2, BASELINE);
+        Spec row4 = spec(3, BASELINE);
+        Spec row5 = spec(2, 3, FILL); // allow the last two rows to overlap the middle two
+        Spec row6 = spec(5);
+        Spec row7 = spec(6);
+
+        Spec col1a = spec(0, 4, CENTER);
+        Spec col1b = spec(0, 4, START);
+        Spec col1c = spec(0, END);
+        Spec col2 = spec(1, START);
+        Spec col3 = spec(2, FILL);
+        Spec col4a = spec(3);
+        Spec col4b = spec(3, FILL);
+
+        {
+            TextView c = new TextView(context);
+            c.setTextSize(32);
+            c.setText("Email setup");
+            layout.addView(c, new GridLayout.LayoutParams(row1, col1a));
+        }
+        {
+            TextView c = new TextView(context);
+            c.setTextSize(16);
+            c.setText("You can configure email in just a few steps:");
+            layout.addView(c, new GridLayout.LayoutParams(row2, col1b));
+        }
+        {
+            TextView c = new TextView(context);
+            c.setText("Email address:");
+            layout.addView(c, new GridLayout.LayoutParams(row3, col1c));
+        }
+        {
+            EditText c = new EditText(context);
+            c.setEms(10);
+            c.setInputType(TYPE_CLASS_TEXT | TYPE_TEXT_VARIATION_EMAIL_ADDRESS);
+            layout.addView(c, new GridLayout.LayoutParams(row3, col2));
+        }
+        {
+            TextView c = new TextView(context);
+            c.setText("Password:");
+            layout.addView(c, new GridLayout.LayoutParams(row4, col1c));
+        }
+        {
+            TextView c = new EditText(context);
+            c.setEms(8);
+            c.setInputType(TYPE_CLASS_TEXT | TYPE_TEXT_VARIATION_PASSWORD);
+            layout.addView(c, new GridLayout.LayoutParams(row4, col2));
+        }
+        {
+            Space c = new Space(context);
+            layout.addView(c, new GridLayout.LayoutParams(row5, col3));
+        }
+        {
+            Button c = new Button(context);
+            c.setText("Manual setup");
+            layout.addView(c, new GridLayout.LayoutParams(row6, col4a));
+        }
+        {
+            Button c = new Button(context);
+            c.setText("Next");
+            layout.addView(c, new GridLayout.LayoutParams(row7, col4b));
+        }
+
+        return layout;
+    }
+}
diff --git a/tests/BiDiTests/src/com/android/bidi/BiDiTestGridLayoutCodeRtl.java b/tests/BiDiTests/src/com/android/bidi/BiDiTestGridLayoutCodeRtl.java
new file mode 100644
index 0000000..fac8c95
--- /dev/null
+++ b/tests/BiDiTests/src/com/android/bidi/BiDiTestGridLayoutCodeRtl.java
@@ -0,0 +1,121 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.bidi;
+
+import android.app.Fragment;
+import android.content.Context;
+import android.os.Bundle;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.*;
+
+import static android.text.InputType.*;
+import static android.widget.GridLayout.*;
+
+public class BiDiTestGridLayoutCodeRtl extends Fragment {
+
+    private FrameLayout currentView;
+
+    @Override
+    public View onCreateView(LayoutInflater inflater, ViewGroup container,
+            Bundle savedInstanceState) {
+        currentView = (FrameLayout) inflater.inflate(R.layout.grid_layout_code, container, false);
+        return currentView;
+    }
+
+    @Override
+    public void onViewCreated(View view, Bundle savedInstanceState) {
+        super.onViewCreated(view, savedInstanceState);
+        currentView.addView(create(currentView.getContext()));
+    }
+
+    public static View create(Context context) {
+        GridLayout layout = new GridLayout(context);
+        layout.setUseDefaultMargins(true);
+        layout.setAlignmentMode(ALIGN_BOUNDS);
+        layout.setRowOrderPreserved(false);
+        layout.setLayoutDirection(View.LAYOUT_DIRECTION_RTL);
+
+        Spec row1 = spec(0);
+        Spec row2 = spec(1);
+        Spec row3 = spec(2, BASELINE);
+        Spec row4 = spec(3, BASELINE);
+        Spec row5 = spec(2, 3, FILL); // allow the last two rows to overlap the middle two
+        Spec row6 = spec(5);
+        Spec row7 = spec(6);
+
+        Spec col1a = spec(0, 4, CENTER);
+        Spec col1b = spec(0, 4, START);
+        Spec col1c = spec(0, END);
+        Spec col2 = spec(1, START);
+        Spec col3 = spec(2, FILL);
+        Spec col4a = spec(3);
+        Spec col4b = spec(3, FILL);
+
+        {
+            TextView c = new TextView(context);
+            c.setTextSize(32);
+            c.setText("Email setup");
+            layout.addView(c, new GridLayout.LayoutParams(row1, col1a));
+        }
+        {
+            TextView c = new TextView(context);
+            c.setTextSize(16);
+            c.setText("You can configure email in just a few steps:");
+            layout.addView(c, new GridLayout.LayoutParams(row2, col1b));
+        }
+        {
+            TextView c = new TextView(context);
+            c.setText("Email address:");
+            layout.addView(c, new GridLayout.LayoutParams(row3, col1c));
+        }
+        {
+            EditText c = new EditText(context);
+            c.setEms(10);
+            c.setInputType(TYPE_CLASS_TEXT | TYPE_TEXT_VARIATION_EMAIL_ADDRESS);
+            layout.addView(c, new GridLayout.LayoutParams(row3, col2));
+        }
+        {
+            TextView c = new TextView(context);
+            c.setText("Password:");
+            layout.addView(c, new GridLayout.LayoutParams(row4, col1c));
+        }
+        {
+            TextView c = new EditText(context);
+            c.setEms(8);
+            c.setInputType(TYPE_CLASS_TEXT | TYPE_TEXT_VARIATION_PASSWORD);
+            layout.addView(c, new GridLayout.LayoutParams(row4, col2));
+        }
+        {
+            Space c = new Space(context);
+            layout.addView(c, new GridLayout.LayoutParams(row5, col3));
+        }
+        {
+            Button c = new Button(context);
+            c.setText("Manual setup");
+            layout.addView(c, new GridLayout.LayoutParams(row6, col4a));
+        }
+        {
+            Button c = new Button(context);
+            c.setText("Next");
+            layout.addView(c, new GridLayout.LayoutParams(row7, col4b));
+        }
+
+        return layout;
+    }
+}
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeIInputMethodManager.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeIInputMethodManager.java
index db0694c..6afdfbe 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeIInputMethodManager.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeIInputMethodManager.java
@@ -197,6 +197,12 @@
     }
 
     @Override
+        public boolean switchToNextInputMethod(IBinder arg0, boolean arg1) throws RemoteException {
+        // TODO Auto-generated method stub
+        return false;
+    }
+
+    @Override
     public void updateStatusIcon(IBinder arg0, String arg1, int arg2) throws RemoteException {
         // TODO Auto-generated method stub