Merge "Secure setting for LocationFudger's accuracy" into jb-mr1-dev
diff --git a/api/current.txt b/api/current.txt
index 1e3e2bb..db378c8 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -555,6 +555,7 @@
     field public static final int indicatorRight = 16843022; // 0x101010e
     field public static final int inflatedId = 16842995; // 0x10100f3
     field public static final int initOrder = 16842778; // 0x101001a
+    field public static final int initialKeyguardLayout = 16843714; // 0x10103c2
     field public static final int initialLayout = 16843345; // 0x1010251
     field public static final int innerRadius = 16843359; // 0x101025f
     field public static final int innerRadiusRatio = 16843163; // 0x101019b
@@ -1120,6 +1121,8 @@
     field public static final int weekNumberColor = 16843589; // 0x1010345
     field public static final int weekSeparatorLineColor = 16843590; // 0x1010346
     field public static final int weightSum = 16843048; // 0x1010128
+    field public static final int widgetCategory = 16843716; // 0x10103c4
+    field public static final int widgetFeatures = 16843715; // 0x10103c3
     field public static final int widgetLayout = 16843243; // 0x10101eb
     field public static final int width = 16843097; // 0x1010159
     field public static final int windowActionBar = 16843469; // 0x10102cd
@@ -4399,6 +4402,7 @@
 
   public class AppWidgetManager {
     method public boolean bindAppWidgetIdIfAllowed(int, android.content.ComponentName);
+    method public boolean bindAppWidgetIdIfAllowed(int, android.content.ComponentName, android.os.Bundle);
     method public int[] getAppWidgetIds(android.content.ComponentName);
     method public android.appwidget.AppWidgetProviderInfo getAppWidgetInfo(int);
     method public android.os.Bundle getAppWidgetOptions(int);
@@ -4428,6 +4432,7 @@
     field public static final java.lang.String EXTRA_CUSTOM_INFO = "customInfo";
     field public static final int INVALID_APPWIDGET_ID = 0; // 0x0
     field public static final java.lang.String META_DATA_APPWIDGET_PROVIDER = "android.appwidget.provider";
+    field public static final java.lang.String OPTION_APPWIDGET_HOST_CATEGORY = "appWidgetCategory";
     field public static final java.lang.String OPTION_APPWIDGET_MAX_HEIGHT = "appWidgetMaxHeight";
     field public static final java.lang.String OPTION_APPWIDGET_MAX_WIDTH = "appWidgetMaxWidth";
     field public static final java.lang.String OPTION_APPWIDGET_MIN_HEIGHT = "appWidgetMinHeight";
@@ -4454,9 +4459,15 @@
     field public static final int RESIZE_HORIZONTAL = 1; // 0x1
     field public static final int RESIZE_NONE = 0; // 0x0
     field public static final int RESIZE_VERTICAL = 2; // 0x2
+    field public static final int WIDGET_CATEGORY_HOME_SCREEN = 1; // 0x1
+    field public static final int WIDGET_CATEGORY_KEYGUARD = 2; // 0x2
+    field public static final int WIDGET_FEATURES_CLOCK = 1; // 0x1
+    field public static final int WIDGET_FEATURES_NONE = 0; // 0x0
+    field public static final int WIDGET_FEATURES_STATUS = 2; // 0x2
     field public int autoAdvanceViewId;
     field public android.content.ComponentName configure;
     field public int icon;
+    field public int initialKeyguardLayout;
     field public int initialLayout;
     field public java.lang.String label;
     field public int minHeight;
@@ -4467,6 +4478,8 @@
     field public android.content.ComponentName provider;
     field public int resizeMode;
     field public int updatePeriodMillis;
+    field public int widgetCategory;
+    field public int widgetFeatures;
   }
 
 }
@@ -9865,6 +9878,7 @@
     field public static final java.lang.String SCENE_MODE_BEACH = "beach";
     field public static final java.lang.String SCENE_MODE_CANDLELIGHT = "candlelight";
     field public static final java.lang.String SCENE_MODE_FIREWORKS = "fireworks";
+    field public static final java.lang.String SCENE_MODE_HDR = "hdr";
     field public static final java.lang.String SCENE_MODE_LANDSCAPE = "landscape";
     field public static final java.lang.String SCENE_MODE_NIGHT = "night";
     field public static final java.lang.String SCENE_MODE_NIGHT_PORTRAIT = "night-portrait";
@@ -25537,12 +25551,12 @@
     method protected boolean isLayoutRtl();
     method public boolean isMarginRelative();
     method public void setLayoutDirection(int);
+    method public void setMarginEnd(int);
+    method public void setMarginStart(int);
     method public void setMargins(int, int, int, int);
     field public int bottomMargin;
-    field public int endMargin;
     field public int leftMargin;
     field public int rightMargin;
-    field public int startMargin;
     field public int topMargin;
   }
 
@@ -26929,6 +26943,9 @@
     method public boolean useHttpAuthUsernamePassword();
   }
 
+  public abstract class JavascriptInterface implements java.lang.annotation.Annotation {
+  }
+
   public class JsPromptResult extends android.webkit.JsResult {
     method public void confirm(java.lang.String);
   }
@@ -27053,7 +27070,7 @@
   }
 
   public abstract class WebSettings {
-    method public boolean enableSmoothTransition();
+    method public deprecated boolean enableSmoothTransition();
     method public boolean getAllowContentAccess();
     method public boolean getAllowFileAccess();
     method public abstract boolean getAllowFileAccessFromFileURLs();
@@ -27119,7 +27136,7 @@
     method public void setDefaultZoom(android.webkit.WebSettings.ZoomDensity);
     method public void setDisplayZoomControls(boolean);
     method public synchronized void setDomStorageEnabled(boolean);
-    method public void setEnableSmoothTransition(boolean);
+    method public deprecated void setEnableSmoothTransition(boolean);
     method public synchronized void setFantasyFontFamily(java.lang.String);
     method public synchronized void setFixedFontFamily(java.lang.String);
     method public synchronized void setGeolocationDatabasePath(java.lang.String);
@@ -27158,7 +27175,7 @@
     field public static final int LOAD_CACHE_ELSE_NETWORK = 1; // 0x1
     field public static final int LOAD_CACHE_ONLY = 3; // 0x3
     field public static final int LOAD_DEFAULT = -1; // 0xffffffff
-    field public static final int LOAD_NORMAL = 0; // 0x0
+    field public static final deprecated int LOAD_NORMAL = 0; // 0x0
     field public static final int LOAD_NO_CACHE = 2; // 0x2
   }
 
@@ -27241,13 +27258,13 @@
     ctor public WebView(android.content.Context);
     ctor public WebView(android.content.Context, android.util.AttributeSet);
     ctor public WebView(android.content.Context, android.util.AttributeSet, int);
-    ctor public WebView(android.content.Context, android.util.AttributeSet, int, boolean);
+    ctor public deprecated WebView(android.content.Context, android.util.AttributeSet, int, boolean);
     method public void addJavascriptInterface(java.lang.Object, java.lang.String);
     method public boolean canGoBack();
     method public boolean canGoBackOrForward(int);
     method public boolean canGoForward();
-    method public boolean canZoomIn();
-    method public boolean canZoomOut();
+    method public deprecated boolean canZoomIn();
+    method public deprecated boolean canZoomOut();
     method public android.graphics.Picture capturePicture();
     method public void clearCache(boolean);
     method public void clearFormData();
@@ -27275,7 +27292,7 @@
     method public java.lang.String[] getHttpAuthUsernamePassword(java.lang.String, java.lang.String);
     method public java.lang.String getOriginalUrl();
     method public int getProgress();
-    method public float getScale();
+    method public deprecated float getScale();
     method public android.webkit.WebSettings getSettings();
     method public java.lang.String getTitle();
     method public java.lang.String getUrl();
@@ -27312,13 +27329,13 @@
     method public android.webkit.WebBackForwardList saveState(android.os.Bundle);
     method public void saveWebArchive(java.lang.String);
     method public void saveWebArchive(java.lang.String, boolean, android.webkit.ValueCallback<java.lang.String>);
-    method public void setCertificate(android.net.http.SslCertificate);
+    method public deprecated void setCertificate(android.net.http.SslCertificate);
     method public void setDownloadListener(android.webkit.DownloadListener);
     method public void setFindListener(android.webkit.WebView.FindListener);
     method public void setHorizontalScrollbarOverlay(boolean);
     method public void setHttpAuthUsernamePassword(java.lang.String, java.lang.String, java.lang.String, java.lang.String);
     method public void setInitialScale(int);
-    method public void setMapTrackballToArrowKeys(boolean);
+    method public deprecated void setMapTrackballToArrowKeys(boolean);
     method public void setNetworkAvailable(boolean);
     method public deprecated void setPictureListener(android.webkit.WebView.PictureListener);
     method public void setVerticalScrollbarOverlay(boolean);
diff --git a/cmds/am/src/com/android/commands/am/Am.java b/cmds/am/src/com/android/commands/am/Am.java
index 89287ad..bc9e74e 100644
--- a/cmds/am/src/com/android/commands/am/Am.java
+++ b/cmds/am/src/com/android/commands/am/Am.java
@@ -1187,32 +1187,26 @@
 
     private void runDisplaySize() throws Exception {
         String size = nextArgRequired();
-        int m, n;
+        int w, h;
         if ("reset".equals(size)) {
-            m = n = -1;
+            w = h = -1;
         } else {
             int div = size.indexOf('x');
             if (div <= 0 || div >= (size.length()-1)) {
                 System.err.println("Error: bad size " + size);
                 return;
             }
-            String mstr = size.substring(0, div);
-            String nstr = size.substring(div+1);
+            String wstr = size.substring(0, div);
+            String hstr = size.substring(div+1);
             try {
-                m = Integer.parseInt(mstr);
-                n = Integer.parseInt(nstr);
+                w = Integer.parseInt(wstr);
+                h = Integer.parseInt(hstr);
             } catch (NumberFormatException e) {
                 System.err.println("Error: bad number " + e);
                 return;
             }
         }
 
-        if (m < n) {
-            int tmp = m;
-            m = n;
-            n = tmp;
-        }
-
         IWindowManager wm = IWindowManager.Stub.asInterface(ServiceManager.checkService(
                 Context.WINDOW_SERVICE));
         if (wm == null) {
@@ -1221,9 +1215,9 @@
         }
 
         try {
-            if (m >= 0 && n >= 0) {
+            if (w >= 0 && h >= 0) {
                 // TODO(multidisplay): For now Configuration only applies to main screen.
-                wm.setForcedDisplaySize(Display.DEFAULT_DISPLAY, m, n);
+                wm.setForcedDisplaySize(Display.DEFAULT_DISPLAY, w, h);
             } else {
                 wm.clearForcedDisplaySize(Display.DEFAULT_DISPLAY);
             }
@@ -1444,7 +1438,7 @@
                 "       am clear-debug-app\n" +
                 "       am monitor [--gdb <port>]\n" +
                 "       am screen-compat [on|off] <PACKAGE>\n" +
-                "       am display-size [reset|MxN]\n" +
+                "       am display-size [reset|WxH]\n" +
                 "       am display-density [reset|DENSITY]\n" +
                 "       am to-uri [INTENT]\n" +
                 "       am to-intent-uri [INTENT]\n" +
diff --git a/cmds/installd/commands.c b/cmds/installd/commands.c
index 68f8400..2934261 100644
--- a/cmds/installd/commands.c
+++ b/cmds/installd/commands.c
@@ -27,13 +27,15 @@
 dir_rec_t android_asec_dir;
 dir_rec_t android_app_dir;
 dir_rec_t android_app_private_dir;
+dir_rec_t android_app_lib_dir;
 dir_rec_t android_media_dir;
 dir_rec_array_t android_system_dirs;
 
 int install(const char *pkgname, uid_t uid, gid_t gid)
 {
     char pkgdir[PKG_PATH_MAX];
-    char libdir[PKG_PATH_MAX];
+    char libsymlink[PKG_PATH_MAX];
+    char applibdir[PKG_PATH_MAX];
 
     if ((uid < AID_SYSTEM) || (gid < AID_SYSTEM)) {
         ALOGE("invalid uid/gid: %d %d\n", uid, gid);
@@ -45,64 +47,49 @@
         return -1;
     }
 
-    if (create_pkg_path(libdir, pkgname, PKG_LIB_POSTFIX, 0)) {
-        ALOGE("cannot create package lib path\n");
+    if (create_pkg_path(libsymlink, pkgname, PKG_LIB_POSTFIX, 0)) {
+        ALOGE("cannot create package lib symlink origin path\n");
+        return -1;
+    }
+
+    if (create_pkg_path_in_dir(applibdir, &android_app_lib_dir, pkgname, PKG_DIR_POSTFIX)) {
+        ALOGE("cannot create package lib symlink dest path\n");
         return -1;
     }
 
     if (mkdir(pkgdir, 0751) < 0) {
         ALOGE("cannot create dir '%s': %s\n", pkgdir, strerror(errno));
-        return -errno;
+        return -1;
     }
     if (chmod(pkgdir, 0751) < 0) {
         ALOGE("cannot chmod dir '%s': %s\n", pkgdir, strerror(errno));
         unlink(pkgdir);
-        return -errno;
+        return -1;
     }
 
-    if (mkdir(libdir, 0755) < 0) {
-        ALOGE("cannot create dir '%s': %s\n", libdir, strerror(errno));
+    if (symlink(applibdir, libsymlink) < 0) {
+        ALOGE("couldn't symlink directory '%s' -> '%s': %s\n", libsymlink, applibdir,
+                strerror(errno));
         unlink(pkgdir);
-        return -errno;
-    }
-    if (chmod(libdir, 0755) < 0) {
-        ALOGE("cannot chmod dir '%s': %s\n", libdir, strerror(errno));
-        unlink(libdir);
-        unlink(pkgdir);
-        return -errno;
-    }
-    if (chown(libdir, AID_SYSTEM, AID_SYSTEM) < 0) {
-        ALOGE("cannot chown dir '%s': %s\n", libdir, strerror(errno));
-        unlink(libdir);
-        unlink(pkgdir);
-        return -errno;
-    }
-
-#ifdef HAVE_SELINUX
-    if (selinux_android_setfilecon(libdir, pkgname, AID_SYSTEM) < 0) {
-        ALOGE("cannot setfilecon dir '%s': %s\n", libdir, strerror(errno));
-        unlink(libdir);
-        unlink(pkgdir);
-        return -errno;
-    }
-#endif
-
-    if (chown(pkgdir, uid, gid) < 0) {
-        ALOGE("cannot chown dir '%s': %s\n", pkgdir, strerror(errno));
-        unlink(libdir);
-        unlink(pkgdir);
-        return -errno;
+        return -1;
     }
 
 #ifdef HAVE_SELINUX
     if (selinux_android_setfilecon(pkgdir, pkgname, uid) < 0) {
         ALOGE("cannot setfilecon dir '%s': %s\n", pkgdir, strerror(errno));
-        unlink(libdir);
+        unlink(libsymlink);
         unlink(pkgdir);
-        return -errno;
+        return -1;
     }
 #endif
 
+    if (chown(pkgdir, uid, gid) < 0) {
+        ALOGE("cannot chown dir '%s': %s\n", pkgdir, strerror(errno));
+        unlink(libsymlink);
+        unlink(pkgdir);
+        return -1;
+    }
+
     return 0;
 }
 
@@ -185,7 +172,6 @@
 int make_user_data(const char *pkgname, uid_t uid, uid_t persona)
 {
     char pkgdir[PKG_PATH_MAX];
-    char real_libdir[PKG_PATH_MAX];
 
     // Create the data dir for the package
     if (create_pkg_path(pkgdir, pkgname, PKG_DIR_POSTFIX, persona)) {
@@ -1038,85 +1024,3 @@
 
     return rc;
 }
-
-int unlinklib(const char* dataDir)
-{
-    char libdir[PKG_PATH_MAX];
-    struct stat s, libStat;
-    int rc = 0;
-
-    const size_t libdirLen = strlen(dataDir) + strlen(PKG_LIB_POSTFIX);
-    if (libdirLen >= PKG_PATH_MAX) {
-        return -1;
-    }
-
-    if (snprintf(libdir, sizeof(libdir), "%s%s", dataDir, PKG_LIB_POSTFIX) != (ssize_t)libdirLen) {
-        ALOGE("library dir not written successfully: %s\n", strerror(errno));
-        return -1;
-    }
-
-    if (stat(dataDir, &s) < 0) {
-        ALOGE("couldn't state data dir");
-        return -1;
-    }
-
-    if (chown(dataDir, AID_INSTALL, AID_INSTALL) < 0) {
-        ALOGE("failed to chown '%s': %s\n", dataDir, strerror(errno));
-        return -1;
-    }
-
-    if (chmod(dataDir, 0700) < 0) {
-        ALOGE("unlinklib() 1: failed to chmod '%s': %s\n", dataDir, strerror(errno));
-        rc = -1;
-        goto out;
-    }
-
-    if (lstat(libdir, &libStat) < 0) {
-        ALOGE("couldn't stat lib dir: %s\n", strerror(errno));
-        rc = -1;
-        goto out;
-    }
-
-    if (S_ISDIR(libStat.st_mode)) {
-        if (delete_dir_contents(libdir, 1, 0) < 0) {
-            rc = -1;
-            goto out;
-        }
-    } else if (S_ISLNK(libStat.st_mode)) {
-        if (unlink(libdir) < 0) {
-            rc = -1;
-            goto out;
-        }
-    }
-
-    if (mkdir(libdir, 0755) < 0) {
-        ALOGE("cannot create dir '%s': %s\n", libdir, strerror(errno));
-        rc = -errno;
-        goto out;
-    }
-    if (chmod(libdir, 0755) < 0) {
-        ALOGE("cannot chmod dir '%s': %s\n", libdir, strerror(errno));
-        unlink(libdir);
-        rc = -errno;
-        goto out;
-    }
-    if (chown(libdir, AID_SYSTEM, AID_SYSTEM) < 0) {
-        ALOGE("cannot chown dir '%s': %s\n", libdir, strerror(errno));
-        unlink(libdir);
-        rc = -errno;
-        goto out;
-    }
-
-out:
-    if (chmod(dataDir, s.st_mode) < 0) {
-        ALOGE("unlinklib() 2: failed to chmod '%s': %s\n", dataDir, strerror(errno));
-        rc = -1;
-    }
-
-    if (chown(dataDir, s.st_uid, s.st_gid) < 0) {
-        ALOGE("failed to chown '%s' : %s\n", dataDir, strerror(errno));
-        return -1;
-    }
-
-    return rc;
-}
diff --git a/cmds/installd/installd.c b/cmds/installd/installd.c
index cc8f014..a099537 100644
--- a/cmds/installd/installd.c
+++ b/cmds/installd/installd.c
@@ -126,11 +126,6 @@
     return linklib(arg[0], arg[1]);
 }
 
-static int do_unlinklib(char **arg, char reply[REPLY_MAX])
-{
-    return unlinklib(arg[0]);
-}
-
 struct cmdinfo {
     const char *name;
     unsigned numargs;
@@ -152,7 +147,6 @@
     { "rmuserdata",           2, do_rm_user_data },
     { "movefiles",            0, do_movefiles },
     { "linklib",              2, do_linklib },
-    { "unlinklib",            1, do_unlinklib },
     { "mkuserdata",           3, do_mk_user_data },
     { "rmuser",               1, do_rm_user },
     { "cloneuserdata",        3, do_clone_user_data },
@@ -290,6 +284,11 @@
         return -1;
     }
 
+    // Get the android app native library directory.
+    if (copy_and_append(&android_app_lib_dir, &android_data_dir, APP_LIB_SUBDIR) < 0) {
+        return -1;
+    }
+
     // Get the sd-card ASEC mount point.
     if (get_path_from_env(&android_asec_dir, "ASEC_MOUNTPOINT") < 0) {
         return -1;
diff --git a/cmds/installd/installd.h b/cmds/installd/installd.h
index 5b81d2c..a229b970 100644
--- a/cmds/installd/installd.h
+++ b/cmds/installd/installd.h
@@ -62,6 +62,8 @@
 
 #define APP_SUBDIR             "app/" // sub-directory under ANDROID_DATA
 
+#define APP_LIB_SUBDIR         "app-lib/" // sub-directory under ANDROID_DATA
+
 #define MEDIA_SUBDIR           "media/" // sub-directory under ANDROID_DATA
 
 /* other handy constants */
@@ -93,6 +95,7 @@
 
 extern dir_rec_t android_app_dir;
 extern dir_rec_t android_app_private_dir;
+extern dir_rec_t android_app_lib_dir;
 extern dir_rec_t android_data_dir;
 extern dir_rec_t android_asec_dir;
 extern dir_rec_t android_media_dir;
@@ -207,4 +210,3 @@
 int dexopt(const char *apk_path, uid_t uid, int is_public);
 int movefiles();
 int linklib(const char* target, const char* source);
-int unlinklib(const char* libPath);
diff --git a/core/java/android/animation/ValueAnimator.java b/core/java/android/animation/ValueAnimator.java
index f874d56..f7460c4 100755
--- a/core/java/android/animation/ValueAnimator.java
+++ b/core/java/android/animation/ValueAnimator.java
@@ -958,13 +958,7 @@
         } else if (!mInitialized) {
             initAnimation();
         }
-        // The final value set on the target varies, depending on whether the animation
-        // was supposed to repeat an odd number of times
-        if (mRepeatCount > 0 && (mRepeatCount & 0x01) == 1) {
-            animateValue(0f);
-        } else {
-            animateValue(1f);
-        }
+        animateValue(mPlayingBackwards ? 0f : 1f);
         endAnimation(handler);
     }
 
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 09fa99c..812ac9e 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -52,6 +52,7 @@
 import android.os.Binder;
 import android.os.Bundle;
 import android.os.Debug;
+import android.os.Environment;
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.Looper;
@@ -4877,6 +4878,8 @@
         // StrictMode) on debug builds, but using DropBox, not logs.
         CloseGuard.setEnabled(false);
 
+        Environment.initForCurrentUser();
+
         Security.addProvider(new AndroidKeyStoreProvider());
 
         Process.setArgV0("<pre-initialized>");
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index 3498919..92104fa 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -1503,7 +1503,7 @@
                     (message != null ? (message + ": ") : "") +
                     (selfToo
                      ? "Neither user " + uid + " nor current process has "
-                     : "User " + uid + " does not have ") +
+                     : "uid " + uid + " does not have ") +
                     permission +
                     ".");
         }
diff --git a/core/java/android/appwidget/AppWidgetHostView.java b/core/java/android/appwidget/AppWidgetHostView.java
index 603ceb7..c86826f 100644
--- a/core/java/android/appwidget/AppWidgetHostView.java
+++ b/core/java/android/appwidget/AppWidgetHostView.java
@@ -513,7 +513,17 @@
                         theirContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
                 inflater = inflater.cloneInContext(theirContext);
                 inflater.setFilter(sInflaterFilter);
-                defaultView = inflater.inflate(mInfo.initialLayout, this, false);
+                AppWidgetManager manager = AppWidgetManager.getInstance(mContext);
+                Bundle options = manager.getAppWidgetOptions(mAppWidgetId);
+
+                int layoutId = mInfo.initialLayout;
+                if (options.containsKey(AppWidgetManager.OPTION_APPWIDGET_HOST_CATEGORY)) {
+                    int category = options.getInt(AppWidgetManager.OPTION_APPWIDGET_HOST_CATEGORY);
+                    if (category == AppWidgetProviderInfo.WIDGET_CATEGORY_KEYGUARD) {
+                        layoutId = mInfo.initialKeyguardLayout;
+                    }
+                }
+                defaultView = inflater.inflate(layoutId, this, false);
             } else {
                 Log.w(TAG, "can't inflate defaultView because mInfo is missing");
             }
diff --git a/core/java/android/appwidget/AppWidgetManager.java b/core/java/android/appwidget/AppWidgetManager.java
index 2df675e..100a2b8 100644
--- a/core/java/android/appwidget/AppWidgetManager.java
+++ b/core/java/android/appwidget/AppWidgetManager.java
@@ -150,26 +150,34 @@
     public static final String EXTRA_APPWIDGET_ID = "appWidgetId";
 
     /**
-     * An bundle extra that contains the lower bound on the current width, in dips, of a widget instance.
+     * A bundle extra that contains the lower bound on the current width, in dips, of a widget instance.
      */
     public static final String OPTION_APPWIDGET_MIN_WIDTH = "appWidgetMinWidth";
 
     /**
-     * An bundle extra that contains the lower bound on the current height, in dips, of a widget instance.
+     * A bundle extra that contains the lower bound on the current height, in dips, of a widget instance.
      */
     public static final String OPTION_APPWIDGET_MIN_HEIGHT = "appWidgetMinHeight";
 
     /**
-     * An bundle extra that contains the upper bound on the current width, in dips, of a widget instance.
+     * A bundle extra that contains the upper bound on the current width, in dips, of a widget instance.
      */
     public static final String OPTION_APPWIDGET_MAX_WIDTH = "appWidgetMaxWidth";
 
     /**
-     * An bundle extra that contains the upper bound on the current width, in dips, of a widget instance.
+     * A bundle extra that contains the upper bound on the current width, in dips, of a widget instance.
      */
     public static final String OPTION_APPWIDGET_MAX_HEIGHT = "appWidgetMaxHeight";
 
     /**
+     * A bundle extra that hints to the AppWidgetProvider the category of host that owns this
+     * this widget. Can have the value {@link
+     * AppWidgetProviderInfo#WIDGET_CATEGORY_HOME_SCREEN} or {@link
+     * AppWidgetProviderInfo#WIDGET_CATEGORY_KEYGUARD}.
+     */
+    public static final String OPTION_APPWIDGET_HOST_CATEGORY = "appWidgetCategory";
+
+    /**
      * An intent extra which points to a bundle of extra information for a particular widget id.
      * In particular this bundle can contain EXTRA_APPWIDGET_WIDTH and EXTRA_APPWIDGET_HEIGHT.
      */
@@ -568,7 +576,31 @@
      */
     public void bindAppWidgetId(int appWidgetId, ComponentName provider) {
         try {
-            sService.bindAppWidgetId(appWidgetId, provider);
+            sService.bindAppWidgetId(appWidgetId, provider, null);
+        }
+        catch (RemoteException e) {
+            throw new RuntimeException("system server dead?", e);
+        }
+    }
+
+    /**
+     * Set the component for a given appWidgetId.
+     *
+     * <p class="note">You need the BIND_APPWIDGET permission or the user must have enabled binding
+     *         widgets always for your component. This method is used by the AppWidget picker and
+     *         should not be used by other apps.
+     *
+     * @param appWidgetId     The AppWidget instance for which to set the RemoteViews.
+     * @param provider      The {@link android.content.BroadcastReceiver} that will be the AppWidget
+     *                      provider for this AppWidget.
+     * @param options       Bundle containing options for the AppWidget. See also
+     *                      {@link #updateAppWidgetOptions(int, Bundle)}
+     *
+     * @hide
+     */
+    public void bindAppWidgetId(int appWidgetId, ComponentName provider, Bundle options) {
+        try {
+            sService.bindAppWidgetId(appWidgetId, provider, options);
         }
         catch (RemoteException e) {
             throw new RuntimeException("system server dead?", e);
@@ -594,7 +626,37 @@
         }
         try {
             return sService.bindAppWidgetIdIfAllowed(
-                    mContext.getPackageName(), appWidgetId, provider);
+                    mContext.getPackageName(), appWidgetId, provider, null);
+        }
+        catch (RemoteException e) {
+            throw new RuntimeException("system server dead?", e);
+        }
+    }
+
+    /**
+     * Set the component for a given appWidgetId.
+     *
+     * <p class="note">You need the BIND_APPWIDGET permission or the user must have enabled binding
+     *         widgets always for your component. Should be used by apps that host widgets; if this
+     *         method returns false, call {@link #ACTION_APPWIDGET_BIND} to request permission to
+     *         bind
+     *
+     * @param appWidgetId     The AppWidget instance for which to set the RemoteViews.
+     * @param provider      The {@link android.content.BroadcastReceiver} that will be the AppWidget
+     *                      provider for this AppWidget.
+     * @param options       Bundle containing options for the AppWidget. See also
+     *                      {@link #updateAppWidgetOptions(int, Bundle)}
+     *
+     * @return true if this component has permission to bind the AppWidget
+     */
+    public boolean bindAppWidgetIdIfAllowed(int appWidgetId, ComponentName provider,
+            Bundle options) {
+        if (mContext == null) {
+            return false;
+        }
+        try {
+            return sService.bindAppWidgetIdIfAllowed(
+                    mContext.getPackageName(), appWidgetId, provider, options);
         }
         catch (RemoteException e) {
             throw new RuntimeException("system server dead?", e);
diff --git a/core/java/android/appwidget/AppWidgetProviderInfo.java b/core/java/android/appwidget/AppWidgetProviderInfo.java
index c33681d..5ef3d39 100644
--- a/core/java/android/appwidget/AppWidgetProviderInfo.java
+++ b/core/java/android/appwidget/AppWidgetProviderInfo.java
@@ -44,6 +44,34 @@
     public static final int RESIZE_BOTH = RESIZE_HORIZONTAL | RESIZE_VERTICAL;
 
     /**
+     * Indicates that the widget can be displayed on the home screen. This is the default value.
+     */
+    public static final int WIDGET_CATEGORY_HOME_SCREEN = 1;
+
+    /**
+     * Indicates that the widget can be displayed on the keyguard.
+     */
+    public static final int WIDGET_CATEGORY_KEYGUARD = 2;
+
+    /**
+     * Indicates that the widget supports no special features.
+     */
+    public static final int WIDGET_FEATURES_NONE = 0;
+
+    /**
+     * Indicates that the widget displays the current time. The host may use this as a hint to not
+     * display the time in other places.
+     */
+    public static final int WIDGET_FEATURES_CLOCK = 1;
+
+    /**
+     * Indicates that the widget is output only, ie. has nothing clickable. This may be enforced by
+     * the host. Presently, this flag is used by the keyguard to indicate that it can be placed
+     * in the first position.
+     */
+    public static final int WIDGET_FEATURES_STATUS = 2;
+
+    /**
      * Identity of this AppWidget component.  This component should be a {@link
      * android.content.BroadcastReceiver}, and it will be sent the AppWidget intents
      * {@link android.appwidget as described in the AppWidget package documentation}.
@@ -111,6 +139,16 @@
     public int initialLayout;
 
     /**
+     * The resource id of the initial layout for this AppWidget when it is displayed on keyguard.
+     * This parameter only needs to be provided if the widget can be displayed on the keyguard,
+     * see {@link #widgetCategory}.
+     *
+     * <p>This field corresponds to the <code>android:initialKeyguardLayout</code> attribute in
+     * the AppWidget meta-data file.
+     */
+    public int initialKeyguardLayout;
+
+    /**
      * The activity to launch that will configure the AppWidget.
      *
      * <p>This class name of field corresponds to the <code>android:configure</code> attribute in
@@ -164,6 +202,27 @@
      */
     public int resizeMode;
 
+    /**
+     * Determines whether this widget can be displayed on the home screen, the keyguard, or both.
+     * A widget which is displayed on both needs to ensure that it follows the design guidelines
+     * for both widget classes. This can be achieved by querying the AppWidget options in its
+     * widget provider's update method.
+     *
+     * <p>This field corresponds to the <code>widgetCategory</code> attribute in
+     * the AppWidget meta-data file.
+     */
+    public int widgetCategory;
+
+    /**
+     * A field which specifies any special features that this widget supports. See
+     * {@link #WIDGET_FEATURES_NONE}, {@link #WIDGET_FEATURES_CLOCK},
+     * {@link #WIDGET_FEATURES_STATUS}.
+     *
+     * <p>This field corresponds to the <code>widgetFeatures</code> attribute in
+     * the AppWidget meta-data file.
+     */
+    public int widgetFeatures;
+
     public AppWidgetProviderInfo() {
     }
 
@@ -180,6 +239,7 @@
         this.minResizeHeight = in.readInt();
         this.updatePeriodMillis = in.readInt();
         this.initialLayout = in.readInt();
+        this.initialKeyguardLayout = in.readInt();
         if (0 != in.readInt()) {
             this.configure = new ComponentName(in);
         }
@@ -188,6 +248,8 @@
         this.previewImage = in.readInt();
         this.autoAdvanceViewId = in.readInt();
         this.resizeMode = in.readInt();
+        this.widgetCategory = in.readInt();
+        this.widgetFeatures = in.readInt();
     }
 
     public void writeToParcel(android.os.Parcel out, int flags) {
@@ -203,6 +265,7 @@
         out.writeInt(this.minResizeHeight);
         out.writeInt(this.updatePeriodMillis);
         out.writeInt(this.initialLayout);
+        out.writeInt(this.initialKeyguardLayout);
         if (this.configure != null) {
             out.writeInt(1);
             this.configure.writeToParcel(out, flags);
@@ -214,6 +277,8 @@
         out.writeInt(this.previewImage);
         out.writeInt(this.autoAdvanceViewId);
         out.writeInt(this.resizeMode);
+        out.writeInt(this.widgetCategory);
+        out.writeInt(this.widgetFeatures);
     }
 
     public int describeContents() {
diff --git a/core/java/android/content/pm/UserInfo.java b/core/java/android/content/pm/UserInfo.java
index 060a235..6bc9a1f 100644
--- a/core/java/android/content/pm/UserInfo.java
+++ b/core/java/android/content/pm/UserInfo.java
@@ -18,7 +18,7 @@
 
 import android.os.Parcel;
 import android.os.Parcelable;
-import android.os.Parcelable.Creator;
+import android.os.UserHandle;
 
 /**
  * Per-user information.
@@ -92,6 +92,10 @@
         serialNumber = orig.serialNumber;
     }
 
+    public UserHandle getUserHandle() {
+        return new UserHandle(id);
+    }
+
     @Override
     public String toString() {
         return "UserInfo{" + id + ":" + name + ":" + Integer.toHexString(flags) + "}";
diff --git a/core/java/android/hardware/Camera.java b/core/java/android/hardware/Camera.java
index 829620b..08e0ebe 100644
--- a/core/java/android/hardware/Camera.java
+++ b/core/java/android/hardware/Camera.java
@@ -1146,6 +1146,31 @@
     public native final void setDisplayOrientation(int degrees);
 
     /**
+     * Enable or disable the default shutter sound when taking a picture.
+     *
+     * By default, the camera plays the system-defined camera shutter sound when
+     * {@link #takePicture} is called. Using this method, the shutter sound can
+     * be disabled. It is strongly recommended that an alternative shutter sound
+     * is played in the {@link ShutterCallback} when the system shutter sound is
+     * disabled.
+     *
+     * Note that devices may not always allow control of the camera shutter
+     * sound. If the shutter sound cannot be controlled, this method will return
+     * false.
+     *
+     * @param enabled whether the camera should play the system shutter sound
+     *                when {@link #takePicture takePicture} is called.
+     * @return true if the shutter sound state was successfully changed. False
+     *         if the shutter sound cannot be controlled; in this case, the
+     *         application should not play its own shutter sound since the
+     *         system shutter sound will play when a picture is taken.
+     * @see #takePicture
+     * @see ShutterCallback
+     * @hide
+     */
+    public native final boolean enableShutterSound(boolean enabled);
+
+    /**
      * Callback interface for zoom changes during a smooth zoom operation.
      *
      * @see #setZoomChangeListener(OnZoomChangeListener)
@@ -1782,6 +1807,14 @@
         public static final String SCENE_MODE_BARCODE = "barcode";
 
         /**
+         * Capture a scene using high dynamic range imaging techniques. The
+         * camera will return an image that has an extended dynamic range
+         * compared to a regular capture. Capturing such an image may take
+         * longer than a regular capture.
+         */
+        public static final String SCENE_MODE_HDR = "hdr";
+
+        /**
          * Auto-focus mode. Applications should call {@link
          * #autoFocus(AutoFocusCallback)} to start the focus in this mode.
          */
@@ -2784,6 +2817,7 @@
          * @see #SCENE_MODE_SPORTS
          * @see #SCENE_MODE_PARTY
          * @see #SCENE_MODE_CANDLELIGHT
+         * @see #SCENE_MODE_BARCODE
          */
         public String getSceneMode() {
             return get(KEY_SCENE_MODE);
diff --git a/core/java/android/net/EthernetDataTracker.java b/core/java/android/net/EthernetDataTracker.java
index c52aa9e..3a06dc0 100644
--- a/core/java/android/net/EthernetDataTracker.java
+++ b/core/java/android/net/EthernetDataTracker.java
@@ -227,7 +227,7 @@
                     mIface = iface;
                     mNMService.setInterfaceUp(iface);
                     InterfaceConfiguration config = mNMService.getInterfaceConfig(iface);
-                    mLinkUp = config.isActive();
+                    mLinkUp = config.hasFlag("up");
                     if (config != null && mHwAddr == null) {
                         mHwAddr = config.getHardwareAddress();
                         if (mHwAddr != null) {
diff --git a/core/java/android/os/Debug.java b/core/java/android/os/Debug.java
index 591cd0e..c08bfeb 100644
--- a/core/java/android/os/Debug.java
+++ b/core/java/android/os/Debug.java
@@ -95,7 +95,7 @@
      * Default trace file path and file
      */
     private static final String DEFAULT_TRACE_PATH_PREFIX =
-        Environment.getExternalStorageDirectory().getPath() + "/";
+        Environment.getLegacyExternalStorageDirectory().getPath() + "/";
     private static final String DEFAULT_TRACE_BODY = "dmtrace";
     private static final String DEFAULT_TRACE_EXTENSION = ".trace";
     private static final String DEFAULT_TRACE_FILE_PATH =
diff --git a/core/java/android/os/Environment.java b/core/java/android/os/Environment.java
index 2fbcf3f..6667a41f 100644
--- a/core/java/android/os/Environment.java
+++ b/core/java/android/os/Environment.java
@@ -16,9 +16,10 @@
 
 package android.os;
 
-import android.content.res.Resources;
 import android.os.storage.IMountService;
+import android.os.storage.StorageManager;
 import android.os.storage.StorageVolume;
+import android.text.TextUtils;
 import android.util.Log;
 
 import java.io.File;
@@ -29,31 +30,125 @@
 public class Environment {
     private static final String TAG = "Environment";
 
+    private static final String ENV_EXTERNAL_STORAGE = "EXTERNAL_STORAGE";
+    private static final String ENV_EMULATED_STORAGE_TARGET = "EMULATED_STORAGE_TARGET";
+
     private static final File ROOT_DIRECTORY
             = getDirectory("ANDROID_ROOT", "/system");
 
     private static final String SYSTEM_PROPERTY_EFS_ENABLED = "persist.security.efs.enabled";
 
-    private static final Object mLock = new Object();
+    private static UserEnvironment sCurrentUser;
 
-    private volatile static StorageVolume mPrimaryVolume = null;
+    private static final Object sLock = new Object();
+
+    // @GuardedBy("sLock")
+    private static volatile StorageVolume sPrimaryVolume;
 
     private static StorageVolume getPrimaryVolume() {
-        if (mPrimaryVolume == null) {
-            synchronized (mLock) {
-                if (mPrimaryVolume == null) {
+        if (sPrimaryVolume == null) {
+            synchronized (sLock) {
+                if (sPrimaryVolume == null) {
                     try {
                         IMountService mountService = IMountService.Stub.asInterface(ServiceManager
                                 .getService("mount"));
-                        Parcelable[] volumes = mountService.getVolumeList();
-                        mPrimaryVolume = (StorageVolume)volumes[0];
+                        final StorageVolume[] volumes = mountService.getVolumeList();
+                        sPrimaryVolume = StorageManager.getPrimaryVolume(volumes);
                     } catch (Exception e) {
                         Log.e(TAG, "couldn't talk to MountService", e);
                     }
                 }
             }
         }
-        return mPrimaryVolume;
+        return sPrimaryVolume;
+    }
+
+    static {
+        initForCurrentUser();
+    }
+
+    /** {@hide} */
+    public static void initForCurrentUser() {
+        final int userId = UserHandle.myUserId();
+        sCurrentUser = new UserEnvironment(userId);
+
+        synchronized (sLock) {
+            sPrimaryVolume = null;
+        }
+    }
+
+    /** {@hide} */
+    public static class UserEnvironment {
+        // TODO: generalize further to create package-specific environment
+
+        private final File mExternalStorage;
+        private final File mExternalStorageAndroidData;
+        private final File mExternalStorageAndroidMedia;
+        private final File mExternalStorageAndroidObb;
+
+        public UserEnvironment(int userId) {
+            // See storage config details at http://source.android.com/tech/storage/
+            String rawExternalStorage = System.getenv(ENV_EXTERNAL_STORAGE);
+            String rawEmulatedStorageTarget = System.getenv(ENV_EMULATED_STORAGE_TARGET);
+
+            if (!TextUtils.isEmpty(rawEmulatedStorageTarget)) {
+                // Device has emulated storage; external storage paths should have
+                // userId burned into them.
+                final File emulatedBase = new File(rawEmulatedStorageTarget);
+
+                // /storage/emulated/0
+                mExternalStorage = buildPath(emulatedBase, Integer.toString(userId));
+                // /storage/emulated/obb
+                mExternalStorageAndroidObb = buildPath(emulatedBase, "obb");
+
+            } else {
+                // Device has physical external storage; use plain paths.
+                if (TextUtils.isEmpty(rawExternalStorage)) {
+                    Log.w(TAG, "EXTERNAL_STORAGE undefined; falling back to default");
+                    rawExternalStorage = "/storage/sdcard0";
+                }
+
+                // /storage/sdcard0
+                mExternalStorage = new File(rawExternalStorage);
+                // /storage/sdcard0/Android/obb
+                mExternalStorageAndroidObb = buildPath(mExternalStorage, "Android", "obb");
+            }
+
+            mExternalStorageAndroidData = buildPath(mExternalStorage, "Android", "data");
+            mExternalStorageAndroidMedia = buildPath(mExternalStorage, "Android", "media");
+        }
+
+        public File getExternalStorageDirectory() {
+            return mExternalStorage;
+        }
+
+        public File getExternalStoragePublicDirectory(String type) {
+            return new File(mExternalStorage, type);
+        }
+
+        public File getExternalStorageAndroidDataDir() {
+            return mExternalStorageAndroidData;
+        }
+
+        public File getExternalStorageAppDataDirectory(String packageName) {
+            return new File(mExternalStorageAndroidData, packageName);
+        }
+
+        public File getExternalStorageAppMediaDirectory(String packageName) {
+            return new File(mExternalStorageAndroidMedia, packageName);
+        }
+
+        public File getExternalStorageAppObbDirectory(String packageName) {
+            return new File(mExternalStorageAndroidObb, packageName);
+        }
+
+        public File getExternalStorageAppFilesDirectory(String packageName) {
+            return new File(new File(mExternalStorageAndroidData, packageName), "files");
+        }
+
+        public File getExternalStorageAppCacheDirectory(String packageName) {
+            return new File(new File(mExternalStorageAndroidData, packageName), "cache");
+        }
     }
 
     /**
@@ -137,20 +232,7 @@
     private static final File MEDIA_STORAGE_DIRECTORY
             = getDirectory("MEDIA_STORAGE", "/data/media");
 
-    private static final File EXTERNAL_STORAGE_DIRECTORY
-            = getDirectory("EXTERNAL_STORAGE", "/storage/sdcard0");
-
-    private static final File EXTERNAL_STORAGE_ANDROID_DATA_DIRECTORY = new File(new File(
-            getDirectory("EXTERNAL_STORAGE", "/storage/sdcard0"), "Android"), "data");
-
-    private static final File EXTERNAL_STORAGE_ANDROID_MEDIA_DIRECTORY = new File(new File(
-            getDirectory("EXTERNAL_STORAGE", "/storage/sdcard0"), "Android"), "media");
-
-    private static final File EXTERNAL_STORAGE_ANDROID_OBB_DIRECTORY = new File(new File(
-            getDirectory("EXTERNAL_STORAGE", "/storage/sdcard0"), "Android"), "obb");
-
-    private static final File DOWNLOAD_CACHE_DIRECTORY
-            = getDirectory("DOWNLOAD_CACHE", "/cache");
+    private static final File DOWNLOAD_CACHE_DIRECTORY = getDirectory("DOWNLOAD_CACHE", "/cache");
 
     /**
      * Gets the Android data directory.
@@ -198,7 +280,13 @@
      * @see #isExternalStorageRemovable()
      */
     public static File getExternalStorageDirectory() {
-        return EXTERNAL_STORAGE_DIRECTORY;
+        throwIfSystem();
+        return sCurrentUser.getExternalStorageDirectory();
+    }
+
+    /** {@hide} */
+    public static File getLegacyExternalStorageDirectory() {
+        return new File(System.getenv(ENV_EXTERNAL_STORAGE));
     }
 
     /**
@@ -318,7 +406,8 @@
      * using it such as with {@link File#mkdirs File.mkdirs()}.
      */
     public static File getExternalStoragePublicDirectory(String type) {
-        return new File(getExternalStorageDirectory(), type);
+        throwIfSystem();
+        return sCurrentUser.getExternalStoragePublicDirectory(type);
     }
 
     /**
@@ -326,7 +415,8 @@
      * @hide
      */
     public static File getExternalStorageAndroidDataDir() {
-        return EXTERNAL_STORAGE_ANDROID_DATA_DIRECTORY;
+        throwIfSystem();
+        return sCurrentUser.getExternalStorageAndroidDataDir();
     }
     
     /**
@@ -334,7 +424,8 @@
      * @hide
      */
     public static File getExternalStorageAppDataDirectory(String packageName) {
-        return new File(EXTERNAL_STORAGE_ANDROID_DATA_DIRECTORY, packageName);
+        throwIfSystem();
+        return sCurrentUser.getExternalStorageAppDataDirectory(packageName);
     }
     
     /**
@@ -342,7 +433,8 @@
      * @hide
      */
     public static File getExternalStorageAppMediaDirectory(String packageName) {
-        return new File(EXTERNAL_STORAGE_ANDROID_MEDIA_DIRECTORY, packageName);
+        throwIfSystem();
+        return sCurrentUser.getExternalStorageAppMediaDirectory(packageName);
     }
     
     /**
@@ -350,7 +442,8 @@
      * @hide
      */
     public static File getExternalStorageAppObbDirectory(String packageName) {
-        return new File(EXTERNAL_STORAGE_ANDROID_OBB_DIRECTORY, packageName);
+        throwIfSystem();
+        return sCurrentUser.getExternalStorageAppObbDirectory(packageName);
     }
     
     /**
@@ -358,17 +451,17 @@
      * @hide
      */
     public static File getExternalStorageAppFilesDirectory(String packageName) {
-        return new File(new File(EXTERNAL_STORAGE_ANDROID_DATA_DIRECTORY,
-                packageName), "files");
+        throwIfSystem();
+        return sCurrentUser.getExternalStorageAppFilesDirectory(packageName);
     }
-    
+
     /**
      * Generates the path to an application's cache.
      * @hide
      */
     public static File getExternalStorageAppCacheDirectory(String packageName) {
-        return new File(new File(EXTERNAL_STORAGE_ANDROID_DATA_DIRECTORY,
-                packageName), "cache");
+        throwIfSystem();
+        return sCurrentUser.getExternalStorageAppCacheDirectory(packageName);
     }
     
     /**
@@ -441,9 +534,10 @@
         try {
             IMountService mountService = IMountService.Stub.asInterface(ServiceManager
                     .getService("mount"));
-            return mountService.getVolumeState(getExternalStorageDirectory()
-                    .toString());
-        } catch (Exception rex) {
+            final StorageVolume primary = getPrimaryVolume();
+            return mountService.getVolumeState(primary.getPath());
+        } catch (RemoteException rex) {
+            Log.w(TAG, "Failed to read external storage state; assuming REMOVED: " + rex);
             return Environment.MEDIA_REMOVED;
         }
     }
@@ -457,8 +551,8 @@
      * <p>See {@link #getExternalStorageDirectory()} for more information.
      */
     public static boolean isExternalStorageRemovable() {
-        StorageVolume volume = getPrimaryVolume();
-        return (volume != null && volume.isRemovable());
+        final StorageVolume primary = getPrimaryVolume();
+        return (primary != null && primary.isRemovable());
     }
 
     /**
@@ -475,12 +569,30 @@
      * android.content.ComponentName, boolean)} for additional details.
      */
     public static boolean isExternalStorageEmulated() {
-        StorageVolume volume = getPrimaryVolume();
-        return (volume != null && volume.isEmulated());
+        final StorageVolume primary = getPrimaryVolume();
+        return (primary != null && primary.isEmulated());
     }
 
     static File getDirectory(String variableName, String defaultPath) {
         String path = System.getenv(variableName);
         return path == null ? new File(defaultPath) : new File(path);
     }
+
+    private static void throwIfSystem() {
+        if (Process.myUid() == Process.SYSTEM_UID) {
+            Log.wtf(TAG, "Static storage paths aren't available from AID_SYSTEM", new Throwable());
+        }
+    }
+
+    private static File buildPath(File base, String... segments) {
+        File cur = base;
+        for (String segment : segments) {
+            if (cur == null) {
+                cur = new File(segment);
+            } else {
+                cur = new File(cur, segment);
+            }
+        }
+        return cur;
+    }
 }
diff --git a/core/java/android/os/storage/IMountService.java b/core/java/android/os/storage/IMountService.java
index ab64866..0b16316 100644
--- a/core/java/android/os/storage/IMountService.java
+++ b/core/java/android/os/storage/IMountService.java
@@ -677,15 +677,15 @@
                 return _result;
             }
 
-            public Parcelable[] getVolumeList() throws RemoteException {
+            public StorageVolume[] getVolumeList() throws RemoteException {
                 Parcel _data = Parcel.obtain();
                 Parcel _reply = Parcel.obtain();
-                Parcelable[] _result;
+                StorageVolume[] _result;
                 try {
                     _data.writeInterfaceToken(DESCRIPTOR);
                     mRemote.transact(Stub.TRANSACTION_getVolumeList, _data, _reply, 0);
                     _reply.readException();
-                    _result = _reply.readParcelableArray(StorageVolume.class.getClassLoader());
+                    _result = _reply.createTypedArray(StorageVolume.CREATOR);
                 } finally {
                     _reply.recycle();
                     _data.recycle();
@@ -1119,9 +1119,9 @@
                 }
                 case TRANSACTION_getVolumeList: {
                     data.enforceInterface(DESCRIPTOR);
-                    Parcelable[] result = getVolumeList();
+                    StorageVolume[] result = getVolumeList();
                     reply.writeNoException();
-                    reply.writeParcelableArray(result, 0);
+                    reply.writeTypedArray(result, android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
                     return true;
                 }
                 case TRANSACTION_getSecureContainerFilesystemPath: {
@@ -1358,7 +1358,7 @@
     /**
      * Returns list of all mountable volumes.
      */
-    public Parcelable[] getVolumeList() throws RemoteException;
+    public StorageVolume[] getVolumeList() throws RemoteException;
 
     /**
      * Gets the path on the filesystem for the ASEC container itself.
diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java
index 8a20a6e..54c8709d 100644
--- a/core/java/android/os/storage/StorageManager.java
+++ b/core/java/android/os/storage/StorageManager.java
@@ -16,6 +16,8 @@
 
 package android.os.storage;
 
+import android.app.NotificationManager;
+import android.content.Context;
 import android.os.Environment;
 import android.os.Handler;
 import android.os.Looper;
@@ -285,6 +287,11 @@
         }
     }
 
+    /** {@hide} */
+    public static StorageManager from(Context context) {
+        return (StorageManager) context.getSystemService(Context.STORAGE_SERVICE);
+    }
+
     /**
      * Constructs a StorageManager object through which an application can
      * can communicate with the systems mount service.
@@ -594,4 +601,20 @@
         }
         return paths;
     }
+
+    /** {@hide} */
+    public StorageVolume getPrimaryVolume() {
+        return getPrimaryVolume(getVolumeList());
+    }
+
+    /** {@hide} */
+    public static StorageVolume getPrimaryVolume(StorageVolume[] volumes) {
+        for (StorageVolume volume : volumes) {
+            if (volume.isPrimary()) {
+                return volume;
+            }
+        }
+        Log.w(TAG, "No primary storage defined");
+        return null;
+    }
 }
diff --git a/core/java/android/os/storage/StorageVolume.java b/core/java/android/os/storage/StorageVolume.java
index b5983d1..177a955 100644
--- a/core/java/android/os/storage/StorageVolume.java
+++ b/core/java/android/os/storage/StorageVolume.java
@@ -19,16 +19,22 @@
 import android.content.Context;
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.os.UserHandle;
+
+import java.io.File;
 
 /**
- * A class representing a storage volume
+ * Description of a storage volume and its capabilities, including the
+ * filesystem path where it may be mounted.
+ *
  * @hide
  */
 public class StorageVolume implements Parcelable {
 
+    // TODO: switch to more durable token
     private int mStorageId;
 
-    private final String mPath;
+    private final File mPath;
     private final int mDescriptionId;
     private final boolean mPrimary;
     private final boolean mRemovable;
@@ -37,14 +43,17 @@
     private final boolean mAllowMassStorage;
     /** Maximum file size for the storage, or zero for no limit */
     private final long mMaxFileSize;
+    /** When set, indicates exclusive ownership of this volume */
+    private final UserHandle mOwner;
 
     // StorageVolume extra for ACTION_MEDIA_REMOVED, ACTION_MEDIA_UNMOUNTED, ACTION_MEDIA_CHECKING,
     // ACTION_MEDIA_NOFS, ACTION_MEDIA_MOUNTED, ACTION_MEDIA_SHARED, ACTION_MEDIA_UNSHARED,
     // ACTION_MEDIA_BAD_REMOVAL, ACTION_MEDIA_UNMOUNTABLE and ACTION_MEDIA_EJECT broadcasts.
     public static final String EXTRA_STORAGE_VOLUME = "storage_volume";
 
-    public StorageVolume(String path, int descriptionId, boolean primary, boolean removable,
-            boolean emulated, int mtpReserveSpace, boolean allowMassStorage, long maxFileSize) {
+    public StorageVolume(File path, int descriptionId, boolean primary, boolean removable,
+            boolean emulated, int mtpReserveSpace, boolean allowMassStorage, long maxFileSize,
+            UserHandle owner) {
         mPath = path;
         mDescriptionId = descriptionId;
         mPrimary = primary;
@@ -53,18 +62,26 @@
         mMtpReserveSpace = mtpReserveSpace;
         mAllowMassStorage = allowMassStorage;
         mMaxFileSize = maxFileSize;
+        mOwner = owner;
     }
 
     private StorageVolume(Parcel in) {
         mStorageId = in.readInt();
-        mPath = in.readString();
+        mPath = new File(in.readString());
         mDescriptionId = in.readInt();
-        mPrimary = in.readByte() != 0;
-        mRemovable = in.readByte() != 0;
-        mEmulated = in.readByte() != 0;
+        mPrimary = in.readInt() != 0;
+        mRemovable = in.readInt() != 0;
+        mEmulated = in.readInt() != 0;
         mMtpReserveSpace = in.readInt();
-        mAllowMassStorage = in.readByte() != 0;
+        mAllowMassStorage = in.readInt() != 0;
         mMaxFileSize = in.readLong();
+        mOwner = in.readParcelable(null);
+    }
+
+    public static StorageVolume fromTemplate(StorageVolume template, File path, UserHandle owner) {
+        return new StorageVolume(path, template.mDescriptionId, template.mPrimary,
+                template.mRemovable, template.mEmulated, template.mMtpReserveSpace,
+                template.mAllowMassStorage, template.mMaxFileSize, owner);
     }
 
     /**
@@ -73,6 +90,10 @@
      * @return the mount path
      */
     public String getPath() {
+        return mPath.toString();
+    }
+
+    public File getPathFile() {
         return mPath;
     }
 
@@ -164,6 +185,10 @@
         return mMaxFileSize;
     }
 
+    public UserHandle getOwner() {
+        return mOwner;
+    }
+
     @Override
     public boolean equals(Object obj) {
         if (obj instanceof StorageVolume && mPath != null) {
@@ -180,10 +205,19 @@
 
     @Override
     public String toString() {
-        return "StorageVolume [mAllowMassStorage=" + mAllowMassStorage + ", mDescriptionId="
-                + mDescriptionId + ", mEmulated=" + mEmulated + ", mMaxFileSize=" + mMaxFileSize
-                + ", mMtpReserveSpace=" + mMtpReserveSpace + ", mPath=" + mPath + ", mRemovable="
-                + mRemovable + ", mStorageId=" + mStorageId + "]";
+        final StringBuilder builder = new StringBuilder("StorageVolume [");
+        builder.append("mStorageId=").append(mStorageId);
+        builder.append(" mPath=").append(mPath);
+        builder.append(" mDescriptionId=").append(mDescriptionId);
+        builder.append(" mPrimary=").append(mPrimary);
+        builder.append(" mRemovable=").append(mRemovable);
+        builder.append(" mEmulated=").append(mEmulated);
+        builder.append(" mMtpReserveSpace=").append(mMtpReserveSpace);
+        builder.append(" mAllowMassStorage=").append(mAllowMassStorage);
+        builder.append(" mMaxFileSize=").append(mMaxFileSize);
+        builder.append(" mOwner=").append(mOwner);
+        builder.append("]");
+        return builder.toString();
     }
 
     public static final Creator<StorageVolume> CREATOR = new Creator<StorageVolume>() {
@@ -206,7 +240,7 @@
     @Override
     public void writeToParcel(Parcel parcel, int flags) {
         parcel.writeInt(mStorageId);
-        parcel.writeString(mPath);
+        parcel.writeString(mPath.toString());
         parcel.writeInt(mDescriptionId);
         parcel.writeInt(mPrimary ? 1 : 0);
         parcel.writeInt(mRemovable ? 1 : 0);
@@ -214,5 +248,6 @@
         parcel.writeInt(mMtpReserveSpace);
         parcel.writeInt(mAllowMassStorage ? 1 : 0);
         parcel.writeLong(mMaxFileSize);
+        parcel.writeParcelable(mOwner, flags);
     }
 }
diff --git a/core/java/android/provider/ContactsContract.java b/core/java/android/provider/ContactsContract.java
index 08621ea..54a2273 100755
--- a/core/java/android/provider/ContactsContract.java
+++ b/core/java/android/provider/ContactsContract.java
@@ -7900,6 +7900,16 @@
                 "com.android.contacts.action.GET_MULTIPLE_PHONES";
 
         /**
+         * A broadcast action which is sent when any change has been made to the profile, such
+         * as the profile name or the picture.  A receiver must have
+         * the android.permission.READ_PROFILE permission.
+         *
+         * @hide
+         */
+        public static final String ACTION_PROFILE_CHANGED =
+                "android.provider.Contacts.PROFILE_CHANGED";
+
+        /**
          * Used with {@link #SHOW_OR_CREATE_CONTACT} to force creating a new
          * contact if no matching contact found. Otherwise, default behavior is
          * to prompt user with dialog before creating.
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 1739205..9ea523d 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -2616,6 +2616,12 @@
             MOVED_TO_GLOBAL.add(Settings.Global.WIFI_WATCHDOG_POOR_NETWORK_TEST_ENABLED);
             MOVED_TO_GLOBAL.add(Settings.Global.WIFI_WATCHDOG_RSSI_FETCH_INTERVAL_MS);
             MOVED_TO_GLOBAL.add(Settings.Global.WIMAX_NETWORKS_AVAILABLE_NOTIFICATION_ON);
+            MOVED_TO_GLOBAL.add(Settings.Global.PACKAGE_VERIFIER_ENABLE);
+            MOVED_TO_GLOBAL.add(Settings.Global.PACKAGE_VERIFIER_TIMEOUT);
+            MOVED_TO_GLOBAL.add(Settings.Global.PACKAGE_VERIFIER_DEFAULT_RESPONSE);
+            MOVED_TO_GLOBAL.add(Settings.Global.DATA_STALL_ALARM_NON_AGGRESSIVE_DELAY_IN_MS);
+            MOVED_TO_GLOBAL.add(Settings.Global.DATA_STALL_ALARM_AGGRESSIVE_DELAY_IN_MS);
+            MOVED_TO_GLOBAL.add(Settings.Global.GPRS_REGISTER_CHECK_PERIOD_MS);
             MOVED_TO_GLOBAL.add(Settings.Global.WTF_IS_FATAL);
         }
 
@@ -4254,30 +4260,28 @@
                 Global.PDP_WATCHDOG_MAX_PDP_RESET_FAIL_COUNT;
 
         /**
-         * The number of milliseconds to delay when checking for data stalls during
-         * non-aggressive detection. (screen is turned off.)
+         * @deprecated Moved to Settings.Global
          * @hide
          */
+        @Deprecated
         public static final String DATA_STALL_ALARM_NON_AGGRESSIVE_DELAY_IN_MS =
-                "data_stall_alarm_non_aggressive_delay_in_ms";
+                Global.DATA_STALL_ALARM_NON_AGGRESSIVE_DELAY_IN_MS;
 
         /**
-         * The number of milliseconds to delay when checking for data stalls during
-         * aggressive detection. (screen on or suspected data stall)
+         * @deprecated Moved to Settings.Global
          * @hide
          */
+        @Deprecated
         public static final String DATA_STALL_ALARM_AGGRESSIVE_DELAY_IN_MS =
-                "data_stall_alarm_aggressive_delay_in_ms";
+                Global.DATA_STALL_ALARM_AGGRESSIVE_DELAY_IN_MS;
 
         /**
-         * The interval in milliseconds at which to check gprs registration
-         * after the first registration mismatch of gprs and voice service,
-         * to detect possible data network registration problems.
-         *
+         * @deprecated Moved to Settings.Global
          * @hide
          */
+        @Deprecated
         public static final String GPRS_REGISTER_CHECK_PERIOD_MS =
-                "gprs_register_check_period_ms";
+                Global.GPRS_REGISTER_CHECK_PERIOD_MS;
 
         /**
          * @deprecated Use {@link android.provider.Settings.Global#NITZ_UPDATE_SPACING} instead
@@ -5604,6 +5608,32 @@
        public static final String WIFI_P2P_DEVICE_NAME = "wifi_p2p_device_name";
 
        /**
+        * The number of milliseconds to delay when checking for data stalls during
+        * non-aggressive detection. (screen is turned off.)
+        * @hide
+        */
+       public static final String DATA_STALL_ALARM_NON_AGGRESSIVE_DELAY_IN_MS =
+               "data_stall_alarm_non_aggressive_delay_in_ms";
+
+       /**
+        * The number of milliseconds to delay when checking for data stalls during
+        * aggressive detection. (screen on or suspected data stall)
+        * @hide
+        */
+       public static final String DATA_STALL_ALARM_AGGRESSIVE_DELAY_IN_MS =
+               "data_stall_alarm_aggressive_delay_in_ms";
+
+       /**
+        * The interval in milliseconds at which to check gprs registration
+        * after the first registration mismatch of gprs and voice service,
+        * to detect possible data network registration problems.
+        *
+        * @hide
+        */
+       public static final String GPRS_REGISTER_CHECK_PERIOD_MS =
+               "gprs_register_check_period_ms";
+
+       /**
         * Nonzero causes Log.wtf() to crash.
         * @hide
         */
diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl
index dae5c0a..a64cbf7 100644
--- a/core/java/android/view/IWindowManager.aidl
+++ b/core/java/android/view/IWindowManager.aidl
@@ -59,7 +59,7 @@
             in IInputContext inputContext);
     boolean inputMethodClientHasFocus(IInputMethodClient client);
 
-    void setForcedDisplaySize(int displayId, int longDimen, int shortDimen);
+    void setForcedDisplaySize(int displayId, int width, int height);
     void clearForcedDisplaySize(int displayId);
     void setForcedDisplayDensity(int displayId, int density);
     void clearForcedDisplayDensity(int displayId);
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 236adab..7567d88 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -39,7 +39,6 @@
 import android.graphics.Shader;
 import android.graphics.drawable.ColorDrawable;
 import android.graphics.drawable.Drawable;
-import android.hardware.display.DisplayManager;
 import android.hardware.display.DisplayManagerGlobal;
 import android.os.Bundle;
 import android.os.Handler;
@@ -4275,25 +4274,42 @@
      * @return Whether any parent scrolled.
      */
     public boolean requestRectangleOnScreen(Rect rectangle, boolean immediate) {
+        if (mAttachInfo == null) {
+            return false;
+        }
+
         View child = this;
+
+        RectF position = mAttachInfo.mTmpTransformRect;
+        position.set(rectangle);
+
         ViewParent parent = mParent;
         boolean scrolled = false;
         while (parent != null) {
+            rectangle.set((int) position.left, (int) position.top,
+                    (int) position.right, (int) position.bottom);
+
             scrolled |= parent.requestChildRectangleOnScreen(child,
                     rectangle, immediate);
 
-            // offset rect so next call has the rectangle in the
-            // coordinate system of its direct child.
-            rectangle.offset(child.getLeft(), child.getTop());
-            rectangle.offset(-child.getScrollX(), -child.getScrollY());
+            if (!child.hasIdentityMatrix()) {
+                child.getMatrix().mapRect(position);
+            }
+
+            position.offset(child.mLeft, child.mTop);
 
             if (!(parent instanceof View)) {
                 break;
             }
 
-            child = (View) parent;
+            View parentView = (View) parent;
+
+            position.offset(-parentView.getScrollX(), -parentView.getScrollY());
+
+            child = parentView;
             parent = child.getParent();
         }
+
         return scrolled;
     }
 
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index 62e1383..ae10fbe 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -5568,7 +5568,7 @@
          * to this field.
          */
         @ViewDebug.ExportedProperty(category = "layout")
-        public int startMargin = DEFAULT_RELATIVE;
+        private int startMargin = DEFAULT_RELATIVE;
 
         /**
          * The end margin in pixels of the child.
@@ -5576,7 +5576,7 @@
          * to this field.
          */
         @ViewDebug.ExportedProperty(category = "layout")
-        public int endMargin = DEFAULT_RELATIVE;
+        private int endMargin = DEFAULT_RELATIVE;
 
         /**
          * The default start and end margin.
@@ -5724,6 +5724,17 @@
         }
 
         /**
+         * Sets the relative start margin.
+         *
+         * @param start the start margin size
+         *
+         * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginStart
+         */
+        public void setMarginStart(int start) {
+            startMargin = start;
+        }
+
+        /**
          * Returns the start margin in pixels.
          *
          * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginStart
@@ -5742,6 +5753,17 @@
         }
 
         /**
+         * Sets the relative end margin.
+         *
+         * @param end the end margin size
+         *
+         * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginEnd
+         */
+        public void setMarginEnd(int end) {
+            endMargin = end;
+        }
+
+        /**
          * Returns the end margin in pixels.
          *
          * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginEnd
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 3006b5d..e1312c1 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -3612,8 +3612,31 @@
                 finishInputEvent(q, true);
             } else {
                 if (q.mEvent instanceof KeyEvent) {
+                    KeyEvent event = (KeyEvent)q.mEvent;
+                    if (event.getAction() != KeyEvent.ACTION_UP) {
+                        // If the window doesn't currently have input focus, then drop
+                        // this event.  This could be an event that came back from the
+                        // IME dispatch but the window has lost focus in the meantime.
+                        if (!mAttachInfo.mHasWindowFocus) {
+                            Slog.w(TAG, "Dropping event due to no window focus: " + event);
+                            finishInputEvent(q, true);
+                            return;
+                        }
+                    }
                     deliverKeyEventPostIme(q);
                 } else {
+                    MotionEvent event = (MotionEvent)q.mEvent;
+                    if (event.getAction() != MotionEvent.ACTION_CANCEL
+                            && event.getAction() != MotionEvent.ACTION_UP) {
+                        // If the window doesn't currently have input focus, then drop
+                        // this event.  This could be an event that came back from the
+                        // IME dispatch but the window has lost focus in the meantime.
+                        if (!mAttachInfo.mHasWindowFocus) {
+                            Slog.w(TAG, "Dropping event due to no window focus: " + event);
+                            finishInputEvent(q, true);
+                            return;
+                        }
+                    }
                     final int source = q.mEvent.getSource();
                     if ((source & InputDevice.SOURCE_CLASS_TRACKBALL) != 0) {
                         deliverTrackballEventPostIme(q);
diff --git a/core/java/android/view/WindowInfo.java b/core/java/android/view/WindowInfo.java
index 00875ae..7d16e14 100644
--- a/core/java/android/view/WindowInfo.java
+++ b/core/java/android/view/WindowInfo.java
@@ -45,13 +45,15 @@
 
     public final Rect touchableRegion = new Rect();
 
-    public int type;
+    public int type = UNDEFINED;
 
-    public float compatibilityScale;
+    public float compatibilityScale = UNDEFINED;
 
     public boolean visible;
 
-    public int displayId;
+    public int displayId = UNDEFINED;
+
+    public int layer = UNDEFINED;
 
     private WindowInfo() {
         /* do nothing - reduce visibility */
@@ -71,6 +73,7 @@
         parcel.writeFloat(compatibilityScale);
         parcel.writeInt(visible ? 1 : 0);
         parcel.writeInt(displayId);
+        parcel.writeInt(layer);
         recycle();
     }
 
@@ -82,6 +85,7 @@
         compatibilityScale = parcel.readFloat();
         visible = (parcel.readInt() == 1);
         displayId = parcel.readInt();
+        layer = parcel.readInt();
     }
 
     public static WindowInfo obtain(WindowInfo other) {
@@ -90,9 +94,10 @@
         info.frame.set(other.frame);
         info.touchableRegion.set(other.touchableRegion);
         info.type = other.type;
-        info.displayId = other.displayId;
         info.compatibilityScale = other.compatibilityScale;
         info.visible = other.visible;
+        info.displayId = other.displayId;
+        info.layer = other.layer;
         return info;
     }
 
@@ -131,8 +136,25 @@
         frame.setEmpty();
         touchableRegion.setEmpty();
         type = UNDEFINED;
-        displayId = UNDEFINED;
+        compatibilityScale = UNDEFINED;
         visible = false;
+        displayId = UNDEFINED;
+        layer = UNDEFINED;
+    }
+
+    @Override
+    public String toString() {
+        StringBuilder builder = new StringBuilder();
+        builder.append("Window [token:").append((token != null) ? token.hashCode() : null);
+        builder.append(", displayId:").append(displayId);
+        builder.append(", type:").append(type);
+        builder.append(", visible:").append(visible);
+        builder.append(", layer:").append(layer);
+        builder.append(", compatibilityScale:").append(compatibilityScale);
+        builder.append(", frame:").append(frame);
+        builder.append(", touchableRegion:").append(touchableRegion);
+        builder.append("]");
+        return builder.toString();
     }
 
     /**
diff --git a/core/java/android/webkit/AccessibilityInjector.java b/core/java/android/webkit/AccessibilityInjector.java
index fd73fda..a51a8f6 100644
--- a/core/java/android/webkit/AccessibilityInjector.java
+++ b/core/java/android/webkit/AccessibilityInjector.java
@@ -34,6 +34,7 @@
 
 import java.net.URI;
 import java.net.URISyntaxException;
+import java.util.HashMap;
 import java.util.Iterator;
 import java.util.List;
 import java.util.concurrent.atomic.AtomicInteger;
@@ -53,7 +54,7 @@
     private final WebView mWebView;
 
     // The Java objects that are exposed to JavaScript.
-    private TextToSpeech mTextToSpeech;
+    private TextToSpeechWrapper mTextToSpeech;
     private CallbackHandler mCallback;
 
     // Lazily loaded helper objects.
@@ -367,10 +368,7 @@
         if (mTextToSpeech != null) {
             return;
         }
-
-        final String pkgName = mContext.getPackageName();
-
-        mTextToSpeech = new TextToSpeech(mContext, null, null, pkgName + ".**webview**", true);
+        mTextToSpeech = new TextToSpeechWrapper(mContext);
         mWebView.addJavascriptInterface(mTextToSpeech, ALIAS_TTS_JS_INTERFACE);
     }
 
@@ -526,6 +524,41 @@
     }
 
     /**
+     * Used to protect the TextToSpeech class, only exposing the methods we want to expose.
+     */
+    private static class TextToSpeechWrapper {
+        private TextToSpeech mTextToSpeech;
+
+        public TextToSpeechWrapper(Context context) {
+            final String pkgName = context.getPackageName();
+            mTextToSpeech = new TextToSpeech(context, null, null, pkgName + ".**webview**", true);
+        }
+
+        @JavascriptInterface
+        @SuppressWarnings("unused")
+        public boolean isSpeaking() {
+            return mTextToSpeech.isSpeaking();
+        }
+
+        @JavascriptInterface
+        @SuppressWarnings("unused")
+        public int speak(String text, int queueMode, HashMap<String, String> params) {
+            return mTextToSpeech.speak(text, queueMode, params);
+        }
+
+        @JavascriptInterface
+        @SuppressWarnings("unused")
+        public int stop() {
+            return mTextToSpeech.stop();
+        }
+
+        @SuppressWarnings("unused")
+        protected void shutdown() {
+            mTextToSpeech.shutdown();
+        }
+    }
+
+    /**
      * Exposes result interface to JavaScript.
      */
     private static class CallbackHandler {
@@ -621,6 +654,7 @@
          * @param id The result id of the request as a {@link String}.
          * @param result The result of the request as a {@link String}.
          */
+        @JavascriptInterface
         @SuppressWarnings("unused")
         public void onResult(String id, String result) {
             final long resultId;
diff --git a/core/java/android/webkit/BrowserFrame.java b/core/java/android/webkit/BrowserFrame.java
index 9e4ec3c..fea427d 100644
--- a/core/java/android/webkit/BrowserFrame.java
+++ b/core/java/android/webkit/BrowserFrame.java
@@ -107,9 +107,6 @@
     // Key store handler when Chromium HTTP stack is used.
     private KeyStoreHandler mKeyStoreHandler = null;
 
-    // Implementation of the searchbox API.
-    private final SearchBoxImpl mSearchBox;
-
     // message ids
     // a message posted when a frame loading is completed
     static final int FRAME_COMPLETED = 1001;
@@ -255,8 +252,6 @@
         mDatabase = WebViewDatabaseClassic.getInstance(appContext);
         mWebViewCore = w;
 
-        mSearchBox = new SearchBoxImpl(mWebViewCore, mCallbackProxy);
-
         AssetManager am = context.getAssets();
         nativeCreateFrame(w, am, proxy.getBackForwardList());
 
@@ -1217,10 +1212,6 @@
         }
     }
 
-    /*package*/ SearchBox getSearchBox() {
-        return mSearchBox;
-    }
-
     /**
      * Called by JNI when processing the X-Auto-Login header.
      */
diff --git a/core/java/android/webkit/CallbackProxy.java b/core/java/android/webkit/CallbackProxy.java
index b47cba8..a326da2 100644
--- a/core/java/android/webkit/CallbackProxy.java
+++ b/core/java/android/webkit/CallbackProxy.java
@@ -117,11 +117,8 @@
     private static final int ADD_HISTORY_ITEM                     = 135;
     private static final int HISTORY_INDEX_CHANGED                = 136;
     private static final int AUTH_CREDENTIALS                     = 137;
-    private static final int NOTIFY_SEARCHBOX_LISTENERS           = 139;
     private static final int AUTO_LOGIN                           = 140;
     private static final int CLIENT_CERT_REQUEST                  = 141;
-    private static final int SEARCHBOX_IS_SUPPORTED_CALLBACK      = 142;
-    private static final int SEARCHBOX_DISPATCH_COMPLETE_CALLBACK = 143;
     private static final int PROCEEDED_AFTER_SSL_ERROR            = 144;
 
     // Message triggered by the client to resume execution
@@ -871,14 +868,6 @@
                         host, realm, username, password);
                 break;
             }
-            case NOTIFY_SEARCHBOX_LISTENERS: {
-                SearchBoxImpl searchBox = (SearchBoxImpl) mWebView.getSearchBox();
-
-                @SuppressWarnings("unchecked")
-                List<String> suggestions = (List<String>) msg.obj;
-                searchBox.handleSuggestions(msg.getData().getString("query"), suggestions);
-                break;
-            }
             case AUTO_LOGIN: {
                 if (mWebViewClient != null) {
                     String realm = msg.getData().getString("realm");
@@ -889,19 +878,6 @@
                 }
                 break;
             }
-            case SEARCHBOX_IS_SUPPORTED_CALLBACK: {
-                SearchBoxImpl searchBox = (SearchBoxImpl) mWebView.getSearchBox();
-                Boolean supported = (Boolean) msg.obj;
-                searchBox.handleIsSupportedCallback(supported);
-                break;
-            }
-            case SEARCHBOX_DISPATCH_COMPLETE_CALLBACK: {
-                SearchBoxImpl searchBox = (SearchBoxImpl) mWebView.getSearchBox();
-                Boolean success = (Boolean) msg.obj;
-                searchBox.handleDispatchCompleteCallback(msg.getData().getString("function"),
-                        msg.getData().getInt("id"), success);
-                break;
-            }
         }
     }
 
@@ -1629,29 +1605,6 @@
         return mContext instanceof Activity;
     }
 
-    void onSearchboxSuggestionsReceived(String query, List<String> suggestions) {
-        Message msg = obtainMessage(NOTIFY_SEARCHBOX_LISTENERS);
-        msg.obj = suggestions;
-        msg.getData().putString("query", query);
-
-        sendMessage(msg);
-    }
-
-    void onIsSupportedCallback(boolean isSupported) {
-        Message msg = obtainMessage(SEARCHBOX_IS_SUPPORTED_CALLBACK);
-        msg.obj = Boolean.valueOf(isSupported);
-        sendMessage(msg);
-    }
-
-    void onSearchboxDispatchCompleteCallback(String function, int id, boolean success) {
-        Message msg = obtainMessage(SEARCHBOX_DISPATCH_COMPLETE_CALLBACK);
-        msg.obj = Boolean.valueOf(success);
-        msg.getData().putString("function", function);
-        msg.getData().putInt("id", id);
-
-        sendMessage(msg);
-    }
-
     private synchronized void sendMessageToUiThreadSync(Message msg) {
         sendMessage(msg);
         WebCoreThreadWatchdog.pause();
diff --git a/core/java/android/webkit/CertTool.java b/core/java/android/webkit/CertTool.java
index a2325c3..e4d09a9 100644
--- a/core/java/android/webkit/CertTool.java
+++ b/core/java/android/webkit/CertTool.java
@@ -16,6 +16,7 @@
 
 package android.webkit;
 
+import com.android.org.bouncycastle.asn1.ASN1Encoding;
 import com.android.org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
 import com.android.org.bouncycastle.asn1.x509.AlgorithmIdentifier;
 import com.android.org.bouncycastle.jce.netscape.NetscapeCertRequest;
@@ -57,7 +58,7 @@
             NetscapeCertRequest request = new NetscapeCertRequest(challenge,
                     MD5_WITH_RSA, pair.getPublic());
             request.sign(pair.getPrivate());
-            byte[] signed = request.toASN1Object().getDEREncoded();
+            byte[] signed = request.toASN1Primitive().getEncoded(ASN1Encoding.DER);
 
             Credentials.getInstance().install(context, pair);
             return new String(Base64.encode(signed));
diff --git a/core/java/android/webkit/JavascriptInterface.java b/core/java/android/webkit/JavascriptInterface.java
index 3f1ed12..6cd2a7b 100644
--- a/core/java/android/webkit/JavascriptInterface.java
+++ b/core/java/android/webkit/JavascriptInterface.java
@@ -25,9 +25,8 @@
  * Annotation that allows exposing methods to JavaScript. Starting from API level
  * {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR1} and above, only methods explicitly
  * marked with this annotation are available to the Javascript code. See
- * {@link android.webkit.Webview#addJavaScriptInterface} for more information about it.
+ * {@link android.webkit.WebView#addJavascriptInterface} for more information about it.
  *
- * @hide
  */
 @SuppressWarnings("javadoc")
 @Retention(RetentionPolicy.RUNTIME)
diff --git a/core/java/android/webkit/SearchBox.java b/core/java/android/webkit/SearchBox.java
deleted file mode 100644
index 38a1740..0000000
--- a/core/java/android/webkit/SearchBox.java
+++ /dev/null
@@ -1,109 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-
-package android.webkit;
-
-import java.util.List;
-
-/**
- * Defines the interaction between the browser/renderer and the page running on
- * a given WebView frame, if the page supports the chromium SearchBox API.
- *
- * http://dev.chromium.org/searchbox
- *
- * The browser or container app can query the page for search results using
- * SearchBox.query() and receive suggestions by registering a listener on the
- * SearchBox object.
- *
- * @hide
- */
-public interface SearchBox {
-    /**
-     * Sets the current searchbox query. Note that the caller must call
-     * onchange() to ensure that the search page processes this query.
-     */
-    void setQuery(String query);
-
-    /**
-     * Verbatim is true if the caller suggests that the search page
-     * treat the current query as a verbatim search query (as opposed to a
-     * partially typed search query). As with setQuery, onchange() must be
-     * called to ensure that the search page processes the query.
-     */
-    void setVerbatim(boolean verbatim);
-
-    /**
-     * These attributes must contain the offset to the characters that immediately
-     * follow the start and end of the selection in the search box. If there is
-     * no such selection, then both selectionStart and selectionEnd must be the offset
-     * to the character that immediately follows the text entry cursor. In the case
-     * that there is no explicit text entry cursor, the cursor is
-     * implicitly at the end of the input.
-     */
-    void setSelection(int selectionStart, int selectionEnd);
-
-    /**
-     * Sets the dimensions of the view (if any) that overlaps the current
-     * window object. This is to ensure that the page renders results in
-     * a manner that allows them to not be obscured by such a view. Note
-     * that a call to onresize() is required if these dimensions change.
-     */
-    void setDimensions(int x, int y, int width, int height);
-
-    /**
-     * Notify the search page of any changes to the searchbox. Such as
-     * a change in the typed query (onchange), the user commiting a given query
-     * (onsubmit), or a change in size of a suggestions dropdown (onresize).
-     *
-     * @param listener an optional listener to notify of the success of the operation,
-     *      indicating if the javascript function existed and could be called or not.
-     *      It will be called on the UI thread.
-     */
-    void onchange(SearchBoxListener listener);
-    void onsubmit(SearchBoxListener listener);
-    void onresize(SearchBoxListener listener);
-    void oncancel(SearchBoxListener listener);
-
-    /**
-     * Add and remove listeners to the given Searchbox. Listeners are notified
-     * of any suggestions to the query that the underlying search engine might
-     * provide.
-     */
-    void addSearchBoxListener(SearchBoxListener l);
-    void removeSearchBoxListener(SearchBoxListener l);
-
-    /**
-     * Indicates if the searchbox API is supported in the current page.
-     */
-    void isSupported(IsSupportedCallback callback);
-
-    /**
-     * Listeners (if any) will be called on the thread that created the
-     * webview.
-     */
-    public abstract class SearchBoxListener {
-        public void onSuggestionsReceived(String query, List<String> suggestions) {}
-        public void onChangeComplete(boolean called) {}
-        public void onSubmitComplete(boolean called) {}
-        public void onResizeComplete(boolean called) {}
-        public void onCancelComplete(boolean called) {}
-    }
-
-    interface IsSupportedCallback {
-        void searchBoxIsSupported(boolean supported);
-    }
-}
diff --git a/core/java/android/webkit/SearchBoxImpl.java b/core/java/android/webkit/SearchBoxImpl.java
deleted file mode 100644
index 9942d25..0000000
--- a/core/java/android/webkit/SearchBoxImpl.java
+++ /dev/null
@@ -1,304 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.webkit;
-
-import android.text.TextUtils;
-import android.util.Log;
-import android.webkit.WebViewCore.EventHub;
-
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-
-import org.json.JSONArray;
-import org.json.JSONException;
-import org.json.JSONObject;
-import org.json.JSONStringer;
-
-/**
- * The default implementation of the SearchBox interface. Implemented
- * as a java bridge object and a javascript adapter that is called into
- * by the page hosted in the frame.
- */
-final class SearchBoxImpl implements SearchBox {
-    private static final String TAG = "WebKit.SearchBoxImpl";
-
-    /* package */ static final String JS_INTERFACE_NAME = "searchBoxJavaBridge_";
-
-    /* package */ static final String JS_BRIDGE
-            = "(function()"
-            + "{"
-            + "if (!window.chrome) {"
-            + "  window.chrome = {};"
-            + "}"
-            + "if (!window.chrome.searchBox) {"
-            + "  var sb = window.chrome.searchBox = {};"
-            + "  sb.setSuggestions = function(suggestions) {"
-            + "    if (window.searchBoxJavaBridge_) {"
-            + "      window.searchBoxJavaBridge_.setSuggestions(JSON.stringify(suggestions));"
-            + "    }"
-            + "  };"
-            + "  sb.setValue = function(valueArray) { sb.value = valueArray[0]; };"
-            + "  sb.value = '';"
-            + "  sb.x = 0;"
-            + "  sb.y = 0;"
-            + "  sb.width = 0;"
-            + "  sb.height = 0;"
-            + "  sb.selectionStart = 0;"
-            + "  sb.selectionEnd = 0;"
-            + "  sb.verbatim = false;"
-            + "}"
-            + "})();";
-
-    private static final String SET_QUERY_SCRIPT
-            = "if (window.chrome && window.chrome.searchBox) {"
-            + "  window.chrome.searchBox.setValue(%s);"
-            + "}";
-
-    private static final String SET_VERBATIM_SCRIPT
-            =  "if (window.chrome && window.chrome.searchBox) {"
-            + "  window.chrome.searchBox.verbatim = %1$s;"
-            + "}";
-
-    private static final String SET_SELECTION_SCRIPT
-            = "if (window.chrome && window.chrome.searchBox) {"
-            + "  var f = window.chrome.searchBox;"
-            + "  f.selectionStart = %d"
-            + "  f.selectionEnd = %d"
-            + "}";
-
-    private static final String SET_DIMENSIONS_SCRIPT
-            = "if (window.chrome && window.chrome.searchBox) { "
-            + "  var f = window.chrome.searchBox;"
-            + "  f.x = %d;"
-            + "  f.y = %d;"
-            + "  f.width = %d;"
-            + "  f.height = %d;"
-            + "}";
-
-    private static final String DISPATCH_EVENT_SCRIPT
-            = "if (window.chrome && window.chrome.searchBox && window.chrome.searchBox.on%1$s) {"
-            + "  window.chrome.searchBox.on%1$s();"
-            + "  window.searchBoxJavaBridge_.dispatchCompleteCallback('%1$s', %2$d, true);"
-            + "} else {"
-            + "  window.searchBoxJavaBridge_.dispatchCompleteCallback('%1$s', %2$d, false);"
-            + "}";
-
-    private static final String EVENT_CHANGE = "change";
-    private static final String EVENT_SUBMIT = "submit";
-    private static final String EVENT_RESIZE = "resize";
-    private static final String EVENT_CANCEL = "cancel";
-
-    private static final String IS_SUPPORTED_SCRIPT
-            = "if (window.searchBoxJavaBridge_) {"
-            + "  if (window.chrome && window.chrome.sv) {"
-            + "    window.searchBoxJavaBridge_.isSupportedCallback(true);"
-            + "  } else {"
-            + "    window.searchBoxJavaBridge_.isSupportedCallback(false);"
-            + "  }}";
-
-    private final List<SearchBoxListener> mListeners;
-    private final WebViewCore mWebViewCore;
-    private final CallbackProxy mCallbackProxy;
-    private IsSupportedCallback mSupportedCallback;
-    private int mNextEventId = 1;
-    private final HashMap<Integer, SearchBoxListener> mEventCallbacks;
-
-    SearchBoxImpl(WebViewCore webViewCore, CallbackProxy callbackProxy) {
-        mListeners = new ArrayList<SearchBoxListener>();
-        mWebViewCore = webViewCore;
-        mCallbackProxy = callbackProxy;
-        mEventCallbacks = new HashMap<Integer, SearchBoxListener>();
-    }
-
-    @Override
-    public void setQuery(String query) {
-        final String formattedQuery = jsonSerialize(query);
-        if (formattedQuery != null) {
-            final String js = String.format(SET_QUERY_SCRIPT, formattedQuery);
-            dispatchJs(js);
-        }
-    }
-
-    @Override
-    public void setVerbatim(boolean verbatim) {
-        final String js = String.format(SET_VERBATIM_SCRIPT, String.valueOf(verbatim));
-        dispatchJs(js);
-    }
-
-
-    @Override
-    public void setSelection(int selectionStart, int selectionEnd) {
-        final String js = String.format(SET_SELECTION_SCRIPT, selectionStart, selectionEnd);
-        dispatchJs(js);
-    }
-
-    @Override
-    public void setDimensions(int x, int y, int width, int height) {
-        final String js = String.format(SET_DIMENSIONS_SCRIPT, x, y, width, height);
-        dispatchJs(js);
-    }
-
-    @Override
-    public void onchange(SearchBoxListener callback) {
-        dispatchEvent(EVENT_CHANGE, callback);
-    }
-
-    @Override
-    public void onsubmit(SearchBoxListener callback) {
-        dispatchEvent(EVENT_SUBMIT, callback);
-    }
-
-    @Override
-    public void onresize(SearchBoxListener callback) {
-        dispatchEvent(EVENT_RESIZE, callback);
-    }
-
-    @Override
-    public void oncancel(SearchBoxListener callback) {
-        dispatchEvent(EVENT_CANCEL, callback);
-    }
-
-    private void dispatchEvent(String eventName, SearchBoxListener callback) {
-        int eventId;
-        if (callback != null) {
-            synchronized(this) {
-                eventId = mNextEventId++;
-                mEventCallbacks.put(eventId, callback);
-            }
-        } else {
-            eventId = 0;
-        }
-        final String js = String.format(DISPATCH_EVENT_SCRIPT, eventName, eventId);
-        dispatchJs(js);
-    }
-
-    private void dispatchJs(String js) {
-        mWebViewCore.sendMessage(EventHub.EXECUTE_JS, js);
-    }
-
-    @Override
-    public void addSearchBoxListener(SearchBoxListener l) {
-        synchronized (mListeners) {
-            mListeners.add(l);
-        }
-    }
-
-    @Override
-    public void removeSearchBoxListener(SearchBoxListener l) {
-        synchronized (mListeners) {
-            mListeners.remove(l);
-        }
-    }
-
-    @Override
-    public void isSupported(IsSupportedCallback callback) {
-        mSupportedCallback = callback;
-        dispatchJs(IS_SUPPORTED_SCRIPT);
-    }
-
-    // Called by Javascript through the Java bridge.
-    public void isSupportedCallback(boolean isSupported) {
-        mCallbackProxy.onIsSupportedCallback(isSupported);
-    }
-
-    public void handleIsSupportedCallback(boolean isSupported) {
-        IsSupportedCallback callback = mSupportedCallback;
-        mSupportedCallback = null;
-        if (callback != null) {
-            callback.searchBoxIsSupported(isSupported);
-        }
-    }
-
-    // Called by Javascript through the Java bridge.
-    public void dispatchCompleteCallback(String function, int id, boolean successful) {
-        mCallbackProxy.onSearchboxDispatchCompleteCallback(function, id, successful);
-    }
-
-    public void handleDispatchCompleteCallback(String function, int id, boolean successful) {
-        if (id != 0) {
-            SearchBoxListener listener;
-            synchronized(this) {
-                listener = mEventCallbacks.get(id);
-                mEventCallbacks.remove(id);
-            }
-            if (listener != null) {
-                if (TextUtils.equals(EVENT_CHANGE, function)) {
-                    listener.onChangeComplete(successful);
-                } else if (TextUtils.equals(EVENT_SUBMIT, function)) {
-                    listener.onSubmitComplete(successful);
-                } else if (TextUtils.equals(EVENT_RESIZE, function)) {
-                    listener.onResizeComplete(successful);
-                } else if (TextUtils.equals(EVENT_CANCEL, function)) {
-                    listener.onCancelComplete(successful);
-                }
-            }
-        }
-    }
-
-    // This is used as a hackish alternative to javascript escaping.
-    // There appears to be no such functionality in the core framework.
-    private static String jsonSerialize(String query) {
-        JSONStringer stringer = new JSONStringer();
-        try {
-            stringer.array().value(query).endArray();
-        } catch (JSONException e) {
-            Log.w(TAG, "Error serializing query : " + query);
-            return null;
-        }
-        return stringer.toString();
-    }
-
-    // Called by Javascript through the Java bridge.
-    public void setSuggestions(String jsonArguments) {
-        if (jsonArguments == null) {
-            return;
-        }
-
-        String query = null;
-        List<String> suggestions = new ArrayList<String>();
-        try {
-            JSONObject suggestionsJson = new JSONObject(jsonArguments);
-            query = suggestionsJson.getString("query");
-
-            final JSONArray suggestionsArray = suggestionsJson.getJSONArray("suggestions");
-            for (int i = 0; i < suggestionsArray.length(); ++i) {
-                final JSONObject suggestion = suggestionsArray.getJSONObject(i);
-                final String value = suggestion.getString("value");
-                if (value != null) {
-                    suggestions.add(value);
-                }
-                // We currently ignore the "type" of the suggestion. This isn't
-                // documented anywhere in the API documents.
-                // final String type = suggestions.getString("type");
-            }
-        } catch (JSONException je) {
-            Log.w(TAG, "Error parsing json [" + jsonArguments + "], exception = " + je);
-            return;
-        }
-
-        mCallbackProxy.onSearchboxSuggestionsReceived(query, suggestions);
-    }
-
-    /* package */ void handleSuggestions(String query, List<String> suggestions) {
-        synchronized (mListeners) {
-            for (int i = mListeners.size() - 1; i >= 0; i--) {
-                mListeners.get(i).onSuggestionsReceived(query, suggestions);
-            }
-        }
-    }
-}
diff --git a/core/java/android/webkit/WebSettings.java b/core/java/android/webkit/WebSettings.java
index e8ff01f..a29ff37 100644
--- a/core/java/android/webkit/WebSettings.java
+++ b/core/java/android/webkit/WebSettings.java
@@ -102,7 +102,12 @@
 
     /**
      * Normal cache usage mode. Use with {@link #setCacheMode}.
+     *
+     * @deprecated This value is obsolete, as from API level
+     * {@link android.os.Build.VERSION_CODES#HONEYCOMB} and onwards it has the
+     * same effect as {@link #LOAD_DEFAULT}.
      */
+    @Deprecated
     public static final int LOAD_NORMAL = 0;
 
     /**
@@ -333,7 +338,10 @@
      * If it is true, WebView will choose a solution to maximize the performance.
      * e.g. the WebView's content may not be updated during the transition.
      * If it is false, WebView will keep its fidelity. The default value is false.
+     *
+     * @deprecated This method is now obsolete, and will become a no-op in future.
      */
+    @Deprecated
     public void setEnableSmoothTransition(boolean enable) {
         throw new MustOverrideException();
     }
@@ -343,7 +351,10 @@
      * zooming.
      *
      * @see #setEnableSmoothTransition
+     *
+     * @deprecated This method is now obsolete, and will become a no-op in future.
      */
+    @Deprecated
     public boolean enableSmoothTransition() {
         throw new MustOverrideException();
     }
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index 9d6d929..e1c30f7 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -26,7 +26,6 @@
 import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
 import android.net.http.SslCertificate;
-import android.os.Build;
 import android.os.Bundle;
 import android.os.Looper;
 import android.os.Message;
@@ -470,7 +469,13 @@
      * @param defStyle the default style resource ID
      * @param privateBrowsing whether this WebView will be initialized in
      *                        private mode
+     *
+     * @deprecated Private browsing is no longer supported directly via 
+     * WebView and will be removed in a future release. Prefer using
+     * {@link WebSettings}, {@link WebViewDatabase}, {@link CookieManager}
+     * and {@link WebStorage} for fine-grained control of privacy data.
      */
+    @Deprecated
     public WebView(Context context, AttributeSet attrs, int defStyle,
             boolean privateBrowsing) {
         this(context, attrs, defStyle, null, privateBrowsing);
@@ -569,7 +574,11 @@
 
     /**
      * Sets the SSL certificate for the main top-level page.
+     *
+     * @deprecated Calling this function has no useful effect, and will be
+     * ignored in future releases.
      */
+    @Deprecated
     public void setCertificate(SslCertificate certificate) {
         checkThread();
         mProvider.setCertificate(certificate);
@@ -1018,7 +1027,12 @@
      * Gets the current scale of this WebView.
      *
      * @return the current scale
+     *
+     * @deprecated This method is prone to inaccuracy due to race conditions
+     * between the web rendering and UI threads; prefer
+     * {@link WebViewClient#onScaleChanged}.
      */
+    @Deprecated
     @ViewDebug.ExportedProperty(category = "webview")
     public float getScale() {
         checkThread();
@@ -1479,10 +1493,22 @@
     /**
      * Injects the supplied Java object into this WebView. The object is
      * injected into the JavaScript context of the main frame, using the
-     * supplied name. This allows the Java object's public methods to be
-     * accessed from JavaScript. Note that that injected objects will not
+     * supplied name. This allows the Java object's methods to be
+     * accessed from JavaScript. For applications targeted to API
+     * level {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR1}
+     * and above, only public methods that are annotated with
+     * {@link android.webkit.JavascriptInterface} can be accessed from JavaScript.
+     * For applications targeted to API level {@link android.os.Build.VERSION_CODES#JELLY_BEAN} or below,
+     * all public methods (including the inherited ones) can be accessed, see the
+     * important security note below for implications.
+     * <p> Note that injected objects will not
      * appear in JavaScript until the page is next (re)loaded. For example:
-     * <pre> webView.addJavascriptInterface(new Object(), "injectedObject");
+     * <pre>
+     * class JsObject {
+     *    {@literal @}JavascriptInterface
+     *    public String toString() { return "injectedObject"; }
+     * }
+     * webView.addJavascriptInterface(new JsObject(), "injectedObject");
      * webView.loadData("<!DOCTYPE html><title></title>", "text/html", null);
      * webView.loadUrl("javascript:alert(injectedObject.toString())");</pre>
      * <p>
@@ -1490,7 +1516,9 @@
      * <ul>
      * <li> This method can be used to allow JavaScript to control the host
      * application. This is a powerful feature, but also presents a security
-     * risk, particularly as JavaScript could use reflection to access an
+     * risk for applications targeted to API level
+     * {@link android.os.Build.VERSION_CODES#JELLY_BEAN} or below, because
+     * JavaScript could use reflection to access an
      * injected object's public fields. Use of this method in a WebView
      * containing untrusted content could allow an attacker to manipulate the
      * host application in unintended ways, executing Java code with the
@@ -1499,6 +1527,7 @@
      * <li> JavaScript interacts with Java object on a private, background
      * thread of this WebView. Care is therefore required to maintain thread
      * safety.</li>
+     * <li> The Java object's fields are not accessible.</li>
      * </ul>
      *
      * @param object the Java object to inject into this WebView's JavaScript
@@ -1508,9 +1537,6 @@
     public void addJavascriptInterface(Object object, String name) {
         checkThread();
         mProvider.addJavascriptInterface(object, name);
-        // TODO in a separate CL provide logic to enable annotations for API level JB_MR1 and above. Don't forget to
-        // update the doc, set a link to annotation and unhide the annotation.
-        // also describe that fields of java objects are not accessible from JS.
     }
 
     /**
@@ -1598,6 +1624,10 @@
     public void onGlobalFocusChanged(View oldFocus, View newFocus) {
     }
 
+    /**
+     * @deprecated Only the default case, true, will be supported in a future version.
+     */
+    @Deprecated
     public void setMapTrackballToArrowKeys(boolean setMap) {
         checkThread();
         mProvider.setMapTrackballToArrowKeys(setMap);
@@ -1631,7 +1661,12 @@
      * Gets whether this WebView can be zoomed in.
      *
      * @return true if this WebView can be zoomed in
+     *
+     * @deprecated This method is prone to inaccuracy due to race conditions
+     * between the web rendering and UI threads; prefer
+     * {@link WebViewClient#onScaleChanged}.
      */
+    @Deprecated
     public boolean canZoomIn() {
         checkThread();
         return mProvider.canZoomIn();
@@ -1641,7 +1676,12 @@
      * Gets whether this WebView can be zoomed out.
      *
      * @return true if this WebView can be zoomed out
+     *
+     * @deprecated This method is prone to inaccuracy due to race conditions
+     * between the web rendering and UI threads; prefer
+     * {@link WebViewClient#onScaleChanged}.
      */
+    @Deprecated
     public boolean canZoomOut() {
         checkThread();
         return mProvider.canZoomOut();
diff --git a/core/java/android/webkit/WebViewClassic.java b/core/java/android/webkit/WebViewClassic.java
index 351c4f3..d23f52c 100644
--- a/core/java/android/webkit/WebViewClassic.java
+++ b/core/java/android/webkit/WebViewClassic.java
@@ -55,6 +55,7 @@
 import android.net.Uri;
 import android.net.http.SslCertificate;
 import android.os.AsyncTask;
+import android.os.Build;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.Looper;
@@ -4119,10 +4120,17 @@
             return;
         }
         WebViewCore.JSInterfaceData arg = new WebViewCore.JSInterfaceData();
-        // TODO in a separate CL provide logic to enable annotations for API level JB_MR1 and above.
+
         arg.mObject = object;
         arg.mInterfaceName = name;
-        arg.mRequireAnnotation = false;
+
+        // starting with JELLY_BEAN_MR1, annotations are mandatory for enabling access to
+        // methods that are accessible from JS.
+        if (mContext.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
+            arg.mRequireAnnotation = true;
+        } else {
+            arg.mRequireAnnotation = false;
+        }
         mWebViewCore.sendMessage(EventHub.ADD_JS_INTERFACE, arg);
     }
 
@@ -5364,16 +5372,6 @@
     }
 
     /**
-     * This is an implementation detail.
-     */
-    public SearchBox getSearchBox() {
-        if ((mWebViewCore == null) || (mWebViewCore.getBrowserFrame() == null)) {
-            return null;
-        }
-        return mWebViewCore.getBrowserFrame().getSearchBox();
-    }
-
-    /**
      * Returns the currently highlighted text as a string.
      */
     String getSelection() {
diff --git a/core/java/android/widget/AbsSeekBar.java b/core/java/android/widget/AbsSeekBar.java
index 34f78c6..62253d3 100644
--- a/core/java/android/widget/AbsSeekBar.java
+++ b/core/java/android/widget/AbsSeekBar.java
@@ -304,7 +304,8 @@
         }
         
         // Canvas will be translated, so 0,0 is where we start drawing
-        thumb.setBounds(thumbPos, topBound, thumbPos + thumbWidth, bottomBound);
+        final int left = isLayoutRtl() ? available - thumbPos : thumbPos;
+        thumb.setBounds(left, topBound, left + thumbWidth, bottomBound);
     }
 
     @Override
diff --git a/core/java/android/widget/Switch.java b/core/java/android/widget/Switch.java
index 1f713d4..689bd02 100644
--- a/core/java/android/widget/Switch.java
+++ b/core/java/android/widget/Switch.java
@@ -750,10 +750,11 @@
         mTextPaint.drawableState = getDrawableState();
 
         Layout switchText = getTargetCheckedState() ? mOnLayout : mOffLayout;
-
-        canvas.translate((thumbLeft + thumbRight) / 2 - switchText.getWidth() / 2,
-                (switchInnerTop + switchInnerBottom) / 2 - switchText.getHeight() / 2);
-        switchText.draw(canvas);
+        if (switchText != null) {
+            canvas.translate((thumbLeft + thumbRight) / 2 - switchText.getWidth() / 2,
+                    (switchInnerTop + switchInnerBottom) / 2 - switchText.getHeight() / 2);
+            switchText.draw(canvas);
+        }
 
         canvas.restore();
     }
diff --git a/core/java/com/android/internal/appwidget/IAppWidgetService.aidl b/core/java/com/android/internal/appwidget/IAppWidgetService.aidl
index 7df45cf..cfb16fa 100644
--- a/core/java/com/android/internal/appwidget/IAppWidgetService.aidl
+++ b/core/java/com/android/internal/appwidget/IAppWidgetService.aidl
@@ -52,9 +52,9 @@
     AppWidgetProviderInfo getAppWidgetInfo(int appWidgetId);
     boolean hasBindAppWidgetPermission(in String packageName);
     void setBindAppWidgetPermission(in String packageName, in boolean permission);
-    void bindAppWidgetId(int appWidgetId, in ComponentName provider);
+    void bindAppWidgetId(int appWidgetId, in ComponentName provider, in Bundle options);
     boolean bindAppWidgetIdIfAllowed(
-            in String packageName, int appWidgetId, in ComponentName provider);
+            in String packageName, int appWidgetId, in ComponentName provider, in Bundle options);
     void bindRemoteViewsService(int appWidgetId, in Intent intent, in IBinder connection);
     void unbindRemoteViewsService(int appWidgetId, in Intent intent);
     int[] getAppWidgetIds(in ComponentName provider);
diff --git a/core/java/com/android/internal/os/storage/ExternalStorageFormatter.java b/core/java/com/android/internal/os/storage/ExternalStorageFormatter.java
index 3905c88..fb7f215 100644
--- a/core/java/com/android/internal/os/storage/ExternalStorageFormatter.java
+++ b/core/java/com/android/internal/os/storage/ExternalStorageFormatter.java
@@ -122,7 +122,7 @@
     public void onCancel(DialogInterface dialog) {
         IMountService mountService = getMountService();
         String extStoragePath = mStorageVolume == null ?
-                Environment.getExternalStorageDirectory().toString() :
+                Environment.getLegacyExternalStorageDirectory().toString() :
                 mStorageVolume.getPath();
         try {
             mountService.mountVolume(extStoragePath);
@@ -149,7 +149,7 @@
             updateProgressDialog(R.string.progress_unmounting);
             IMountService mountService = getMountService();
             final String extStoragePath = mStorageVolume == null ?
-                    Environment.getExternalStorageDirectory().toString() :
+                    Environment.getLegacyExternalStorageDirectory().toString() :
                     mStorageVolume.getPath();
             try {
                 // Remove encryption mapping if this is an unmount for a factory reset.
@@ -163,7 +163,7 @@
             updateProgressDialog(R.string.progress_erasing);
             final IMountService mountService = getMountService();
             final String extStoragePath = mStorageVolume == null ?
-                    Environment.getExternalStorageDirectory().toString() :
+                    Environment.getLegacyExternalStorageDirectory().toString() :
                     mStorageVolume.getPath();
             if (mountService != null) {
                 new Thread() {
diff --git a/core/jni/android/graphics/TextLayoutCache.cpp b/core/jni/android/graphics/TextLayoutCache.cpp
index 7abfcf1..8032ed8 100644
--- a/core/jni/android/graphics/TextLayoutCache.cpp
+++ b/core/jni/android/graphics/TextLayoutCache.cpp
@@ -78,8 +78,10 @@
 /*
  * Cache clearing
  */
-void TextLayoutCache::clear() {
+void TextLayoutCache::purgeCaches() {
+    AutoMutex _l(mLock);
     mCache.clear();
+    mShaper->purgeCaches();
 }
 
 /*
@@ -965,8 +967,7 @@
 
 void TextLayoutEngine::purgeCaches() {
 #if USE_TEXT_LAYOUT_CACHE
-    mTextLayoutCache->clear();
-    mShaper->purgeCaches();
+    mTextLayoutCache->purgeCaches();
 #if DEBUG_GLYPHS
     ALOGD("Purged TextLayoutEngine caches");
 #endif
diff --git a/core/jni/android/graphics/TextLayoutCache.h b/core/jni/android/graphics/TextLayoutCache.h
index 64b33a0..1f4e22c 100644
--- a/core/jni/android/graphics/TextLayoutCache.h
+++ b/core/jni/android/graphics/TextLayoutCache.h
@@ -276,7 +276,7 @@
     /**
      * Clear the cache
      */
-    void clear();
+    void purgeCaches();
 
 private:
     TextLayoutShaper* mShaper;
diff --git a/core/jni/android_hardware_Camera.cpp b/core/jni/android_hardware_Camera.cpp
index 6cd8955..e1e97a1 100644
--- a/core/jni/android_hardware_Camera.cpp
+++ b/core/jni/android_hardware_Camera.cpp
@@ -781,6 +781,25 @@
     }
 }
 
+static jboolean android_hardware_Camera_enableShutterSound(JNIEnv *env, jobject thiz,
+        jboolean enabled)
+{
+    ALOGV("enableShutterSound");
+    sp<Camera> camera = get_native_camera(env, thiz, NULL);
+    if (camera == 0) return JNI_FALSE;
+
+    int32_t value = (enabled == JNI_TRUE) ? 1 : 0;
+    status_t rc = camera->sendCommand(CAMERA_CMD_ENABLE_SHUTTER_SOUND, value, 0);
+    if (rc == NO_ERROR) {
+        return JNI_TRUE;
+    } else if (rc == PERMISSION_DENIED) {
+        return JNI_FALSE;
+    } else {
+        jniThrowRuntimeException(env, "enable shutter sound failed");
+        return JNI_FALSE;
+    }
+}
+
 static void android_hardware_Camera_startFaceDetection(JNIEnv *env, jobject thiz,
         jint type)
 {
@@ -890,6 +909,9 @@
   { "setDisplayOrientation",
     "(I)V",
     (void *)android_hardware_Camera_setDisplayOrientation },
+  { "enableShutterSound",
+    "(Z)Z",
+    (void *)android_hardware_Camera_enableShutterSound },
   { "_startFaceDetection",
     "(I)V",
     (void *)android_hardware_Camera_startFaceDetection },
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 012be3e..58296de 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -1753,7 +1753,8 @@
                  android:allowClearUserData="false"
                  android:backupAgent="com.android.server.SystemBackupAgent"
                  android:killAfterRestore="false"
-                 android:icon="@drawable/ic_launcher_android">
+                 android:icon="@drawable/ic_launcher_android"
+                 android:supportsRtl="true">
         <activity android:name="com.android.internal.app.ChooserActivity"
                 android:theme="@style/Theme.Holo.Dialog.Alert"
                 android:finishOnCloseSystemDialogs="true"
diff --git a/core/res/res/layout-land/keyguard_host_view.xml b/core/res/res/layout-land/keyguard_host_view.xml
index 8e7c232..01e1866 100644
--- a/core/res/res/layout-land/keyguard_host_view.xml
+++ b/core/res/res/layout-land/keyguard_host_view.xml
@@ -35,6 +35,8 @@
 
         <!-- TODO: Remove this once supported as a widget -->
         <include layout="@layout/keyguard_status_view"/>
+        <include layout="@layout/keyguard_transport_control_view"/>
+
     </com.android.internal.policy.impl.keyguard.KeyguardWidgetPager>
 
 
diff --git a/core/res/res/layout-sw600dp-land/keyguard_host_view.xml b/core/res/res/layout-sw600dp-land/keyguard_host_view.xml
index 652bdde..ea0b3ba 100644
--- a/core/res/res/layout-sw600dp-land/keyguard_host_view.xml
+++ b/core/res/res/layout-sw600dp-land/keyguard_host_view.xml
@@ -35,6 +35,7 @@
 
         <!-- TODO: Remove this once supported as a widget -->
         <include layout="@layout/keyguard_status_view"/>
+        <include layout="@layout/keyguard_transport_control_view"/>
 
     </com.android.internal.policy.impl.keyguard.KeyguardWidgetPager>
 
diff --git a/core/res/res/layout-sw600dp-port/keyguard_host_view.xml b/core/res/res/layout-sw600dp-port/keyguard_host_view.xml
index 0c1dd0c..84b1b03 100644
--- a/core/res/res/layout-sw600dp-port/keyguard_host_view.xml
+++ b/core/res/res/layout-sw600dp-port/keyguard_host_view.xml
@@ -25,8 +25,7 @@
     android:orientation="vertical"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
-    android:gravity="center_horizontal"
-    android:clipChildren="false">
+    android:gravity="center_horizontal">
 
     <com.android.internal.policy.impl.keyguard.KeyguardWidgetPager
         android:id="@+id/app_widget_container"
@@ -37,6 +36,7 @@
 
         <!-- TODO: Remove this once supported as a widget -->
         <include layout="@layout/keyguard_status_view"/>
+        <include layout="@layout/keyguard_transport_control_view"/>
 
     </com.android.internal.policy.impl.keyguard.KeyguardWidgetPager>
 
diff --git a/core/res/res/layout/keyguard_selector_view.xml b/core/res/res/layout/keyguard_selector_view.xml
index d7f98f9..b090f13 100644
--- a/core/res/res/layout/keyguard_selector_view.xml
+++ b/core/res/res/layout/keyguard_selector_view.xml
@@ -34,6 +34,7 @@
         android:visibility="gone">
             <!-- TODO: Remove this when supported as a widget -->
             <include layout="@layout/keyguard_status_view"/>
+            <include layout="@layout/keyguard_transport_control_view"/>
     </com.android.internal.policy.impl.keyguard.KeyguardWidgetPager>
 
     <RelativeLayout
diff --git a/core/res/res/layout/keyguard_status_view.xml b/core/res/res/layout/keyguard_status_view.xml
index 37e6779..5d8020e 100644
--- a/core/res/res/layout/keyguard_status_view.xml
+++ b/core/res/res/layout/keyguard_status_view.xml
@@ -22,6 +22,7 @@
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="wrap_content"
     android:layout_height="wrap_content"
+    android:id="@+id/keyguard_status_view"
     android:gravity="center_horizontal">
 
     <com.android.internal.policy.impl.keyguard.KeyguardStatusView
diff --git a/core/res/res/layout/keyguard_transport_control.xml b/core/res/res/layout/keyguard_transport_control_view.xml
similarity index 89%
rename from core/res/res/layout/keyguard_transport_control.xml
rename to core/res/res/layout/keyguard_transport_control_view.xml
index 3e702df..8e76164 100644
--- a/core/res/res/layout/keyguard_transport_control.xml
+++ b/core/res/res/layout/keyguard_transport_control_view.xml
@@ -14,19 +14,21 @@
      limitations under the License.
 -->
 
-<!-- Note: This file is meant to be included in various password unlock screens. As such,
-     LayoutParams (layout_*) for TransportControlView should *NOT* be specified here,
-     but rather as include tags for this file or the layout will break. -->
-<com.android.internal.widget.TransportControlView
+<!-- This is a view to control music playback in keyguard. -->
+<com.android.internal.policy.impl.keyguard.KeyguardTransportControlView
     xmlns:android="http://schemas.android.com/apk/res/android"
-    android:id="@+id/transport_controls">
+    android:layout_width="wrap_content"
+    android:layout_height="wrap_content"
+    android:gravity="center_horizontal"
+    android:layerType="hardware"
+    android:id="@+id/keyguard_transport_control">
 
     <!-- FrameLayout used as scrim to show between album art and buttons -->
     <FrameLayout
         android:layout_width="match_parent"
         android:layout_height="match_parent"
         android:foreground="@drawable/ic_lockscreen_player_background">
-        <!-- We use ImageView for its cropping features, otherwise could be android:background -->
+        <!-- Use ImageView for its cropping features; otherwise could be android:background -->
         <ImageView
             android:id="@+id/albumart"
             android:layout_width="match_parent"
@@ -107,4 +109,4 @@
         </LinearLayout>
     </LinearLayout>
 
-</com.android.internal.widget.TransportControlView>
+</com.android.internal.policy.impl.keyguard.KeyguardTransportControlView>
\ No newline at end of file
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index e6fd538..3dacc61 100755
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -5180,6 +5180,8 @@
         <attr name="updatePeriodMillis" format="integer" />
         <!-- A resource id of a layout. -->
         <attr name="initialLayout" format="reference" />
+        <!-- A resource id of a layout. -->
+        <attr name="initialKeyguardLayout" format="reference" />
         <!-- A class name in the AppWidget's package to be launched to configure.
              If not supplied, then no activity will be launched. -->
         <attr name="configure" format="string" />
@@ -5190,12 +5192,26 @@
              by the widget's host. -->
         <attr name="autoAdvanceViewId" format="reference" />
         <!-- Optional parameter which indicates if and how this widget can be
-             resized. -->
+             resized. Supports combined values using | operator. -->
         <attr name="resizeMode" format="integer">
             <flag name="none" value="0x0" />
             <flag name="horizontal" value="0x1" />
             <flag name="vertical" value="0x2" />
         </attr>
+        <!-- Optional parameter which indicates where this widget can be shown,
+             ie. home screen, keyguard or both.
+             resized. Supports combined values using | operator. -->
+        <attr name="widgetCategory" format="integer">
+            <flag name="home_screen" value="0x1" />
+            <flag name="keyguard" value="0x2" />
+        </attr>
+        <!-- Optional parameter which indicates any feature(s) that this widget
+             supports. Supports combined values using | operator. -->
+        <attr name="widgetFeatures" format="integer">
+            <flag name="none" value="0x0" />
+            <flag name="clock" value="0x1" />
+            <flag name="status" value="0x2" />
+        </attr>
     </declare-styleable>
 
     <!-- =============================== -->
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index bba2252..7895489 100755
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -537,17 +537,18 @@
 
     <!-- Array of light sensor LUX values to define our levels for auto backlight brightness support.
          The N entries of this array define N + 1 control points as follows:
+         (1-based arrays)
 
-         Point 1:        LUX <= 0 (implicit)
-         Point 2:        0 < level[1] == LUX < level[2]
+         Point 1:            (0, value[1]):             lux <= 0
+         Point 2:     (level[1], value[2]):  0        < lux <= level[1]
+         Point 3:     (level[2], value[3]):  level[2] < lux <= level[3]
          ...
-         Point N:        level[N - 1] == LUX < level[N]
-         Point N + 1:    level[N] <= LUX < infinity
+         Point N+1: (level[N], value[N+1]):  level[N] < lux
 
          The control points must be strictly increasing.  Each control point
          corresponds to an entry in the brightness backlight values arrays.
          For example, if LUX == level[1] (first element of the levels array)
-         then the brightness will be determined by value[1] (first element
+         then the brightness will be determined by value[2] (second element
          of the brightness values array).
 
          Spline interpolation is used to determine the auto-brightness
@@ -923,5 +924,5 @@
     <bool name="config_syncstorageengine_masterSyncAutomatically">true</bool>
 
     <!--  Maximum number of supported users -->
-    <integer name="config_multiuserMaximumUsers">10</integer>
+    <integer name="config_multiuserMaximumUsers">1</integer>
 </resources>
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 3101cde..02bf710 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -2013,5 +2013,8 @@
   <public type="attr" name="singleUser" />
   <public type="attr" name="presentationTheme" />
   <public type="attr" name="subtypeId"/>
+  <public type="attr" name="initialKeyguardLayout" />
+  <public type="attr" name="widgetFeatures" />
+  <public type="attr" name="widgetCategory" />
 
 </resources>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 669ffe7..54dbf2e 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -1343,6 +1343,8 @@
   <java-symbol type="id" name="keyguard_user_name" />
   <java-symbol type="id" name="keyguard_active_user" />
   <java-symbol type="id" name="keyguard_inactive_users" />
+  <java-symbol type="id" name="keyguard_transport_control" />
+  <java-symbol type="id" name="keyguard_status_view" />
   <java-symbol type="integer" name="config_carDockRotation" />
   <java-symbol type="integer" name="config_defaultUiModeType" />
   <java-symbol type="integer" name="config_deskDockRotation" />
diff --git a/data/fonts/DroidNaskh-Regular-SystemUI.ttf b/data/fonts/DroidNaskh-Regular-SystemUI.ttf
index 2f495cf..c961de2 100644
--- a/data/fonts/DroidNaskh-Regular-SystemUI.ttf
+++ b/data/fonts/DroidNaskh-Regular-SystemUI.ttf
Binary files differ
diff --git a/docs/html/guide/google/play/publishing/multiple-apks.jd b/docs/html/guide/google/play/publishing/multiple-apks.jd
index e41817e..0619dfc 100644
--- a/docs/html/guide/google/play/publishing/multiple-apks.jd
+++ b/docs/html/guide/google/play/publishing/multiple-apks.jd
@@ -9,9 +9,7 @@
   <ul>
     <li>Simultaneously publish different APKs for different
 device configurations</li>
-    <li>Different APKs are distributed to different devices based on filters declared in the
-manifest file</li>
-    <li>You should publish multiple APKs only when it's not possible or reasonable to
+    <li>You should publish multiple APKs only when it's not possible to
 support all desired devices with a single APK</li>
   </ul>
 
@@ -39,16 +37,17 @@
       <li><a href="#TextureOptions">Supporting multiple GL textures</a></li>
       <li><a href="#ScreenOptions">Supporting multiple screens</a></li>
       <li><a href="#ApiLevelOptions">Supporting multiple API levels</a></li>
+      <li><a href="#CpuArchOptions">Supporting multiple CPU architectures</a></li>
     </ol>
   </li>
 </ol>
 
   <h2>See also</h2>
 <ol>
+  <li><a href="{@docRoot}guide/google/play/expansion-files.html">APK Expansion Files</a></li>
   <li><a href="{@docRoot}guide/google/play/filters.html">Filters on Google Play</a></li>
   <li><a href="{@docRoot}guide/practices/screens_support.html">Supporting Multiple Screens</a></li>
-  <li><a href="{@docRoot}tools/extras/support-library.html">Compatibility
-Package</a></li>
+  <li><a href="{@docRoot}tools/extras/support-library.html">Support Library</a></li>
   <li><a href="{@docRoot}guide/topics/manifest/uses-sdk-element.html#ApiLevels">Android API Levels</a></li>
 </ol>
 
@@ -76,14 +75,16 @@
 you publish your application for as many devices as possible, Google Play allows you to
 publish multiple APKs under the same application listing. Google Play then supplies each APK to
 the appropriate devices based on configuration support you've declared in the manifest file of each
-APK.</p>
+APK. </p>
 
 <p>By publishing your application with multiple APKs, you can:</p>
 
 <ul>
   <li>Support different OpenGL texture compression formats with each APK.</li>
-  <li>Support different screen configurations with each APK.</li>
+  <li>Support different screen sizes and densities with each APK.</li>
   <li>Support different platform versions with each APK.</li>
+  <li>Support different CPU architectures with each APK (such as for ARM, x86, and MIPS, when your
+  app uses the <a href="{@docRoot}tools/sdk/ndk/index.html">Android NDK</a>).</li>
 </ul>
 
 <p>Currently, these are the only device characteristics that Google Play supports for publishing
@@ -91,7 +92,8 @@
 
 <p class="note"><strong>Note:</strong> You should generally use multiple APKs to support
 different device configurations <strong>only when your APK is too large</strong> (greater than
-50MB). Using a single APK to support different configurations is always the best practice,
+50MB) due to the alternative resources needed for different device configurations.
+Using a single APK to support different configurations is always the best practice,
 because it makes the path for application updates simple and clear for users (and also makes
 your life simpler by avoiding development and publishing complexity). Read the section below about
 <a href="#SingleAPK">Using a Single APK Instead</a> to
@@ -283,14 +285,19 @@
     </ul>
   </div>
   </li>
+
+  <li><strong>CPU architecture (ABI)</strong>
+    <p>This is based on the native libraries included in each APK (which are
+    determined by the architectures you declare in the {@code Application.mk}
+    file) when using the Android NDK.</p></li>
 </ul>
 
 <p>Other manifest elements that enable <a
 href="{@docRoot}guide/google/play/filters.html">Google Play filters</a>&mdash;but are not
 listed above&mdash;are still applied for each APK as usual. However, Google Play does not allow
-you to publish multiple APKs based on variations of them. Thus, you cannot publish
-multiple APKs if the above listed filters are the same for each APK (but the APKs differ based on
-other characteristics in the manifest file). For
+you to publish separate APKs based on variations of those device characteristics. Thus, you cannot
+publish multiple APKs if the above listed filters are the same for each APK (but the APKs differ
+based on other characteristics in the manifest or APK). For
 example, you cannot provide different APKs that differ purely on the <a
 href="{@docRoot}guide/topics/manifest/uses-configuration-element.html">{@code
 &lt;uses-configuration&gt;}</a> characteristics.</p>
@@ -349,7 +356,8 @@
 get an update when they receive a system update.</li>
       <li>If you have one APK that's for API level 4 (and above) <em>and</em> small -
 large screens, and another APK for API level 8 (and above) <em>and</em> large - xlarge screens, then
-the version codes <strong>must increase</strong>. In this case, the API level filter is used to
+the version codes <strong>must increase</strong> in correlation with the API levels.
+In this case, the API level filter is used to
 distinguish each APK, but so is the screen size. Because the screen sizes overlap (both APKs
 support large screens), the version codes must still be in order. This ensures that a large screen
 device that receives a system update to API level 8 will receive an update for the second
@@ -360,6 +368,21 @@
 levels. Because there is no overlap within the screen size filter, there are no devices that
 could potentially move between these two APKs, so there's no need for the version codes to
 increase from the lower API level to the higher API level.</li>
+      <li>If you have one APK that's for API level 4 (and above) <em>and</em> ARMv7 CPUs,
+and another APK for API level 8 (and above) <em>and</em> ARMv5TE CPUs,
+then the version codes <strong>must increase</strong> in correlation with the API levels.
+In this case, the API level filter is used to
+distinguish each APK, but so is the CPU architecture. Because an APK with ARMv5TE libraries is
+compatible with devices that have an ARMv7 CPU, the APKs overlap on this characteristic.
+As such, the version code for the APK that supports API level 8 and above must be higher.
+This ensures that a device with an ARMv7 CPU that receives a system update to API level 8
+will receive an update for the second APK that's designed for API level 8.
+However, because this kind of update results in the ARMv7 device using an APK that's not
+fully optimized for that device's CPU, you should provide an
+APK for both the ARMv5TE and the ARMv7 architecture at each API level in order to optimize
+the app performance on each CPU.
+<strong>Note:</strong> This applies <em>only</em> when comparing APKs with the ARMv5TE and
+ARMv7 libraries, and not when comparing other native libraries.</li>
     </ul>
   </li>
 
@@ -384,7 +407,12 @@
 sizes small, normal, and large, while another APK supports sizes large and xlarge, there is an
 overlap, because both APKs support large screens. If you do not resolve this, then devices that
 qualify for both APKs (large screen devices in the example) will receive whichever APK has the
-highest version code.</li>
+highest version code.
+  <p class="note"><strong>Note:</strong> If you're creating separate APKs for different CPU
+  architectures, be aware that an APK for ARMv5TE will overlap with an APK for ARMv7. That is,
+  an APK designed for ARMv5TE is compatible with an ARMv7 device,
+but the reverse is not true (an APK with only the ARMv7 libraries is
+<em>not</em> compatible with an ARMv5TE device).</li>
 </ul>
 
 <p>When such conflicts occur, you will see a warning message, but you can still publish your
@@ -641,3 +669,17 @@
 }
 </pre>
 
+
+<h3 id="CpuArchOptions">Supporting multiple CPU architectures</h3>
+
+<p>When using the Android NDK, you can create a single APK that supports multiple CPU architectures
+by declaring each of the desired architectures with the {@code APP_ABI} variable in the
+<code>Application.mk</code> file.</p>
+
+<p>For example, here's an <code>Application.mk</code> file that declares support for three
+different CPU architectures:</p>
+
+<pre>
+APP_ABI := armeabi armeabi-v7a mips
+APP_PLATFORM := android-9
+</pre>
diff --git a/docs/html/guide/topics/ui/themes.jd b/docs/html/guide/topics/ui/themes.jd
index d787492..bc1c4f0 100644
--- a/docs/html/guide/topics/ui/themes.jd
+++ b/docs/html/guide/topics/ui/themes.jd
@@ -413,8 +413,8 @@
 themes will give you a better understanding of what style properties each one provides.
 For a better reference to the Android styles and themes, see the following source code:</p>
 <ul>
-	<li><a href="http://android.git.kernel.org/?p=platform/frameworks/base.git;a=blob;f=core/res/res/values/styles.xml;h=d7b654e49809cb97a35682754b1394af5c8bc88b;hb=HEAD">Android Styles (styles.xml)</a></li>
-	<li><a href="http://android.git.kernel.org/?p=platform/frameworks/base.git;a=blob;f=core/res/res/values/themes.xml;h=6b3d7407d1c895a3c297e60d5beac98e2d34c271;hb=HEAD">Android Themes (themes.xml)</a></li>
+	<li><a href="https://android.googlesource.com/platform/frameworks/base/+/refs/heads/master/core/res/res/values/styles.xml">Android Styles (styles.xml)</a></li>
+	<li><a href="https://android.googlesource.com/platform/frameworks/base/+/refs/heads/master/core/res/res/values/themes.xml">Android Themes (themes.xml)</a></li>
 </ul>
 
 <p>These files will help you learn through example. For instance, in the Android themes source code,
@@ -422,9 +422,8 @@
 you'll see all of the properties that are used to style dialogs that are used by the Android
 framework.</p>
 
-<p>For more information about the syntax used to create styles in XML, see
-<a href="{@docRoot}guide/topics/resources/available-resources.html#stylesandthemes">Available Resource Types:
-Style and Themes</a>.</p>
+<p>For more information about the syntax for styles and themes in XML, see the
+<a href="{@docRoot}guide/topics/resources/style-resource.html">Style Resource</a> document.</p>
 
 <p>For a reference of available style attributes that you can use to define a style or theme
 (e.g., "windowBackground" or "textAppearance"), see {@link android.R.attr} or the respective
diff --git a/docs/html/sdk/installing/installing-adt.jd b/docs/html/sdk/installing/installing-adt.jd
index 4fc2ca6..feec56df 100644
--- a/docs/html/sdk/installing/installing-adt.jd
+++ b/docs/html/sdk/installing/installing-adt.jd
@@ -63,23 +63,20 @@
 
 <h2 id="Configure">Configure the ADT Plugin</h2>
 
-<p>After you've installed ADT and restarted Eclipse, you
+<p>Once Eclipse restarts, you
   must specify the location of your Android SDK directory:</p>
 
 <ol>
-    <li>Select <strong>Window</strong> &gt; <strong>Preferences...</strong> to open the Preferences
-        panel (on Mac OS X, select <strong>Eclipse</strong> &gt; <strong>Preferences</strong>).</li>
-    <li>Select <strong>Android</strong> from the left panel.</li>
-      <p>You may see a dialog asking whether you want to send usage statistics to Google. If so,
-make your choice and click <strong>Proceed</strong>.</p>
-    <li>For the <em>SDK Location</em> in the main panel, click <strong>Browse...</strong> and
-        locate your downloaded Android SDK directory (such as <code>android-sdk-windows</code>).</li>
-    <li>Click <strong>Apply</strong>, then <strong>OK</strong>.</li>
+    <li>In the "Welcome to Android Development" window that appears, select <strong>Use
+existing SDKs</strong>.</li>
+    <li>Browse and select the location of the Android SDK directory you recently
+downloaded.</li>
+    <li>Click <strong>Next</strong>.</li>
 </ol>
 
 
 <p>If you haven't encountered any errors, you're done setting up ADT
-  and can continue to the next step of the SDK installation.</p>
+  and can continue to <a href="{@docRoot}sdk/installing/next.html">Next Steps</a>.</p>
 
 
 
diff --git a/graphics/java/android/graphics/Rect.java b/graphics/java/android/graphics/Rect.java
index 6c204ab..8b5609f 100644
--- a/graphics/java/android/graphics/Rect.java
+++ b/graphics/java/android/graphics/Rect.java
@@ -69,10 +69,14 @@
      *          rectangle.
      */
     public Rect(Rect r) {
-        left = r.left;
-        top = r.top;
-        right = r.right;
-        bottom = r.bottom;
+        if (r == null) {
+            left = top = right = bottom = 0;
+        } else {
+            left = r.left;
+            top = r.top;
+            right = r.right;
+            bottom = r.bottom;
+        }
     }
 
     @Override
diff --git a/graphics/java/android/graphics/RectF.java b/graphics/java/android/graphics/RectF.java
index 108b7f9..53178b0 100644
--- a/graphics/java/android/graphics/RectF.java
+++ b/graphics/java/android/graphics/RectF.java
@@ -66,17 +66,25 @@
      *          rectangle.
      */
     public RectF(RectF r) {
-        left = r.left;
-        top = r.top;
-        right = r.right;
-        bottom = r.bottom;
+        if (r == null) {
+            left = top = right = bottom = 0.0f;
+        } else {
+            left = r.left;
+            top = r.top;
+            right = r.right;
+            bottom = r.bottom;
+        }
     }
     
     public RectF(Rect r) {
-        left = r.left;
-        top = r.top;
-        right = r.right;
-        bottom = r.bottom;
+        if (r == null) {
+            left = top = right = bottom = 0.0f;
+        } else {
+            left = r.left;
+            top = r.top;
+            right = r.right;
+            bottom = r.bottom;
+        }
     }
 
     @Override
diff --git a/graphics/java/android/renderscript/ScriptIntrinsicBlur.java b/graphics/java/android/renderscript/ScriptIntrinsicBlur.java
new file mode 100644
index 0000000..56c5426
--- /dev/null
+++ b/graphics/java/android/renderscript/ScriptIntrinsicBlur.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.renderscript;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.util.Log;
+
+/**
+ * @hide
+ **/
+public class ScriptIntrinsicBlur extends ScriptIntrinsic {
+    private float[] mValues = new float[9];
+    private Allocation mInput;
+
+    ScriptIntrinsicBlur(int id, RenderScript rs) {
+        super(id, rs);
+    }
+
+    /**
+     * Supported elements types are float, float4, uchar, uchar4
+     *
+     *
+     * @param rs
+     * @param e
+     *
+     * @return ScriptIntrinsicConvolve3x3
+     */
+    public static ScriptIntrinsicBlur create(RenderScript rs, Element e) {
+        int id = rs.nScriptIntrinsicCreate(5, e.getID(rs));
+        return new ScriptIntrinsicBlur(id, rs);
+
+    }
+
+    public void setInput(Allocation ain) {
+        mInput = ain;
+        bindAllocation(ain, 1);
+    }
+
+    public void setRadius(float v) {
+        if (v < 0 || v > 25) {
+            throw new RSIllegalArgumentException("Radius out of range (0-25).");
+        }
+        setVar(0, v);
+    }
+
+    public void forEach(Allocation aout) {
+        forEach(0, null, aout, null);
+    }
+
+}
+
diff --git a/graphics/java/android/renderscript/ScriptIntrinsicConvolve5x5.java b/graphics/java/android/renderscript/ScriptIntrinsicConvolve5x5.java
new file mode 100644
index 0000000..242623b
--- /dev/null
+++ b/graphics/java/android/renderscript/ScriptIntrinsicConvolve5x5.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.renderscript;
+
+import android.util.Log;
+
+/**
+ * @hide
+ **/
+public class ScriptIntrinsicConvolve5x5 extends ScriptIntrinsic {
+    private float[] mValues = new float[25];
+    private Allocation mInput;
+
+    ScriptIntrinsicConvolve5x5(int id, RenderScript rs) {
+        super(id, rs);
+    }
+
+    /**
+     * Supported elements types are float, float4, uchar, uchar4
+     *
+     *
+     * @param rs
+     * @param e
+     *
+     * @return ScriptIntrinsicConvolve5x5
+     */
+    public static ScriptIntrinsicConvolve5x5 create(RenderScript rs, Element e) {
+        int id = rs.nScriptIntrinsicCreate(4, e.getID(rs));
+        return new ScriptIntrinsicConvolve5x5(id, rs);
+
+    }
+
+    public void setInput(Allocation ain) {
+        mInput = ain;
+        bindAllocation(ain, 1);
+    }
+
+    public void setCoefficients(float v[]) {
+        FieldPacker fp = new FieldPacker(25*4);
+        for (int ct=0; ct < mValues.length; ct++) {
+            mValues[ct] = v[ct];
+            fp.addF32(mValues[ct]);
+        }
+        setVar(0, fp);
+    }
+
+    public void forEach(Allocation aout) {
+        forEach(0, null, aout, null);
+    }
+
+}
+
diff --git a/graphics/java/android/renderscript/ScriptIntrinsicLUT.java b/graphics/java/android/renderscript/ScriptIntrinsicLUT.java
new file mode 100644
index 0000000..e7d8d34
--- /dev/null
+++ b/graphics/java/android/renderscript/ScriptIntrinsicLUT.java
@@ -0,0 +1,110 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.renderscript;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.util.Log;
+
+
+/**
+ * @hide
+ **/
+public class ScriptIntrinsicLUT extends ScriptIntrinsic {
+    private Matrix4f mMatrix = new Matrix4f();
+    private Allocation mTables;
+    private byte mCache[] = new byte[1024];
+    private boolean mDirty = true;
+
+    ScriptIntrinsicLUT(int id, RenderScript rs) {
+        super(id, rs);
+        mTables = Allocation.createSized(rs, Element.U8(rs), 1024);
+        for (int ct=0; ct < 256; ct++) {
+            mCache[ct] = (byte)ct;
+            mCache[ct + 256] = (byte)ct;
+            mCache[ct + 512] = (byte)ct;
+            mCache[ct + 768] = (byte)ct;
+        }
+        bindAllocation(mTables, 0);
+    }
+
+    /**
+     * Supported elements types are uchar4
+     *
+     * @param rs
+     * @param e
+     *
+     * @return ScriptIntrinsicColorMatrix
+     */
+    public static ScriptIntrinsicLUT create(RenderScript rs, Element e) {
+        int id = rs.nScriptIntrinsicCreate(3, e.getID(rs));
+        return new ScriptIntrinsicLUT(id, rs);
+
+    }
+
+
+    private void validate(int index, int value) {
+        if (index < 0 || index > 255) {
+            throw new RSIllegalArgumentException("Index out of range (0-255).");
+        }
+        if (value < 0 || value > 255) {
+            throw new RSIllegalArgumentException("Value out of range (0-255).");
+        }
+    }
+
+    public void setRed(int index, int value) {
+        validate(index, value);
+        mCache[index] = (byte)value;
+        mDirty = true;
+    }
+
+    public void setGreen(int index, int value) {
+        validate(index, value);
+        mCache[index+256] = (byte)value;
+        mDirty = true;
+    }
+
+    public void setBlue(int index, int value) {
+        validate(index, value);
+        mCache[index+512] = (byte)value;
+        mDirty = true;
+    }
+
+    public void setAlpha(int index, int value) {
+        validate(index, value);
+        mCache[index+768] = (byte)value;
+        mDirty = true;
+    }
+
+
+    /**
+     * Invoke the kernel and apply the matrix to each cell of ain and copy to
+     * aout.
+     *
+     * @param ain Input allocation
+     * @param aout Output allocation
+     */
+    public void forEach(Allocation ain, Allocation aout) {
+        if (mDirty) {
+            mDirty = false;
+            mTables.copyFromUnchecked(mCache);
+        }
+        forEach(0, ain, aout, null);
+    }
+
+}
+
diff --git a/keystore/java/android/security/Credentials.java b/keystore/java/android/security/Credentials.java
index b233ff6..d8109ce 100644
--- a/keystore/java/android/security/Credentials.java
+++ b/keystore/java/android/security/Credentials.java
@@ -20,8 +20,9 @@
 import android.content.Context;
 import android.content.Intent;
 import android.util.Log;
-import com.android.org.bouncycastle.openssl.PEMReader;
-import com.android.org.bouncycastle.openssl.PEMWriter;
+import com.android.org.bouncycastle.util.io.pem.PemObject;
+import com.android.org.bouncycastle.util.io.pem.PemReader;
+import com.android.org.bouncycastle.util.io.pem.PemWriter;
 import java.io.ByteArrayInputStream;
 import java.io.ByteArrayOutputStream;
 import java.io.IOException;
@@ -32,6 +33,10 @@
 import java.io.Writer;
 import java.nio.charset.Charsets;
 import java.security.KeyPair;
+import java.security.cert.Certificate;
+import java.security.cert.CertificateEncodingException;
+import java.security.cert.CertificateException;
+import java.security.cert.CertificateFactory;
 import java.security.cert.X509Certificate;
 import java.util.ArrayList;
 import java.util.List;
@@ -108,34 +113,41 @@
     public static final String EXTRA_CA_CERTIFICATES_DATA = "ca_certificates_data";
 
     /**
-     * Convert objects to a PEM format, which is used for
-     * CA_CERTIFICATE, USER_CERTIFICATE, and USER_PRIVATE_KEY
-     * entries.
+     * Convert objects to a PEM format which is used for
+     * CA_CERTIFICATE and USER_CERTIFICATE entries.
      */
-    public static byte[] convertToPem(Object... objects) throws IOException {
+    public static byte[] convertToPem(Certificate... objects)
+            throws IOException, CertificateEncodingException {
         ByteArrayOutputStream bao = new ByteArrayOutputStream();
         Writer writer = new OutputStreamWriter(bao, Charsets.US_ASCII);
-        PEMWriter pw = new PEMWriter(writer);
-        for (Object o : objects) {
-            pw.writeObject(o);
+        PemWriter pw = new PemWriter(writer);
+        for (Certificate o : objects) {
+            pw.writeObject(new PemObject("CERTIFICATE", o.getEncoded()));
         }
         pw.close();
         return bao.toByteArray();
     }
     /**
      * Convert objects from PEM format, which is used for
-     * CA_CERTIFICATE, USER_CERTIFICATE, and USER_PRIVATE_KEY
-     * entries.
+     * CA_CERTIFICATE and USER_CERTIFICATE entries.
      */
-    public static List<Object> convertFromPem(byte[] bytes) throws IOException {
+    public static List<X509Certificate> convertFromPem(byte[] bytes)
+            throws IOException, CertificateException {
         ByteArrayInputStream bai = new ByteArrayInputStream(bytes);
         Reader reader = new InputStreamReader(bai, Charsets.US_ASCII);
-        PEMReader pr = new PEMReader(reader);
+        PemReader pr = new PemReader(reader);
 
-        List<Object> result = new ArrayList<Object>();
-        Object o;
-        while ((o = pr.readObject()) != null) {
-            result.add(o);
+        CertificateFactory cf = CertificateFactory.getInstance("X509");
+
+        List<X509Certificate> result = new ArrayList<X509Certificate>();
+        PemObject o;
+        while ((o = pr.readPemObject()) != null) {
+            if (o.getType().equals("CERTIFICATE")) {
+                Certificate c = cf.generateCertificate(new ByteArrayInputStream(o.getContent()));
+                result.add((X509Certificate) c);
+            } else {
+                throw new IllegalArgumentException("Unknown type " + o.getType());
+            }
         }
         pr.close();
         return result;
diff --git a/libs/hwui/DisplayListRenderer.cpp b/libs/hwui/DisplayListRenderer.cpp
index 755170f..c3444e6 100644
--- a/libs/hwui/DisplayListRenderer.cpp
+++ b/libs/hwui/DisplayListRenderer.cpp
@@ -1412,8 +1412,12 @@
     mSnapshot = new Snapshot(mFirstSnapshot,
             SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag);
     mSaveCount = 1;
+
     mSnapshot->setClip(0.0f, 0.0f, mWidth, mHeight);
+    mDirtyClip = opaque;
+
     mRestoreSaveCount = -1;
+
     return DrawGlInfo::kStatusDone; // No invalidate needed at record-time
 }
 
diff --git a/libs/hwui/LayerRenderer.cpp b/libs/hwui/LayerRenderer.cpp
index 5a8f2b7..02af5e2 100644
--- a/libs/hwui/LayerRenderer.cpp
+++ b/libs/hwui/LayerRenderer.cpp
@@ -332,10 +332,10 @@
         if (Caches::getInstance().extensions.hasDiscardFramebuffer()) {
             GLuint previousFbo;
             glGetIntegerv(GL_FRAMEBUFFER_BINDING, (GLint*) &previousFbo);
-
-            GLenum attachments = GL_COLOR_ATTACHMENT0;
             if (fbo != previousFbo) glBindFramebuffer(GL_FRAMEBUFFER, fbo);
-            glDiscardFramebufferEXT(GL_FRAMEBUFFER, 1, &attachments);
+
+            const GLenum attachments[] = { GL_COLOR_ATTACHMENT0 };
+            glDiscardFramebufferEXT(GL_FRAMEBUFFER, 1, attachments);
 
             if (fbo != previousFbo) glBindFramebuffer(GL_FRAMEBUFFER, previousFbo);
         }
diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp
index f4c2675..02448e8 100644
--- a/libs/hwui/OpenGLRenderer.cpp
+++ b/libs/hwui/OpenGLRenderer.cpp
@@ -173,6 +173,15 @@
     mSnapshot->setClip(left, top, right, bottom);
     mDirtyClip = opaque;
 
+    // If we know that we are going to redraw the entire framebuffer,
+    // perform a discard to let the driver know we don't need to preserve
+    // the back buffer for this frame.
+    if (mCaches.extensions.hasDiscardFramebuffer() &&
+            left <= 0.0f && top <= 0.0f && right >= mWidth && bottom >= mHeight) {
+        const GLenum attachments[] = { getTargetFbo() == 0 ? GL_COLOR_EXT : GL_COLOR_ATTACHMENT0 };
+        glDiscardFramebufferEXT(GL_FRAMEBUFFER, 1, attachments);
+    }
+
     syncState();
 
     if (!opaque) {
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index b5613f3..ea00ec8 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -1164,7 +1164,7 @@
     /**
      * Indicates if current platform supports use of SCO for off call use cases.
      * Application wanted to use bluetooth SCO audio when the phone is not in call
-     * must first call thsi method to make sure that the platform supports this
+     * must first call this method to make sure that the platform supports this
      * feature.
      * @return true if bluetooth SCO can be used for audio when not in call
      *         false otherwise
@@ -1300,6 +1300,19 @@
     }
 
     /**
+     * @hide
+     * Signals whether remote submix audio rerouting is enabled.
+     */
+    public void setRemoteSubmixOn(boolean on, int address) {
+        IAudioService service = getService();
+        try {
+            service.setRemoteSubmixOn(on, address);
+        } catch (RemoteException e) {
+            Log.e(TAG, "Dead object in setRemoteSubmixOn", e);
+        }
+    }
+
+    /**
      * Sets audio routing to the wired headset on or off.
      *
      * @param on set <var>true</var> to route audio to/from wired
diff --git a/media/java/android/media/AudioService.java b/media/java/android/media/AudioService.java
index aea8a88..e5c2a4d 100644
--- a/media/java/android/media/AudioService.java
+++ b/media/java/android/media/AudioService.java
@@ -151,6 +151,8 @@
     private static final int MSG_SET_WIRED_DEVICE_CONNECTION_STATE = 21;
     private static final int MSG_SET_A2DP_CONNECTION_STATE = 22;
     // end of messages handled under wakelock
+    private static final int MSG_SET_RSX_CONNECTION_STATE = 23; // change remote submix connection
+    private static final int MSG_SET_FORCE_RSX_USE = 24;        // force remote submix audio routing
 
     // flags for MSG_PERSIST_VOLUME indicating if current and/or last audible volume should be
     // persisted
@@ -2109,6 +2111,33 @@
         }
     };
 
+    /** see AudioManager.setRemoteSubmixOn(boolean on) */
+    public void setRemoteSubmixOn(boolean on, int address) {
+        sendMsg(mAudioHandler, MSG_SET_RSX_CONNECTION_STATE,
+                SENDMSG_REPLACE /* replace with QUEUE when multiple addresses are supported */,
+                on ? 1 : 0 /*arg1*/,
+                address /*arg2*/,
+                null/*obj*/, 0/*delay*/);
+
+        // Note that we are  currently forcing use of remote submix as soon as corresponding device
+        //   is made available
+        sendMsg(mAudioHandler, MSG_SET_FORCE_RSX_USE, SENDMSG_REPLACE,
+                AudioSystem.FOR_MEDIA,
+                on ? AudioSystem.FORCE_REMOTE_SUBMIX : AudioSystem.FORCE_NONE,
+                null/*obj*/, 0/*delay*/);
+    }
+
+    private void onSetRsxConnectionState(int available, int address) {
+        AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_IN_REMOTE_SUBMIX,
+                available == 1 ?
+                        AudioSystem.DEVICE_STATE_AVAILABLE : AudioSystem.DEVICE_STATE_UNAVAILABLE,
+                String.valueOf(address) /*device_address*/);
+        AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_REMOTE_SUBMIX,
+                available == 1 ?
+                        AudioSystem.DEVICE_STATE_AVAILABLE : AudioSystem.DEVICE_STATE_UNAVAILABLE,
+                String.valueOf(address) /*device_address*/);
+    }
+
     ///////////////////////////////////////////////////////////////////////////
     // Internal methods
     ///////////////////////////////////////////////////////////////////////////
@@ -3072,6 +3101,7 @@
 
                 case MSG_SET_FORCE_USE:
                 case MSG_SET_FORCE_BT_A2DP_USE:
+                case MSG_SET_FORCE_RSX_USE:
                     setForceUse(msg.arg1, msg.arg2);
                     break;
 
@@ -3134,6 +3164,10 @@
                     onRegisterVolumeObserverForRcc(msg.arg1 /* rccId */,
                             (IRemoteVolumeObserver)msg.obj /* rvo */);
                     break;
+
+                case MSG_SET_RSX_CONNECTION_STATE:
+                    onSetRsxConnectionState(msg.arg1/*available*/, msg.arg2/*address*/);
+                    break;
             }
         }
     }
diff --git a/media/java/android/media/AudioSystem.java b/media/java/android/media/AudioSystem.java
index f3a8558..129e84f 100644
--- a/media/java/android/media/AudioSystem.java
+++ b/media/java/android/media/AudioSystem.java
@@ -214,6 +214,7 @@
     public static final int DEVICE_OUT_REMOTE_SUBMIX = 0x8000;
 
     public static final int DEVICE_OUT_DEFAULT = DEVICE_BIT_DEFAULT;
+
     public static final int DEVICE_OUT_ALL = (DEVICE_OUT_EARPIECE |
                                               DEVICE_OUT_SPEAKER |
                                               DEVICE_OUT_WIRED_HEADSET |
@@ -352,7 +353,8 @@
     public static final int FORCE_ANALOG_DOCK = 8;
     public static final int FORCE_DIGITAL_DOCK = 9;
     public static final int FORCE_NO_BT_A2DP = 10;
-    private static final int NUM_FORCE_CONFIG = 11;
+    public static final int FORCE_REMOTE_SUBMIX = 11;
+    private static final int NUM_FORCE_CONFIG = 12;
     public static final int FORCE_DEFAULT = FORCE_NONE;
 
     // usage for setForceUse, must match AudioSystem::force_use
diff --git a/media/java/android/media/IAudioService.aidl b/media/java/android/media/IAudioService.aidl
index 854eb3f..7ae61cd 100644
--- a/media/java/android/media/IAudioService.aidl
+++ b/media/java/android/media/IAudioService.aidl
@@ -108,6 +108,8 @@
 
     boolean isBluetoothA2dpOn();
 
+    oneway void setRemoteSubmixOn(boolean on, int address);
+
     int requestAudioFocus(int mainStreamType, int durationHint, IBinder cb, IAudioFocusDispatcher l,
             String clientId, String callingPackageName);
 
diff --git a/media/java/android/media/MediaRecorder.java b/media/java/android/media/MediaRecorder.java
index 2d1be02..48bea52 100644
--- a/media/java/android/media/MediaRecorder.java
+++ b/media/java/android/media/MediaRecorder.java
@@ -177,6 +177,12 @@
          *  is applied.
          */
         public static final int VOICE_COMMUNICATION = 7;
+
+        /**
+         * @hide
+         * Audio source for remote submix.
+         */
+        public static final int REMOTE_SUBMIX_SOURCE = 8;
     }
 
     /**
@@ -291,7 +297,12 @@
      * Gets the maximum value for audio sources.
      * @see android.media.MediaRecorder.AudioSource
      */
-    public static final int getAudioSourceMax() { return AudioSource.VOICE_COMMUNICATION; }
+    public static final int getAudioSourceMax() {
+        // FIXME disable selection of the remote submxi source selection once test code
+        //       doesn't rely on it
+        return AudioSource.REMOTE_SUBMIX_SOURCE;
+        //return AudioSource.VOICE_COMMUNICATION;
+    }
 
     /**
      * Sets the video source to be used for recording. If this method is not
diff --git a/media/java/android/mtp/MtpDatabase.java b/media/java/android/mtp/MtpDatabase.java
index 7532d79..bd65b9e 100755
--- a/media/java/android/mtp/MtpDatabase.java
+++ b/media/java/android/mtp/MtpDatabase.java
@@ -84,11 +84,10 @@
             Files.FileColumns._ID, // 0
             Files.FileColumns.DATA, // 1
     };
-    private static final String[] PATH_SIZE_FORMAT_PROJECTION = new String[] {
+    private static final String[] PATH_FORMAT_PROJECTION = new String[] {
             Files.FileColumns._ID, // 0
             Files.FileColumns.DATA, // 1
-            Files.FileColumns.SIZE, // 2
-            Files.FileColumns.FORMAT, // 3
+            Files.FileColumns.FORMAT, // 2
     };
     private static final String[] OBJECT_INFO_PROJECTION = new String[] {
             Files.FileColumns._ID, // 0
@@ -96,15 +95,14 @@
             Files.FileColumns.FORMAT, // 2
             Files.FileColumns.PARENT, // 3
             Files.FileColumns.DATA, // 4
-            Files.FileColumns.SIZE, // 5
-            Files.FileColumns.DATE_MODIFIED, // 6
+            Files.FileColumns.DATE_MODIFIED, // 5
     };
     private static final String ID_WHERE = Files.FileColumns._ID + "=?";
     private static final String PATH_WHERE = Files.FileColumns.DATA + "=?";
 
     private static final String STORAGE_WHERE = Files.FileColumns.STORAGE_ID + "=?";
-    private static final String FORMAT_WHERE = Files.FileColumns.PARENT + "=?";
-    private static final String PARENT_WHERE = Files.FileColumns.FORMAT + "=?";
+    private static final String FORMAT_WHERE = Files.FileColumns.FORMAT + "=?";
+    private static final String PARENT_WHERE = Files.FileColumns.PARENT + "=?";
     private static final String STORAGE_FORMAT_WHERE = STORAGE_WHERE + " AND "
                                             + Files.FileColumns.FORMAT + "=?";
     private static final String STORAGE_PARENT_WHERE = STORAGE_WHERE + " AND "
@@ -834,7 +832,7 @@
     }
 
     private boolean getObjectInfo(int handle, int[] outStorageFormatParent,
-                        char[] outName, long[] outSizeModified) {
+                        char[] outName, long[] outModified) {
         Cursor c = null;
         try {
             c = mMediaProvider.query(mObjectsUri, OBJECT_INFO_PROJECTION,
@@ -855,8 +853,7 @@
                 path.getChars(start, end, outName, 0);
                 outName[end - start] = 0;
 
-                outSizeModified[0] = c.getLong(5);
-                outSizeModified[1] = c.getLong(6);
+                outModified[0] = c.getLong(5);
                 return true;
             }
         } catch (RemoteException e) {
@@ -880,14 +877,16 @@
         }
         Cursor c = null;
         try {
-            c = mMediaProvider.query(mObjectsUri, PATH_SIZE_FORMAT_PROJECTION,
+            c = mMediaProvider.query(mObjectsUri, PATH_FORMAT_PROJECTION,
                             ID_WHERE, new String[] {  Integer.toString(handle) }, null, null);
             if (c != null && c.moveToNext()) {
                 String path = c.getString(1);
                 path.getChars(0, path.length(), outFilePath, 0);
                 outFilePath[path.length()] = 0;
-                outFileLengthFormat[0] = c.getLong(2);
-                outFileLengthFormat[1] = c.getLong(3);
+                // File transfers from device to host will likely fail if the size is incorrect.
+                // So to be safe, use the actual file size here.
+                outFileLengthFormat[0] = new File(path).length();
+                outFileLengthFormat[1] = c.getLong(2);
                 return MtpConstants.RESPONSE_OK;
             } else {
                 return MtpConstants.RESPONSE_INVALID_OBJECT_HANDLE;
@@ -909,13 +908,13 @@
 
         Cursor c = null;
         try {
-            c = mMediaProvider.query(mObjectsUri, PATH_SIZE_FORMAT_PROJECTION,
+            c = mMediaProvider.query(mObjectsUri, PATH_FORMAT_PROJECTION,
                             ID_WHERE, new String[] {  Integer.toString(handle) }, null, null);
             if (c != null && c.moveToNext()) {
                 // don't convert to media path here, since we will be matching
                 // against paths in the database matching /data/media
                 path = c.getString(1);
-                format = c.getInt(3);
+                format = c.getInt(2);
             } else {
                 return MtpConstants.RESPONSE_INVALID_OBJECT_HANDLE;
             }
diff --git a/media/jni/android_mtp_MtpDatabase.cpp b/media/jni/android_mtp_MtpDatabase.cpp
index 99e543b..bc65de5 100644
--- a/media/jni/android_mtp_MtpDatabase.cpp
+++ b/media/jni/android_mtp_MtpDatabase.cpp
@@ -751,13 +751,22 @@
 
 MtpResponseCode MyMtpDatabase::getObjectInfo(MtpObjectHandle handle,
                                             MtpObjectInfo& info) {
-    char    date[20];
+    char            date[20];
+    MtpString       path;
+    int64_t         length;
+    MtpObjectFormat format;
+
+    MtpResponseCode result = getObjectFilePath(handle, path, length, format);
+    if (result != MTP_RESPONSE_OK) {
+        return result;
+    }
+    info.mCompressedSize = (length > 0xFFFFFFFFLL ? 0xFFFFFFFF : (uint32_t)length);
 
     JNIEnv* env = AndroidRuntime::getJNIEnv();
-    jboolean result = env->CallBooleanMethod(mDatabase, method_getObjectInfo,
-                (jint)handle, mIntBuffer, mStringBuffer, mLongBuffer);
-    if (!result)
+    if (!env->CallBooleanMethod(mDatabase, method_getObjectInfo,
+                (jint)handle, mIntBuffer, mStringBuffer, mLongBuffer)) {
         return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
+    }
 
     jint* intValues = env->GetIntArrayElements(mIntBuffer, 0);
     info.mStorageID = intValues[0];
@@ -766,9 +775,7 @@
     env->ReleaseIntArrayElements(mIntBuffer, intValues, 0);
 
     jlong* longValues = env->GetLongArrayElements(mLongBuffer, 0);
-    uint64_t size = longValues[0];
-    info.mCompressedSize = (size > 0xFFFFFFFFLL ? 0xFFFFFFFF : size);
-    info.mDateModified = longValues[1];
+    info.mDateModified = longValues[0];
     env->ReleaseLongArrayElements(mLongBuffer, longValues, 0);
 
 //    info.mAssociationType = (format == MTP_FORMAT_ASSOCIATION ?
@@ -783,28 +790,23 @@
 
     // read EXIF data for thumbnail information
     if (info.mFormat == MTP_FORMAT_EXIF_JPEG || info.mFormat == MTP_FORMAT_JFIF) {
-        MtpString path;
-        int64_t length;
-        MtpObjectFormat format;
-        if (getObjectFilePath(handle, path, length, format) == MTP_RESPONSE_OK) {
-            ResetJpgfile();
-             // Start with an empty image information structure.
-            memset(&ImageInfo, 0, sizeof(ImageInfo));
-            ImageInfo.FlashUsed = -1;
-            ImageInfo.MeteringMode = -1;
-            ImageInfo.Whitebalance = -1;
-            strncpy(ImageInfo.FileName, (const char *)path, PATH_MAX);
-            if (ReadJpegFile((const char*)path, READ_METADATA)) {
-                Section_t* section = FindSection(M_EXIF);
-                if (section) {
-                    info.mThumbCompressedSize = ImageInfo.ThumbnailSize;
-                    info.mThumbFormat = MTP_FORMAT_EXIF_JPEG;
-                    info.mImagePixWidth = ImageInfo.Width;
-                    info.mImagePixHeight = ImageInfo.Height;
-                }
+        ResetJpgfile();
+         // Start with an empty image information structure.
+        memset(&ImageInfo, 0, sizeof(ImageInfo));
+        ImageInfo.FlashUsed = -1;
+        ImageInfo.MeteringMode = -1;
+        ImageInfo.Whitebalance = -1;
+        strncpy(ImageInfo.FileName, (const char *)path, PATH_MAX);
+        if (ReadJpegFile((const char*)path, READ_METADATA)) {
+            Section_t* section = FindSection(M_EXIF);
+            if (section) {
+                info.mThumbCompressedSize = ImageInfo.ThumbnailSize;
+                info.mThumbFormat = MTP_FORMAT_EXIF_JPEG;
+                info.mImagePixWidth = ImageInfo.Width;
+                info.mImagePixHeight = ImageInfo.Height;
             }
-            DiscardData();
         }
+        DiscardData();
     }
 
     checkAndClearExceptionFromCallback(env, __FUNCTION__);
diff --git a/media/mca/filterpacks/java/android/filterpacks/imageproc/RedEyeFilter.java b/media/mca/filterpacks/java/android/filterpacks/imageproc/RedEyeFilter.java
index 3450ef1..8618804 100644
--- a/media/mca/filterpacks/java/android/filterpacks/imageproc/RedEyeFilter.java
+++ b/media/mca/filterpacks/java/android/filterpacks/imageproc/RedEyeFilter.java
@@ -72,9 +72,7 @@
             "void main() {\n" +
             "  vec4 color = texture2D(tex_sampler_0, v_texcoord);\n" +
             "  vec4 mask = texture2D(tex_sampler_1, v_texcoord);\n" +
-            "  gl_FragColor = vec4(mask.a, mask.a, mask.a, 1.0) * intensity + color * (1.0 - intensity);\n" +
             "  if (mask.a > 0.0) {\n" +
-            "    gl_FragColor.r = 0.0;\n" +
             "    float green_blue = color.g + color.b;\n" +
             "    float red_intensity = color.r / green_blue;\n" +
             "    if (red_intensity > intensity) {\n" +
@@ -105,8 +103,8 @@
                 ShaderProgram shaderProgram = new ShaderProgram(context, mRedEyeShader);
                 shaderProgram.setMaximumTileSize(mTileSize);
                 mProgram = shaderProgram;
+                mProgram.setHostValue("intensity", DEFAULT_RED_INTENSITY);
                 break;
-
             default:
                 throw new RuntimeException("Filter RedEye does not support frames of " +
                     "target " + target + "!");
@@ -180,8 +178,6 @@
     }
 
     private void updateProgramParams() {
-        mProgram.setHostValue("intensity", DEFAULT_RED_INTENSITY);
-
         if ( mCenters.length % 2 == 1) {
             throw new RuntimeException("The size of center array must be even.");
         }
diff --git a/media/tests/EffectsTest/res/layout/bassboosttest.xml b/media/tests/EffectsTest/res/layout/bassboosttest.xml
index 0888e98..ac912c8 100755
--- a/media/tests/EffectsTest/res/layout/bassboosttest.xml
+++ b/media/tests/EffectsTest/res/layout/bassboosttest.xml
@@ -105,6 +105,8 @@
             style="@android:style/TextAppearance.Medium" />
 
         <EditText android:id="@+id/sessionEdit"
+            android:singleLine="true"
+            android:numeric="integer"
             android:layout_width="fill_parent"
             android:layout_height="wrap_content"
             android:layout_weight="0.5"
diff --git a/media/tests/EffectsTest/res/layout/equalizertest.xml b/media/tests/EffectsTest/res/layout/equalizertest.xml
index 2223c48..5ef035d 100755
--- a/media/tests/EffectsTest/res/layout/equalizertest.xml
+++ b/media/tests/EffectsTest/res/layout/equalizertest.xml
@@ -105,6 +105,8 @@
             style="@android:style/TextAppearance.Medium" />
 
         <EditText android:id="@+id/sessionEdit"
+            android:singleLine="true"
+            android:numeric="integer"
             android:layout_width="fill_parent"
             android:layout_height="wrap_content"
             android:layout_weight="0.5"
diff --git a/media/tests/EffectsTest/res/layout/presetreverbtest.xml b/media/tests/EffectsTest/res/layout/presetreverbtest.xml
index b648899..cd7fbd3 100755
--- a/media/tests/EffectsTest/res/layout/presetreverbtest.xml
+++ b/media/tests/EffectsTest/res/layout/presetreverbtest.xml
@@ -105,6 +105,8 @@
             style="@android:style/TextAppearance.Medium" />
 
         <EditText android:id="@+id/sessionEdit"
+            android:singleLine="true"
+            android:numeric="integer"
             android:layout_width="fill_parent"
             android:layout_height="wrap_content"
             android:layout_weight="0.5"
diff --git a/media/tests/EffectsTest/res/layout/virtualizertest.xml b/media/tests/EffectsTest/res/layout/virtualizertest.xml
index c9203de..1fafeab 100755
--- a/media/tests/EffectsTest/res/layout/virtualizertest.xml
+++ b/media/tests/EffectsTest/res/layout/virtualizertest.xml
@@ -105,6 +105,8 @@
             style="@android:style/TextAppearance.Medium" />
 
         <EditText android:id="@+id/sessionEdit"
+            android:singleLine="true"
+            android:numeric="integer"
             android:layout_width="fill_parent"
             android:layout_height="wrap_content"
             android:layout_weight="0.5"
diff --git a/media/tests/EffectsTest/res/layout/visualizertest.xml b/media/tests/EffectsTest/res/layout/visualizertest.xml
index 8611e8c..50ac7bb 100755
--- a/media/tests/EffectsTest/res/layout/visualizertest.xml
+++ b/media/tests/EffectsTest/res/layout/visualizertest.xml
@@ -105,6 +105,8 @@
             style="@android:style/TextAppearance.Medium" />
 
         <EditText android:id="@+id/sessionEdit"
+            android:singleLine="true"
+            android:numeric="integer"
             android:layout_width="fill_parent"
             android:layout_height="wrap_content"
             android:layout_weight="0.5"
diff --git a/media/tests/EffectsTest/src/com/android/effectstest/EffectsTest.java b/media/tests/EffectsTest/src/com/android/effectstest/EffectsTest.java
index 6612766..7020246 100755
--- a/media/tests/EffectsTest/src/com/android/effectstest/EffectsTest.java
+++ b/media/tests/EffectsTest/src/com/android/effectstest/EffectsTest.java
@@ -32,6 +32,7 @@
 import android.widget.BaseAdapter;
 import android.widget.LinearLayout;
 import android.media.audiofx.AudioEffect;
+
 import java.util.UUID;
 
 public class EffectsTest extends Activity {
@@ -154,13 +155,35 @@
             this.setOrientation(VERTICAL);
         }
 
+        public String effectUuidToString(UUID effectType) {
+            if (effectType.equals(AudioEffect.EFFECT_TYPE_VIRTUALIZER)) {
+                return "Virtualizer";
+            } else if (effectType.equals(AudioEffect.EFFECT_TYPE_ENV_REVERB)){
+                return "Reverb";
+            } else if (effectType.equals(AudioEffect.EFFECT_TYPE_PRESET_REVERB)){
+                return "Preset Reverb";
+            } else if (effectType.equals(AudioEffect.EFFECT_TYPE_EQUALIZER)){
+                return "Equalizer";
+            } else if (effectType.equals(AudioEffect.EFFECT_TYPE_BASS_BOOST)){
+                return "Bass Boost";
+            } else if (effectType.equals(AudioEffect.EFFECT_TYPE_AGC)){
+                return "Automatic Gain Control";
+            } else if (effectType.equals(AudioEffect.EFFECT_TYPE_AEC)){
+                return "Acoustic Echo Canceler";
+            } else if (effectType.equals(AudioEffect.EFFECT_TYPE_NS)){
+                return "Noise Suppressor";
+            }
+
+            return effectType.toString();
+        }
+
         public void set(int position) {
             TextView tv = new TextView(mContext);
             tv.setText("Effect "+ position);
             addView(tv, new LinearLayout.LayoutParams(
                     LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT));
             tv = new TextView(mContext);
-            tv.setText(" type: "+ mDescriptors[position].type.toString());
+            tv.setText(" type: "+ effectUuidToString(mDescriptors[position].type));
             addView(tv, new LinearLayout.LayoutParams(
                     LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT));
             tv = new TextView(mContext);
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
index 05673c3..23b4b59 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
@@ -67,7 +67,7 @@
     // database gets upgraded properly. At a minimum, please confirm that 'upgradeVersion'
     // is properly propagated through your change.  Not doing so will result in a loss of user
     // settings.
-    private static final int DATABASE_VERSION = 87;
+    private static final int DATABASE_VERSION = 88;
 
     private Context mContext;
     private int mUserHandle;
@@ -1305,6 +1305,23 @@
             upgradeVersion = 87;
         }
 
+        if (upgradeVersion == 87) {
+            db.beginTransaction();
+            try {
+                String[] settingsToMove = {
+                        Settings.Secure.DATA_STALL_ALARM_NON_AGGRESSIVE_DELAY_IN_MS,
+                        Settings.Secure.DATA_STALL_ALARM_AGGRESSIVE_DELAY_IN_MS,
+                        Settings.Secure.GPRS_REGISTER_CHECK_PERIOD_MS
+                };
+                moveSettingsToNewTable(db, TABLE_SECURE, TABLE_GLOBAL, settingsToMove, true);
+
+                db.setTransactionSuccessful();
+            } finally {
+                db.endTransaction();
+            }
+            upgradeVersion = 88;
+        }
+
         // *** Remember to update DATABASE_VERSION above!
 
         if (upgradeVersion != currentVersion) {
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index 6773482..e1a5b52 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -61,7 +61,7 @@
 
 public class SettingsProvider extends ContentProvider {
     private static final String TAG = "SettingsProvider";
-    private static final boolean LOCAL_LOGV = true;
+    private static final boolean LOCAL_LOGV = false;
 
     private static final String TABLE_SYSTEM = "system";
     private static final String TABLE_SECURE = "secure";
@@ -195,6 +195,12 @@
         sSecureGlobalKeys.add(Settings.Secure.WIFI_WATCHDOG_POOR_NETWORK_TEST_ENABLED);
         sSecureGlobalKeys.add(Settings.Secure.WIFI_WATCHDOG_RSSI_FETCH_INTERVAL_MS);
         sSecureGlobalKeys.add(Settings.Secure.WIMAX_NETWORKS_AVAILABLE_NOTIFICATION_ON);
+        sSecureGlobalKeys.add(Settings.Secure.PACKAGE_VERIFIER_ENABLE);
+        sSecureGlobalKeys.add(Settings.Secure.PACKAGE_VERIFIER_TIMEOUT);
+        sSecureGlobalKeys.add(Settings.Secure.PACKAGE_VERIFIER_DEFAULT_RESPONSE);
+        sSecureGlobalKeys.add(Settings.Secure.DATA_STALL_ALARM_NON_AGGRESSIVE_DELAY_IN_MS);
+        sSecureGlobalKeys.add(Settings.Secure.DATA_STALL_ALARM_AGGRESSIVE_DELAY_IN_MS);
+        sSecureGlobalKeys.add(Settings.Secure.GPRS_REGISTER_CHECK_PERIOD_MS);
         sSecureGlobalKeys.add(Settings.Secure.WTF_IS_FATAL);
 
         // Keys from the 'system' table now moved to 'global'
@@ -422,10 +428,6 @@
                     }
                 }
             }, userFilter);
-
-            if (!ensureAndroidIdIsSet()) {
-                return false;
-            }
         }
         return true;
     }
@@ -473,6 +475,8 @@
         sObserverInstances.append(userHandle, observer);
         observer.startWatching();
 
+        ensureAndroidIdIsSet(userHandle);
+
         startAsyncCachePopulation(userHandle);
     }
 
@@ -537,24 +541,27 @@
         }
     }
 
-    private boolean ensureAndroidIdIsSet() {
-        final Cursor c = query(Settings.Secure.CONTENT_URI,
+    private boolean ensureAndroidIdIsSet(int userHandle) {
+        final Cursor c = queryForUser(Settings.Secure.CONTENT_URI,
                 new String[] { Settings.NameValueTable.VALUE },
                 Settings.NameValueTable.NAME + "=?",
-                new String[] { Settings.Secure.ANDROID_ID }, null);
+                new String[] { Settings.Secure.ANDROID_ID }, null,
+                userHandle);
         try {
             final String value = c.moveToNext() ? c.getString(0) : null;
             if (value == null) {
                 final SecureRandom random = new SecureRandom();
                 final String newAndroidIdValue = Long.toHexString(random.nextLong());
-                Log.d(TAG, "Generated and saved new ANDROID_ID [" + newAndroidIdValue + "]");
                 final ContentValues values = new ContentValues();
                 values.put(Settings.NameValueTable.NAME, Settings.Secure.ANDROID_ID);
                 values.put(Settings.NameValueTable.VALUE, newAndroidIdValue);
-                final Uri uri = insert(Settings.Secure.CONTENT_URI, values);
+                final Uri uri = insertForUser(Settings.Secure.CONTENT_URI, values, userHandle);
                 if (uri == null) {
+                    Slog.e(TAG, "Unable to generate new ANDROID_ID for user " + userHandle);
                     return false;
                 }
+                Slog.d(TAG, "Generated and saved new ANDROID_ID [" + newAndroidIdValue
+                        + "] for user " + userHandle);
             }
             return true;
         } finally {
@@ -732,12 +739,17 @@
 
     @Override
     public Cursor query(Uri url, String[] select, String where, String[] whereArgs, String sort) {
-        final int callingUser = UserHandle.getCallingUserId();
-        if (LOCAL_LOGV) Slog.v(TAG, "query() for user " + callingUser);
+        return queryForUser(url, select, where, whereArgs, sort, UserHandle.getCallingUserId());
+    }
+
+    public Cursor queryForUser(Uri url, String[] select, String where, String[] whereArgs,
+            String sort, int forUser) {
+        if (LOCAL_LOGV) Slog.v(TAG, "query(" + url + ") for user " + forUser);
         SqlArguments args = new SqlArguments(url, where, whereArgs);
         DatabaseHelper dbH;
         synchronized (this) {
-            dbH = getOrEstablishDatabaseLocked(callingUser);
+            dbH = getOrEstablishDatabaseLocked(
+                    TABLE_GLOBAL.equals(args.table) ? UserHandle.USER_OWNER : forUser);
         }
         SQLiteDatabase db = dbH.getReadableDatabase();
 
@@ -795,7 +807,8 @@
         mutationCount.incrementAndGet();
         DatabaseHelper dbH;
         synchronized (this) {
-            dbH = getOrEstablishDatabaseLocked(callingUser);
+            dbH = getOrEstablishDatabaseLocked(
+                    TABLE_GLOBAL.equals(args.table) ? UserHandle.USER_OWNER : callingUser);
         }
         SQLiteDatabase db = dbH.getWritableDatabase();
         db.beginTransaction();
@@ -898,7 +911,7 @@
     private Uri insertForUser(Uri url, ContentValues initialValues, int desiredUserHandle) {
         final int callingUser = UserHandle.getCallingUserId();
         if (callingUser != desiredUserHandle) {
-            getContext().enforceCallingPermission(
+            getContext().enforceCallingOrSelfPermission(
                     android.Manifest.permission.INTERACT_ACROSS_USERS_FULL,
                     "Not permitted to access settings for other users");
         }
@@ -934,7 +947,7 @@
         mutationCount.incrementAndGet();
         DatabaseHelper dbH;
         synchronized (this) {
-            dbH = getOrEstablishDatabaseLocked(callingUser);
+            dbH = getOrEstablishDatabaseLocked(desiredUserHandle);
         }
         SQLiteDatabase db = dbH.getWritableDatabase();
         final long rowId = db.insert(args.table, null, initialValues);
@@ -952,13 +965,15 @@
 
     @Override
     public int delete(Uri url, String where, String[] whereArgs) {
-        final int callingUser = UserHandle.getCallingUserId();
+        int callingUser = UserHandle.getCallingUserId();
         if (LOCAL_LOGV) Slog.v(TAG, "delete() for user " + callingUser);
         SqlArguments args = new SqlArguments(url, where, whereArgs);
         if (TABLE_FAVORITES.equals(args.table)) {
             return 0;
         } else if (TABLE_OLD_FAVORITES.equals(args.table)) {
             args.table = TABLE_FAVORITES;
+        } else if (TABLE_GLOBAL.equals(args.table)) {
+            callingUser = UserHandle.USER_OWNER;
         }
         checkWritePermissions(args);
 
@@ -987,11 +1002,13 @@
         // intended effect (the update will be invisible to the rest of the system).
         // This should have no practical effect, since writes to the Secure db can only
         // be done by system code, and that code should be using the correct API up front.
-        final int callingUser = UserHandle.getCallingUserId();
+        int callingUser = UserHandle.getCallingUserId();
         if (LOCAL_LOGV) Slog.v(TAG, "update() for user " + callingUser);
         SqlArguments args = new SqlArguments(url, where, whereArgs);
         if (TABLE_FAVORITES.equals(args.table)) {
             return 0;
+        } else if (TABLE_GLOBAL.equals(args.table)) {
+            callingUser = UserHandle.USER_OWNER;
         }
         checkWritePermissions(args);
 
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index 22a97bd..44fb49a 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -19,6 +19,7 @@
     <uses-permission android:name="android.permission.READ_PROFILE" />
     <uses-permission android:name="android.permission.READ_CONTACTS" />
     <uses-permission android:name="android.permission.CONFIGURE_WIFI_DISPLAY" />
+    <uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" />
 
     <!-- Networking and telephony -->
     <uses-permission android:name="android.permission.BLUETOOTH" />
diff --git a/packages/SystemUI/res/layout/quick_settings_tile_ime.xml b/packages/SystemUI/res/layout/quick_settings_tile_ime.xml
index 93db6db..528b54f 100644
--- a/packages/SystemUI/res/layout/quick_settings_tile_ime.xml
+++ b/packages/SystemUI/res/layout/quick_settings_tile_ime.xml
@@ -15,9 +15,12 @@
 -->
 <TextView
     xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/ime_textview"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
+    android:layout_gravity="center"
     android:gravity="center"
+    android:drawableTop="@drawable/stat_sys_roaming_cdma_0"
     android:text="@string/quick_settings_ime_label"
-    android:singleLine="true"
+    android:textAppearance="@style/TextAppearance.QuickSettings.TileView"
     />
\ No newline at end of file
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 8fb703f..25bc656 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -155,13 +155,13 @@
     <string name="always_use_accessory">Use by default for this USB accessory</string>
 
     <!-- Title of confirmation dialog for USB debugging -->
-    <string name="usb_debugging_title">Allow USB Debugging?</string>
+    <string name="usb_debugging_title">Allow USB debugging?</string>
 
     <!-- Message of confirmation dialog for USB debugging -->
-    <string name="usb_debugging_message">Allow USB Debugging from this computer?\nYour RSA key fingerprint is\n<xliff:g id="fingerprint">%1$s</xliff:g></string>
+    <string name="usb_debugging_message">The computer\'s RSA key fingerprint is:\n<xliff:g id="fingerprint">%1$s</xliff:g></string>
 
     <!-- Option to always allow USB debugging from the attached computer -->
-    <string name="usb_debugging_always">Always allow this computer</string>
+    <string name="usb_debugging_always">Always allow from this computer</string>
 
     <!-- Checkbox label for application compatibility mode ON (zooming app to look like it's running
          on a phone).  [CHAR LIMIT=25] -->
@@ -410,7 +410,7 @@
     <!-- QuickSettings: Brightness [CHAR LIMIT=NONE] -->
     <string name="quick_settings_brightness_label">Brightness</string>
     <!-- QuickSettings: IME [CHAR LIMIT=NONE] -->
-    <string name="quick_settings_ime_label">IME</string>
+    <string name="quick_settings_ime_label">Input Method</string>
     <!-- QuickSettings: Location [CHAR LIMIT=NONE] -->
     <string name="quick_settings_location_label">Location in use</string>
     <!-- QuickSettings: Media device [CHAR LIMIT=NONE] -->
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
index bee63ee..91f29a2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
@@ -360,6 +360,10 @@
         mBar = panelBar;
     }
 
+    public void setImeWindowStatus(boolean visible) {
+        // To be implemented by classes extending PanelView
+    }
+
     public void setup(NetworkController network, BluetoothController bt, BatteryController batt,
             LocationController location) {
         // To be implemented by classes extending PanelView
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
index 6231d0d..b335b5f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -1413,10 +1413,11 @@
         mCommandQueue.setNavigationIconHints(
                 altBack ? (mNavigationIconHints | StatusBarManager.NAVIGATION_HINT_BACK_ALT)
                         : (mNavigationIconHints & ~StatusBarManager.NAVIGATION_HINT_BACK_ALT));
+        mSettingsPanel.setImeWindowStatus(vis > 0);
     }
 
     @Override
-    public void setHardKeyboardStatus(boolean available, boolean enabled) { }
+    public void setHardKeyboardStatus(boolean available, boolean enabled) {}
 
     @Override
     protected void tick(IBinder key, StatusBarNotification n, boolean firstTime) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettings.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettings.java
index 7056e03..c34e012 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettings.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettings.java
@@ -17,6 +17,7 @@
 package com.android.systemui.statusbar.phone;
 
 import android.app.Dialog;
+import android.app.PendingIntent;
 import android.content.BroadcastReceiver;
 import android.content.ComponentName;
 import android.content.Context;
@@ -32,8 +33,8 @@
 import android.hardware.display.WifiDisplay;
 import android.hardware.display.WifiDisplayStatus;
 import android.net.Uri;
-import android.os.UserHandle;
 import android.provider.ContactsContract;
+import android.provider.Settings;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
@@ -103,6 +104,10 @@
         mBar = bar;
     }
 
+    public void setImeWindowStatus(boolean visible) {
+        mModel.onImeWindowStatusChanged(visible);
+    }
+
     void setup(NetworkController networkController, BluetoothController bluetoothController,
             BatteryController batteryController, LocationController locationController) {
         networkController.addNetworkSignalChangedCallback(mModel);
@@ -167,10 +172,9 @@
         startSettingsActivity(intent);
     }
     private void startSettingsActivity(Intent intent) {
-        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
+        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
         mBar.collapseAllPanels(true);
         mContext.startActivity(intent);
-
     }
 
     private void addUserTiles(ViewGroup parent, LayoutInflater inflater) {
@@ -248,29 +252,31 @@
         });
         parent.addView(wifiTile);
 
-        // RSSI
-        QuickSettingsTileView rssiTile = (QuickSettingsTileView)
-                inflater.inflate(R.layout.quick_settings_tile, parent, false);
-        rssiTile.setContent(R.layout.quick_settings_tile_rssi, inflater);
-        rssiTile.setOnClickListener(new View.OnClickListener() {
-            @Override
-            public void onClick(View v) {
-                Intent intent = new Intent();
-                intent.setComponent(new ComponentName(
-                        "com.android.settings",
-                        "com.android.settings.Settings$DataUsageSummaryActivity"));
-                startSettingsActivity(intent);
-            }
-        });
-        mModel.addRSSITile(rssiTile, new QuickSettingsModel.RefreshCallback() {
-            @Override
-            public void refreshView(QuickSettingsTileView view, State state) {
-                TextView tv = (TextView) view.findViewById(R.id.rssi_textview);
-                tv.setCompoundDrawablesRelativeWithIntrinsicBounds(0, state.iconId, 0, 0);
-                tv.setText(state.label);
-            }
-        });
-        parent.addView(rssiTile);
+        if (mModel.deviceSupportsTelephony()) {
+            // RSSI
+            QuickSettingsTileView rssiTile = (QuickSettingsTileView)
+                    inflater.inflate(R.layout.quick_settings_tile, parent, false);
+            rssiTile.setContent(R.layout.quick_settings_tile_rssi, inflater);
+            rssiTile.setOnClickListener(new View.OnClickListener() {
+                @Override
+                public void onClick(View v) {
+                    Intent intent = new Intent();
+                    intent.setComponent(new ComponentName(
+                            "com.android.settings",
+                            "com.android.settings.Settings$DataUsageSummaryActivity"));
+                    startSettingsActivity(intent);
+                }
+            });
+            mModel.addRSSITile(rssiTile, new QuickSettingsModel.RefreshCallback() {
+                @Override
+                public void refreshView(QuickSettingsTileView view, State state) {
+                    TextView tv = (TextView) view.findViewById(R.id.rssi_textview);
+                    tv.setCompoundDrawablesRelativeWithIntrinsicBounds(0, state.iconId, 0, 0);
+                    tv.setText(state.label);
+                }
+            });
+            parent.addView(rssiTile);
+        }
 
         // Battery
         QuickSettingsTileView batteryTile = (QuickSettingsTileView)
@@ -310,23 +316,25 @@
         parent.addView(airplaneTile);
 
         // Bluetooth
-        QuickSettingsTileView bluetoothTile = (QuickSettingsTileView)
-                inflater.inflate(R.layout.quick_settings_tile, parent, false);
-        bluetoothTile.setContent(R.layout.quick_settings_tile_bluetooth, inflater);
-        bluetoothTile.setOnClickListener(new View.OnClickListener() {
-            @Override
-            public void onClick(View v) {
-                startSettingsActivity(android.provider.Settings.ACTION_BLUETOOTH_SETTINGS);
-            }
-        });
-        mModel.addBluetoothTile(bluetoothTile, new QuickSettingsModel.RefreshCallback() {
-            @Override
-            public void refreshView(QuickSettingsTileView view, State state) {
-                TextView tv = (TextView) view.findViewById(R.id.bluetooth_textview);
-                tv.setCompoundDrawablesRelativeWithIntrinsicBounds(0, state.iconId, 0, 0);
-            }
-        });
-        parent.addView(bluetoothTile);
+        if (mModel.deviceSupportsBluetooth()) {
+            QuickSettingsTileView bluetoothTile = (QuickSettingsTileView)
+                    inflater.inflate(R.layout.quick_settings_tile, parent, false);
+            bluetoothTile.setContent(R.layout.quick_settings_tile_bluetooth, inflater);
+            bluetoothTile.setOnClickListener(new View.OnClickListener() {
+                @Override
+                public void onClick(View v) {
+                    startSettingsActivity(android.provider.Settings.ACTION_BLUETOOTH_SETTINGS);
+                }
+            });
+            mModel.addBluetoothTile(bluetoothTile, new QuickSettingsModel.RefreshCallback() {
+                @Override
+                public void refreshView(QuickSettingsTileView view, State state) {
+                    TextView tv = (TextView) view.findViewById(R.id.bluetooth_textview);
+                    tv.setCompoundDrawablesRelativeWithIntrinsicBounds(0, state.iconId, 0, 0);
+                }
+            });
+            parent.addView(bluetoothTile);
+        }
 
         // Brightness
         QuickSettingsTileView brightnessTile = (QuickSettingsTileView)
@@ -385,6 +393,33 @@
         });
         parent.addView(wifiDisplayTile);
 
+        // IME
+        QuickSettingsTileView imeTile = (QuickSettingsTileView)
+                inflater.inflate(R.layout.quick_settings_tile, parent, false);
+        imeTile.setContent(R.layout.quick_settings_tile_ime, inflater);
+        imeTile.setOnClickListener(new View.OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                try {
+                    mBar.collapseAllPanels(true);
+                    Intent intent = new Intent(Settings.ACTION_SHOW_INPUT_METHOD_PICKER);
+                    PendingIntent pendingIntent = PendingIntent.getBroadcast(mContext, 0, intent, 0);
+                    pendingIntent.send();
+                } catch (Exception e) {}
+            }
+        });
+        mModel.addImeTile(imeTile, new QuickSettingsModel.RefreshCallback() {
+            @Override
+            public void refreshView(QuickSettingsTileView view, State state) {
+                TextView tv = (TextView) view.findViewById(R.id.ime_textview);
+                if (state.label != null) {
+                    tv.setText(state.label);
+                }
+                view.setVisibility(state.enabled ? View.VISIBLE : View.GONE);
+            }
+        });
+        parent.addView(imeTile);
+
         /*
         QuickSettingsTileView mediaTile = (QuickSettingsTileView)
                 inflater.inflate(R.layout.quick_settings_tile, parent, false);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettingsModel.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettingsModel.java
index 0d7540b..a0a5282 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettingsModel.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettingsModel.java
@@ -23,18 +23,25 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
+import android.content.pm.PackageManager;
 import android.content.res.Resources;
 import android.database.ContentObserver;
 import android.hardware.display.WifiDisplayStatus;
 import android.os.Handler;
 import android.provider.Settings;
+import android.text.TextUtils;
 import android.view.View;
+import android.view.inputmethod.InputMethodInfo;
+import android.view.inputmethod.InputMethodManager;
+import android.view.inputmethod.InputMethodSubtype;
 
 import com.android.systemui.R;
 import com.android.systemui.statusbar.policy.BatteryController.BatteryStateChangeCallback;
 import com.android.systemui.statusbar.policy.LocationController.LocationGpsStateChangeCallback;
 import com.android.systemui.statusbar.policy.NetworkController.NetworkSignalChangedCallback;
 
+import java.util.List;
+
 
 class QuickSettingsModel implements BluetoothStateChangeCallback,
         NetworkSignalChangedCallback,
@@ -126,6 +133,10 @@
     private RefreshCallback mLocationCallback;
     private State mLocationState = new State();
 
+    private QuickSettingsTileView mImeTile;
+    private RefreshCallback mImeCallback;
+    private State mImeState = new State();
+
     public QuickSettingsModel(Context context) {
         mContext = context;
         mHandler = new Handler();
@@ -186,7 +197,7 @@
         // TODO: Sets the view to be "awaiting" if not already awaiting
 
         // Change the system setting
-        Settings.System.putInt(mContext.getContentResolver(), Settings.Global.AIRPLANE_MODE_ON,
+        Settings.Global.putInt(mContext.getContentResolver(), Settings.Global.AIRPLANE_MODE_ON,
                                 enabled ? 1 : 0);
 
         // Post the intent
@@ -234,20 +245,26 @@
         mRSSICallback = cb;
         mRSSICallback.refreshView(mRSSITile, mRSSIState);
     }
+    boolean deviceSupportsTelephony() {
+        PackageManager pm = mContext.getPackageManager();
+        return pm.hasSystemFeature(PackageManager.FEATURE_TELEPHONY);
+    }
     // NetworkSignalChanged callback
     @Override
     public void onMobileDataSignalChanged(boolean enabled, String description) {
-        // TODO: If view is in awaiting state, disable
-        Resources r = mContext.getResources();
-        // TODO: Check if RSSI is enabled
-        mRSSIState.enabled = enabled;
-        mRSSIState.iconId = (enabled ?
-                R.drawable.ic_qs_rssi_enabled :
-                R.drawable.ic_qs_rssi_normal);
-        mRSSIState.label = (enabled ?
-                description :
-                r.getString(R.string.quick_settings_rssi_emergency_only));
-        mRSSICallback.refreshView(mRSSITile, mRSSIState);
+        if (deviceSupportsTelephony()) {
+            // TODO: If view is in awaiting state, disable
+            Resources r = mContext.getResources();
+            // TODO: Check if RSSI is enabled
+            mRSSIState.enabled = enabled;
+            mRSSIState.iconId = (enabled ?
+                    R.drawable.ic_qs_rssi_enabled :
+                    R.drawable.ic_qs_rssi_normal);
+            mRSSIState.label = (enabled ?
+                    description :
+                    r.getString(R.string.quick_settings_rssi_emergency_only));
+            mRSSICallback.refreshView(mRSSITile, mRSSIState);
+        }
     }
 
     // Bluetooth
@@ -258,6 +275,9 @@
         final BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
         onBluetoothStateChange(adapter.isEnabled());
     }
+    boolean deviceSupportsBluetooth() {
+        return (BluetoothAdapter.getDefaultAdapter() != null);
+    }
     // BluetoothController callback
     @Override
     public void onBluetoothStateChange(boolean on) {
@@ -317,4 +337,39 @@
 
     }
 
+    // IME
+    void addImeTile(QuickSettingsTileView view, RefreshCallback cb) {
+        mImeTile = view;
+        mImeCallback = cb;
+        mImeCallback.refreshView(mImeTile, mImeState);
+    }
+    void onImeWindowStatusChanged(boolean visible) {
+        InputMethodManager imm =
+                (InputMethodManager) mContext.getSystemService(Context.INPUT_METHOD_SERVICE);
+        List<InputMethodInfo> imis = imm.getInputMethodList();
+
+        mImeState.enabled = visible;
+        mImeState.label = getCurrentInputMethodName(mContext, mContext.getContentResolver(),
+                imm, imis, mContext.getPackageManager());
+        mImeCallback.refreshView(mImeTile, mImeState);
+    }
+    private static String getCurrentInputMethodName(Context context, ContentResolver resolver,
+            InputMethodManager imm, List<InputMethodInfo> imis, PackageManager pm) {
+        if (resolver == null || imis == null) return null;
+        final String currentInputMethodId = Settings.Secure.getString(resolver,
+                Settings.Secure.DEFAULT_INPUT_METHOD);
+        if (TextUtils.isEmpty(currentInputMethodId)) return null;
+        for (InputMethodInfo imi : imis) {
+            if (currentInputMethodId.equals(imi.getId())) {
+                final InputMethodSubtype subtype = imm.getCurrentInputMethodSubtype();
+                final CharSequence summary = subtype != null
+                        ? subtype.getDisplayName(context, imi.getPackageName(),
+                                imi.getServiceInfo().applicationInfo)
+                        : context.getString(R.string.quick_settings_ime_label);
+                return summary.toString();
+            }
+        }
+        return null;
+    }
+
 }
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SettingsPanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SettingsPanelView.java
index f896d57..4a7a424 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SettingsPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SettingsPanelView.java
@@ -59,6 +59,13 @@
     }
 
     @Override
+    public void setImeWindowStatus(boolean visible) {
+        if (mQS != null) {
+            mQS.setImeWindowStatus(visible);
+        }
+    }
+
+    @Override
     public void setup(NetworkController networkController, BluetoothController bluetoothController,
             BatteryController batteryController, LocationController locationController) {
         super.setup(networkController, bluetoothController, batteryController, locationController);
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardHostView.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardHostView.java
index 863f4f8..1405260 100644
--- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardHostView.java
+++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardHostView.java
@@ -41,6 +41,8 @@
 import android.view.LayoutInflater;
 import android.view.MotionEvent;
 import android.view.View;
+import android.view.ViewGroup;
+import android.view.ViewGroup.LayoutParams;
 import android.view.WindowManager;
 import android.view.animation.AnimationUtils;
 import android.widget.RemoteViews.OnClickHandler;
@@ -54,13 +56,14 @@
 import java.util.List;
 
 public class KeyguardHostView extends KeyguardViewBase {
+    private static final String TAG = "KeyguardViewHost";
+
     // Use this to debug all of keyguard
     public static boolean DEBUG;
 
     static final int APPWIDGET_HOST_ID = 0x4B455947;
     private static final String KEYGUARD_WIDGET_PREFS = "keyguard_widget_prefs";
 
-    private static final String TAG = "KeyguardViewHost";
     private AppWidgetHost mAppWidgetHost;
     private KeyguardWidgetPager mAppWidgetContainer;
     private ViewFlipper mSecurityViewContainer;
@@ -77,6 +80,12 @@
     private KeyguardSecurityModel mSecurityModel;
 
     private Rect mTempRect = new Rect();
+    private KeyguardTransportControlView mTransportControl;
+
+    /*package*/ interface TransportCallback {
+        void hide();
+        void show();
+    }
 
     public KeyguardHostView(Context context) {
         this(context, null);
@@ -111,11 +120,59 @@
         mViewMediatorCallback.keyguardDoneDrawing();
     }
 
+    private int getWidgetPosition(int id) {
+        final int children = mAppWidgetContainer.getChildCount();
+        for (int i = 0; i < children; i++) {
+            if (mAppWidgetContainer.getChildAt(i).getId() == id) {
+                return i;
+            }
+        }
+        return -1;
+    }
+
     @Override
     protected void onFinishInflate() {
         mAppWidgetContainer = (KeyguardWidgetPager) findViewById(R.id.app_widget_container);
         mAppWidgetContainer.setVisibility(VISIBLE);
         mSecurityViewContainer = (ViewFlipper) findViewById(R.id.view_flipper);
+
+        // This code manages showing/hiding the transport control. We keep it around and only
+        // add it to the hierarchy if it needs to be present.
+        mTransportControl =
+                (KeyguardTransportControlView) findViewById(R.id.keyguard_transport_control);
+        if (mTransportControl != null) {
+            mTransportControl.setKeyguardCallback(new TransportCallback() {
+                boolean mSticky = false;
+                @Override
+                public void hide() {
+                    int page = getWidgetPosition(R.id.keyguard_transport_control);
+                    if (page != -1 && !mSticky) {
+                        if (page == mAppWidgetContainer.getCurrentPage()) {
+                            // Switch back to clock view if music was showing.
+                            mAppWidgetContainer
+                                .setCurrentPage(getWidgetPosition(R.id.keyguard_status_view));
+                        }
+                        mAppWidgetContainer.removeView(mTransportControl);
+                        // XXX keep view attached to hierarchy so we still get show/hide events
+                        // from AudioManager
+                        KeyguardHostView.this.addView(mTransportControl);
+                        mTransportControl.setVisibility(View.GONE);
+                    }
+                }
+
+                @Override
+                public void show() {
+                    if (getWidgetPosition(R.id.keyguard_transport_control) == -1) {
+                        KeyguardHostView.this.removeView(mTransportControl);
+                        mAppWidgetContainer.addView(mTransportControl,
+                                getWidgetPosition(R.id.keyguard_status_view) + 1);
+                        mTransportControl.setVisibility(View.VISIBLE);
+                        // Once shown, leave it showing
+                        mSticky = true;
+                    }
+                }
+            });
+        }
         updateSecurityViews();
     }
 
@@ -214,6 +271,17 @@
         dialog.show();
     }
 
+    @Override
+    public boolean dispatchKeyEvent(KeyEvent event) {
+        if (event.getAction() == KeyEvent.ACTION_UP
+                && event.getKeyCode() == KeyEvent.KEYCODE_BACK
+                && mCurrentSecuritySelection != SecurityMode.None) {
+            mCallback.dismiss(false);
+            return true;
+        }
+        return super.dispatchKeyEvent(event);
+    }
+
     private void showTimeoutDialog() {
         int timeoutInSeconds = (int) LockPatternUtils.FAILED_ATTEMPT_TIMEOUT_MS / 1000;
         int messageId = 0;
@@ -412,6 +480,7 @@
     @Override
     public void reset() {
         mIsVerifyUnlockOnly = false;
+        mAppWidgetContainer.setCurrentPage(getWidgetPosition(R.id.keyguard_status_view));
         requestFocus();
     }
 
@@ -496,6 +565,11 @@
         if (DEBUG) Log.d(TAG, "screen on");
         showSecurityScreen(mCurrentSecuritySelection);
         getSecurityView(mCurrentSecuritySelection).onResume();
+
+        // This is a an attempt to fix bug 7137389 where the device comes back on but the entire
+        // layout is blank but forcing a layout causes it to reappear (e.g. with with
+        // hierarchyviewer).
+        requestLayout();
     }
 
     @Override
@@ -623,8 +697,8 @@
             KeyguardWidgetFrame userswitcher = (KeyguardWidgetFrame)
                 LayoutInflater.from(mContext).inflate(R.layout.keyguard_multi_user_selector_widget,
                         mAppWidgetContainer, false);
-            // add the switcher in the first position
-            mAppWidgetContainer.addView(userswitcher, 0);
+            // add the switcher to the left of status view
+            mAppWidgetContainer.addView(userswitcher, getWidgetPosition(R.id.keyguard_status_view));
         }
     }
 
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardTransportControlView.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardTransportControlView.java
new file mode 100644
index 0000000..3b4ed13
--- /dev/null
+++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardTransportControlView.java
@@ -0,0 +1,534 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.policy.impl.keyguard;
+
+import java.lang.ref.WeakReference;
+
+import android.app.PendingIntent;
+import android.app.PendingIntent.CanceledException;
+import android.content.Context;
+import android.content.Intent;
+import android.graphics.Bitmap;
+import android.media.AudioManager;
+import android.media.MediaMetadataRetriever;
+import android.media.RemoteControlClient;
+import android.media.IRemoteControlDisplay;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Message;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.os.RemoteException;
+import android.os.SystemClock;
+import android.text.Spannable;
+import android.text.TextUtils;
+import android.text.style.ForegroundColorSpan;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.KeyEvent;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.widget.FrameLayout;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+
+import com.android.internal.R;
+import com.android.internal.policy.impl.keyguard.KeyguardHostView.TransportCallback;
+
+/**
+ * This is the widget responsible for showing music controls in keyguard.
+ */
+public class KeyguardTransportControlView extends KeyguardWidgetFrame implements OnClickListener {
+
+    private static final int MSG_UPDATE_STATE = 100;
+    private static final int MSG_SET_METADATA = 101;
+    private static final int MSG_SET_TRANSPORT_CONTROLS = 102;
+    private static final int MSG_SET_ARTWORK = 103;
+    private static final int MSG_SET_GENERATION_ID = 104;
+    private static final int MAXDIM = 512;
+    private static final int DISPLAY_TIMEOUT_MS = 5000; // 5s
+    protected static final boolean DEBUG = false;
+    protected static final String TAG = "TransportControlView";
+
+    private ImageView mAlbumArt;
+    private TextView mTrackTitle;
+    private ImageView mBtnPrev;
+    private ImageView mBtnPlay;
+    private ImageView mBtnNext;
+    private int mClientGeneration;
+    private Metadata mMetadata = new Metadata();
+    private boolean mAttached;
+    private PendingIntent mClientIntent;
+    private int mTransportControlFlags;
+    private int mCurrentPlayState;
+    private AudioManager mAudioManager;
+    private IRemoteControlDisplayWeak mIRCD;
+
+    /**
+     * The metadata which should be populated into the view once we've been attached
+     */
+    private Bundle mPopulateMetadataWhenAttached = null;
+
+    // This handler is required to ensure messages from IRCD are handled in sequence and on
+    // the UI thread.
+    private Handler mHandler = new Handler() {
+        @Override
+        public void handleMessage(Message msg) {
+            switch (msg.what) {
+            case MSG_UPDATE_STATE:
+                if (mClientGeneration == msg.arg1) updatePlayPauseState(msg.arg2);
+                break;
+
+            case MSG_SET_METADATA:
+                if (mClientGeneration == msg.arg1) updateMetadata((Bundle) msg.obj);
+                break;
+
+            case MSG_SET_TRANSPORT_CONTROLS:
+                if (mClientGeneration == msg.arg1) updateTransportControls(msg.arg2);
+                break;
+
+            case MSG_SET_ARTWORK:
+                if (mClientGeneration == msg.arg1) {
+                    if (mMetadata.bitmap != null) {
+                        mMetadata.bitmap.recycle();
+                    }
+                    mMetadata.bitmap = (Bitmap) msg.obj;
+                    mAlbumArt.setImageBitmap(mMetadata.bitmap);
+                }
+                break;
+
+            case MSG_SET_GENERATION_ID:
+                if (msg.arg2 != 0) {
+                    // This means nobody is currently registered. Hide the view.
+                    hide();
+                }
+                if (DEBUG) Log.v(TAG, "New genId = " + msg.arg1 + ", clearing = " + msg.arg2);
+                mClientGeneration = msg.arg1;
+                mClientIntent = (PendingIntent) msg.obj;
+                break;
+
+            }
+        }
+    };
+    private TransportCallback mTransportCallback;
+
+    /**
+     * This class is required to have weak linkage to the current TransportControlView
+     * because the remote process can hold a strong reference to this binder object and
+     * we can't predict when it will be GC'd in the remote process. Without this code, it
+     * would allow a heavyweight object to be held on this side of the binder when there's
+     * no requirement to run a GC on the other side.
+     */
+    private static class IRemoteControlDisplayWeak extends IRemoteControlDisplay.Stub {
+        private WeakReference<Handler> mLocalHandler;
+
+        IRemoteControlDisplayWeak(Handler handler) {
+            mLocalHandler = new WeakReference<Handler>(handler);
+        }
+
+        public void setPlaybackState(int generationId, int state, long stateChangeTimeMs) {
+            Handler handler = mLocalHandler.get();
+            if (handler != null) {
+                handler.obtainMessage(MSG_UPDATE_STATE, generationId, state).sendToTarget();
+            }
+        }
+
+        public void setMetadata(int generationId, Bundle metadata) {
+            Handler handler = mLocalHandler.get();
+            if (handler != null) {
+                handler.obtainMessage(MSG_SET_METADATA, generationId, 0, metadata).sendToTarget();
+            }
+        }
+
+        public void setTransportControlFlags(int generationId, int flags) {
+            Handler handler = mLocalHandler.get();
+            if (handler != null) {
+                handler.obtainMessage(MSG_SET_TRANSPORT_CONTROLS, generationId, flags)
+                        .sendToTarget();
+            }
+        }
+
+        public void setArtwork(int generationId, Bitmap bitmap) {
+            Handler handler = mLocalHandler.get();
+            if (handler != null) {
+                handler.obtainMessage(MSG_SET_ARTWORK, generationId, 0, bitmap).sendToTarget();
+            }
+        }
+
+        public void setAllMetadata(int generationId, Bundle metadata, Bitmap bitmap) {
+            Handler handler = mLocalHandler.get();
+            if (handler != null) {
+                handler.obtainMessage(MSG_SET_METADATA, generationId, 0, metadata).sendToTarget();
+                handler.obtainMessage(MSG_SET_ARTWORK, generationId, 0, bitmap).sendToTarget();
+            }
+        }
+
+        public void setCurrentClientId(int clientGeneration, PendingIntent mediaIntent,
+                boolean clearing) throws RemoteException {
+            Handler handler = mLocalHandler.get();
+            if (handler != null) {
+                handler.obtainMessage(MSG_SET_GENERATION_ID,
+                    clientGeneration, (clearing ? 1 : 0), mediaIntent).sendToTarget();
+            }
+        }
+    };
+
+    public KeyguardTransportControlView(Context context, AttributeSet attrs) {
+        super(context, attrs);
+        Log.v(TAG, "Create TCV " + this);
+        mAudioManager = new AudioManager(mContext);
+        mCurrentPlayState = RemoteControlClient.PLAYSTATE_NONE; // until we get a callback
+        mIRCD = new IRemoteControlDisplayWeak(mHandler);
+    }
+
+    protected void hide() {
+        if (DEBUG) Log.v(TAG, "Transport was told to hide");
+        if (mTransportCallback != null) {
+            mTransportCallback.hide();
+        } else {
+            Log.w(TAG, "Hide music, but callback wasn't set");
+        }
+    }
+
+    private void show() {
+        if (DEBUG) Log.v(TAG, "Transport was told to show");
+        if (mTransportCallback != null) {
+            mTransportCallback.show();
+        } else {
+            Log.w(TAG, "Show music, but callback wasn't set");
+        }
+    }
+
+    private void userActivity() {
+        // TODO Auto-generated method stub
+    }
+
+    private void updateTransportControls(int transportControlFlags) {
+        mTransportControlFlags = transportControlFlags;
+    }
+
+    @Override
+    public void onFinishInflate() {
+        super.onFinishInflate();
+        mTrackTitle = (TextView) findViewById(R.id.title);
+        mTrackTitle.setSelected(true); // enable marquee
+        mAlbumArt = (ImageView) findViewById(R.id.albumart);
+        mBtnPrev = (ImageView) findViewById(R.id.btn_prev);
+        mBtnPlay = (ImageView) findViewById(R.id.btn_play);
+        mBtnNext = (ImageView) findViewById(R.id.btn_next);
+        final View buttons[] = { mBtnPrev, mBtnPlay, mBtnNext };
+        for (View view : buttons) {
+            view.setOnClickListener(this);
+        }
+    }
+
+    @Override
+    public void onAttachedToWindow() {
+        super.onAttachedToWindow();
+        if (DEBUG) Log.v(TAG, "onAttachToWindow()");
+        if (mPopulateMetadataWhenAttached != null) {
+            updateMetadata(mPopulateMetadataWhenAttached);
+            mPopulateMetadataWhenAttached = null;
+        }
+        if (!mAttached) {
+            if (DEBUG) Log.v(TAG, "Registering TCV " + this);
+            mAudioManager.registerRemoteControlDisplay(mIRCD);
+        }
+        mAttached = true;
+    }
+
+    @Override
+    public void onDetachedFromWindow() {
+        if (DEBUG) Log.v(TAG, "onDetachFromWindow()");
+        super.onDetachedFromWindow();
+        if (mAttached) {
+            if (DEBUG) Log.v(TAG, "Unregistering TCV " + this);
+            mAudioManager.unregisterRemoteControlDisplay(mIRCD);
+        }
+        mAttached = false;
+    }
+
+    @Override
+    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+        int dim = Math.min(MAXDIM, Math.max(getWidth(), getHeight()));
+//        Log.v(TAG, "setting max bitmap size: " + dim + "x" + dim);
+//        mAudioManager.remoteControlDisplayUsesBitmapSize(mIRCD, dim, dim);
+    }
+
+    class Metadata {
+        private String artist;
+        private String trackTitle;
+        private String albumTitle;
+        private Bitmap bitmap;
+
+        public String toString() {
+            return "Metadata[artist=" + artist + " trackTitle=" + trackTitle + " albumTitle=" + albumTitle + "]";
+        }
+    }
+
+    private String getMdString(Bundle data, int id) {
+        return data.getString(Integer.toString(id));
+    }
+
+    private void updateMetadata(Bundle data) {
+        if (mAttached) {
+            mMetadata.artist = getMdString(data, MediaMetadataRetriever.METADATA_KEY_ALBUMARTIST);
+            mMetadata.trackTitle = getMdString(data, MediaMetadataRetriever.METADATA_KEY_TITLE);
+            mMetadata.albumTitle = getMdString(data, MediaMetadataRetriever.METADATA_KEY_ALBUM);
+            populateMetadata();
+        } else {
+            mPopulateMetadataWhenAttached = data;
+        }
+    }
+
+    /**
+     * Populates the given metadata into the view
+     */
+    private void populateMetadata() {
+        StringBuilder sb = new StringBuilder();
+        int trackTitleLength = 0;
+        if (!TextUtils.isEmpty(mMetadata.trackTitle)) {
+            sb.append(mMetadata.trackTitle);
+            trackTitleLength = mMetadata.trackTitle.length();
+        }
+        if (!TextUtils.isEmpty(mMetadata.artist)) {
+            if (sb.length() != 0) {
+                sb.append(" - ");
+            }
+            sb.append(mMetadata.artist);
+        }
+        if (!TextUtils.isEmpty(mMetadata.albumTitle)) {
+            if (sb.length() != 0) {
+                sb.append(" - ");
+            }
+            sb.append(mMetadata.albumTitle);
+        }
+        mTrackTitle.setText(sb.toString(), TextView.BufferType.SPANNABLE);
+        Spannable str = (Spannable) mTrackTitle.getText();
+        if (trackTitleLength != 0) {
+            str.setSpan(new ForegroundColorSpan(0xffffffff), 0, trackTitleLength,
+                    Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+            trackTitleLength++;
+        }
+        if (sb.length() > trackTitleLength) {
+            str.setSpan(new ForegroundColorSpan(0x7fffffff), trackTitleLength, sb.length(),
+                    Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+        }
+
+        mAlbumArt.setImageBitmap(mMetadata.bitmap);
+        final int flags = mTransportControlFlags;
+        setVisibilityBasedOnFlag(mBtnPrev, flags, RemoteControlClient.FLAG_KEY_MEDIA_PREVIOUS);
+        setVisibilityBasedOnFlag(mBtnNext, flags, RemoteControlClient.FLAG_KEY_MEDIA_NEXT);
+        setVisibilityBasedOnFlag(mBtnPlay, flags,
+                RemoteControlClient.FLAG_KEY_MEDIA_PLAY
+                | RemoteControlClient.FLAG_KEY_MEDIA_PAUSE
+                | RemoteControlClient.FLAG_KEY_MEDIA_PLAY_PAUSE
+                | RemoteControlClient.FLAG_KEY_MEDIA_STOP);
+
+        updatePlayPauseState(mCurrentPlayState);
+    }
+
+    private static void setVisibilityBasedOnFlag(View view, int flags, int flag) {
+        if ((flags & flag) != 0) {
+            view.setVisibility(View.VISIBLE);
+        } else {
+            view.setVisibility(View.GONE);
+        }
+    }
+
+    private void updatePlayPauseState(int state) {
+        if (DEBUG) Log.v(TAG,
+                "updatePlayPauseState(), old=" + mCurrentPlayState + ", state=" + state);
+        if (state == mCurrentPlayState) {
+            return;
+        }
+        final int imageResId;
+        final int imageDescId;
+        boolean showIfHidden = false;
+        switch (state) {
+            case RemoteControlClient.PLAYSTATE_ERROR:
+                imageResId = com.android.internal.R.drawable.stat_sys_warning;
+                // TODO use more specific image description string for warning, but here the "play"
+                //      message is still valid because this button triggers a play command.
+                imageDescId = com.android.internal.R.string.lockscreen_transport_play_description;
+                break;
+
+            case RemoteControlClient.PLAYSTATE_PLAYING:
+                imageResId = com.android.internal.R.drawable.ic_media_pause;
+                imageDescId = com.android.internal.R.string.lockscreen_transport_pause_description;
+                showIfHidden = true;
+                break;
+
+            case RemoteControlClient.PLAYSTATE_BUFFERING:
+                imageResId = com.android.internal.R.drawable.ic_media_stop;
+                imageDescId = com.android.internal.R.string.lockscreen_transport_stop_description;
+                showIfHidden = true;
+                break;
+
+            case RemoteControlClient.PLAYSTATE_PAUSED:
+            default:
+                imageResId = com.android.internal.R.drawable.ic_media_play;
+                imageDescId = com.android.internal.R.string.lockscreen_transport_play_description;
+                showIfHidden = false;
+                break;
+        }
+        mBtnPlay.setImageResource(imageResId);
+        mBtnPlay.setContentDescription(getResources().getString(imageDescId));
+        if (showIfHidden) {
+            show();
+        }
+        mCurrentPlayState = state;
+    }
+
+    static class SavedState extends BaseSavedState {
+        boolean wasShowing;
+
+        SavedState(Parcelable superState) {
+            super(superState);
+        }
+
+        private SavedState(Parcel in) {
+            super(in);
+            this.wasShowing = in.readInt() != 0;
+        }
+
+        @Override
+        public void writeToParcel(Parcel out, int flags) {
+            super.writeToParcel(out, flags);
+            out.writeInt(this.wasShowing ? 1 : 0);
+        }
+
+        public static final Parcelable.Creator<SavedState> CREATOR
+                = new Parcelable.Creator<SavedState>() {
+            public SavedState createFromParcel(Parcel in) {
+                return new SavedState(in);
+            }
+
+            public SavedState[] newArray(int size) {
+                return new SavedState[size];
+            }
+        };
+    }
+
+    @Override
+    public Parcelable onSaveInstanceState() {
+        if (DEBUG) Log.v(TAG, "onSaveInstanceState()");
+        Parcelable superState = super.onSaveInstanceState();
+        SavedState ss = new SavedState(superState);
+        ss.wasShowing = getVisibility() == View.VISIBLE;
+        return ss;
+    }
+
+    @Override
+    public void onRestoreInstanceState(Parcelable state) {
+        if (DEBUG) Log.v(TAG, "onRestoreInstanceState()");
+        if (!(state instanceof SavedState)) {
+            super.onRestoreInstanceState(state);
+            return;
+        }
+        SavedState ss = (SavedState) state;
+        super.onRestoreInstanceState(ss.getSuperState());
+        if (ss.wasShowing) {
+            show();
+        }
+    }
+
+    public void onClick(View v) {
+        int keyCode = -1;
+        if (v == mBtnPrev) {
+            keyCode = KeyEvent.KEYCODE_MEDIA_PREVIOUS;
+        } else if (v == mBtnNext) {
+            keyCode = KeyEvent.KEYCODE_MEDIA_NEXT;
+        } else if (v == mBtnPlay) {
+            keyCode = KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE;
+
+        }
+        if (keyCode != -1) {
+            sendMediaButtonClick(keyCode);
+            userActivity();
+        }
+    }
+
+    private void sendMediaButtonClick(int keyCode) {
+        if (mClientIntent == null) {
+            // Shouldn't be possible because this view should be hidden in this case.
+            Log.e(TAG, "sendMediaButtonClick(): No client is currently registered");
+            return;
+        }
+        // use the registered PendingIntent that will be processed by the registered
+        //    media button event receiver, which is the component of mClientIntent
+        KeyEvent keyEvent = new KeyEvent(KeyEvent.ACTION_DOWN, keyCode);
+        Intent intent = new Intent(Intent.ACTION_MEDIA_BUTTON);
+        intent.putExtra(Intent.EXTRA_KEY_EVENT, keyEvent);
+        try {
+            mClientIntent.send(getContext(), 0, intent);
+        } catch (CanceledException e) {
+            Log.e(TAG, "Error sending intent for media button down: "+e);
+            e.printStackTrace();
+        }
+
+        keyEvent = new KeyEvent(KeyEvent.ACTION_UP, keyCode);
+        intent = new Intent(Intent.ACTION_MEDIA_BUTTON);
+        intent.putExtra(Intent.EXTRA_KEY_EVENT, keyEvent);
+        try {
+            mClientIntent.send(getContext(), 0, intent);
+        } catch (CanceledException e) {
+            Log.e(TAG, "Error sending intent for media button up: "+e);
+            e.printStackTrace();
+        }
+    }
+
+    public boolean providesClock() {
+        return false;
+    }
+
+    private boolean wasPlayingRecently(int state, long stateChangeTimeMs) {
+        switch (state) {
+            case RemoteControlClient.PLAYSTATE_PLAYING:
+            case RemoteControlClient.PLAYSTATE_FAST_FORWARDING:
+            case RemoteControlClient.PLAYSTATE_REWINDING:
+            case RemoteControlClient.PLAYSTATE_SKIPPING_FORWARDS:
+            case RemoteControlClient.PLAYSTATE_SKIPPING_BACKWARDS:
+            case RemoteControlClient.PLAYSTATE_BUFFERING:
+                // actively playing or about to play
+                return true;
+            case RemoteControlClient.PLAYSTATE_NONE:
+                return false;
+            case RemoteControlClient.PLAYSTATE_STOPPED:
+            case RemoteControlClient.PLAYSTATE_PAUSED:
+            case RemoteControlClient.PLAYSTATE_ERROR:
+                // we have stopped playing, check how long ago
+                if (DEBUG) {
+                    if ((SystemClock.elapsedRealtime() - stateChangeTimeMs) < DISPLAY_TIMEOUT_MS) {
+                        Log.v(TAG, "wasPlayingRecently: time < TIMEOUT was playing recently");
+                    } else {
+                        Log.v(TAG, "wasPlayingRecently: time > TIMEOUT");
+                    }
+                }
+                return ((SystemClock.elapsedRealtime() - stateChangeTimeMs) < DISPLAY_TIMEOUT_MS);
+            default:
+                Log.e(TAG, "Unknown playback state " + state + " in wasPlayingRecently()");
+                return false;
+        }
+    }
+
+    public void setKeyguardCallback(TransportCallback transportCallback) {
+        mTransportCallback = transportCallback;
+    }
+}
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardViewManager.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardViewManager.java
index 61003bf..d9088e0 100644
--- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardViewManager.java
+++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardViewManager.java
@@ -214,9 +214,9 @@
      */
     public synchronized void reset() {
         if (DEBUG) Log.d(TAG, "reset()");
-        if (mKeyguardView != null) {
-            mKeyguardView.reset();
-        }
+        // User might have switched, check if we need to go back to keyguard
+        // TODO: It's preferable to stay and show the correct lockscreen or unlock if none
+        maybeCreateKeyguardLocked(shouldEnableScreenRotation());
     }
 
     public synchronized void onScreenTurnedOff() {
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/PagedView.java b/policy/src/com/android/internal/policy/impl/keyguard/PagedView.java
index 1b46efa..fc7c90f 100644
--- a/policy/src/com/android/internal/policy/impl/keyguard/PagedView.java
+++ b/policy/src/com/android/internal/policy/impl/keyguard/PagedView.java
@@ -595,6 +595,10 @@
 
     @Override
     public void onChildViewRemoved(View parent, View child) {
+        invalidate();
+        invalidateCachedOffsets();
+        // This prevents a crash when a child is removed that was the current page.
+        mCurrentPage = Math.min(mCurrentPage, getChildCount() - 1);
     }
 
     protected void invalidateCachedOffsets() {
diff --git a/services/common_time/common_clock_service.h b/services/common_time/common_clock_service.h
index a65e398..bd663f0 100644
--- a/services/common_time/common_clock_service.h
+++ b/services/common_time/common_clock_service.h
@@ -14,11 +14,12 @@
  * limitations under the License.
  */
 
-#include <common_time/ICommonClock.h>
-
 #ifndef ANDROID_COMMON_CLOCK_SERVICE_H
 #define ANDROID_COMMON_CLOCK_SERVICE_H
 
+#include <sys/socket.h>
+#include <common_time/ICommonClock.h>
+
 namespace android {
 
 class CommonTimeServer;
diff --git a/services/common_time/common_time_config_service.h b/services/common_time/common_time_config_service.h
index 8283c24..89806dd 100644
--- a/services/common_time/common_time_config_service.h
+++ b/services/common_time/common_time_config_service.h
@@ -13,11 +13,12 @@
  * limitations under the License.
  */
 
-#include <common_time/ICommonTimeConfig.h>
-
 #ifndef ANDROID_COMMON_TIME_CONFIG_SERVICE_H
 #define ANDROID_COMMON_TIME_CONFIG_SERVICE_H
 
+#include <sys/socket.h>
+#include <common_time/ICommonTimeConfig.h>
+
 namespace android {
 
 class String16;
diff --git a/services/common_time/common_time_server.h b/services/common_time/common_time_server.h
index f6a2419..6e18050 100644
--- a/services/common_time/common_time_server.h
+++ b/services/common_time/common_time_server.h
@@ -19,7 +19,7 @@
 
 #include <arpa/inet.h>
 #include <stdint.h>
-#include <linux/socket.h>
+#include <sys/socket.h>
 
 #include <common_time/ICommonClock.h>
 #include <common_time/local_clock.h>
diff --git a/services/java/com/android/server/AppWidgetService.java b/services/java/com/android/server/AppWidgetService.java
index 343e70d..385681e 100644
--- a/services/java/com/android/server/AppWidgetService.java
+++ b/services/java/com/android/server/AppWidgetService.java
@@ -215,15 +215,18 @@
     }
 
     @Override
-    public void bindAppWidgetId(int appWidgetId, ComponentName provider) throws RemoteException {
-        getImplForUser(UserHandle.getCallingUserId()).bindAppWidgetId(appWidgetId, provider);
+    public void bindAppWidgetId(int appWidgetId, ComponentName provider, Bundle options)
+            throws RemoteException {
+        getImplForUser(UserHandle.getCallingUserId()).bindAppWidgetId(appWidgetId, provider,
+                options);
     }
 
     @Override
     public boolean bindAppWidgetIdIfAllowed(
-            String packageName, int appWidgetId, ComponentName provider) throws RemoteException {
+            String packageName, int appWidgetId, ComponentName provider, Bundle options)
+                    throws RemoteException {
         return getImplForUser(UserHandle.getCallingUserId()).bindAppWidgetIdIfAllowed(
-                packageName, appWidgetId, provider);
+                packageName, appWidgetId, provider, options);
     }
 
     @Override
diff --git a/services/java/com/android/server/AppWidgetServiceImpl.java b/services/java/com/android/server/AppWidgetServiceImpl.java
index 57ab921..95d7623 100644
--- a/services/java/com/android/server/AppWidgetServiceImpl.java
+++ b/services/java/com/android/server/AppWidgetServiceImpl.java
@@ -316,6 +316,8 @@
                 pw.print(info.updatePeriodMillis);
                 pw.print(" resizeMode=");
                 pw.print(info.resizeMode);
+                pw.print(info.widgetCategory);
+                pw.print(info.widgetFeatures);
                 pw.print(" autoAdvanceViewId=");
                 pw.print(info.autoAdvanceViewId);
                 pw.print(" initialLayout=#");
@@ -527,7 +529,7 @@
         }
     }
 
-    private void bindAppWidgetIdImpl(int appWidgetId, ComponentName provider) {
+    private void bindAppWidgetIdImpl(int appWidgetId, ComponentName provider, Bundle options) {
         final long ident = Binder.clearCallingIdentity();
         try {
             synchronized (mAppWidgetIds) {
@@ -550,6 +552,17 @@
                 }
 
                 id.provider = p;
+                if (options == null) {
+                    options = new Bundle();
+                }
+                id.options = options;
+
+                // We need to provide a default value for the widget category if it is not specified
+                if (!options.containsKey(AppWidgetManager.OPTION_APPWIDGET_HOST_CATEGORY)) {
+                    options.putInt(AppWidgetManager.OPTION_APPWIDGET_HOST_CATEGORY,
+                            AppWidgetProviderInfo.WIDGET_CATEGORY_HOME_SCREEN);
+                }
+
                 p.instances.add(id);
                 int instancesSize = p.instances.size();
                 if (instancesSize == 1) {
@@ -572,14 +585,14 @@
         }
     }
 
-    public void bindAppWidgetId(int appWidgetId, ComponentName provider) {
+    public void bindAppWidgetId(int appWidgetId, ComponentName provider, Bundle options) {
         mContext.enforceCallingPermission(android.Manifest.permission.BIND_APPWIDGET,
             "bindAppWidgetId appWidgetId=" + appWidgetId + " provider=" + provider);
-        bindAppWidgetIdImpl(appWidgetId, provider);
+        bindAppWidgetIdImpl(appWidgetId, provider, options);
     }
 
     public boolean bindAppWidgetIdIfAllowed(
-            String packageName, int appWidgetId, ComponentName provider) {
+            String packageName, int appWidgetId, ComponentName provider, Bundle options) {
         try {
             mContext.enforceCallingPermission(android.Manifest.permission.BIND_APPWIDGET, null);
         } catch (SecurityException se) {
@@ -587,7 +600,7 @@
                 return false;
             }
         }
-        bindAppWidgetIdImpl(appWidgetId, provider);
+        bindAppWidgetIdImpl(appWidgetId, provider, options);
         return true;
     }
 
@@ -873,15 +886,18 @@
             if (id == null) {
                 return;
             }
+
             Provider p = id.provider;
-            id.options = options;
+            // Merge the options
+            id.options.putAll(options);
 
             // send the broacast saying that this appWidgetId has been deleted
             Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_OPTIONS_CHANGED);
             intent.setComponent(p.info.provider);
             intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, id.appWidgetId);
-            intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_OPTIONS, options);
+            intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_OPTIONS, id.options);
             mContext.sendBroadcastAsUser(intent, new UserHandle(mUserId));
+            saveStateLocked();
         }
     }
 
@@ -1325,6 +1341,8 @@
                     com.android.internal.R.styleable.AppWidgetProviderInfo_updatePeriodMillis, 0);
             info.initialLayout = sa.getResourceId(
                     com.android.internal.R.styleable.AppWidgetProviderInfo_initialLayout, 0);
+            info.initialKeyguardLayout = sa.getResourceId(com.android.internal.R.styleable.
+                    AppWidgetProviderInfo_initialKeyguardLayout, 0);
             String className = sa
                     .getString(com.android.internal.R.styleable.AppWidgetProviderInfo_configure);
             if (className != null) {
@@ -1339,6 +1357,12 @@
             info.resizeMode = sa.getInt(
                     com.android.internal.R.styleable.AppWidgetProviderInfo_resizeMode,
                     AppWidgetProviderInfo.RESIZE_NONE);
+            info.widgetCategory = sa.getInt(
+                    com.android.internal.R.styleable.AppWidgetProviderInfo_resizeMode,
+                    AppWidgetProviderInfo.WIDGET_CATEGORY_HOME_SCREEN);
+            info.widgetFeatures = sa.getInt(
+                    com.android.internal.R.styleable.AppWidgetProviderInfo_resizeMode,
+                    AppWidgetProviderInfo.WIDGET_FEATURES_NONE);
 
             sa.recycle();
         } catch (Exception e) {
@@ -1476,6 +1500,18 @@
                 if (id.provider != null) {
                     out.attribute(null, "p", Integer.toHexString(id.provider.tag));
                 }
+                if (id.options != null) {
+                    out.attribute(null, "min_width", Integer.toHexString(id.options.getInt(
+                            AppWidgetManager.OPTION_APPWIDGET_MIN_WIDTH)));
+                    out.attribute(null, "min_height", Integer.toHexString(id.options.getInt(
+                            AppWidgetManager.OPTION_APPWIDGET_MIN_HEIGHT)));
+                    out.attribute(null, "max_width", Integer.toHexString(id.options.getInt(
+                            AppWidgetManager.OPTION_APPWIDGET_MAX_WIDTH)));
+                    out.attribute(null, "max_height", Integer.toHexString(id.options.getInt(
+                            AppWidgetManager.OPTION_APPWIDGET_MAX_HEIGHT)));
+                    out.attribute(null, "host_category", Integer.toHexString(id.options.getInt(
+                            AppWidgetManager.OPTION_APPWIDGET_HOST_CATEGORY)));
+                }
                 out.endTag(null, "g");
             }
 
@@ -1496,6 +1532,7 @@
         }
     }
 
+    @SuppressWarnings("unused")
     void readStateFromFileLocked(FileInputStream stream) {
         boolean success = false;
         try {
@@ -1569,6 +1606,34 @@
                             mNextAppWidgetId = id.appWidgetId + 1;
                         }
 
+                        Bundle options = new Bundle();
+                        String minWidthString = parser.getAttributeValue(null, "min_width");
+                        if (minWidthString != null) {
+                            options.putInt(AppWidgetManager.OPTION_APPWIDGET_MIN_WIDTH,
+                                    Integer.parseInt(minWidthString, 16));
+                        }
+                        String minHeightString = parser.getAttributeValue(null, "min_height");
+                        if (minWidthString != null) {
+                            options.putInt(AppWidgetManager.OPTION_APPWIDGET_MIN_HEIGHT,
+                                    Integer.parseInt(minHeightString, 16));
+                        }
+                        String maxWidthString = parser.getAttributeValue(null, "max_height");
+                        if (minWidthString != null) {
+                            options.putInt(AppWidgetManager.OPTION_APPWIDGET_MAX_WIDTH,
+                                    Integer.parseInt(maxWidthString, 16));
+                        }
+                        String maxHeightString = parser.getAttributeValue(null, "max_height");
+                        if (minWidthString != null) {
+                            options.putInt(AppWidgetManager.OPTION_APPWIDGET_MAX_HEIGHT,
+                                    Integer.parseInt(maxHeightString, 16));
+                        }
+                        String categoryString = parser.getAttributeValue(null, "host_category");
+                        if (minWidthString != null) {
+                            options.putInt(AppWidgetManager.OPTION_APPWIDGET_HOST_CATEGORY,
+                                    Integer.parseInt(categoryString, 16));
+                        }
+                        id.options = options;
+
                         String providerString = parser.getAttributeValue(null, "p");
                         if (providerString != null) {
                             // there's no provider if it hasn't been bound yet.
diff --git a/services/java/com/android/server/BackupManagerService.java b/services/java/com/android/server/BackupManagerService.java
index 1d40f4f..5e2b425 100644
--- a/services/java/com/android/server/BackupManagerService.java
+++ b/services/java/com/android/server/BackupManagerService.java
@@ -67,6 +67,7 @@
 import android.os.SystemClock;
 import android.os.UserHandle;
 import android.os.WorkSource;
+import android.os.Environment.UserEnvironment;
 import android.os.storage.IMountService;
 import android.provider.Settings;
 import android.util.EventLog;
@@ -2720,9 +2721,13 @@
             FullBackup.backupToTar(pkg.packageName, FullBackup.APK_TREE_TOKEN, null,
                     apkDir, appSourceDir, output);
 
+            // TODO: migrate this to SharedStorageBackup, since AID_SYSTEM
+            // doesn't have access to external storage.
+
             // Save associated .obb content if it exists and we did save the apk
             // check for .obb and save those too
-            final File obbDir = Environment.getExternalStorageAppObbDirectory(pkg.packageName);
+            final UserEnvironment userEnv = new UserEnvironment(UserHandle.USER_OWNER);
+            final File obbDir = userEnv.getExternalStorageAppObbDirectory(pkg.packageName);
             if (obbDir != null) {
                 if (MORE_DEBUG) Log.i(TAG, "obb dir: " + obbDir.getAbsolutePath());
                 File[] obbFiles = obbDir.listFiles();
diff --git a/services/java/com/android/server/MountService.java b/services/java/com/android/server/MountService.java
index f40333d..32ab154 100644
--- a/services/java/com/android/server/MountService.java
+++ b/services/java/com/android/server/MountService.java
@@ -16,11 +16,7 @@
 
 package com.android.server;
 
-import com.android.internal.app.IMediaContainerService;
-import com.android.internal.util.XmlUtils;
-import com.android.server.am.ActivityManagerService;
-import com.android.server.pm.PackageManagerService;
-import com.android.server.NativeDaemonConnector.Command;
+import static android.content.pm.PackageManager.PERMISSION_GRANTED;
 
 import android.Manifest;
 import android.content.BroadcastReceiver;
@@ -30,6 +26,7 @@
 import android.content.IntentFilter;
 import android.content.ServiceConnection;
 import android.content.pm.PackageManager;
+import android.content.pm.UserInfo;
 import android.content.res.ObbInfo;
 import android.content.res.Resources;
 import android.content.res.TypedArray;
@@ -38,15 +35,14 @@
 import android.net.Uri;
 import android.os.Binder;
 import android.os.Environment;
+import android.os.Environment.UserEnvironment;
 import android.os.Handler;
 import android.os.HandlerThread;
 import android.os.IBinder;
 import android.os.Looper;
 import android.os.Message;
-import android.os.Parcelable;
 import android.os.RemoteException;
 import android.os.ServiceManager;
-import android.os.SystemClock;
 import android.os.SystemProperties;
 import android.os.UserHandle;
 import android.os.storage.IMountService;
@@ -61,9 +57,18 @@
 import android.util.Slog;
 import android.util.Xml;
 
-import org.xmlpull.v1.XmlPullParser;
+import com.android.internal.app.IMediaContainerService;
+import com.android.internal.util.XmlUtils;
+import com.android.server.NativeDaemonConnector.Command;
+import com.android.server.am.ActivityManagerService;
+import com.android.server.pm.PackageManagerService;
+import com.android.server.pm.UserManagerService;
+import com.google.android.collect.Lists;
+import com.google.android.collect.Maps;
+
 import org.xmlpull.v1.XmlPullParserException;
 
+import java.io.File;
 import java.io.FileDescriptor;
 import java.io.IOException;
 import java.io.PrintWriter;
@@ -81,7 +86,6 @@
 import java.util.Map.Entry;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
-import java.util.Set;
 
 import javax.crypto.SecretKey;
 import javax.crypto.SecretKeyFactory;
@@ -96,9 +100,11 @@
 class MountService extends IMountService.Stub
         implements INativeDaemonConnectorCallbacks, Watchdog.Monitor {
 
-    private static final boolean LOCAL_LOGD = false;
-    private static final boolean DEBUG_UNMOUNT = false;
-    private static final boolean DEBUG_EVENTS = false;
+    // TODO: listen for user creation/deletion
+
+    private static final boolean LOCAL_LOGD = true;
+    private static final boolean DEBUG_UNMOUNT = true;
+    private static final boolean DEBUG_EVENTS = true;
     private static final boolean DEBUG_OBB = false;
 
     // Disable this since it messes up long-running cryptfs operations.
@@ -166,25 +172,34 @@
         public static final int VolumeBadRemoval               = 632;
     }
 
-    private Context                               mContext;
-    private NativeDaemonConnector                 mConnector;
-    private final ArrayList<StorageVolume>        mVolumes = new ArrayList<StorageVolume>();
-    private StorageVolume                         mPrimaryVolume;
-    private final HashMap<String, String>         mVolumeStates = new HashMap<String, String>();
-    private final HashMap<String, StorageVolume>  mVolumeMap = new HashMap<String, StorageVolume>();
-    private String                                mExternalStoragePath;
+    private Context mContext;
+    private NativeDaemonConnector mConnector;
+
+    private final Object mVolumesLock = new Object();
+
+    /** When defined, base template for user-specific {@link StorageVolume}. */
+    private StorageVolume mEmulatedTemplate;
+
+    // @GuardedBy("mVolumesLock")
+    private final ArrayList<StorageVolume> mVolumes = Lists.newArrayList();
+    /** Map from path to {@link StorageVolume} */
+    // @GuardedBy("mVolumesLock")
+    private final HashMap<String, StorageVolume> mVolumesByPath = Maps.newHashMap();
+    /** Map from path to state */
+    // @GuardedBy("mVolumesLock")
+    private final HashMap<String, String> mVolumeStates = Maps.newHashMap();
+
+    private volatile boolean mSystemReady = false;
+
     private PackageManagerService                 mPms;
     private boolean                               mUmsEnabling;
     private boolean                               mUmsAvailable = false;
     // Used as a lock for methods that register/unregister listeners.
     final private ArrayList<MountServiceBinderListener> mListeners =
             new ArrayList<MountServiceBinderListener>();
-    private boolean                               mBooted = false;
     private CountDownLatch                        mConnectedSignal = new CountDownLatch(1);
     private CountDownLatch                        mAsecsScanned = new CountDownLatch(1);
     private boolean                               mSendUmsConnectedOnBoot = false;
-    // true if we should fake MEDIA_MOUNTED state for external storage
-    private boolean                               mEmulateExternalStorage = false;
 
     /**
      * Private hash of currently mounted secure containers.
@@ -303,6 +318,8 @@
     private static final int H_UNMOUNT_PM_UPDATE = 1;
     private static final int H_UNMOUNT_PM_DONE = 2;
     private static final int H_UNMOUNT_MS = 3;
+    private static final int H_SYSTEM_READY = 4;
+
     private static final int RETRY_UNMOUNT_DELAY = 30; // in ms
     private static final int MAX_UNMOUNT_RETRIES = 4;
 
@@ -437,17 +454,26 @@
                     }
                     break;
                 }
-                case H_UNMOUNT_MS : {
+                case H_UNMOUNT_MS: {
                     if (DEBUG_UNMOUNT) Slog.i(TAG, "H_UNMOUNT_MS");
                     UnmountCallBack ucb = (UnmountCallBack) msg.obj;
                     ucb.handleFinished();
                     break;
                 }
+                case H_SYSTEM_READY: {
+                    try {
+                        handleSystemReady();
+                    } catch (Exception ex) {
+                        Slog.e(TAG, "Boot-time mount exception", ex);
+                    }
+                    break;
+                }
             }
         }
     };
-    final private HandlerThread mHandlerThread;
-    final private Handler mHandler;
+
+    private final HandlerThread mHandlerThread;
+    private final Handler mHandler;
 
     void waitForAsecScan() {
         waitForLatch(mAsecsScanned);
@@ -476,90 +502,119 @@
         }
     }
 
-    private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
+    private void handleSystemReady() {
+        // Snapshot current volume states since it's not safe to call into vold
+        // while holding locks.
+        final HashMap<String, String> snapshot;
+        synchronized (mVolumesLock) {
+            snapshot = new HashMap<String, String>(mVolumeStates);
+        }
+
+        for (Map.Entry<String, String> entry : snapshot.entrySet()) {
+            final String path = entry.getKey();
+            final String state = entry.getValue();
+
+            if (state.equals(Environment.MEDIA_UNMOUNTED)) {
+                int rc = doMountVolume(path);
+                if (rc != StorageResultCode.OperationSucceeded) {
+                    Slog.e(TAG, String.format("Boot-time mount failed (%d)",
+                            rc));
+                }
+            } else if (state.equals(Environment.MEDIA_SHARED)) {
+                /*
+                 * Bootstrap UMS enabled state since vold indicates
+                 * the volume is shared (runtime restart while ums enabled)
+                 */
+                notifyVolumeStateChange(null, path, VolumeState.NoMedia,
+                        VolumeState.Shared);
+            }
+        }
+
+        // Push mounted state for all emulated storage
+        synchronized (mVolumesLock) {
+            for (StorageVolume volume : mVolumes) {
+                if (volume.isEmulated()) {
+                    updatePublicVolumeState(volume, Environment.MEDIA_MOUNTED);
+                }
+            }
+        }
+
+        /*
+         * If UMS was connected on boot, send the connected event
+         * now that we're up.
+         */
+        if (mSendUmsConnectedOnBoot) {
+            sendUmsIntent(true);
+            mSendUmsConnectedOnBoot = false;
+        }
+    }
+
+    private final BroadcastReceiver mBootReceiver = new BroadcastReceiver() {
         @Override
         public void onReceive(Context context, Intent intent) {
-            String action = intent.getAction();
+            final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
+            if (userId == -1) return;
+            final UserHandle user = new UserHandle(userId);
 
-            if (action.equals(Intent.ACTION_BOOT_COMPLETED)) {
-                mBooted = true;
+            Slog.d(TAG, "BOOT_COMPLETED for " + user);
 
-                /*
-                 * In the simulator, we need to broadcast a volume mounted event
-                 * to make the media scanner run.
-                 */
-                if ("simulator".equals(SystemProperties.get("ro.product.device"))) {
-                    notifyVolumeStateChange(null, "/sdcard", VolumeState.NoMedia,
-                            VolumeState.Mounted);
-                    return;
-                }
-                new Thread() {
-                    @Override
-                    public void run() {
-                        try {
-                            // it is not safe to call vold with mVolumeStates locked
-                            // so we make a copy of the paths and states and process them
-                            // outside the lock
-                            String[] paths;
-                            String[] states;
-                            int count;
-                            synchronized (mVolumeStates) {
-                                Set<String> keys = mVolumeStates.keySet();
-                                count = keys.size();
-                                paths = keys.toArray(new String[count]);
-                                states = new String[count];
-                                for (int i = 0; i < count; i++) {
-                                    states[i] = mVolumeStates.get(paths[i]);
-                                }
-                            }
+            // Broadcast mounted volumes to newly booted user. This kicks off
+            // media scanner when a user becomes active.
+            synchronized (mVolumesLock) {
+                for (StorageVolume volume : mVolumes) {
+                    final UserHandle owner = volume.getOwner();
+                    final boolean ownerMatch = owner == null
+                            || owner.getIdentifier() == user.getIdentifier();
 
-                            for (int i = 0; i < count; i++) {
-                                String path = paths[i];
-                                String state = states[i];
+                    final String state = mVolumeStates.get(volume.getPath());
 
-                                if (state.equals(Environment.MEDIA_UNMOUNTED)) {
-                                    int rc = doMountVolume(path);
-                                    if (rc != StorageResultCode.OperationSucceeded) {
-                                        Slog.e(TAG, String.format("Boot-time mount failed (%d)",
-                                                rc));
-                                    }
-                                } else if (state.equals(Environment.MEDIA_SHARED)) {
-                                    /*
-                                     * Bootstrap UMS enabled state since vold indicates
-                                     * the volume is shared (runtime restart while ums enabled)
-                                     */
-                                    notifyVolumeStateChange(null, path, VolumeState.NoMedia,
-                                            VolumeState.Shared);
-                                }
-                            }
-
-                            /* notify external storage has mounted to trigger media scanner */
-                            if (mEmulateExternalStorage) {
-                                notifyVolumeStateChange(null,
-                                        Environment.getExternalStorageDirectory().getPath(),
-                                        VolumeState.NoMedia, VolumeState.Mounted);
-                            }
-
-                            /*
-                             * If UMS was connected on boot, send the connected event
-                             * now that we're up.
-                             */
-                            if (mSendUmsConnectedOnBoot) {
-                                sendUmsIntent(true);
-                                mSendUmsConnectedOnBoot = false;
-                            }
-                        } catch (Exception ex) {
-                            Slog.e(TAG, "Boot-time mount exception", ex);
-                        }
+                    if (ownerMatch && (Environment.MEDIA_MOUNTED.equals(state)
+                            || Environment.MEDIA_MOUNTED_READ_ONLY.equals(state))) {
+                        sendStorageIntent(Intent.ACTION_MEDIA_MOUNTED, volume, user);
                     }
-                }.start();
-            } else if (action.equals(UsbManager.ACTION_USB_STATE)) {
-                boolean available = (intent.getBooleanExtra(UsbManager.USB_CONNECTED, false) &&
-                        intent.getBooleanExtra(UsbManager.USB_FUNCTION_MASS_STORAGE, false));
-                notifyShareAvailabilityChange(available);
+                }
             }
         }
     };
+
+    private final BroadcastReceiver mUserReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
+            if (userId == -1) return;
+            final UserHandle user = new UserHandle(userId);
+
+            final String action = intent.getAction();
+            if (Intent.ACTION_USER_ADDED.equals(action)) {
+                synchronized (mVolumesLock) {
+                    createEmulatedVolumeForUserLocked(user);
+                }
+
+            } else if (Intent.ACTION_USER_REMOVED.equals(action)) {
+                synchronized (mVolumesLock) {
+                    final List<StorageVolume> toRemove = Lists.newArrayList();
+                    for (StorageVolume volume : mVolumes) {
+                        if (user.equals(volume.getOwner())) {
+                            toRemove.add(volume);
+                        }
+                    }
+                    for (StorageVolume volume : toRemove) {
+                        removeVolumeLocked(volume);
+                    }
+                }
+            }
+        }
+    };
+
+    private final BroadcastReceiver mUsbReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            boolean available = (intent.getBooleanExtra(UsbManager.USB_CONNECTED, false) &&
+                    intent.getBooleanExtra(UsbManager.USB_FUNCTION_MASS_STORAGE, false));
+            notifyShareAvailabilityChange(available);
+        }
+    };
+
     private final class MountServiceBinderListener implements IBinder.DeathRecipient {
         final IMountServiceListener mListener;
 
@@ -590,11 +645,13 @@
         }
     }
 
-    private void updatePublicVolumeState(String path, String state) {
-        String oldState;
-        synchronized(mVolumeStates) {
+    private void updatePublicVolumeState(StorageVolume volume, String state) {
+        final String path = volume.getPath();
+        final String oldState;
+        synchronized (mVolumesLock) {
             oldState = mVolumeStates.put(path, state);
         }
+
         if (state.equals(oldState)) {
             Slog.w(TAG, String.format("Duplicate state transition (%s -> %s) for %s",
                     state, state, path));
@@ -603,24 +660,24 @@
 
         Slog.d(TAG, "volume state changed for " + path + " (" + oldState + " -> " + state + ")");
 
-        if (path.equals(mExternalStoragePath)) {
-            // Update state on PackageManager, but only of real events
-            if (!mEmulateExternalStorage) {
-                if (Environment.MEDIA_UNMOUNTED.equals(state)) {
-                    mPms.updateExternalMediaStatus(false, false);
+        // Tell PackageManager about changes to primary volume state, but only
+        // when not emulated.
+        if (volume.isPrimary() && !volume.isEmulated()) {
+            if (Environment.MEDIA_UNMOUNTED.equals(state)) {
+                mPms.updateExternalMediaStatus(false, false);
 
-                    /*
-                     * Some OBBs might have been unmounted when this volume was
-                     * unmounted, so send a message to the handler to let it know to
-                     * remove those from the list of mounted OBBS.
-                     */
-                    mObbActionHandler.sendMessage(mObbActionHandler.obtainMessage(
-                            OBB_FLUSH_MOUNT_STATE, path));
-                } else if (Environment.MEDIA_MOUNTED.equals(state)) {
-                    mPms.updateExternalMediaStatus(true, false);
-                }
+                /*
+                 * Some OBBs might have been unmounted when this volume was
+                 * unmounted, so send a message to the handler to let it know to
+                 * remove those from the list of mounted OBBS.
+                 */
+                mObbActionHandler.sendMessage(mObbActionHandler.obtainMessage(
+                        OBB_FLUSH_MOUNT_STATE, path));
+            } else if (Environment.MEDIA_MOUNTED.equals(state)) {
+                mPms.updateExternalMediaStatus(true, false);
             }
         }
+
         synchronized (mListeners) {
             for (int i = mListeners.size() -1; i >= 0; i--) {
                 MountServiceBinderListener bl = mListeners.get(i);
@@ -637,7 +694,6 @@
     }
 
     /**
-     *
      * Callback from NativeDaemonConnector
      */
     public void onDaemonConnected() {
@@ -661,6 +717,11 @@
                         String path = tok[1];
                         String state = Environment.MEDIA_REMOVED;
 
+                        final StorageVolume volume;
+                        synchronized (mVolumesLock) {
+                            volume = mVolumesByPath.get(path);
+                        }
+
                         int st = Integer.parseInt(tok[2]);
                         if (st == VolumeState.NoMedia) {
                             state = Environment.MEDIA_REMOVED;
@@ -678,12 +739,15 @@
 
                         if (state != null) {
                             if (DEBUG_EVENTS) Slog.i(TAG, "Updating valid state " + state);
-                            updatePublicVolumeState(path, state);
+                            updatePublicVolumeState(volume, state);
                         }
                     }
                 } catch (Exception e) {
                     Slog.e(TAG, "Error processing initial volume state", e);
-                    updatePublicVolumeState(mExternalStoragePath, Environment.MEDIA_REMOVED);
+                    final StorageVolume primary = getPrimaryPhysicalVolume();
+                    if (primary != null) {
+                        updatePublicVolumeState(primary, Environment.MEDIA_REMOVED);
+                    }
                 }
 
                 /*
@@ -749,6 +813,13 @@
                 Slog.e(TAG, "Failed to parse major/minor", ex);
             }
 
+            final StorageVolume volume;
+            final String state;
+            synchronized (mVolumesLock) {
+                volume = mVolumesByPath.get(path);
+                state = mVolumeStates.get(path);
+            }
+
             if (code == VoldResponseCode.VolumeDiskInserted) {
                 new Thread() {
                     @Override
@@ -772,27 +843,27 @@
                 }
                 /* Send the media unmounted event first */
                 if (DEBUG_EVENTS) Slog.i(TAG, "Sending unmounted event first");
-                updatePublicVolumeState(path, Environment.MEDIA_UNMOUNTED);
-                sendStorageIntent(Environment.MEDIA_UNMOUNTED, path);
+                updatePublicVolumeState(volume, Environment.MEDIA_UNMOUNTED);
+                sendStorageIntent(Environment.MEDIA_UNMOUNTED, volume, UserHandle.ALL);
 
                 if (DEBUG_EVENTS) Slog.i(TAG, "Sending media removed");
-                updatePublicVolumeState(path, Environment.MEDIA_REMOVED);
+                updatePublicVolumeState(volume, Environment.MEDIA_REMOVED);
                 action = Intent.ACTION_MEDIA_REMOVED;
             } else if (code == VoldResponseCode.VolumeBadRemoval) {
                 if (DEBUG_EVENTS) Slog.i(TAG, "Sending unmounted event first");
                 /* Send the media unmounted event first */
-                updatePublicVolumeState(path, Environment.MEDIA_UNMOUNTED);
+                updatePublicVolumeState(volume, Environment.MEDIA_UNMOUNTED);
                 action = Intent.ACTION_MEDIA_UNMOUNTED;
 
                 if (DEBUG_EVENTS) Slog.i(TAG, "Sending media bad removal");
-                updatePublicVolumeState(path, Environment.MEDIA_BAD_REMOVAL);
+                updatePublicVolumeState(volume, Environment.MEDIA_BAD_REMOVAL);
                 action = Intent.ACTION_MEDIA_BAD_REMOVAL;
             } else {
                 Slog.e(TAG, String.format("Unknown code {%d}", code));
             }
 
             if (action != null) {
-                sendStorageIntent(action, path);
+                sendStorageIntent(action, volume, UserHandle.ALL);
             }
         } else {
             return false;
@@ -802,14 +873,20 @@
     }
 
     private void notifyVolumeStateChange(String label, String path, int oldState, int newState) {
-        String vs = getVolumeState(path);
-        if (DEBUG_EVENTS) Slog.i(TAG, "notifyVolumeStateChanged::" + vs);
+        final StorageVolume volume;
+        final String state;
+        synchronized (mVolumesLock) {
+            volume = mVolumesByPath.get(path);
+            state = getVolumeState(path);
+        }
+
+        if (DEBUG_EVENTS) Slog.i(TAG, "notifyVolumeStateChange::" + state);
 
         String action = null;
 
         if (oldState == VolumeState.Shared && newState != oldState) {
             if (LOCAL_LOGD) Slog.d(TAG, "Sending ACTION_MEDIA_UNSHARED intent");
-            sendStorageIntent(Intent.ACTION_MEDIA_UNSHARED,  path);
+            sendStorageIntent(Intent.ACTION_MEDIA_UNSHARED, volume, UserHandle.ALL);
         }
 
         if (newState == VolumeState.Init) {
@@ -820,22 +897,22 @@
              * Don't notify if we're in BAD_REMOVAL, NOFS, UNMOUNTABLE, or
              * if we're in the process of enabling UMS
              */
-            if (!vs.equals(
-                    Environment.MEDIA_BAD_REMOVAL) && !vs.equals(
-                            Environment.MEDIA_NOFS) && !vs.equals(
+            if (!state.equals(
+                    Environment.MEDIA_BAD_REMOVAL) && !state.equals(
+                            Environment.MEDIA_NOFS) && !state.equals(
                                     Environment.MEDIA_UNMOUNTABLE) && !getUmsEnabling()) {
                 if (DEBUG_EVENTS) Slog.i(TAG, "updating volume state for media bad removal nofs and unmountable");
-                updatePublicVolumeState(path, Environment.MEDIA_UNMOUNTED);
+                updatePublicVolumeState(volume, Environment.MEDIA_UNMOUNTED);
                 action = Intent.ACTION_MEDIA_UNMOUNTED;
             }
         } else if (newState == VolumeState.Pending) {
         } else if (newState == VolumeState.Checking) {
             if (DEBUG_EVENTS) Slog.i(TAG, "updating volume state checking");
-            updatePublicVolumeState(path, Environment.MEDIA_CHECKING);
+            updatePublicVolumeState(volume, Environment.MEDIA_CHECKING);
             action = Intent.ACTION_MEDIA_CHECKING;
         } else if (newState == VolumeState.Mounted) {
             if (DEBUG_EVENTS) Slog.i(TAG, "updating volume state mounted");
-            updatePublicVolumeState(path, Environment.MEDIA_MOUNTED);
+            updatePublicVolumeState(volume, Environment.MEDIA_MOUNTED);
             action = Intent.ACTION_MEDIA_MOUNTED;
         } else if (newState == VolumeState.Unmounting) {
             action = Intent.ACTION_MEDIA_EJECT;
@@ -843,11 +920,11 @@
         } else if (newState == VolumeState.Shared) {
             if (DEBUG_EVENTS) Slog.i(TAG, "Updating volume state media mounted");
             /* Send the media unmounted event first */
-            updatePublicVolumeState(path, Environment.MEDIA_UNMOUNTED);
-            sendStorageIntent(Intent.ACTION_MEDIA_UNMOUNTED, path);
+            updatePublicVolumeState(volume, Environment.MEDIA_UNMOUNTED);
+            sendStorageIntent(Intent.ACTION_MEDIA_UNMOUNTED, volume, UserHandle.ALL);
 
             if (DEBUG_EVENTS) Slog.i(TAG, "Updating media shared");
-            updatePublicVolumeState(path, Environment.MEDIA_SHARED);
+            updatePublicVolumeState(volume, Environment.MEDIA_SHARED);
             action = Intent.ACTION_MEDIA_SHARED;
             if (LOCAL_LOGD) Slog.d(TAG, "Sending ACTION_MEDIA_SHARED intent");
         } else if (newState == VolumeState.SharedMnt) {
@@ -858,13 +935,18 @@
         }
 
         if (action != null) {
-            sendStorageIntent(action, path);
+            sendStorageIntent(action, volume, UserHandle.ALL);
         }
     }
 
     private int doMountVolume(String path) {
         int rc = StorageResultCode.OperationSucceeded;
 
+        final StorageVolume volume;
+        synchronized (mVolumesLock) {
+            volume = mVolumesByPath.get(path);
+        }
+
         if (DEBUG_EVENTS) Slog.i(TAG, "doMountVolume: Mouting " + path);
         try {
             mConnector.execute("volume", "mount", path);
@@ -884,7 +966,7 @@
                 /*
                  * Media is blank or does not contain a supported filesystem
                  */
-                updatePublicVolumeState(path, Environment.MEDIA_NOFS);
+                updatePublicVolumeState(volume, Environment.MEDIA_NOFS);
                 action = Intent.ACTION_MEDIA_NOFS;
                 rc = StorageResultCode.OperationFailedMediaBlank;
             } else if (code == VoldResponseCode.OpFailedMediaCorrupt) {
@@ -892,7 +974,7 @@
                 /*
                  * Volume consistency check failed
                  */
-                updatePublicVolumeState(path, Environment.MEDIA_UNMOUNTABLE);
+                updatePublicVolumeState(volume, Environment.MEDIA_UNMOUNTABLE);
                 action = Intent.ACTION_MEDIA_UNMOUNTABLE;
                 rc = StorageResultCode.OperationFailedMediaCorrupt;
             } else {
@@ -903,7 +985,7 @@
              * Send broadcast intent (if required for the failure)
              */
             if (action != null) {
-                sendStorageIntent(action, path);
+                sendStorageIntent(action, volume, UserHandle.ALL);
             }
         }
 
@@ -1011,14 +1093,16 @@
             }
         }
 
-        if (mBooted == true) {
+        if (mSystemReady == true) {
             sendUmsIntent(avail);
         } else {
             mSendUmsConnectedOnBoot = avail;
         }
 
-        final String path = Environment.getExternalStorageDirectory().getPath();
-        if (avail == false && getVolumeState(path).equals(Environment.MEDIA_SHARED)) {
+        final StorageVolume primary = getPrimaryPhysicalVolume();
+        if (avail == false && primary != null
+                && Environment.MEDIA_SHARED.equals(getVolumeState(primary.getPath()))) {
+            final String path = primary.getPath();
             /*
              * USB mass storage disconnected while enabled
              */
@@ -1042,12 +1126,11 @@
         }
     }
 
-    private void sendStorageIntent(String action, String path) {
-        Intent intent = new Intent(action, Uri.parse("file://" + path));
-        // add StorageVolume extra
-        intent.putExtra(StorageVolume.EXTRA_STORAGE_VOLUME, mVolumeMap.get(path));
-        Slog.d(TAG, "sendStorageIntent " + intent);
-        mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
+    private void sendStorageIntent(String action, StorageVolume volume, UserHandle user) {
+        final Intent intent = new Intent(action, Uri.parse("file://" + volume.getPath()));
+        intent.putExtra(StorageVolume.EXTRA_STORAGE_VOLUME, volume);
+        Slog.d(TAG, "sendStorageIntent " + intent + " to " + user);
+        mContext.sendBroadcastAsUser(intent, user);
     }
 
     private void sendUmsIntent(boolean c) {
@@ -1066,7 +1149,10 @@
     private static final String TAG_STORAGE_LIST = "StorageList";
     private static final String TAG_STORAGE = "storage";
 
-    private void readStorageList() {
+    private void readStorageListLocked() {
+        mVolumes.clear();
+        mVolumeStates.clear();
+
         Resources resources = mContext.getResources();
 
         int id = com.android.internal.R.xml.storage_list;
@@ -1085,7 +1171,7 @@
                     TypedArray a = resources.obtainAttributes(attrs,
                             com.android.internal.R.styleable.Storage);
 
-                    CharSequence path = a.getText(
+                    String path = a.getString(
                             com.android.internal.R.styleable.Storage_mountPoint);
                     int descriptionId = a.getResourceId(
                             com.android.internal.R.styleable.Storage_storageDescription, -1);
@@ -1110,27 +1196,29 @@
                             " emulated: " + emulated +  " mtpReserve: " + mtpReserve +
                             " allowMassStorage: " + allowMassStorage +
                             " maxFileSize: " + maxFileSize);
-                    if (path == null || description == null) {
-                        Slog.e(TAG, "path or description is null in readStorageList");
+
+                    if (emulated) {
+                        // For devices with emulated storage, we create separate
+                        // volumes for each known user.
+                        mEmulatedTemplate = new StorageVolume(null, descriptionId, true, false,
+                                true, mtpReserve, false, maxFileSize, null);
+
+                        final UserManagerService userManager = UserManagerService.getInstance();
+                        for (UserInfo user : userManager.getUsers()) {
+                            createEmulatedVolumeForUserLocked(user.getUserHandle());
+                        }
+
                     } else {
-                        String pathString = path.toString();
-                        StorageVolume volume = new StorageVolume(pathString, descriptionId, primary,
-                                removable, emulated, mtpReserve, allowMassStorage, maxFileSize);
-                        if (primary) {
-                            if (mPrimaryVolume == null) {
-                                mPrimaryVolume = volume;
-                            } else {
-                                Slog.e(TAG, "multiple primary volumes in storage list");
-                            }
-                        }
-                        if (mPrimaryVolume == volume) {
-                            // primay volume must be first
-                            mVolumes.add(0, volume);
+                        if (path == null || description == null) {
+                            Slog.e(TAG, "Missing storage path or description in readStorageList");
                         } else {
-                            mVolumes.add(volume);
+                            final StorageVolume volume = new StorageVolume(new File(path),
+                                    descriptionId, primary, removable, emulated, mtpReserve,
+                                    allowMassStorage, maxFileSize, null);
+                            addVolumeLocked(volume);
                         }
-                        mVolumeMap.put(pathString, volume);
                     }
+
                     a.recycle();
                 }
             }
@@ -1139,48 +1227,105 @@
         } catch (IOException e) {
             throw new RuntimeException(e);
         } finally {
-            // compute storage ID for each volume
-            int length = mVolumes.size();
-            for (int i = 0; i < length; i++) {
-                mVolumes.get(i).setStorageId(i);
+            // Compute storage ID for each physical volume; emulated storage is
+            // always 0 when defined.
+            int index = isExternalStorageEmulated() ? 1 : 0;
+            for (StorageVolume volume : mVolumes) {
+                if (!volume.isEmulated()) {
+                    volume.setStorageId(index++);
+                }
             }
             parser.close();
         }
     }
 
     /**
+     * Create and add new {@link StorageVolume} for given {@link UserHandle}
+     * using {@link #mEmulatedTemplate} as template.
+     */
+    private void createEmulatedVolumeForUserLocked(UserHandle user) {
+        if (mEmulatedTemplate == null) {
+            throw new IllegalStateException("Missing emulated volume multi-user template");
+        }
+
+        final UserEnvironment userEnv = new UserEnvironment(user.getIdentifier());
+        final File path = userEnv.getExternalStorageDirectory();
+        final StorageVolume volume = StorageVolume.fromTemplate(mEmulatedTemplate, path, user);
+        volume.setStorageId(0);
+        addVolumeLocked(volume);
+
+        if (mSystemReady) {
+            updatePublicVolumeState(volume, Environment.MEDIA_MOUNTED);
+        } else {
+            // Place stub status for early callers to find
+            mVolumeStates.put(volume.getPath(), Environment.MEDIA_MOUNTED);
+        }
+    }
+
+    private void addVolumeLocked(StorageVolume volume) {
+        Slog.d(TAG, "addVolumeLocked() " + volume);
+        mVolumes.add(volume);
+        final StorageVolume existing = mVolumesByPath.put(volume.getPath(), volume);
+        if (existing != null) {
+            throw new IllegalStateException(
+                    "Volume at " + volume.getPath() + " already exists: " + existing);
+        }
+    }
+
+    private void removeVolumeLocked(StorageVolume volume) {
+        Slog.d(TAG, "removeVolumeLocked() " + volume);
+        mVolumes.remove(volume);
+        mVolumesByPath.remove(volume.getPath());
+        mVolumeStates.remove(volume.getPath());
+    }
+
+    private StorageVolume getPrimaryPhysicalVolume() {
+        synchronized (mVolumesLock) {
+            for (StorageVolume volume : mVolumes) {
+                if (volume.isPrimary() && !volume.isEmulated()) {
+                    return volume;
+                }
+            }
+        }
+        return null;
+    }
+
+    /**
      * Constructs a new MountService instance
      *
      * @param context  Binder context for this service
      */
     public MountService(Context context) {
         mContext = context;
-        readStorageList();
 
-        if (mPrimaryVolume != null) {
-            mExternalStoragePath = mPrimaryVolume.getPath();
-            mEmulateExternalStorage = mPrimaryVolume.isEmulated();
-            if (mEmulateExternalStorage) {
-                Slog.d(TAG, "using emulated external storage");
-                mVolumeStates.put(mExternalStoragePath, Environment.MEDIA_MOUNTED);
-            }
+        synchronized (mVolumesLock) {
+            readStorageListLocked();
         }
 
         // XXX: This will go away soon in favor of IMountServiceObserver
         mPms = (PackageManagerService) ServiceManager.getService("package");
 
-        IntentFilter filter = new IntentFilter();
-        filter.addAction(Intent.ACTION_BOOT_COMPLETED);
-        // don't bother monitoring USB if mass storage is not supported on our primary volume
-        if (mPrimaryVolume != null && mPrimaryVolume.allowMassStorage()) {
-            filter.addAction(UsbManager.ACTION_USB_STATE);
-        }
-        mContext.registerReceiver(mBroadcastReceiver, filter, null, null);
-
         mHandlerThread = new HandlerThread("MountService");
         mHandlerThread.start();
         mHandler = new MountServiceHandler(mHandlerThread.getLooper());
 
+        // Watch for user boot completion
+        mContext.registerReceiverAsUser(mBootReceiver, UserHandle.ALL,
+                new IntentFilter(Intent.ACTION_BOOT_COMPLETED), null, mHandler);
+
+        // Watch for user changes
+        final IntentFilter userFilter = new IntentFilter();
+        userFilter.addAction(Intent.ACTION_USER_ADDED);
+        userFilter.addAction(Intent.ACTION_USER_REMOVED);
+        mContext.registerReceiver(mUserReceiver, userFilter, null, mHandler);
+
+        // Watch for USB changes on primary volume
+        final StorageVolume primary = getPrimaryPhysicalVolume();
+        if (primary != null && primary.allowMassStorage()) {
+            mContext.registerReceiver(
+                    mUsbReceiver, new IntentFilter(UsbManager.ACTION_USB_STATE), null, mHandler);
+        }
+
         // Add OBB Action Handler to MountService thread.
         mObbActionHandler = new ObbActionHandler(mHandlerThread.getLooper());
 
@@ -1200,6 +1345,11 @@
         }
     }
 
+    public void systemReady() {
+        mSystemReady = true;
+        mHandler.obtainMessage(H_SYSTEM_READY).sendToTarget();
+    }
+
     /**
      * Exposed API calls below here
      */
@@ -1232,7 +1382,7 @@
         validatePermission(android.Manifest.permission.SHUTDOWN);
 
         Slog.i(TAG, "Shutting down");
-        synchronized (mVolumeStates) {
+        synchronized (mVolumesLock) {
             for (String path : mVolumeStates.keySet()) {
                 String state = mVolumeStates.get(path);
 
@@ -1313,12 +1463,15 @@
         waitForReady();
         validatePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS);
 
+        final StorageVolume primary = getPrimaryPhysicalVolume();
+        if (primary == null) return;
+
         // TODO: Add support for multiple share methods
 
         /*
          * If the volume is mounted and we're enabling then unmount it
          */
-        String path = Environment.getExternalStorageDirectory().getPath();
+        String path = primary.getPath();
         String vs = getVolumeState(path);
         String method = "ums";
         if (enable && vs.equals(Environment.MEDIA_MOUNTED)) {
@@ -1348,14 +1501,20 @@
 
     public boolean isUsbMassStorageEnabled() {
         waitForReady();
-        return doGetVolumeShared(Environment.getExternalStorageDirectory().getPath(), "ums");
+
+        final StorageVolume primary = getPrimaryPhysicalVolume();
+        if (primary != null) {
+            return doGetVolumeShared(primary.getPath(), "ums");
+        } else {
+            return false;
+        }
     }
 
     /**
      * @return state of the volume at the specified mount point
      */
     public String getVolumeState(String mountPoint) {
-        synchronized (mVolumeStates) {
+        synchronized (mVolumesLock) {
             String state = mVolumeStates.get(mountPoint);
             if (state == null) {
                 Slog.w(TAG, "getVolumeState(" + mountPoint + "): Unknown volume");
@@ -1370,8 +1529,9 @@
         }
     }
 
+    @Override
     public boolean isExternalStorageEmulated() {
-        return mEmulateExternalStorage;
+        return mEmulatedTemplate != null;
     }
 
     public int mountVolume(String path) {
@@ -1437,7 +1597,9 @@
     }
 
     private void warnOnNotMounted() {
-        if (!Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
+        final StorageVolume primary = getPrimaryPhysicalVolume();
+        if (primary != null
+                && Environment.MEDIA_MOUNTED.equals(getVolumeState(primary.getPath()))) {
             Slog.w(TAG, "getSecureContainerList() called when storage not mounted");
         }
     }
@@ -1935,14 +2097,23 @@
         }
     }
 
-    public Parcelable[] getVolumeList() {
-        synchronized(mVolumes) {
-            int size = mVolumes.size();
-            Parcelable[] result = new Parcelable[size];
-            for (int i = 0; i < size; i++) {
-                result[i] = mVolumes.get(i);
+    @Override
+    public StorageVolume[] getVolumeList() {
+        final int callingUserId = UserHandle.getCallingUserId();
+        final boolean accessAll = (mContext.checkPermission(
+                android.Manifest.permission.ACCESS_ALL_EXTERNAL_STORAGE,
+                Binder.getCallingPid(), Binder.getCallingUid()) == PERMISSION_GRANTED);
+
+        synchronized (mVolumesLock) {
+            final ArrayList<StorageVolume> filtered = Lists.newArrayList();
+            for (StorageVolume volume : mVolumes) {
+                final UserHandle owner = volume.getOwner();
+                final boolean ownerMatch = owner == null || owner.getIdentifier() == callingUserId;
+                if (accessAll || ownerMatch) {
+                    filtered.add(volume);
+                }
             }
-            return result;
+            return filtered.toArray(new StorageVolume[filtered.size()]);
         }
     }
 
@@ -2458,7 +2629,7 @@
 
         pw.println("");
 
-        synchronized (mVolumes) {
+        synchronized (mVolumesLock) {
             pw.println("  mVolumes:");
 
             final int N = mVolumes.size();
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 73e82ab..90783b7 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -55,6 +55,7 @@
 import com.android.server.input.InputManagerService;
 import com.android.server.net.NetworkPolicyManagerService;
 import com.android.server.net.NetworkStatsService;
+import com.android.server.pm.Installer;
 import com.android.server.pm.PackageManagerService;
 import com.android.server.pm.UserManagerService;
 import com.android.server.power.PowerManagerService;
@@ -117,6 +118,7 @@
                 : Integer.parseInt(factoryTestStr);
         final boolean headless = "1".equals(SystemProperties.get("ro.config.headless", "0"));
 
+        Installer installer = null;
         AccountManagerService accountManager = null;
         ContentService contentService = null;
         LightsService lights = null;
@@ -125,6 +127,7 @@
         BatteryService battery = null;
         VibratorService vibrator = null;
         AlarmManagerService alarm = null;
+        MountService mountService = null;
         NetworkManagementService networkManagement = null;
         NetworkStatsService networkStats = null;
         NetworkPolicyManagerService networkPolicy = null;
@@ -194,6 +197,13 @@
         // Critical services...
         boolean onlyCore = false;
         try {
+            // Wait for installd to finished starting up so that it has a chance to
+            // create critical directories such as /data/user with the appropriate
+            // permissions.  We need this to complete before we initialize other services.
+            Slog.i(TAG, "Waiting for installd to be ready.");
+            installer = new Installer();
+            installer.ping();
+
             Slog.i(TAG, "Entropy Mixer");
             ServiceManager.addService("entropy", new EntropyMixer());
 
@@ -233,7 +243,7 @@
                 onlyCore = true;
             }
 
-            pm = PackageManagerService.main(context,
+            pm = PackageManagerService.main(context, installer,
                     factoryTest != SystemServer.FACTORY_TEST_OFF,
                     onlyCore);
             boolean firstBoot = false;
@@ -374,7 +384,6 @@
         }
 
         if (factoryTest != SystemServer.FACTORY_TEST_LOW_LEVEL) {
-            MountService mountService = null;
             if (!"0".equals(SystemProperties.get("system_init.startmountservice"))) {
                 try {
                     /*
@@ -813,6 +822,7 @@
 
         // These are needed to propagate to the runnable below.
         final Context contextF = context;
+        final MountService mountServiceF = mountService;
         final BatteryService batteryF = battery;
         final NetworkManagementService networkManagementF = networkManagement;
         final NetworkStatsService networkStatsF = networkStats;
@@ -847,6 +857,11 @@
 
                 if (!headless) startSystemUi(contextF);
                 try {
+                    if (mountServiceF != null) mountServiceF.systemReady();
+                } catch (Throwable e) {
+                    reportWtf("making Mount Service ready", e);
+                }
+                try {
                     if (batteryF != null) batteryF.systemReady();
                 } catch (Throwable e) {
                     reportWtf("making Battery Service ready", e);
diff --git a/services/java/com/android/server/accessibility/ScreenMagnifier.java b/services/java/com/android/server/accessibility/ScreenMagnifier.java
index 8301211..48781ac 100644
--- a/services/java/com/android/server/accessibility/ScreenMagnifier.java
+++ b/services/java/com/android/server/accessibility/ScreenMagnifier.java
@@ -28,7 +28,6 @@
 import android.graphics.Canvas;
 import android.graphics.Color;
 import android.graphics.PixelFormat;
-import android.graphics.PointF;
 import android.graphics.PorterDuff.Mode;
 import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
@@ -40,18 +39,20 @@
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.provider.Settings;
-import android.util.MathUtils;
 import android.util.Property;
 import android.util.Slog;
 import android.view.Display;
 import android.view.DisplayInfo;
+import android.view.GestureDetector;
+import android.view.GestureDetector.SimpleOnGestureListener;
 import android.view.Gravity;
 import android.view.IDisplayContentChangeListener;
 import android.view.IWindowManager;
 import android.view.MotionEvent;
 import android.view.MotionEvent.PointerCoords;
 import android.view.MotionEvent.PointerProperties;
-import android.view.ScaleGestureDetector.SimpleOnScaleGestureListener;
+import android.view.ScaleGestureDetector;
+import android.view.ScaleGestureDetector.OnScaleGestureListener;
 import android.view.Surface;
 import android.view.View;
 import android.view.ViewConfiguration;
@@ -67,6 +68,8 @@
 import com.android.internal.os.SomeArgs;
 
 import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
 
 /**
  * This class handles the screen magnification when accessibility is enabled.
@@ -117,7 +120,6 @@
     private static final boolean DEBUG_VIEWPORT_WINDOW = false;
     private static final boolean DEBUG_WINDOW_TRANSITIONS = false;
     private static final boolean DEBUG_ROTATION = false;
-    private static final boolean DEBUG_GESTURE_DETECTOR = false;
     private static final boolean DEBUG_MAGNIFICATION_CONTROLLER = false;
 
     private static final String LOG_TAG = ScreenMagnifier.class.getSimpleName();
@@ -139,7 +141,7 @@
     private final DisplayProvider mDisplayProvider;
 
     private final DetectingStateHandler mDetectingStateHandler = new DetectingStateHandler();
-    private final GestureDetector mGestureDetector;
+    private final MagnifiedContentInteractonStateHandler mMagnifiedContentInteractonStateHandler;
     private final StateViewportDraggingHandler mStateViewportDraggingHandler =
             new StateViewportDraggingHandler();
 
@@ -194,14 +196,15 @@
         mScreenStateObserver = new ScreenStateObserver(mContext, mViewport,
                 mMagnificationController);
 
-        mGestureDetector = new GestureDetector(context);
+        mMagnifiedContentInteractonStateHandler = new MagnifiedContentInteractonStateHandler(
+                context);
 
         transitionToState(STATE_DETECTING);
     }
 
     @Override
     public void onMotionEvent(MotionEvent event, int policyFlags) {
-        mGestureDetector.onMotionEvent(event);
+        mMagnifiedContentInteractonStateHandler.onMotionEvent(event);
         switch (mCurrentState) {
             case STATE_DELEGATING: {
                 handleMotionEventStateDelegating(event, policyFlags);
@@ -213,9 +216,9 @@
                 mStateViewportDraggingHandler.onMotionEvent(event, policyFlags);
             } break;
             case STATE_MAGNIFIED_INTERACTION: {
-                // Handled by the gesture detector. Since the detector
-                // needs all touch events to work properly we cannot
-                // call it only for this state.
+                // mMagnifiedContentInteractonStateHandler handles events only
+                // if this is the current state since it uses ScaleGestureDetecotr
+                // and a GestureDetector which need well formed event stream.
             } break;
             default: {
                 throw new IllegalStateException("Unknown state: " + mCurrentState);
@@ -240,7 +243,7 @@
         mCurrentState = STATE_DETECTING;
         mDetectingStateHandler.clear();
         mStateViewportDraggingHandler.clear();
-        mGestureDetector.clear();
+        mMagnifiedContentInteractonStateHandler.clear();
         if (mNext != null) {
             mNext.clear();
         }
@@ -345,46 +348,29 @@
         mCurrentState = state;
     }
 
-    private final class GestureDetector implements OnScaleGestureListener {
+    private final class MagnifiedContentInteractonStateHandler
+            extends SimpleOnGestureListener implements OnScaleGestureListener {
         private static final float MIN_SCALE = 1.3f;
         private static final float MAX_SCALE = 5.0f;
 
-        private static final float DETECT_SCALING_THRESHOLD = 0.30f;
-        private static final int DETECT_PANNING_THRESHOLD_DIP = 30;
-
-        private final float mScaledDetectPanningThreshold;
+        private static final float SCALING_THRESHOLD = 0.3f;
 
         private final ScaleGestureDetector mScaleGestureDetector;
+        private final GestureDetector mGestureDetector;
 
-        private final PointF mPrevFocus = new PointF(Float.NaN, Float.NaN);
-        private final PointF mInitialFocus = new PointF(Float.NaN, Float.NaN);
-
-        private float mCurrScale = Float.NaN;
-        private float mCurrScaleFactor = 1.0f;
-        private float mPrevScaleFactor = 1.0f;
-        private float mCurrPan;
-        private float mPrevPan;
-
-        private float mScaleFocusX = Float.NaN;
-        private float mScaleFocusY = Float.NaN;
-
+        private float mInitialScaleFactor = -1;
         private boolean mScaling;
-        private boolean mPanning;
 
-        public GestureDetector(Context context) {
-            final float density = context.getResources().getDisplayMetrics().density;
-            mScaledDetectPanningThreshold = DETECT_PANNING_THRESHOLD_DIP * density;
-            mScaleGestureDetector = new ScaleGestureDetector(this);
+        public MagnifiedContentInteractonStateHandler(Context context) {
+            mScaleGestureDetector = new ScaleGestureDetector(context, this);
+            mGestureDetector = new GestureDetector(context, this);
         }
 
         public void onMotionEvent(MotionEvent event) {
             mScaleGestureDetector.onTouchEvent(event);
-            switch (mCurrentState) {
-                case STATE_DETECTING:
-                case STATE_DELEGATING:
-                case STATE_VIEWPORT_DRAGGING: {
-                    return;
-                }
+            mGestureDetector.onTouchEvent(event);
+            if (mCurrentState != STATE_MAGNIFIED_INTERACTION) {
+                return;
             }
             if (event.getActionMasked() == MotionEvent.ACTION_UP) {
                 clear();
@@ -401,121 +387,62 @@
         }
 
         @Override
-        public boolean onScale(ScaleGestureDetector detector) {
-            switch (mCurrentState) {
-                case STATE_DETECTING:
-                case STATE_DELEGATING:
-                case STATE_VIEWPORT_DRAGGING: {
-                    return true;
-                }
-                case STATE_MAGNIFIED_INTERACTION: {
-                    mCurrScaleFactor = mScaleGestureDetector.getScaleFactor();
-                    final float scaleDelta = Math.abs(1.0f - mCurrScaleFactor * mPrevScaleFactor);
-                    if (DEBUG_GESTURE_DETECTOR) {
-                        Slog.i(LOG_TAG, "scaleDelta: " + scaleDelta);
-                    }
-                    if (!mScaling && scaleDelta > DETECT_SCALING_THRESHOLD) {
-                        mScaling = true;
-                        clearContextualState();
-                        return true;
-                    }
-                    if (mScaling) {
-                        performScale(detector);
-                    }
-                    mCurrPan = (float) MathUtils.dist(
-                            mScaleGestureDetector.getFocusX(),
-                            mScaleGestureDetector.getFocusY(),
-                            mInitialFocus.x, mInitialFocus.y);
-                    final float panDelta = mCurrPan + mPrevPan;
-                    if (DEBUG_GESTURE_DETECTOR) {
-                        Slog.i(LOG_TAG, "panDelta: " + panDelta);
-                    }
-                    if (!mPanning && panDelta > mScaledDetectPanningThreshold) {
-                        mPanning = true;
-                        clearContextualState();
-                        return true;
-                    }
-                    if (mPanning) {
-                        performPan(detector);
-                    }
-                } break;
-            }
-            return false;
-        }
-
-        @Override
-        public boolean onScaleBegin(ScaleGestureDetector detector) {
-            mPrevScaleFactor *= mCurrScaleFactor;
-            mCurrScale = Float.NaN;
-            mPrevPan += mCurrPan;
-            mPrevFocus.x = mInitialFocus.x = detector.getFocusX();
-            mPrevFocus.y = mInitialFocus.y = detector.getFocusY();
-            return true;
-        }
-
-        @Override
-        public void onScaleEnd(ScaleGestureDetector detector) {
-            clearContextualState();
-        }
-
-        public void clear() {
-            clearContextualState();
-            mScaling = false;
-            mPanning = false;
-        }
-
-        private void clearContextualState() {
-            mCurrScaleFactor = 1.0f;
-            mPrevScaleFactor = 1.0f;
-            mPrevPan = 0;
-            mCurrPan = 0;
-            mInitialFocus.set(Float.NaN, Float.NaN);
-            mPrevFocus.set(Float.NaN, Float.NaN);
-            mCurrScale = Float.NaN;
-            mScaleFocusX = Float.NaN;
-            mScaleFocusY = Float.NaN;
-        }
-
-        private void performPan(ScaleGestureDetector detector) {
-            if (Float.compare(mPrevFocus.x, Float.NaN) == 0
-                    && Float.compare(mPrevFocus.y, Float.NaN) == 0) {
-                mPrevFocus.set(detector.getFocusX(), detector.getFocusY());
-                return;
+        public boolean onScroll(MotionEvent first, MotionEvent second, float distanceX,
+                float distanceY) {
+            if (mCurrentState != STATE_MAGNIFIED_INTERACTION) {
+                return true;
             }
             final float scale = mMagnificationController.getScale();
-            final float scrollX = (detector.getFocusX() - mPrevFocus.x) / scale;
-            final float scrollY = (detector.getFocusY() - mPrevFocus.y) / scale;
-            final float centerX = mMagnificationController.getMagnifiedRegionCenterX()
-                    - scrollX;
-            final float centerY = mMagnificationController.getMagnifiedRegionCenterY()
-                    - scrollY;
+            final float scrollX = distanceX / scale;
+            final float scrollY = distanceY / scale;
+            final float centerX = mMagnificationController.getMagnifiedRegionCenterX() + scrollX;
+            final float centerY = mMagnificationController.getMagnifiedRegionCenterY() + scrollY;
             if (DEBUG_PANNING) {
                 Slog.i(LOG_TAG, "Panned content by scrollX: " + scrollX
                         + " scrollY: " + scrollY);
             }
             mMagnificationController.setMagnifiedRegionCenter(centerX, centerY, false);
-            mPrevFocus.set(detector.getFocusX(), detector.getFocusY());
+            return true;
         }
 
-        private void performScale(ScaleGestureDetector detector) {
-            if (Float.compare(mCurrScale, Float.NaN) == 0) {
-                mCurrScale = mMagnificationController.getScale();
-                return;
+        @Override
+        public boolean onScale(ScaleGestureDetector detector) {
+            if (!mScaling) {
+                if (mInitialScaleFactor < 0) {
+                    mInitialScaleFactor = detector.getScaleFactor();
+                } else {
+                    final float deltaScale = detector.getScaleFactor() - mInitialScaleFactor;
+                    if (Math.abs(deltaScale) > SCALING_THRESHOLD) {
+                        mScaling = true;
+                        return true;
+                    }
+                }
+                return false;
             }
-            final float totalScaleFactor = mPrevScaleFactor * detector.getScaleFactor();
-            final float newScale = mCurrScale * totalScaleFactor;
-            final float normalizedNewScale = Math.min(Math.max(newScale, MIN_SCALE),
-                    MAX_SCALE);
+            final float newScale = mMagnificationController.getScale()
+                    * detector.getScaleFactor();
+            final float normalizedNewScale = Math.min(Math.max(newScale, MIN_SCALE), MAX_SCALE);
             if (DEBUG_SCALING) {
                 Slog.i(LOG_TAG, "normalizedNewScale: " + normalizedNewScale);
             }
-            if (Float.compare(mScaleFocusX, Float.NaN) == 0
-                    && Float.compare(mScaleFocusY, Float.NaN) == 0) {
-                mScaleFocusX = detector.getFocusX();
-                mScaleFocusY = detector.getFocusY();
-            }
-            mMagnificationController.setScale(normalizedNewScale, mScaleFocusX,
-                    mScaleFocusY, false);
+            mMagnificationController.setScale(normalizedNewScale, detector.getFocusX(),
+                    detector.getFocusY(), false);
+            return true;
+        }
+
+        @Override
+        public boolean onScaleBegin(ScaleGestureDetector detector) {
+            return (mCurrentState == STATE_MAGNIFIED_INTERACTION);
+        }
+
+        @Override
+        public void onScaleEnd(ScaleGestureDetector detector) {
+            clear();
+        }
+
+        private void clear() {
+            mInitialScaleFactor = -1;
+            mScaling = false;
         }
     }
 
@@ -1020,7 +947,9 @@
                 }
                 if (info.type == WindowManager.LayoutParams.TYPE_NAVIGATION_BAR
                         || info.type == WindowManager.LayoutParams.TYPE_INPUT_METHOD
-                        || info.type == WindowManager.LayoutParams.TYPE_INPUT_METHOD_DIALOG) {
+                        || info.type == WindowManager.LayoutParams.TYPE_INPUT_METHOD_DIALOG
+                        || info.type == WindowManager.LayoutParams.TYPE_KEYGUARD
+                        || info.type == WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG) {
                     switch (transition) {
                         case WindowManagerPolicy.TRANSIT_ENTER:
                         case WindowManagerPolicy.TRANSIT_SHOW:
@@ -1473,7 +1402,9 @@
 
         private final ArrayList<WindowInfo> mTempWindowInfoList = new ArrayList<WindowInfo>();
 
-        private final Rect mTempRect = new Rect();
+        private final Rect mTempRect1 = new Rect();
+        private final Rect mTempRect2 = new Rect();
+        private final Rect mTempRect3 = new Rect();
 
         private final IWindowManager mWindowManagerService;
         private final DisplayProvider mDisplayProvider;
@@ -1542,31 +1473,83 @@
             recomputeBounds(false);
         }
 
+        private final Comparator<WindowInfo> mWindowInfoInverseComparator =
+                new Comparator<WindowInfo>() {
+            @Override
+            public int compare(WindowInfo lhs, WindowInfo rhs) {
+                if (lhs.layer != rhs.layer) {
+                    return rhs.layer - lhs.layer;
+                }
+                if (lhs.touchableRegion.top != rhs.touchableRegion.top) {
+                    return rhs.touchableRegion.top - lhs.touchableRegion.top;
+                }
+                if (lhs.touchableRegion.left != rhs.touchableRegion.left) {
+                    return rhs.touchableRegion.left - lhs.touchableRegion.left;
+                }
+                if (lhs.touchableRegion.right != rhs.touchableRegion.right) {
+                    return rhs.touchableRegion.right - lhs.touchableRegion.right;
+                }
+                if (lhs.touchableRegion.bottom != rhs.touchableRegion.bottom) {
+                    return rhs.touchableRegion.bottom - lhs.touchableRegion.bottom;
+                }
+                return 0;
+            }
+        };
+
         public void recomputeBounds(boolean animate) {
-            Rect frame = mTempRect;
-            frame.set(0, 0, mDisplayProvider.getDisplayInfo().logicalWidth,
-                    mDisplayProvider.getDisplayInfo().logicalHeight);
+            Rect magnifiedFrame = mTempRect1;
+            magnifiedFrame.set(0, 0, 0, 0);
+
+            Rect notMagnifiedFrame = mTempRect2;
+            notMagnifiedFrame.set(0, 0, 0, 0);
+
             ArrayList<WindowInfo> infos = mTempWindowInfoList;
             infos.clear();
+            int windowCount = 0;
             try {
                 mWindowManagerService.getVisibleWindowsForDisplay(
                         mDisplayProvider.getDisplay().getDisplayId(), infos);
-                final int windowCount = infos.size();
+                Collections.sort(infos, mWindowInfoInverseComparator);
+                windowCount = infos.size();
                 for (int i = 0; i < windowCount; i++) {
                     WindowInfo info = infos.get(i);
-                    if (info.type == WindowManager.LayoutParams.TYPE_NAVIGATION_BAR
-                            || info.type == WindowManager.LayoutParams.TYPE_INPUT_METHOD
-                            || info.type == WindowManager.LayoutParams.TYPE_INPUT_METHOD_DIALOG) {
-                        subtract(frame, info.touchableRegion);
+                    if (info.type == WindowManager.LayoutParams.TYPE_MAGNIFICATION_OVERLAY) {
+                        continue;
                     }
-                    info.recycle();
+                    if (isWindowMagnified(info.type)) {
+                        Rect clippedFrame = mTempRect3;
+                        clippedFrame.set(info.touchableRegion);
+                        subtract(clippedFrame, notMagnifiedFrame);
+                        magnifiedFrame.union(clippedFrame);
+                    } else {
+                        Rect clippedFrame = mTempRect3;
+                        clippedFrame.set(info.touchableRegion);
+                        subtract(clippedFrame, magnifiedFrame);
+                        notMagnifiedFrame.union(clippedFrame);
+                    }
+                    if (magnifiedFrame.bottom >= notMagnifiedFrame.top) {
+                        break;
+                    }
                 }
             } catch (RemoteException re) {
                 /* ignore */
             } finally {
-                infos.clear();
+                for (int i = windowCount - 1; i >= 0; i--) {
+                    infos.remove(i).recycle();
+                }
             }
-            resize(frame, animate);
+
+            final int displayWidth = mDisplayProvider.getDisplayInfo().logicalWidth;
+            final int displayHeight = mDisplayProvider.getDisplayInfo().logicalHeight;
+            magnifiedFrame.intersect(0, 0, displayWidth, displayHeight);
+
+            resize(magnifiedFrame, animate);
+        }
+
+        private boolean isWindowMagnified(int type) {
+            return (type != WindowManager.LayoutParams.TYPE_NAVIGATION_BAR
+                    && type != WindowManager.LayoutParams.TYPE_INPUT_METHOD
+                    && type != WindowManager.LayoutParams.TYPE_INPUT_METHOD_DIALOG);
         }
 
         public void rotationChanged() {
@@ -1812,482 +1795,4 @@
             updateDisplayInfo();
         }
     }
-
-    /**
-     * The listener for receiving notifications when gestures occur.
-     * If you want to listen for all the different gestures then implement
-     * this interface. If you only want to listen for a subset it might
-     * be easier to extend {@link SimpleOnScaleGestureListener}.
-     *
-     * An application will receive events in the following order:
-     * <ul>
-     *  <li>One {@link OnScaleGestureListener#onScaleBegin(ScaleGestureDetector)}
-     *  <li>Zero or more {@link OnScaleGestureListener#onScale(ScaleGestureDetector)}
-     *  <li>One {@link OnScaleGestureListener#onScaleEnd(ScaleGestureDetector)}
-     * </ul>
-     */
-    interface OnScaleGestureListener {
-        /**
-         * Responds to scaling events for a gesture in progress.
-         * Reported by pointer motion.
-         *
-         * @param detector The detector reporting the event - use this to
-         *          retrieve extended info about event state.
-         * @return Whether or not the detector should consider this event
-         *          as handled. If an event was not handled, the detector
-         *          will continue to accumulate movement until an event is
-         *          handled. This can be useful if an application, for example,
-         *          only wants to update scaling factors if the change is
-         *          greater than 0.01.
-         */
-        public boolean onScale(ScaleGestureDetector detector);
-
-        /**
-         * Responds to the beginning of a scaling gesture. Reported by
-         * new pointers going down.
-         *
-         * @param detector The detector reporting the event - use this to
-         *          retrieve extended info about event state.
-         * @return Whether or not the detector should continue recognizing
-         *          this gesture. For example, if a gesture is beginning
-         *          with a focal point outside of a region where it makes
-         *          sense, onScaleBegin() may return false to ignore the
-         *          rest of the gesture.
-         */
-        public boolean onScaleBegin(ScaleGestureDetector detector);
-
-        /**
-         * Responds to the end of a scale gesture. Reported by existing
-         * pointers going up.
-         *
-         * Once a scale has ended, {@link ScaleGestureDetector#getFocusX()}
-         * and {@link ScaleGestureDetector#getFocusY()} will return the location
-         * of the pointer remaining on the screen.
-         *
-         * @param detector The detector reporting the event - use this to
-         *          retrieve extended info about event state.
-         */
-        public void onScaleEnd(ScaleGestureDetector detector);
-    }
-
-    class ScaleGestureDetector {
-
-        private final MinCircleFinder mMinCircleFinder = new MinCircleFinder();
-
-        private final OnScaleGestureListener mListener;
-
-        private float mFocusX;
-        private float mFocusY;
-
-        private float mCurrSpan;
-        private float mPrevSpan;
-        private float mCurrSpanX;
-        private float mCurrSpanY;
-        private float mPrevSpanX;
-        private float mPrevSpanY;
-        private long mCurrTime;
-        private long mPrevTime;
-        private boolean mInProgress;
-
-        public ScaleGestureDetector(OnScaleGestureListener listener) {
-            mListener = listener;
-        }
-
-        /**
-         * Accepts MotionEvents and dispatches events to a {@link OnScaleGestureListener}
-         * when appropriate.
-         *
-         * <p>Applications should pass a complete and consistent event stream to this method.
-         * A complete and consistent event stream involves all MotionEvents from the initial
-         * ACTION_DOWN to the final ACTION_UP or ACTION_CANCEL.</p>
-         *
-         * @param event The event to process
-         * @return true if the event was processed and the detector wants to receive the
-         *         rest of the MotionEvents in this event stream.
-         */
-        public boolean onTouchEvent(MotionEvent event) {
-            boolean streamEnded = false;
-            boolean contextChanged = false;
-            int excludedPtrIdx = -1;
-            final int action = event.getActionMasked();
-            switch (action) {
-                case MotionEvent.ACTION_DOWN:
-                case MotionEvent.ACTION_POINTER_DOWN: {
-                    contextChanged = true;
-                } break;
-                case MotionEvent.ACTION_POINTER_UP: {
-                    contextChanged = true;
-                    excludedPtrIdx = event.getActionIndex();
-                } break;
-                case MotionEvent.ACTION_UP:
-                case MotionEvent.ACTION_CANCEL: {
-                    streamEnded = true;
-                } break;
-            }
-
-            if (mInProgress && (contextChanged || streamEnded)) {
-                mListener.onScaleEnd(this);
-                mInProgress = false;
-                mPrevSpan = 0;
-                mPrevSpanX = 0;
-                mPrevSpanY = 0;
-                return true;
-            }
-
-            final long currTime = mCurrTime;
-
-            mFocusX = 0;
-            mFocusY = 0;
-            mCurrSpan = 0;
-            mCurrSpanX = 0;
-            mCurrSpanY = 0;
-            mCurrTime = 0;
-            mPrevTime = 0;
-
-            if (!streamEnded) {
-                MinCircleFinder.Circle circle =
-                        mMinCircleFinder.computeMinCircleAroundPointers(event);
-                mFocusX = circle.centerX;
-                mFocusY = circle.centerY;
-
-                double sumSlope = 0;
-                final int pointerCount = event.getPointerCount();
-                for (int i = 0; i < pointerCount; i++) {
-                    if (i == excludedPtrIdx) {
-                        continue;
-                    }
-                    float x = event.getX(i) - mFocusX;
-                    float y = event.getY(i) - mFocusY;
-                    if (x == 0) {
-                        x += 0.1f;
-                    }
-                    sumSlope += y / x;
-                }
-                final double avgSlope = sumSlope
-                        / ((excludedPtrIdx < 0) ? pointerCount : pointerCount - 1);
-
-                double angle = Math.atan(avgSlope);
-                mCurrSpan = 2 * circle.radius;
-                mCurrSpanX = (float) Math.abs((Math.cos(angle) * mCurrSpan));
-                mCurrSpanY = (float) Math.abs((Math.sin(angle) * mCurrSpan));
-            }
-
-            if (contextChanged || mPrevSpan == 0 || mPrevSpanX == 0 || mPrevSpanY == 0) {
-                mPrevSpan = mCurrSpan;
-                mPrevSpanX = mCurrSpanX;
-                mPrevSpanY = mCurrSpanY;
-            }
-
-            if (!mInProgress && mCurrSpan != 0 && !streamEnded) {
-                mInProgress = mListener.onScaleBegin(this);
-            }
-
-            if (mInProgress) {
-                mPrevTime = (currTime != 0) ? currTime : event.getEventTime();
-                mCurrTime = event.getEventTime();
-                if (mCurrSpan == 0) {
-                    mListener.onScaleEnd(this);
-                    mInProgress = false;
-                } else {
-                    if (mListener.onScale(this)) {
-                        mPrevSpanX = mCurrSpanX;
-                        mPrevSpanY = mCurrSpanY;
-                        mPrevSpan = mCurrSpan;
-                    }
-                }
-            }
-
-            return true;
-        }
-
-        /**
-         * Returns {@code true} if a scale gesture is in progress.
-         */
-        public boolean isInProgress() {
-            return mInProgress;
-        }
-
-        /**
-         * Get the X coordinate of the current gesture's focal point.
-         * If a gesture is in progress, the focal point is between
-         * each of the pointers forming the gesture.
-         *
-         * If {@link #isInProgress()} would return false, the result of this
-         * function is undefined.
-         *
-         * @return X coordinate of the focal point in pixels.
-         */
-        public float getFocusX() {
-            return mFocusX;
-        }
-
-        /**
-         * Get the Y coordinate of the current gesture's focal point.
-         * If a gesture is in progress, the focal point is between
-         * each of the pointers forming the gesture.
-         *
-         * If {@link #isInProgress()} would return false, the result of this
-         * function is undefined.
-         *
-         * @return Y coordinate of the focal point in pixels.
-         */
-        public float getFocusY() {
-            return mFocusY;
-        }
-
-        /**
-         * Return the average distance between each of the pointers forming the
-         * gesture in progress through the focal point.
-         *
-         * @return Distance between pointers in pixels.
-         */
-        public float getCurrentSpan() {
-            return mCurrSpan;
-        }
-
-        /**
-         * Return the average X distance between each of the pointers forming the
-         * gesture in progress through the focal point.
-         *
-         * @return Distance between pointers in pixels.
-         */
-        public float getCurrentSpanX() {
-            return mCurrSpanX;
-        }
-
-        /**
-         * Return the average Y distance between each of the pointers forming the
-         * gesture in progress through the focal point.
-         *
-         * @return Distance between pointers in pixels.
-         */
-        public float getCurrentSpanY() {
-            return mCurrSpanY;
-        }
-
-        /**
-         * Return the previous average distance between each of the pointers forming the
-         * gesture in progress through the focal point.
-         *
-         * @return Previous distance between pointers in pixels.
-         */
-        public float getPreviousSpan() {
-            return mPrevSpan;
-        }
-
-        /**
-         * Return the previous average X distance between each of the pointers forming the
-         * gesture in progress through the focal point.
-         *
-         * @return Previous distance between pointers in pixels.
-         */
-        public float getPreviousSpanX() {
-            return mPrevSpanX;
-        }
-
-        /**
-         * Return the previous average Y distance between each of the pointers forming the
-         * gesture in progress through the focal point.
-         *
-         * @return Previous distance between pointers in pixels.
-         */
-        public float getPreviousSpanY() {
-            return mPrevSpanY;
-        }
-
-        /**
-         * Return the scaling factor from the previous scale event to the current
-         * event. This value is defined as
-         * ({@link #getCurrentSpan()} / {@link #getPreviousSpan()}).
-         *
-         * @return The current scaling factor.
-         */
-        public float getScaleFactor() {
-            return mPrevSpan > 0 ? mCurrSpan / mPrevSpan : 1;
-        }
-
-        /**
-         * Return the time difference in milliseconds between the previous
-         * accepted scaling event and the current scaling event.
-         *
-         * @return Time difference since the last scaling event in milliseconds.
-         */
-        public long getTimeDelta() {
-            return mCurrTime - mPrevTime;
-        }
-
-        /**
-         * Return the event time of the current event being processed.
-         *
-         * @return Current event time in milliseconds.
-         */
-        public long getEventTime() {
-            return mCurrTime;
-        }
-    }
-
-    private static final class MinCircleFinder {
-        private final ArrayList<PointHolder> mPoints = new ArrayList<PointHolder>();
-        private final ArrayList<PointHolder> sBoundary = new ArrayList<PointHolder>();
-        private final Circle mMinCircle = new Circle();
-
-        /**
-         * Finds the minimal circle that contains all pointers of a motion event.
-         *
-         * @param event A motion event.
-         * @return The minimal circle.
-         */
-        public Circle computeMinCircleAroundPointers(MotionEvent event) {
-            ArrayList<PointHolder> points = mPoints;
-            points.clear();
-            final int pointerCount = event.getPointerCount();
-            for (int i = 0; i < pointerCount; i++) {
-                PointHolder point = PointHolder.obtain(event.getX(i), event.getY(i));
-                points.add(point);
-            }
-            ArrayList<PointHolder> boundary = sBoundary;
-            boundary.clear();
-            computeMinCircleAroundPointsRecursive(points, boundary, mMinCircle);
-            for (int i = points.size() - 1; i >= 0; i--) {
-                points.remove(i).recycle();
-            }
-            boundary.clear();
-            return mMinCircle;
-        }
-
-        private static void computeMinCircleAroundPointsRecursive(ArrayList<PointHolder> points,
-                ArrayList<PointHolder> boundary, Circle outCircle) {
-            if (points.isEmpty()) {
-                if (boundary.size() == 0) {
-                    outCircle.initialize();
-                } else if (boundary.size() == 1) {
-                    outCircle.initialize(boundary.get(0).mData, boundary.get(0).mData);
-                } else if (boundary.size() == 2) {
-                    outCircle.initialize(boundary.get(0).mData, boundary.get(1).mData);
-                } else if (boundary.size() == 3) {
-                    outCircle.initialize(boundary.get(0).mData, boundary.get(1).mData,
-                            boundary.get(2).mData);
-                }
-                return;
-            }
-            PointHolder point = points.remove(points.size() - 1);
-            computeMinCircleAroundPointsRecursive(points, boundary, outCircle);
-            if (!outCircle.contains(point.mData)) {
-                boundary.add(point);
-                computeMinCircleAroundPointsRecursive(points, boundary, outCircle);
-                boundary.remove(point);
-            }
-            points.add(point);
-        }
-
-        private static final class PointHolder {
-            private static final int MAX_POOL_SIZE = 20;
-            private static PointHolder sPool;
-            private static int sPoolSize;
-
-            private PointHolder mNext;
-            private boolean mIsInPool;
-
-            private final PointF mData = new PointF();
-
-            public static PointHolder obtain(float x, float y) {
-                PointHolder holder;
-                if (sPoolSize > 0) {
-                    sPoolSize--;
-                    holder = sPool;
-                    sPool = sPool.mNext;
-                    holder.mNext = null;
-                    holder.mIsInPool = false;
-                } else {
-                    holder = new PointHolder();
-                }
-                holder.mData.set(x, y);
-                return holder;
-            }
-
-            public void recycle() {
-                if (mIsInPool) {
-                    throw new IllegalStateException("Already recycled.");
-                }
-                clear();
-                if (sPoolSize < MAX_POOL_SIZE) {
-                    sPoolSize++;
-                    mNext = sPool;
-                    sPool = this;
-                    mIsInPool = true;
-                }
-            }
-
-            private void clear() {
-                mData.set(0, 0);
-            }
-        }
-
-        public static final class Circle {
-            public float centerX;
-            public float centerY;
-            public float radius;
-
-            private void initialize() {
-                centerX = 0;
-                centerY = 0;
-                radius = 0;
-            }
-
-            private void initialize(PointF first, PointF second, PointF third) {
-                if (!hasLineWithInfiniteSlope(first, second, third)) {
-                    initializeInternal(first, second, third);
-                } else if (!hasLineWithInfiniteSlope(first, third, second)) {
-                    initializeInternal(first, third, second);
-                } else if (!hasLineWithInfiniteSlope(second, first, third)) {
-                    initializeInternal(second, first, third);
-                } else if (!hasLineWithInfiniteSlope(second, third, first)) {
-                    initializeInternal(second, third, first);
-                } else if (!hasLineWithInfiniteSlope(third, first, second)) {
-                    initializeInternal(third, first, second);
-                } else if (!hasLineWithInfiniteSlope(third, second, first)) {
-                    initializeInternal(third, second, first);
-                } else {
-                    initialize();
-                }
-            }
-
-            private void initialize(PointF first, PointF second) {
-                radius = (float) (Math.hypot(second.x - first.x, second.y - first.y) / 2);
-                centerX = (float) (second.x + first.x) / 2;
-                centerY = (float) (second.y + first.y) / 2;
-            }
-
-            public boolean contains(PointF point) {
-                return (int) (Math.hypot(point.x - centerX, point.y - centerY)) <= radius;
-            }
-
-            private void initializeInternal(PointF first, PointF second, PointF third) {
-                final float x1 = first.x;
-                final float y1 = first.y;
-                final float x2 = second.x;
-                final float y2 = second.y;
-                final float x3 = third.x;
-                final float y3 = third.y;
-
-                final float sl1 = (y2 - y1) / (x2 - x1);
-                final float sl2 = (y3 - y2) / (x3 - x2);
-
-                centerX = (int) ((sl1 * sl2 * (y1 - y3) + sl2 * (x1 + x2) - sl1 * (x2 + x3))
-                        / (2 * (sl2 - sl1)));
-                centerY = (int) (-1 / sl1 * (centerX - (x1 + x2) / 2) + (y1 + y2) / 2);
-                radius = (int) Math.hypot(x1 - centerX, y1 - centerY);
-            }
-
-            private boolean hasLineWithInfiniteSlope(PointF first, PointF second, PointF third) {
-                return (second.x - first.x == 0 || third.x - second.x == 0
-                        || second.y - first.y == 0 || third.y - second.y == 0);
-            }
-
-            @Override
-            public String toString() {
-                return "cetner: [" + centerX + ", " + centerY + "] radius: " + radius;
-            }
-        }
-    }
 }
diff --git a/services/java/com/android/server/am/ActiveServices.java b/services/java/com/android/server/am/ActiveServices.java
index 3bb95a8..aefc264 100644
--- a/services/java/com/android/server/am/ActiveServices.java
+++ b/services/java/com/android/server/am/ActiveServices.java
@@ -478,12 +478,6 @@
         if (res.record == null) {
             return -1;
         }
-        if (mAm.isSingleton(res.record.processName, res.record.appInfo,
-                res.record.serviceInfo.name, res.record.serviceInfo.flags)) {
-            userId = 0;
-            res = retrieveServiceLocked(service, resolvedType, Binder.getCallingPid(),
-                    Binder.getCallingUid(), 0, true);
-        }
         ServiceRecord s = res.record;
 
         final long origId = Binder.clearCallingIdentity();
diff --git a/services/java/com/android/server/display/WifiDisplayAdapter.java b/services/java/com/android/server/display/WifiDisplayAdapter.java
index ee82050..b75940e 100644
--- a/services/java/com/android/server/display/WifiDisplayAdapter.java
+++ b/services/java/com/android/server/display/WifiDisplayAdapter.java
@@ -27,7 +27,6 @@
 import android.media.RemoteDisplay;
 import android.os.Handler;
 import android.os.IBinder;
-import android.util.Slog;
 import android.view.Surface;
 
 import java.io.PrintWriter;
@@ -50,8 +49,8 @@
 final class WifiDisplayAdapter extends DisplayAdapter {
     private static final String TAG = "WifiDisplayAdapter";
 
-    private WifiDisplayHandle mDisplayHandle;
     private WifiDisplayController mDisplayController;
+    private WifiDisplayDevice mDisplayDevice;
 
     private WifiDisplayStatus mCurrentStatus;
     private boolean mEnabled;
@@ -71,13 +70,6 @@
     public void dumpLocked(PrintWriter pw) {
         super.dumpLocked(pw);
 
-        if (mDisplayHandle == null) {
-            pw.println("mDisplayHandle=null");
-        } else {
-            pw.println("mDisplayHandle:");
-            mDisplayHandle.dumpLocked(pw);
-        }
-
         pw.println("mCurrentStatus=" + getWifiDisplayStatusLocked());
         pw.println("mEnabled=" + mEnabled);
         pw.println("mScanState=" + mScanState);
@@ -151,16 +143,29 @@
         return mCurrentStatus;
     }
 
-    private void handleConnectLocked(WifiDisplay display, String iface) {
+    private void handleConnectLocked(WifiDisplay display,
+            Surface surface, int width, int height, int flags) {
         handleDisconnectLocked();
 
-        mDisplayHandle = new WifiDisplayHandle(display.getDeviceName(), iface);
+        int deviceFlags = 0;
+        if ((flags & RemoteDisplay.DISPLAY_FLAG_SECURE) != 0) {
+            deviceFlags |= DisplayDeviceInfo.FLAG_SECURE;
+        }
+
+        float refreshRate = 60.0f; // TODO: get this for real
+
+        String name = display.getDeviceName();
+        IBinder displayToken = Surface.createDisplay(name);
+        mDisplayDevice = new WifiDisplayDevice(displayToken, name, width, height,
+                refreshRate, deviceFlags, surface);
+        sendDisplayDeviceEventLocked(mDisplayDevice, DISPLAY_DEVICE_EVENT_ADDED);
     }
 
     private void handleDisconnectLocked() {
-        if (mDisplayHandle != null) {
-            mDisplayHandle.disposeLocked();
-            mDisplayHandle = null;
+        if (mDisplayDevice != null) {
+            mDisplayDevice.clearSurfaceLocked();
+            sendDisplayDeviceEventLocked(mDisplayDevice, DISPLAY_DEVICE_EVENT_REMOVED);
+            mDisplayDevice = null;
         }
     }
 
@@ -258,9 +263,10 @@
         }
 
         @Override
-        public void onDisplayConnected(WifiDisplay display, String iface) {
+        public void onDisplayConnected(WifiDisplay display, Surface surface,
+                int width, int height, int flags) {
             synchronized (getSyncRoot()) {
-                handleConnectLocked(display, iface);
+                handleConnectLocked(display, surface, width, height, flags);
 
                 if (mActiveDisplayState != WifiDisplayStatus.DISPLAY_STATE_CONNECTED
                         || mActiveDisplay == null
@@ -337,92 +343,4 @@
             return mInfo;
         }
     }
-
-    private final class WifiDisplayHandle implements RemoteDisplay.Listener {
-        private final String mName;
-        private final String mIface;
-        private final RemoteDisplay mRemoteDisplay;
-
-        private WifiDisplayDevice mDevice;
-        private int mLastError;
-
-        public WifiDisplayHandle(String name, String iface) {
-            mName = name;
-            mIface = iface;
-            mRemoteDisplay = RemoteDisplay.listen(iface, this, getHandler());
-
-            Slog.i(TAG, "Listening for Wifi display connections on " + iface
-                    + " from " + mName);
-        }
-
-        public void disposeLocked() {
-            Slog.i(TAG, "Stopped listening for Wifi display connections on " + mIface
-                    + " from " + mName);
-
-            removeDisplayLocked();
-            mRemoteDisplay.dispose();
-        }
-
-        public void dumpLocked(PrintWriter pw) {
-            pw.println("  " + mName + ": " + (mDevice != null ? "connected" : "disconnected"));
-            pw.println("    mIface=" + mIface);
-            pw.println("    mLastError=" + mLastError);
-        }
-
-        // Called on the handler thread.
-        @Override
-        public void onDisplayConnected(Surface surface, int width, int height, int flags) {
-            synchronized (getSyncRoot()) {
-                mLastError = 0;
-                removeDisplayLocked();
-                addDisplayLocked(surface, width, height, flags);
-
-                Slog.i(TAG, "Wifi display connected: " + mName);
-            }
-        }
-
-        // Called on the handler thread.
-        @Override
-        public void onDisplayDisconnected() {
-            synchronized (getSyncRoot()) {
-                mLastError = 0;
-                removeDisplayLocked();
-
-                Slog.i(TAG, "Wifi display disconnected: " + mName);
-            }
-        }
-
-        // Called on the handler thread.
-        @Override
-        public void onDisplayError(int error) {
-            synchronized (getSyncRoot()) {
-                mLastError = error;
-                removeDisplayLocked();
-
-                Slog.i(TAG, "Wifi display disconnected due to error " + error + ": " + mName);
-            }
-        }
-
-        private void addDisplayLocked(Surface surface, int width, int height, int flags) {
-            int deviceFlags = 0;
-            if ((flags & RemoteDisplay.DISPLAY_FLAG_SECURE) != 0) {
-                deviceFlags |= DisplayDeviceInfo.FLAG_SECURE;
-            }
-
-            float refreshRate = 60.0f; // TODO: get this for real
-
-            IBinder displayToken = Surface.createDisplay(mName);
-            mDevice = new WifiDisplayDevice(displayToken, mName, width, height,
-                    refreshRate, deviceFlags, surface);
-            sendDisplayDeviceEventLocked(mDevice, DISPLAY_DEVICE_EVENT_ADDED);
-        }
-
-        private void removeDisplayLocked() {
-            if (mDevice != null) {
-                mDevice.clearSurfaceLocked();
-                sendDisplayDeviceEventLocked(mDevice, DISPLAY_DEVICE_EVENT_REMOVED);
-                mDevice = null;
-            }
-        }
-    }
 }
diff --git a/services/java/com/android/server/display/WifiDisplayController.java b/services/java/com/android/server/display/WifiDisplayController.java
index 67691df..6e0be55 100644
--- a/services/java/com/android/server/display/WifiDisplayController.java
+++ b/services/java/com/android/server/display/WifiDisplayController.java
@@ -23,6 +23,7 @@
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.hardware.display.WifiDisplay;
+import android.media.RemoteDisplay;
 import android.net.NetworkInfo;
 import android.net.wifi.p2p.WifiP2pConfig;
 import android.net.wifi.p2p.WifiP2pDevice;
@@ -36,6 +37,7 @@
 import android.net.wifi.p2p.WifiP2pManager.PeerListListener;
 import android.os.Handler;
 import android.util.Slog;
+import android.view.Surface;
 
 import java.io.PrintWriter;
 import java.net.Inet4Address;
@@ -64,6 +66,7 @@
     private static final int DEFAULT_CONTROL_PORT = 7236;
     private static final int MAX_THROUGHPUT = 50;
     private static final int CONNECTION_TIMEOUT_SECONDS = 30;
+    private static final int RTSP_TIMEOUT_SECONDS = 15;
 
     private static final int DISCOVER_PEERS_MAX_RETRIES = 10;
     private static final int DISCOVER_PEERS_RETRY_DELAY_MILLIS = 500;
@@ -104,12 +107,19 @@
     // The group info obtained after connecting.
     private WifiP2pGroup mConnectedDeviceGroupInfo;
 
-    // The device that we announced to the rest of the system.
-    private WifiP2pDevice mPublishedDevice;
-
     // Number of connection retries remaining.
     private int mConnectionRetriesLeft;
 
+    // The remote display that is listening on the connection.
+    // Created after the Wifi P2P network is connected.
+    private RemoteDisplay mRemoteDisplay;
+
+    // The remote display interface.
+    private String mRemoteDisplayInterface;
+
+    // True if RTSP has connected.
+    private boolean mRemoteDisplayConnected;
+
     public WifiDisplayController(Context context, Handler handler, Listener listener) {
         mContext = context;
         mHandler = handler;
@@ -135,8 +145,10 @@
         pw.println("mDesiredDevice=" + describeWifiP2pDevice(mDesiredDevice));
         pw.println("mConnectingDisplay=" + describeWifiP2pDevice(mConnectingDevice));
         pw.println("mConnectedDevice=" + describeWifiP2pDevice(mConnectedDevice));
-        pw.println("mPublishedDevice=" + describeWifiP2pDevice(mPublishedDevice));
         pw.println("mConnectionRetriesLeft=" + mConnectionRetriesLeft);
+        pw.println("mRemoteDisplay=" + mRemoteDisplay);
+        pw.println("mRemoteDisplayInterface=" + mRemoteDisplayInterface);
+        pw.println("mRemoteDisplayConnected=" + mRemoteDisplayConnected);
 
         pw.println("mKnownWifiDisplayPeers: size=" + mKnownWifiDisplayPeers.size());
         for (WifiP2pDevice device : mKnownWifiDisplayPeers) {
@@ -179,7 +191,6 @@
                     if (mWfdEnabling) {
                         mWfdEnabling = false;
                         setWfdEnabled(true);
-                        discoverPeers();
                     }
                 }
 
@@ -342,7 +353,7 @@
     }
 
     private void retryConnection() {
-        if (mDesiredDevice != null && mPublishedDevice != mDesiredDevice
+        if (mDesiredDevice != null && mConnectedDevice != mDesiredDevice
                 && mConnectionRetriesLeft > 0) {
             mConnectionRetriesLeft -= 1;
             Slog.i(TAG, "Retrying Wifi display connection.  Retries left: "
@@ -364,14 +375,22 @@
     private void updateConnection() {
         // Step 1. Before we try to connect to a new device, tell the system we
         // have disconnected from the old one.
-        if (mPublishedDevice != null && mPublishedDevice != mDesiredDevice) {
+        if (mRemoteDisplay != null && mConnectedDevice != mDesiredDevice) {
+            Slog.i(TAG, "Stopped listening for RTSP connection on " + mRemoteDisplayInterface
+                    + " from Wifi display: " + mConnectedDevice.deviceName);
+
+            mRemoteDisplay.dispose();
+            mRemoteDisplay = null;
+            mRemoteDisplayInterface = null;
+            mRemoteDisplayConnected = false;
+            mHandler.removeCallbacks(mRtspTimeout);
+
             mHandler.post(new Runnable() {
                 @Override
                 public void run() {
                     mListener.onDisplayDisconnected();
                 }
             });
-            mPublishedDevice = null;
 
             // continue to next step
         }
@@ -472,9 +491,9 @@
 
                 @Override
                 public void onFailure(int reason) {
-                    Slog.i(TAG, "Failed to initiate connection to Wifi display: "
-                            + newDevice.deviceName + ", reason=" + reason);
                     if (mConnectingDevice == newDevice) {
+                        Slog.i(TAG, "Failed to initiate connection to Wifi display: "
+                                + newDevice.deviceName + ", reason=" + reason);
                         mConnectingDevice = null;
                         handleConnectionFailure(false);
                     }
@@ -483,8 +502,8 @@
             return; // wait for asynchronous callback
         }
 
-        // Step 6. Publish the new connection.
-        if (mConnectedDevice != null && mPublishedDevice == null) {
+        // Step 6. Listen for incoming connections.
+        if (mConnectedDevice != null && mRemoteDisplay == null) {
             Inet4Address addr = getInterfaceAddress(mConnectedDeviceGroupInfo);
             if (addr == null) {
                 Slog.i(TAG, "Failed to get local interface address for communicating "
@@ -493,23 +512,57 @@
                 return; // done
             }
 
-            int port = DEFAULT_CONTROL_PORT;
-            if (mConnectedDevice.deviceName.startsWith("DIRECT-")
-                    && mConnectedDevice.deviceName.endsWith("Broadcom")) {
-                // These dongles ignore the port we broadcast in our WFD IE.
-                port = 8554;
-            }
-
-            final WifiDisplay display = createWifiDisplay(mConnectedDevice);
+            final WifiP2pDevice oldDevice = mConnectedDevice;
+            final int port = getPortNumber(mConnectedDevice);
             final String iface = addr.getHostAddress() + ":" + port;
+            mRemoteDisplayInterface = iface;
 
-            mPublishedDevice = mConnectedDevice;
-            mHandler.post(new Runnable() {
+            Slog.i(TAG, "Listening for RTSP connection on " + iface
+                    + " from Wifi display: " + mConnectedDevice.deviceName);
+
+            mRemoteDisplay = RemoteDisplay.listen(iface, new RemoteDisplay.Listener() {
                 @Override
-                public void run() {
-                    mListener.onDisplayConnected(display, iface);
+                public void onDisplayConnected(final Surface surface,
+                        final int width, final int height, final int flags) {
+                    if (mConnectedDevice == oldDevice && !mRemoteDisplayConnected) {
+                        Slog.i(TAG, "Opened RTSP connection with Wifi display: "
+                                + mConnectedDevice.deviceName);
+                        mRemoteDisplayConnected = true;
+                        mHandler.removeCallbacks(mRtspTimeout);
+
+                        final WifiDisplay display = createWifiDisplay(mConnectedDevice);
+                        mHandler.post(new Runnable() {
+                            @Override
+                            public void run() {
+                                mListener.onDisplayConnected(display,
+                                        surface, width, height, flags);
+                            }
+                        });
+                    }
                 }
-            });
+
+                @Override
+                public void onDisplayDisconnected() {
+                    if (mConnectedDevice == oldDevice) {
+                        Slog.i(TAG, "Closed RTSP connection with Wifi display: "
+                                + mConnectedDevice.deviceName);
+                        mHandler.removeCallbacks(mRtspTimeout);
+                        disconnect();
+                    }
+                }
+
+                @Override
+                public void onDisplayError(int error) {
+                    if (mConnectedDevice == oldDevice) {
+                        Slog.i(TAG, "Lost RTSP connection with Wifi display due to error "
+                                + error + ": " + mConnectedDevice.deviceName);
+                        mHandler.removeCallbacks(mRtspTimeout);
+                        handleConnectionFailure(false);
+                    }
+                }
+            }, mHandler);
+
+            mHandler.postDelayed(mRtspTimeout, RTSP_TIMEOUT_SECONDS * 1000);
         }
     }
 
@@ -517,9 +570,7 @@
         if (mWifiP2pEnabled != enabled) {
             mWifiP2pEnabled = enabled;
             if (enabled) {
-                if (mWfdEnabled) {
-                    discoverPeers();
-                } else {
+                if (!mWfdEnabled) {
                     enableWfd();
                 }
             } else {
@@ -600,17 +651,30 @@
         }
     };
 
+    private final Runnable mRtspTimeout = new Runnable() {
+        @Override
+        public void run() {
+            if (mConnectedDevice != null
+                    && mRemoteDisplay != null && !mRemoteDisplayConnected) {
+                Slog.i(TAG, "Timed out waiting for Wifi display RTSP connection after "
+                        + RTSP_TIMEOUT_SECONDS + " seconds: "
+                        + mConnectedDevice.deviceName);
+                handleConnectionFailure(true);
+            }
+        }
+    };
+
     private void handleConnectionFailure(boolean timeoutOccurred) {
+        Slog.i(TAG, "Wifi display connection failed!");
+
+        mHandler.post(new Runnable() {
+            @Override
+            public void run() {
+                mListener.onDisplayConnectionFailed();
+            }
+        });
+
         if (mDesiredDevice != null) {
-            Slog.i(TAG, "Wifi display connection failed!");
-
-            mHandler.post(new Runnable() {
-                @Override
-                public void run() {
-                    mListener.onDisplayConnectionFailed();
-                }
-            });
-
             if (mConnectionRetriesLeft > 0) {
                 mHandler.postDelayed(new Runnable() {
                     @Override
@@ -647,12 +711,24 @@
         return null;
     }
 
+    private static int getPortNumber(WifiP2pDevice device) {
+        if (device.deviceName.startsWith("DIRECT-")
+                && device.deviceName.endsWith("Broadcom")) {
+            // These dongles ignore the port we broadcast in our WFD IE.
+            return 8554;
+        }
+        return DEFAULT_CONTROL_PORT;
+    }
+
     private static boolean isWifiDisplay(WifiP2pDevice device) {
-        // FIXME: the wfdInfo API doesn't work yet
-        return device.deviceName.startsWith("DWD-")
-                || device.deviceName.startsWith("DIRECT-")
-                || device.deviceName.startsWith("CAVM-");
-        //device.wfdInfo != null && device.wfdInfo.isWfdEnabled();
+        return device.wfdInfo != null
+                && device.wfdInfo.isWfdEnabled()
+                && isPrimarySinkDeviceType(device.wfdInfo.getDeviceType());
+    }
+
+    private static boolean isPrimarySinkDeviceType(int deviceType) {
+        return deviceType == WifiP2pWfdInfo.PRIMARY_SINK
+                || deviceType == WifiP2pWfdInfo.SOURCE_OR_PRIMARY_SINK;
     }
 
     private static String describeWifiP2pDevice(WifiP2pDevice device) {
@@ -711,7 +787,8 @@
 
         void onDisplayConnecting(WifiDisplay display);
         void onDisplayConnectionFailed();
-        void onDisplayConnected(WifiDisplay display, String iface);
+        void onDisplayConnected(WifiDisplay display,
+                Surface surface, int width, int height, int flags);
         void onDisplayDisconnected();
     }
 }
diff --git a/services/java/com/android/server/pm/Installer.java b/services/java/com/android/server/pm/Installer.java
index 4268ae0..ad85c0d 100644
--- a/services/java/com/android/server/pm/Installer.java
+++ b/services/java/com/android/server/pm/Installer.java
@@ -25,7 +25,7 @@
 import java.io.InputStream;
 import java.io.OutputStream;
 
-class Installer {
+public final class Installer {
     private static final String TAG = "Installer";
 
     private static final boolean LOCAL_DEBUG = false;
@@ -369,10 +369,10 @@
      */
     public int linkNativeLibraryDirectory(String dataPath, String nativeLibPath) {
         if (dataPath == null) {
-            Slog.e(TAG, "unlinkNativeLibraryDirectory dataPath is null");
+            Slog.e(TAG, "linkNativeLibraryDirectory dataPath is null");
             return -1;
         } else if (nativeLibPath == null) {
-            Slog.e(TAG, "unlinkNativeLibraryDirectory nativeLibPath is null");
+            Slog.e(TAG, "linkNativeLibraryDirectory nativeLibPath is null");
             return -1;
         }
 
@@ -383,16 +383,4 @@
 
         return execute(builder.toString());
     }
-
-    public int unlinkNativeLibraryDirectory(String dataPath) {
-        if (dataPath == null) {
-            Slog.e(TAG, "unlinkNativeLibraryDirectory dataPath is null");
-            return -1;
-        }
-
-        StringBuilder builder = new StringBuilder("unlinklib ");
-        builder.append(dataPath);
-
-        return execute(builder.toString());
-    }
 }
diff --git a/services/java/com/android/server/pm/PackageManagerService.java b/services/java/com/android/server/pm/PackageManagerService.java
index b5ae214..ba63efd 100644
--- a/services/java/com/android/server/pm/PackageManagerService.java
+++ b/services/java/com/android/server/pm/PackageManagerService.java
@@ -109,6 +109,7 @@
 import android.os.SystemClock;
 import android.os.SystemProperties;
 import android.os.UserHandle;
+import android.os.Environment.UserEnvironment;
 import android.provider.Settings.Secure;
 import android.security.SystemKeyStore;
 import android.util.DisplayMetrics;
@@ -214,7 +215,7 @@
     /**
      * Whether verification is enabled by default.
      */
-    private static final boolean DEFAULT_VERIFY_ENABLE = true;
+    private static final boolean DEFAULT_VERIFY_ENABLE = false;
 
     /**
      * The default maximum time to wait for the verification agent to return in
@@ -936,9 +937,10 @@
         }
     }
 
-    public static final IPackageManager main(Context context, boolean factoryTest,
-            boolean onlyCore) {
-        PackageManagerService m = new PackageManagerService(context, factoryTest, onlyCore);
+    public static final IPackageManager main(Context context, Installer installer,
+            boolean factoryTest, boolean onlyCore) {
+        PackageManagerService m = new PackageManagerService(context, installer,
+                factoryTest, onlyCore);
         ServiceManager.addService("package", m);
         return m;
     }
@@ -965,7 +967,8 @@
         return res;
     }
 
-    public PackageManagerService(Context context, boolean factoryTest, boolean onlyCore) {
+    public PackageManagerService(Context context, Installer installer,
+            boolean factoryTest, boolean onlyCore) {
         EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_START,
                 SystemClock.uptimeMillis());
 
@@ -1003,7 +1006,7 @@
             mSeparateProcesses = null;
         }
 
-        mInstaller = new Installer();
+        mInstaller = installer;
 
         WindowManager wm = (WindowManager)context.getSystemService(Context.WINDOW_SERVICE);
         Display d = wm.getDefaultDisplay();
@@ -4468,9 +4471,11 @@
             throws IOException {
         if (!nativeLibraryDir.isDirectory()) {
             nativeLibraryDir.delete();
+
             if (!nativeLibraryDir.mkdir()) {
                 throw new IOException("Cannot create " + nativeLibraryDir.getPath());
             }
+
             try {
                 Libcore.os.chmod(nativeLibraryDir.getPath(), S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH
                         | S_IXOTH);
@@ -4478,6 +4483,8 @@
                 throw new IOException("Cannot chmod native library directory "
                         + nativeLibraryDir.getPath(), e);
             }
+        } else if (!SELinux.restorecon(nativeLibraryDir)) {
+            throw new IOException("Cannot set SELinux context for " + nativeLibraryDir.getPath());
         }
 
         /*
@@ -6131,19 +6138,20 @@
                 mounted = true;
             } else {
                 final String status = Environment.getExternalStorageState();
-
-                mounted = status.equals(Environment.MEDIA_MOUNTED)
-                        || status.equals(Environment.MEDIA_MOUNTED_READ_ONLY);
+                mounted = (Environment.MEDIA_MOUNTED.equals(status)
+                        || Environment.MEDIA_MOUNTED_READ_ONLY.equals(status));
             }
 
             if (mounted) {
-                final File externalCacheDir = Environment
+                final UserEnvironment userEnv = new UserEnvironment(mStats.userHandle);
+
+                final File externalCacheDir = userEnv
                         .getExternalStorageAppCacheDirectory(mStats.packageName);
                 final long externalCacheSize = mContainerService
                         .calculateDirectorySize(externalCacheDir.getPath());
                 mStats.externalCacheSize = externalCacheSize;
 
-                final File externalDataDir = Environment
+                final File externalDataDir = userEnv
                         .getExternalStorageAppDataDirectory(mStats.packageName);
                 long externalDataSize = mContainerService.calculateDirectorySize(externalDataDir
                         .getPath());
@@ -6153,12 +6161,12 @@
                 }
                 mStats.externalDataSize = externalDataSize;
 
-                final File externalMediaDir = Environment
+                final File externalMediaDir = userEnv
                         .getExternalStorageAppMediaDirectory(mStats.packageName);
                 mStats.externalMediaSize = mContainerService
                         .calculateDirectorySize(externalMediaDir.getPath());
 
-                final File externalObbDir = Environment
+                final File externalObbDir = userEnv
                         .getExternalStorageAppObbDirectory(mStats.packageName);
                 mStats.externalObbSize = mContainerService.calculateDirectorySize(externalObbDir
                         .getPath());
@@ -8357,20 +8365,22 @@
                     if (conn.mContainerService == null) {
                         return;
                     }
-                    final File externalCacheDir = Environment
+
+                    final UserEnvironment userEnv = new UserEnvironment(curUser);
+                    final File externalCacheDir = userEnv
                             .getExternalStorageAppCacheDirectory(packageName);
                     try {
                         conn.mContainerService.clearDirectory(externalCacheDir.toString());
                     } catch (RemoteException e) {
                     }
                     if (allData) {
-                        final File externalDataDir = Environment
+                        final File externalDataDir = userEnv
                                 .getExternalStorageAppDataDirectory(packageName);
                         try {
                             conn.mContainerService.clearDirectory(externalDataDir.toString());
                         } catch (RemoteException e) {
                         }
-                        final File externalMediaDir = Environment
+                        final File externalMediaDir = userEnv
                                 .getExternalStorageAppMediaDirectory(packageName);
                         try {
                             conn.mContainerService.clearDirectory(externalMediaDir.toString());
diff --git a/services/java/com/android/server/pm/UserManagerService.java b/services/java/com/android/server/pm/UserManagerService.java
index 3391668..fc01f60 100644
--- a/services/java/com/android/server/pm/UserManagerService.java
+++ b/services/java/com/android/server/pm/UserManagerService.java
@@ -238,6 +238,18 @@
     }
 
     /**
+     * Check if we've hit the limit of how many users can be created.
+     */
+    private boolean isUserLimitReached() {
+        synchronized (mInstallLock) {
+            int nUsers = mUsers.size();
+            int userLimit = mContext.getResources().getInteger(
+                    com.android.internal.R.integer.config_multiuserMaximumUsers);
+            return nUsers >= userLimit;
+        }
+    }
+
+    /**
      * Enforces that only the system UID or root's UID or apps that have the
      * {@link android.Manifest.permission.MANAGE_USERS MANAGE_USERS}
      * permission can make certain calls to the UserManager.
@@ -522,6 +534,9 @@
     @Override
     public UserInfo createUser(String name, int flags) {
         checkManageUsersPermission("Only the system can create users");
+
+        if (isUserLimitReached()) return null;
+
         int userId = getNextAvailableId();
         UserInfo userInfo = new UserInfo(userId, name, null, flags);
         File userPath = new File(mBaseUserPath, Integer.toString(userId));
diff --git a/services/java/com/android/server/usb/UsbDeviceManager.java b/services/java/com/android/server/usb/UsbDeviceManager.java
index 607ff39..3ef6d4c 100644
--- a/services/java/com/android/server/usb/UsbDeviceManager.java
+++ b/services/java/com/android/server/usb/UsbDeviceManager.java
@@ -183,12 +183,9 @@
         // We do not show the USB notification if the primary volume supports mass storage.
         // The legacy mass storage UI will be used instead.
         boolean massStorageSupported = false;
-        StorageManager storageManager = (StorageManager)
-                mContext.getSystemService(Context.STORAGE_SERVICE);
-        StorageVolume[] volumes = storageManager.getVolumeList();
-        if (volumes.length > 0) {
-            massStorageSupported = volumes[0].allowMassStorage();
-        }
+        final StorageManager storageManager = StorageManager.from(mContext);
+        final StorageVolume primary = storageManager.getPrimaryVolume();
+        massStorageSupported = primary != null && primary.allowMassStorage();
         mUseUsbNotification = !massStorageSupported;
 
         // make sure the ADB_ENABLED setting value matches the current state
diff --git a/services/java/com/android/server/wm/DragState.java b/services/java/com/android/server/wm/DragState.java
index dc52fcf..545fce5 100644
--- a/services/java/com/android/server/wm/DragState.java
+++ b/services/java/com/android/server/wm/DragState.java
@@ -189,7 +189,7 @@
             Slog.d(WindowManagerService.TAG, "broadcasting DRAG_STARTED at (" + touchX + ", " + touchY + ")");
         }
 
-        final WindowList windows = mService.getWindowList(mDisplay);
+        final WindowList windows = mService.getWindowListLocked(mDisplay);
         final int N = windows.size();
         for (int i = 0; i < N; i++) {
             sendDragStartedLw(windows.get(i), touchX, touchY, mDataDescription);
@@ -392,7 +392,7 @@
         final int x = (int) xf;
         final int y = (int) yf;
 
-        final WindowList windows = mService.getWindowList(mDisplay);
+        final WindowList windows = mService.getWindowListLocked(mDisplay);
         final int N = windows.size();
         for (int i = N - 1; i >= 0; i--) {
             WindowState child = windows.get(i);
diff --git a/services/java/com/android/server/wm/WindowManagerService.java b/services/java/com/android/server/wm/WindowManagerService.java
index 622ab63..dd98007 100755
--- a/services/java/com/android/server/wm/WindowManagerService.java
+++ b/services/java/com/android/server/wm/WindowManagerService.java
@@ -610,11 +610,11 @@
         public AppWindowAnimParams(final AppWindowAnimator appAnimator) {
             mAppAnimator = appAnimator;
 
-            final AppWindowToken wtoken = appAnimator.mAppToken;
+            final AppWindowToken atoken = appAnimator.mAppToken;
             mWinAnimators = new ArrayList<WindowStateAnimator>();
-            final int N = wtoken.allAppWindows.size();
+            final int N = atoken.allAppWindows.size();
             for (int i = 0; i < N; i++) {
-                mWinAnimators.add(wtoken.allAppWindows.get(i).mWinAnimator);
+                mWinAnimators.add(atoken.allAppWindows.get(i).mWinAnimator);
             }
         }
     }
@@ -788,7 +788,7 @@
         mDisplayManager.registerDisplayListener(this, null);
         Display[] displays = mDisplayManager.getDisplays();
         for (Display display : displays) {
-            createDisplayContent(display);
+            createDisplayContentLocked(display);
         }
 
         mKeyguardDisableHandler = new KeyguardDisableHandler(mContext, mPolicy);
@@ -1166,7 +1166,7 @@
         // TODO(multidisplay): Needs some serious rethought when the target and IME are not on the
         // same display. Or even when the current IME/target are not on the same screen as the next
         // IME/target. For now only look for input windows on the main screen.
-        WindowList windows = getDefaultWindowList();
+        WindowList windows = getDefaultWindowListLocked();
         final int N = windows.size();
         WindowState w = null;
         int i = N;
@@ -1308,7 +1308,7 @@
             if (DEBUG_WINDOW_MOVEMENT || DEBUG_ADD_REMOVE) Slog.v(
                     TAG, "Adding input method window " + win + " at " + pos);
             // TODO(multidisplay): IMEs are only supported on the default display.
-            getDefaultWindowList().add(pos, win);
+            getDefaultWindowListLocked().add(pos, win);
             mWindowsChanged = true;
             moveInputMethodDialogsLocked(pos+1);
             return;
@@ -1396,7 +1396,7 @@
         ArrayList<WindowState> dialogs = mInputMethodDialogs;
 
         // TODO(multidisplay): IMEs are only supported on the default display.
-        WindowList windows = getDefaultWindowList();
+        WindowList windows = getDefaultWindowListLocked();
         final int N = dialogs.size();
         if (DEBUG_INPUT_METHOD) Slog.v(TAG, "Removing " + N + " dialogs w/pos=" + pos);
         for (int i=0; i<N; i++) {
@@ -1446,7 +1446,7 @@
         }
 
         // TODO(multidisplay): IMEs are only supported on the default display.
-        WindowList windows = getDefaultWindowList();
+        WindowList windows = getDefaultWindowListLocked();
 
         int imPos = findDesiredInputMethodWindowIndexLocked(true);
         if (imPos >= 0) {
@@ -1566,13 +1566,13 @@
         int changed = 0;
 
         // TODO(multidisplay): Wallpapers on main screen only.
-        final DisplayInfo displayInfo = getDefaultDisplayContent().getDisplayInfo();
+        final DisplayInfo displayInfo = getDefaultDisplayContentLocked().getDisplayInfo();
         final int dw = displayInfo.appWidth;
         final int dh = displayInfo.appHeight;
 
         // First find top-most window that has asked to be on top of the
         // wallpaper; all wallpapers go behind it.
-        final WindowList windows = getDefaultWindowList();
+        final WindowList windows = getDefaultWindowListLocked();
         int N = windows.size();
         WindowState w = null;
         WindowState foundW = null;
@@ -1812,7 +1812,7 @@
                 token.hidden = !visible;
                 // Need to do a layout to ensure the wallpaper now has the
                 // correct size.
-                getDefaultDisplayContent().layoutNeeded = true;
+                getDefaultDisplayContentLocked().layoutNeeded = true;
             }
 
             int curWallpaperIndex = token.windows.size();
@@ -2055,7 +2055,7 @@
                 token.hidden = !visible;
                 // Need to do a layout to ensure the wallpaper now has the
                 // correct size.
-                getDefaultDisplayContent().layoutNeeded = true;
+                getDefaultDisplayContentLocked().layoutNeeded = true;
             }
 
             int curWallpaperIndex = token.windows.size();
@@ -2173,7 +2173,7 @@
                 }
             }
 
-            final DisplayContent displayContent = getDisplayContent(displayId);
+            final DisplayContent displayContent = getDisplayContentLocked(displayId);
             win = new WindowState(this, session, client, token,
                     attachedWindow, seq, attrs, viewVisibility, displayContent);
             if (win.mDeathRecipient == null) {
@@ -2668,7 +2668,7 @@
             boolean immediate) {
         RemoteCallbackList<IDisplayContentChangeListener> callbacks = null;
         synchronized (mWindowMap) {
-            DisplayContent displayContent = getDisplayContent(displayId);
+            DisplayContent displayContent = getDisplayContentLocked(displayId);
             if (displayContent == null) {
                 return;
             }
@@ -2966,7 +2966,7 @@
             configChanged = updateOrientationFromAppTokensLocked(false);
             performLayoutAndPlaceSurfacesLocked();
             if (toBeDisplayed && win.mIsWallpaper) {
-                DisplayInfo displayInfo = getDefaultDisplayInfo();
+                DisplayInfo displayInfo = getDefaultDisplayInfoLocked();
                 updateWallpaperOffsetLocked(win,
                         displayInfo.appWidth, displayInfo.appHeight, false);
             }
@@ -3089,7 +3089,7 @@
             throw new SecurityException("Requires RETRIEVE_WINDOW_INFO permission.");
         }
         synchronized (mWindowMap) {
-            DisplayContent displayContent = getDisplayContent(displayId);
+            DisplayContent displayContent = getDisplayContentLocked(displayId);
             if (displayContent == null) {
                 return;
             }
@@ -3106,6 +3106,7 @@
         }
     }
 
+    @Override
     public void magnifyDisplay(int displayId, float scale, float offsetX, float offsetY) {
         if (!checkCallingPermission(
                 android.Manifest.permission.MAGNIFY_DISPLAY, "magnifyDisplay()")) {
@@ -3134,7 +3135,7 @@
     }
 
     MagnificationSpec getDisplayMagnificationSpecLocked(int displayId) {
-        DisplayContent displayContent = getDisplayContent(displayId);
+        DisplayContent displayContent = getDisplayContentLocked(displayId);
         if (displayContent != null) {
             if (displayContent.mMagnificationSpec == null) {
                 displayContent.mMagnificationSpec = new MagnificationSpec();
@@ -3153,6 +3154,7 @@
         info.compatibilityScale = window.mGlobalScale;
         info.visible = window.isVisibleLw()
                 || info.type == WindowManager.LayoutParams.TYPE_UNIVERSE_BACKGROUND;
+        info.layer = window.mLayer;
         window.getTouchableRegion(mTempRegion);
         mTempRegion.getBounds(info.touchableRegion);
         return info;
@@ -3279,7 +3281,7 @@
                 break;
         }
         // TODO(multidisplay): For now assume all app animation is on main display.
-        final DisplayInfo displayInfo = getDefaultDisplayInfo();
+        final DisplayInfo displayInfo = getDefaultDisplayInfoLocked();
         if (enter) {
             // Entering app zooms out from the center of the initial rect.
             float scaleW = mNextAppTransitionStartWidth / (float) displayInfo.appWidth;
@@ -3330,7 +3332,7 @@
                 break;
         }
         // TOOD(multidisplay): For now assume all app animation is on the main screen.
-        DisplayInfo displayInfo = getDefaultDisplayInfo();
+        DisplayInfo displayInfo = getDefaultDisplayInfoLocked();
         if (thumb) {
             // Animation for zooming thumbnail from its initial size to
             // filling the screen.
@@ -3425,7 +3427,7 @@
         return a;
     }
 
-    private boolean applyAnimationLocked(AppWindowToken wtoken,
+    private boolean applyAnimationLocked(AppWindowToken atoken,
             WindowManager.LayoutParams lp, int transit, boolean enter) {
         // Only apply an animation if the display isn't frozen.  If it is
         // frozen, there is no reason to animate and it can cause strange
@@ -3438,14 +3440,14 @@
                 a = loadAnimation(mNextAppTransitionPackage, enter ?
                         mNextAppTransitionEnter : mNextAppTransitionExit);
                 if (DEBUG_APP_TRANSITIONS || DEBUG_ANIM) Slog.v(TAG,
-                        "applyAnimation: wtoken=" + wtoken
+                        "applyAnimation: atoken=" + atoken
                         + " anim=" + a + " nextAppTransition=ANIM_CUSTOM"
                         + " transit=" + transit + " Callers " + Debug.getCallers(3));
             } else if (mNextAppTransitionType == ActivityOptions.ANIM_SCALE_UP) {
                 a = createScaleUpAnimationLocked(transit, enter);
                 initialized = true;
                 if (DEBUG_APP_TRANSITIONS || DEBUG_ANIM) Slog.v(TAG,
-                        "applyAnimation: wtoken=" + wtoken
+                        "applyAnimation: atoken=" + atoken
                         + " anim=" + a + " nextAppTransition=ANIM_SCALE_UP"
                         + " transit=" + transit + " Callers " + Debug.getCallers(3));
             } else if (mNextAppTransitionType == ActivityOptions.ANIM_THUMBNAIL_SCALE_UP ||
@@ -3456,7 +3458,7 @@
 
                 if (DEBUG_APP_TRANSITIONS || DEBUG_ANIM) {
                     String animName = scaleUp ? "ANIM_THUMBNAIL_SCALE_UP" : "ANIM_THUMBNAIL_SCALE_DOWN";
-                    Slog.v(TAG, "applyAnimation: wtoken=" + wtoken
+                    Slog.v(TAG, "applyAnimation: atoken=" + atoken
                             + " anim=" + a + " nextAppTransition=" + animName
                             + " transit=" + transit + " Callers " + Debug.getCallers(3));
                 }
@@ -3516,7 +3518,7 @@
                 }
                 a = animAttr != 0 ? loadAnimation(lp, animAttr) : null;
                 if (DEBUG_APP_TRANSITIONS || DEBUG_ANIM) Slog.v(TAG,
-                        "applyAnimation: wtoken=" + wtoken
+                        "applyAnimation: atoken=" + atoken
                         + " anim=" + a
                         + " animAttr=0x" + Integer.toHexString(animAttr)
                         + " transit=" + transit + " Callers " + Debug.getCallers(3));
@@ -3528,15 +3530,15 @@
                         e = new RuntimeException();
                         e.fillInStackTrace();
                     }
-                    Slog.v(TAG, "Loaded animation " + a + " for " + wtoken, e);
+                    Slog.v(TAG, "Loaded animation " + a + " for " + atoken, e);
                 }
-                wtoken.mAppAnimator.setAnimation(a, initialized);
+                atoken.mAppAnimator.setAnimation(a, initialized);
             }
         } else {
-            wtoken.mAppAnimator.clearAnimation();
+            atoken.mAppAnimator.clearAnimation();
         }
 
-        return wtoken.mAppAnimator.animation != null;
+        return atoken.mAppAnimator.animation != null;
     }
 
     // -------------------------------------------------------------
@@ -3547,14 +3549,14 @@
         int v = tokens.size()-1;
         int m = mAppTokens.size()-1;
         while (v >= 0 && m >= 0) {
-            AppWindowToken wtoken = mAppTokens.get(m);
-            if (wtoken.removed) {
+            AppWindowToken atoken = mAppTokens.get(m);
+            if (atoken.removed) {
                 m--;
                 continue;
             }
-            if (tokens.get(v) != wtoken.token) {
+            if (tokens.get(v) != atoken.token) {
                 Slog.w(TAG, "Tokens out of sync: external is " + tokens.get(v)
-                      + " @ " + v + ", internal is " + wtoken.token + " @ " + m);
+                      + " @ " + v + ", internal is " + atoken.token + " @ " + m);
             }
             v--;
             m--;
@@ -3564,9 +3566,9 @@
             v--;
         }
         while (m >= 0) {
-            AppWindowToken wtoken = mAppTokens.get(m);
-            if (!wtoken.removed) {
-                Slog.w(TAG, "Invalid internal token: " + wtoken.token + " @ " + m);
+            AppWindowToken atoken = mAppTokens.get(m);
+            if (!atoken.removed) {
+                Slog.w(TAG, "Invalid internal atoken: " + atoken.token + " @ " + m);
             }
             m--;
         }
@@ -3685,18 +3687,18 @@
      *  Find the location to insert a new AppWindowToken into the window-ordered app token list.
      *  Note that mAppTokens.size() == mAnimatingAppTokens.size() + 1.
      * @param addPos The location the token was inserted into in mAppTokens.
-     * @param wtoken The token to insert.
+     * @param atoken The token to insert.
      */
-    private void addAppTokenToAnimating(final int addPos, final AppWindowToken wtoken) {
+    private void addAppTokenToAnimating(final int addPos, final AppWindowToken atoken) {
         if (addPos == 0 || addPos == mAnimatingAppTokens.size()) {
             // It was inserted into the beginning or end of mAppTokens. Honor that.
-            mAnimatingAppTokens.add(addPos, wtoken);
+            mAnimatingAppTokens.add(addPos, atoken);
             return;
         }
         // Find the item immediately above the mAppTokens insertion point and put the token
         // immediately below that one in mAnimatingAppTokens.
         final AppWindowToken aboveAnchor = mAppTokens.get(addPos + 1);
-        mAnimatingAppTokens.add(mAnimatingAppTokens.indexOf(aboveAnchor), wtoken);
+        mAnimatingAppTokens.add(mAnimatingAppTokens.indexOf(aboveAnchor), atoken);
     }
 
     @Override
@@ -3722,25 +3724,25 @@
         }
 
         synchronized(mWindowMap) {
-            AppWindowToken wtoken = findAppWindowToken(token.asBinder());
-            if (wtoken != null) {
+            AppWindowToken atoken = findAppWindowToken(token.asBinder());
+            if (atoken != null) {
                 Slog.w(TAG, "Attempted to add existing app token: " + token);
                 return;
             }
-            wtoken = new AppWindowToken(this, token);
-            wtoken.inputDispatchingTimeoutNanos = inputDispatchingTimeoutNanos;
-            wtoken.groupId = groupId;
-            wtoken.appFullscreen = fullscreen;
-            wtoken.requestedOrientation = requestedOrientation;
-            if (DEBUG_TOKEN_MOVEMENT || DEBUG_ADD_REMOVE) Slog.v(TAG, "addAppToken: " + wtoken
+            atoken = new AppWindowToken(this, token);
+            atoken.inputDispatchingTimeoutNanos = inputDispatchingTimeoutNanos;
+            atoken.groupId = groupId;
+            atoken.appFullscreen = fullscreen;
+            atoken.requestedOrientation = requestedOrientation;
+            if (DEBUG_TOKEN_MOVEMENT || DEBUG_ADD_REMOVE) Slog.v(TAG, "addAppToken: " + atoken
                     + " at " + addPos);
-            mAppTokens.add(addPos, wtoken);
-            addAppTokenToAnimating(addPos, wtoken);
-            mTokenMap.put(token.asBinder(), wtoken);
+            mAppTokens.add(addPos, atoken);
+            addAppTokenToAnimating(addPos, atoken);
+            mTokenMap.put(token.asBinder(), atoken);
 
             // Application tokens start out hidden.
-            wtoken.hidden = true;
-            wtoken.hiddenRequested = true;
+            atoken.hidden = true;
+            atoken.hiddenRequested = true;
 
             //dump();
         }
@@ -3754,12 +3756,12 @@
         }
 
         synchronized(mWindowMap) {
-            AppWindowToken wtoken = findAppWindowToken(token);
-            if (wtoken == null) {
+            AppWindowToken atoken = findAppWindowToken(token);
+            if (atoken == null) {
                 Slog.w(TAG, "Attempted to set group id of non-existing app token: " + token);
                 return;
             }
-            wtoken.groupId = groupId;
+            atoken.groupId = groupId;
         }
     }
 
@@ -3774,7 +3776,7 @@
         }
 
         // TODO(multidisplay): Change to the correct display.
-        final WindowList windows = getDefaultWindowList();
+        final WindowList windows = getDefaultWindowListLocked();
         int pos = windows.size() - 1;
         while (pos >= 0) {
             WindowState wtoken = windows.get(pos);
@@ -3805,20 +3807,20 @@
         boolean haveGroup = false;
         boolean lastFullscreen = false;
         for (int pos = mAppTokens.size() - 1; pos >= 0; pos--) {
-            AppWindowToken wtoken = mAppTokens.get(pos);
+            AppWindowToken atoken = mAppTokens.get(pos);
 
-            if (DEBUG_APP_ORIENTATION) Slog.v(TAG, "Checking app orientation: " + wtoken);
+            if (DEBUG_APP_ORIENTATION) Slog.v(TAG, "Checking app orientation: " + atoken);
 
             // if we're about to tear down this window and not seek for
             // the behind activity, don't use it for orientation
             if (!findingBehind
-                    && (!wtoken.hidden && wtoken.hiddenRequested)) {
-                if (DEBUG_ORIENTATION) Slog.v(TAG, "Skipping " + wtoken
+                    && (!atoken.hidden && atoken.hiddenRequested)) {
+                if (DEBUG_ORIENTATION) Slog.v(TAG, "Skipping " + atoken
                         + " -- going to hide");
                 continue;
             }
 
-            if (haveGroup == true && curGroup != wtoken.groupId) {
+            if (haveGroup == true && curGroup != atoken.groupId) {
                 // If we have hit a new application group, and the bottom
                 // of the previous group didn't explicitly say to use
                 // the orientation behind it, and the last app was
@@ -3826,33 +3828,33 @@
                 // user's orientation.
                 if (lastOrientation != ActivityInfo.SCREEN_ORIENTATION_BEHIND
                         && lastFullscreen) {
-                    if (DEBUG_ORIENTATION) Slog.v(TAG, "Done at " + wtoken
+                    if (DEBUG_ORIENTATION) Slog.v(TAG, "Done at " + atoken
                             + " -- end of group, return " + lastOrientation);
                     return lastOrientation;
                 }
             }
 
             // We ignore any hidden applications on the top.
-            if (wtoken.hiddenRequested || wtoken.willBeHidden) {
-                if (DEBUG_ORIENTATION) Slog.v(TAG, "Skipping " + wtoken
+            if (atoken.hiddenRequested || atoken.willBeHidden) {
+                if (DEBUG_ORIENTATION) Slog.v(TAG, "Skipping " + atoken
                         + " -- hidden on top");
                 continue;
             }
 
             if (!haveGroup) {
                 haveGroup = true;
-                curGroup = wtoken.groupId;
-                lastOrientation = wtoken.requestedOrientation;
+                curGroup = atoken.groupId;
+                lastOrientation = atoken.requestedOrientation;
             }
 
-            int or = wtoken.requestedOrientation;
+            int or = atoken.requestedOrientation;
             // If this application is fullscreen, and didn't explicitly say
             // to use the orientation behind it, then just take whatever
             // orientation it has and ignores whatever is under it.
-            lastFullscreen = wtoken.appFullscreen;
+            lastFullscreen = atoken.appFullscreen;
             if (lastFullscreen
                     && or != ActivityInfo.SCREEN_ORIENTATION_BEHIND) {
-                if (DEBUG_ORIENTATION) Slog.v(TAG, "Done at " + wtoken
+                if (DEBUG_ORIENTATION) Slog.v(TAG, "Done at " + atoken
                         + " -- full screen, return " + or);
                 return or;
             }
@@ -3860,7 +3862,7 @@
             // then use it.
             if (or != ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED
                     && or != ActivityInfo.SCREEN_ORIENTATION_BEHIND) {
-                if (DEBUG_ORIENTATION) Slog.v(TAG, "Done at " + wtoken
+                if (DEBUG_ORIENTATION) Slog.v(TAG, "Done at " + atoken
                         + " -- explicitly set, return " + or);
                 return or;
             }
@@ -3896,10 +3898,10 @@
 
         if (updateOrientationFromAppTokensLocked(false)) {
             if (freezeThisOneIfNeeded != null) {
-                AppWindowToken wtoken = findAppWindowToken(
+                AppWindowToken atoken = findAppWindowToken(
                         freezeThisOneIfNeeded);
-                if (wtoken != null) {
-                    startAppFreezingScreenLocked(wtoken,
+                if (atoken != null) {
+                    startAppFreezingScreenLocked(atoken,
                             ActivityInfo.CONFIG_ORIENTATION);
                 }
             }
@@ -3915,7 +3917,7 @@
             if (computeScreenConfigurationLocked(mTempConfiguration)) {
                 if (currentConfig.diff(mTempConfiguration) != 0) {
                     mWaitingForConfig = true;
-                    getDefaultDisplayContent().layoutNeeded = true;
+                    getDefaultDisplayContentLocked().layoutNeeded = true;
                     startFreezingDisplayLocked(false, 0, 0);
                     config = new Configuration(mTempConfiguration);
                 }
@@ -3991,13 +3993,13 @@
         }
 
         synchronized(mWindowMap) {
-            AppWindowToken wtoken = findAppWindowToken(token.asBinder());
-            if (wtoken == null) {
+            AppWindowToken atoken = findAppWindowToken(token.asBinder());
+            if (atoken == null) {
                 Slog.w(TAG, "Attempted to set orientation of non-existing app token: " + token);
                 return;
             }
 
-            wtoken.requestedOrientation = requestedOrientation;
+            atoken.requestedOrientation = requestedOrientation;
         }
     }
 
@@ -4090,6 +4092,7 @@
         }
     }
 
+    @Override
     public int getPendingAppTransition() {
         return mNextAppTransition;
     }
@@ -4100,6 +4103,7 @@
         }
     }
 
+    @Override
     public void overridePendingAppTransition(String packageName,
             int enterAnim, int exitAnim, IRemoteCallback startedCallback) {
         synchronized(mWindowMap) {
@@ -4281,7 +4285,7 @@
 
                         updateFocusedWindowLocked(UPDATE_FOCUS_WILL_PLACE_SURFACES,
                                 true /*updateInputWindows*/);
-                        getDefaultDisplayContent().layoutNeeded = true;
+                        getDefaultDisplayContentLocked().layoutNeeded = true;
                         performLayoutAndPlaceSurfacesLocked();
                         Binder.restoreCallingIdentity(origId);
                         return;
@@ -5475,7 +5479,7 @@
                         && !mOnlyCore;
                 boolean haveKeyguard = true;
                 // TODO(multidisplay): Expand to all displays?
-                final WindowList windows = getDefaultWindowList();
+                final WindowList windows = getDefaultWindowListLocked();
                 final int N = windows.size();
                 for (int i=0; i<N; i++) {
                     WindowState w = windows.get(i);
@@ -5650,7 +5654,7 @@
                 // TODO(multi-display): support multiple displays
                 if (mStrictModeFlash == null) {
                     mStrictModeFlash = new StrictModeFlash(
-                            getDefaultDisplayContent().getDisplay(), mFxSession);
+                            getDefaultDisplayContentLocked().getDisplay(), mFxSession);
                 }
                 mStrictModeFlash.setVisibility(on);
             } finally {
@@ -5692,7 +5696,7 @@
         synchronized(mWindowMap) {
             long ident = Binder.clearCallingIdentity();
 
-            final DisplayContent displayContent = getDisplayContent(displayId);
+            final DisplayContent displayContent = getDisplayContentLocked(displayId);
             final DisplayInfo displayInfo = displayContent.getDisplayInfo();
             dw = displayInfo.logicalWidth;
             dh = displayInfo.logicalHeight;
@@ -5763,7 +5767,7 @@
             }
 
             // The screenshot API does not apply the current screen rotation.
-            rot = getDefaultDisplayContent().getDisplay().getRotation();
+            rot = getDefaultDisplayContentLocked().getDisplay().getRotation();
             int fw = frame.width();
             int fh = frame.height();
 
@@ -5912,7 +5916,7 @@
         synchronized(mWindowMap) {
             changed = updateRotationUncheckedLocked(false);
             if (!changed || forceRelayout) {
-                getDefaultDisplayContent().layoutNeeded = true;
+                getDefaultDisplayContentLocked().layoutNeeded = true;
                 performLayoutAndPlaceSurfacesLocked();
             }
         }
@@ -5990,7 +5994,7 @@
         mH.removeMessages(H.WINDOW_FREEZE_TIMEOUT);
         mH.sendMessageDelayed(mH.obtainMessage(H.WINDOW_FREEZE_TIMEOUT), 2000);
         mWaitingForConfig = true;
-        getDefaultDisplayContent().layoutNeeded = true;
+        getDefaultDisplayContentLocked().layoutNeeded = true;
         startFreezingDisplayLocked(inTransaction, 0, 0);
 
         // We need to update our screen size information to match the new
@@ -6001,7 +6005,7 @@
         // the rotation animation for the new rotation.
         computeScreenConfigurationLocked(null);
 
-        final DisplayContent displayContent = getDefaultDisplayContent();
+        final DisplayContent displayContent = getDefaultDisplayContentLocked();
         final DisplayInfo displayInfo = displayContent.getDisplayInfo();
         if (!inTransaction) {
             if (SHOW_TRANSACTIONS) {
@@ -6112,7 +6116,7 @@
             final int rotation = getRotation();
 
             // TODO(multidisplay): Assume that such devices physical keys are on the main screen.
-            final DisplayContent displayContent = getDefaultDisplayContent();
+            final DisplayContent displayContent = getDefaultDisplayContentLocked();
             if (displayContent.mInitialDisplayWidth < displayContent.mInitialDisplayHeight) {
                 // On devices with a natural orientation of portrait
                 switch (rotation) {
@@ -6440,7 +6444,7 @@
             throw new SecurityException("Requires RETRIEVE_WINDOW_INFO permission");
         }
         synchronized(mWindowMap) {
-            DisplayContent displayContent = getDisplayContent(displayId);
+            DisplayContent displayContent = getDisplayContentLocked(displayId);
             if (displayContent.mDisplayContentChangeListeners == null) {
                 displayContent.mDisplayContentChangeListeners =
                         new RemoteCallbackList<IDisplayContentChangeListener>();
@@ -6456,7 +6460,7 @@
             throw new SecurityException("Requires RETRIEVE_WINDOW_INFO permission");
         }
         synchronized(mWindowMap) {
-            DisplayContent displayContent = getDisplayContent(displayId);
+            DisplayContent displayContent = getDisplayContentLocked(displayId);
             if (displayContent.mDisplayContentChangeListeners != null) {
                 displayContent.mDisplayContentChangeListeners.unregister(listener);
                 if (displayContent.mDisplayContentChangeListeners
@@ -6478,7 +6482,7 @@
     private void handleNotifyWindowTranstion(int transition, WindowInfo info) {
         RemoteCallbackList<IDisplayContentChangeListener> callbacks = null;
         synchronized (mWindowMap) {
-            DisplayContent displayContent = getDisplayContent(info.displayId);
+            DisplayContent displayContent = getDisplayContentLocked(info.displayId);
             if (displayContent == null) {
                 return;
             }
@@ -6514,7 +6518,7 @@
     private void handleNotifyRotationChanged(int displayId, int rotation) {
         RemoteCallbackList<IDisplayContentChangeListener> callbacks = null;
         synchronized (mWindowMap) {
-            DisplayContent displayContent = getDisplayContent(displayId);
+            DisplayContent displayContent = getDisplayContentLocked(displayId);
             if (displayContent == null) {
                 return;
             }
@@ -6801,7 +6805,7 @@
         }
 
         // TODO(multidisplay): For now, apply Configuration to main screen only.
-        final DisplayContent displayContent = getDefaultDisplayContent();
+        final DisplayContent displayContent = getDefaultDisplayContentLocked();
 
         // Use the effective "visual" dimensions based on current rotation
         final boolean rotated = (mRotation == Surface.ROTATION_90
@@ -6999,7 +7003,7 @@
                 try {
                     if (mDragState == null) {
                         // TODO(multi-display): support other displays
-                        final DisplayContent displayContent = getDefaultDisplayContent();
+                        final DisplayContent displayContent = getDefaultDisplayContentLocked();
                         final Display display = displayContent.getDisplay();
                         Surface surface = new Surface(session, "drag surface",
                                 width, height, PixelFormat.TRANSLUCENT, Surface.HIDDEN);
@@ -7148,7 +7152,7 @@
         displayReady(Display.DEFAULT_DISPLAY);
 
         synchronized(mWindowMap) {
-            final DisplayContent displayContent = getDefaultDisplayContent();
+            final DisplayContent displayContent = getDefaultDisplayContentLocked();
             final Display display = displayContent.getDisplay();
             readForcedDisplaySizeAndDensityLocked(displayContent);
 
@@ -7158,7 +7162,7 @@
 
             mAnimator.initializeLocked(display.getLayerStack());
 
-            final DisplayInfo displayInfo = getDefaultDisplayInfo();
+            final DisplayInfo displayInfo = getDefaultDisplayInfoLocked();
             mAnimator.setDisplayDimensions(
                     displayInfo.logicalWidth, displayInfo.logicalHeight,
                     displayInfo.appWidth, displayInfo.appHeight);
@@ -7177,7 +7181,7 @@
 
     public void displayReady(int displayId) {
         synchronized(mWindowMap) {
-            final DisplayContent displayContent = getDisplayContent(displayId);
+            final DisplayContent displayContent = getDisplayContentLocked(displayId);
             final DisplayInfo displayInfo;
             synchronized(displayContent.mDisplaySizeLock) {
                 // Bootstrap the default logical display from the display manager.
@@ -7493,7 +7497,7 @@
                     // TODO(multidisplay): Can non-default displays rotate?
                     synchronized (mWindowMap) {
                         Slog.w(TAG, "Window freeze timeout expired.");
-                        final WindowList windows = getDefaultWindowList();
+                        final WindowList windows = getDefaultWindowListLocked();
                         int i = windows.size();
                         while (i > 0) {
                             i--;
@@ -7768,7 +7772,7 @@
             int idx = findDesiredInputMethodWindowIndexLocked(false);
             if (idx > 0) {
                 // TODO(multidisplay): IMEs are only supported on the default display.
-                WindowState imFocus = getDefaultWindowList().get(idx-1);
+                WindowState imFocus = getDefaultWindowListLocked().get(idx-1);
                 if (DEBUG_INPUT_METHOD) {
                     Slog.i(TAG, "Desired input method target: " + imFocus);
                     Slog.i(TAG, "Current focus: " + mCurrentFocus);
@@ -7824,31 +7828,29 @@
     public void getInitialDisplaySize(int displayId, Point size) {
         // TODO(cmautner): Access to DisplayContent should be locked on mWindowMap. Doing that
         //  could lead to deadlock since this is called from ActivityManager.
-        final DisplayContent displayContent = getDisplayContent(displayId);
+        final DisplayContent displayContent = getDisplayContentLocked(displayId);
         synchronized(displayContent.mDisplaySizeLock) {
             size.x = displayContent.mInitialDisplayWidth;
             size.y = displayContent.mInitialDisplayHeight;
         }
     }
 
-    public void setForcedDisplaySize(int displayId, int longDimen, int shortDimen) {
+    public void setForcedDisplaySize(int displayId, int width, int height) {
         synchronized(mWindowMap) {
-            final DisplayContent displayContent = getDisplayContent(displayId);
-            int width, height;
-            if (displayContent.mInitialDisplayWidth < displayContent.mInitialDisplayHeight) {
-                width = shortDimen < displayContent.mInitialDisplayWidth
-                        ? shortDimen : displayContent.mInitialDisplayWidth;
-                height = longDimen < displayContent.mInitialDisplayHeight
-                        ? longDimen : displayContent.mInitialDisplayHeight;
-            } else {
-                width = longDimen < displayContent.mInitialDisplayWidth
-                        ? longDimen : displayContent.mInitialDisplayWidth;
-                height = shortDimen < displayContent.mInitialDisplayHeight
-                        ? shortDimen : displayContent.mInitialDisplayHeight;
-            }
+            // Set some sort of reasonable bounds on the size of the display that we
+            // will try to emulate.
+            final int MIN_WIDTH = 200;
+            final int MIN_HEIGHT = 200;
+            final int MAX_SCALE = 2;
+            final DisplayContent displayContent = getDisplayContentLocked(displayId);
+
+            width = Math.min(Math.max(width, MIN_WIDTH),
+                    displayContent.mInitialDisplayWidth * MAX_SCALE);
+            height = Math.min(Math.max(height, MIN_HEIGHT),
+                    displayContent.mInitialDisplayHeight * MAX_SCALE);
             setForcedDisplaySizeLocked(displayContent, width, height);
-            Settings.Secure.putString(mContext.getContentResolver(),
-                    Settings.Secure.DISPLAY_SIZE_FORCED, width + "," + height);
+            Settings.Global.putString(mContext.getContentResolver(),
+                    Settings.Global.DISPLAY_SIZE_FORCED, width + "," + height);
         }
     }
 
@@ -7858,7 +7860,7 @@
             mBlackFrame = null;
         }
         // TODO(multidisplay): For now rotations are only main screen.
-        final DisplayContent displayContent = getDefaultDisplayContent();
+        final DisplayContent displayContent = getDefaultDisplayContentLocked();
         final Display display = displayContent.getDisplay();
         if (displayContent.mBaseDisplayWidth < displayContent.mInitialDisplayWidth
                 || displayContent.mBaseDisplayHeight < displayContent.mInitialDisplayHeight) {
@@ -7895,8 +7897,8 @@
 
     private void readForcedDisplaySizeAndDensityLocked(final DisplayContent displayContent) {
         boolean changed = false;
-        final String sizeStr = Settings.Secure.getString(mContext.getContentResolver(),
-                Settings.Secure.DISPLAY_SIZE_FORCED);
+        final String sizeStr = Settings.Global.getString(mContext.getContentResolver(),
+                Settings.Global.DISPLAY_SIZE_FORCED);
         if (sizeStr != null && sizeStr.length() > 0) {
             final int pos = sizeStr.indexOf(',');
             if (pos > 0 && sizeStr.lastIndexOf(',') == pos) {
@@ -7917,8 +7919,8 @@
                 }
             }
         }
-        final String densityStr = Settings.Secure.getString(mContext.getContentResolver(),
-                Settings.Secure.DISPLAY_DENSITY_FORCED);
+        final String densityStr = Settings.Global.getString(mContext.getContentResolver(),
+                Settings.Global.DISPLAY_DENSITY_FORCED);
         if (densityStr != null && densityStr.length() > 0) {
             int density;
             try {
@@ -7950,20 +7952,20 @@
 
     public void clearForcedDisplaySize(int displayId) {
         synchronized(mWindowMap) {
-            final DisplayContent displayContent = getDisplayContent(displayId);
+            final DisplayContent displayContent = getDisplayContentLocked(displayId);
             setForcedDisplaySizeLocked(displayContent, displayContent.mInitialDisplayWidth,
                     displayContent.mInitialDisplayHeight);
-            Settings.Secure.putString(mContext.getContentResolver(),
-                    Settings.Secure.DISPLAY_SIZE_FORCED, "");
+            Settings.Global.putString(mContext.getContentResolver(),
+                    Settings.Global.DISPLAY_SIZE_FORCED, "");
         }
     }
 
     public void setForcedDisplayDensity(int displayId, int density) {
         synchronized(mWindowMap) {
-            final DisplayContent displayContent = getDisplayContent(displayId);
+            final DisplayContent displayContent = getDisplayContentLocked(displayId);
             setForcedDisplayDensityLocked(displayContent, density);
-            Settings.Secure.putString(mContext.getContentResolver(),
-                    Settings.Secure.DISPLAY_DENSITY_FORCED, Integer.toString(density));
+            Settings.Global.putString(mContext.getContentResolver(),
+                    Settings.Global.DISPLAY_DENSITY_FORCED, Integer.toString(density));
         }
     }
 
@@ -7978,7 +7980,7 @@
 
     public void clearForcedDisplayDensity(int displayId) {
         synchronized(mWindowMap) {
-            final DisplayContent displayContent = getDisplayContent(displayId);
+            final DisplayContent displayContent = getDisplayContentLocked(displayId);
             setForcedDisplayDensityLocked(displayContent, displayContent.mInitialDisplayDensity);
             Settings.Secure.putString(mContext.getContentResolver(),
                     Settings.Secure.DISPLAY_DENSITY_FORCED, "");
@@ -8661,7 +8663,7 @@
                         mNextAppTransitionThumbnail.getHeight());
                 try {
                     // TODO(multi-display): support other displays
-                    final DisplayContent displayContent = getDefaultDisplayContent();
+                    final DisplayContent displayContent = getDefaultDisplayContentLocked();
                     final Display display = displayContent.getDisplay();
                     Surface surface = new Surface(mFxSession,
                             "thumbnail anim",
@@ -8705,10 +8707,11 @@
             // a new layout to get them all up-to-date.
             changes |= WindowManagerPolicy.FINISH_LAYOUT_REDO_LAYOUT
                     | WindowManagerPolicy.FINISH_LAYOUT_REDO_CONFIG;
-            getDefaultDisplayContent().layoutNeeded = true;
+            getDefaultDisplayContentLocked().layoutNeeded = true;
 
             // TODO(multidisplay): IMEs are only supported on the default display.
-            if (windows == getDefaultWindowList() && !moveInputMethodWindowsIfNeededLocked(true)) {
+            if (windows == getDefaultWindowListLocked()
+                    && !moveInputMethodWindowsIfNeededLocked(true)) {
                 assignLayersLocked(windows);
             }
             updateFocusedWindowLocked(UPDATE_FOCUS_PLACING_SURFACES, false /*updateInputWindows*/);
@@ -8958,7 +8961,7 @@
         mInnerFields.mButtonBrightness = -1;
         mTransactionSequence++;
 
-        final DisplayContent defaultDisplay = getDefaultDisplayContent();
+        final DisplayContent defaultDisplay = getDefaultDisplayContentLocked();
         final DisplayInfo defaultInfo = defaultDisplay.getDisplayInfo();
         final int defaultDw = defaultInfo.logicalWidth;
         final int defaultDh = defaultInfo.logicalHeight;
@@ -9382,7 +9385,7 @@
         }
 
         if (wallpaperDestroyed && (adjustWallpaperWindowsLocked() != 0)) {
-            getDefaultDisplayContent().layoutNeeded = true;
+            getDefaultDisplayContentLocked().layoutNeeded = true;
         }
 
         DisplayContentsIterator iterator = new DisplayContentsIterator();
@@ -9667,7 +9670,8 @@
                 doRequest = true;
             }
             for (int i = 0; i < count; ++i) {
-                final DisplayContent displayContent = getDisplayContent(pendingLayouts.keyAt(i));
+                final DisplayContent displayContent =
+                        getDisplayContentLocked(pendingLayouts.keyAt(i));
                 displayContent.pendingLayoutChanges |= pendingLayouts.valueAt(i);
             }
 
@@ -9798,14 +9802,14 @@
             int focusChanged = mPolicy.focusChangedLw(oldFocus, newFocus);
 
             // TODO(multidisplay): Focused windows on default display only.
-            final DisplayContent displayContent = getDefaultDisplayContent();
+            final DisplayContent displayContent = getDefaultDisplayContentLocked();
 
             final WindowState imWindow = mInputMethodWindow;
             if (newFocus != imWindow && oldFocus != imWindow) {
                 if (moveInputMethodWindowsIfNeededLocked(
                         mode != UPDATE_FOCUS_WILL_ASSIGN_LAYERS &&
                         mode != UPDATE_FOCUS_WILL_PLACE_SURFACES)) {
-                    getDefaultDisplayContent().layoutNeeded = true;
+                    getDefaultDisplayContentLocked().layoutNeeded = true;
                 }
                 if (mode == UPDATE_FOCUS_PLACING_SURFACES) {
                     performLayoutLockedInner(displayContent, true /*initial*/, updateInputWindows);
@@ -9819,7 +9823,7 @@
 
             if ((focusChanged & WindowManagerPolicy.FINISH_LAYOUT_REDO_LAYOUT) != 0) {
                 // The change in focus caused us to need to do a layout.  Okay.
-                getDefaultDisplayContent().layoutNeeded = true;
+                getDefaultDisplayContentLocked().layoutNeeded = true;
                 if (mode == UPDATE_FOCUS_PLACING_SURFACES) {
                     performLayoutLockedInner(displayContent, true /*initial*/, updateInputWindows);
                 }
@@ -9855,7 +9859,7 @@
             ? mAppTokens.get(nextAppIndex) : null;
 
         // TODO(multidisplay): IMEs are only supported on the default display.
-        WindowList windows = getDefaultWindowList();
+        WindowList windows = getDefaultWindowListLocked();
         for (int i = windows.size() - 1; i >= 0; i--) {
             win = windows.get(i);
 
@@ -9953,7 +9957,7 @@
             }
 
             // TODO(multidisplay): rotation on main screen only.
-            final DisplayContent displayContent = getDefaultDisplayContent();
+            final DisplayContent displayContent = getDefaultDisplayContentLocked();
             final Display display = displayContent.getDisplay();
             final DisplayInfo displayInfo = displayContent.getDisplayInfo();
             mAnimator.mScreenRotationAnimation = new ScreenRotationAnimation(mContext,
@@ -9991,7 +9995,7 @@
                 && mAnimator.mScreenRotationAnimation.hasScreenshot()) {
             if (DEBUG_ORIENTATION) Slog.i(TAG, "**** Dismissing screen rotation animation");
             // TODO(multidisplay): rotation on main screen only.
-            DisplayInfo displayInfo = getDefaultDisplayContent().getDisplayInfo();
+            DisplayInfo displayInfo = getDefaultDisplayContentLocked().getDisplayInfo();
             if (mAnimator.mScreenRotationAnimation.dismiss(mFxSession, MAX_ANIMATION_DURATION,
                     mTransitionAnimationScale, displayInfo.logicalWidth,
                         displayInfo.logicalHeight)) {
@@ -10073,7 +10077,7 @@
             if (line != null) {
                 String[] toks = line.split("%");
                 if (toks != null && toks.length > 0) {
-                    mWatermark = new Watermark(getDefaultDisplayContent().getDisplay(),
+                    mWatermark = new Watermark(getDefaultDisplayContentLocked().getDisplay(),
                             mRealDisplayMetrics, mFxSession, toks);
                 }
             }
@@ -10107,7 +10111,7 @@
     // TOOD(multidisplay): StatusBar on multiple screens?
     void updateStatusBarVisibilityLocked(int visibility) {
         mInputManager.setSystemUiVisibility(visibility);
-        final WindowList windows = getDefaultWindowList();
+        final WindowList windows = getDefaultWindowListLocked();
         final int N = windows.size();
         for (int i = 0; i < N; i++) {
             WindowState ws = windows.get(i);
@@ -10178,7 +10182,7 @@
     public void saveLastInputMethodWindowForTransition() {
         synchronized (mWindowMap) {
             // TODO(multidisplay): Pass in the displayID.
-            DisplayContent displayContent = getDefaultDisplayContent();
+            DisplayContent displayContent = getDefaultDisplayContentLocked();
             if (mInputMethodWindow != null) {
                 mPolicy.setLastInputMethodWindowLw(mInputMethodWindow, mInputMethodTarget);
             }
@@ -10776,7 +10780,7 @@
         }
     }
 
-    public void createDisplayContent(final Display display) {
+    public void createDisplayContentLocked(final Display display) {
         if (display == null) {
             throw new IllegalArgumentException("getDisplayContent: display must not be null");
         }
@@ -10784,7 +10788,7 @@
         mDisplayContents.put(display.getDisplayId(), displayContent);
     }
 
-    public DisplayContent getDisplayContent(final int displayId) {
+    public DisplayContent getDisplayContentLocked(final int displayId) {
         DisplayContent displayContent = mDisplayContents.get(displayId);
         if (displayContent == null) {
             displayContent = new DisplayContent(mDisplayManager.getDisplay(displayId));
@@ -10874,20 +10878,20 @@
         }
     }
 
-    public DisplayContent getDefaultDisplayContent() {
-        return getDisplayContent(Display.DEFAULT_DISPLAY);
+    public DisplayContent getDefaultDisplayContentLocked() {
+        return getDisplayContentLocked(Display.DEFAULT_DISPLAY);
     }
 
-    public WindowList getDefaultWindowList() {
-        return getDefaultDisplayContent().getWindowList();
+    public WindowList getDefaultWindowListLocked() {
+        return getDefaultDisplayContentLocked().getWindowList();
     }
 
-    public DisplayInfo getDefaultDisplayInfo() {
-        return getDefaultDisplayContent().getDisplayInfo();
+    public DisplayInfo getDefaultDisplayInfoLocked() {
+        return getDefaultDisplayContentLocked().getDisplayInfo();
     }
 
-    public WindowList getWindowList(final Display display) {
-        return getDisplayContent(display.getDisplayId()).getWindowList();
+    public WindowList getWindowListLocked(final Display display) {
+        return getDisplayContentLocked(display.getDisplayId()).getWindowList();
     }
 
     @Override
@@ -10896,7 +10900,7 @@
     }
 
     private void handleDisplayAddedLocked(int displayId) {
-        createDisplayContent(mDisplayManager.getDisplay(displayId));
+        createDisplayContentLocked(mDisplayManager.getDisplay(displayId));
     }
 
     @Override
@@ -10905,7 +10909,7 @@
     }
 
     private void handleDisplayRemovedLocked(int displayId) {
-        final DisplayContent displayContent = getDisplayContent(displayId);
+        final DisplayContent displayContent = getDisplayContentLocked(displayId);
         mDisplayContents.delete(displayId);
         WindowList windows = displayContent.getWindowList();
         for (int i = windows.size() - 1; i >= 0; --i) {
@@ -10920,7 +10924,7 @@
     }
 
     private void handleDisplayChangedLocked(int displayId) {
-        final DisplayContent displayContent = getDisplayContent(displayId);
+        final DisplayContent displayContent = getDisplayContentLocked(displayId);
         if (displayContent != null) {
             displayContent.updateDisplayInfo();
         }
diff --git a/services/java/com/android/server/wm/WindowState.java b/services/java/com/android/server/wm/WindowState.java
index 478475d..a06ee3b 100644
--- a/services/java/com/android/server/wm/WindowState.java
+++ b/services/java/com/android/server/wm/WindowState.java
@@ -806,7 +806,7 @@
         return mViewVisibility == View.GONE
                 || !mRelayoutCalled
                 || (atoken == null && mRootToken.hidden)
-                || (atoken != null && (atoken.hiddenRequested || atoken.hidden))
+                || (atoken != null && atoken.hiddenRequested)
                 || mAttachedHidden
                 || mExiting || mDestroying;
     }
diff --git a/telephony/java/com/android/internal/telephony/IccCardConstants.java b/telephony/java/com/android/internal/telephony/IccCardConstants.java
index 4d1eb3f..236bb2f 100644
--- a/telephony/java/com/android/internal/telephony/IccCardConstants.java
+++ b/telephony/java/com/android/internal/telephony/IccCardConstants.java
@@ -47,10 +47,13 @@
     /* PERM_DISABLED means ICC is permanently disabled due to puk fails */
     public static final String INTENT_VALUE_ABSENT_ON_PERM_DISABLED = "PERM_DISABLED";
 
-    /*
-      UNKNOWN is a transient state, for example, after uesr inputs ICC pin under
-      PIN_REQUIRED state, the query for ICC status returns UNKNOWN before it
-      turns to READY
+    /**
+     * This is combination of IccCardStatus.CardState and IccCardApplicationStatus.AppState
+     * for external apps (like PhoneApp) to use
+     *
+     * UNKNOWN is a transient state, for example, after user inputs ICC pin under
+     * PIN_REQUIRED state, the query for ICC status returns UNKNOWN before it
+     * turns to READY
      */
     public enum State {
         UNKNOWN,
diff --git a/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/Blur25.java b/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/Blur25.java
index 697bbb1..9728c12 100644
--- a/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/Blur25.java
+++ b/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/Blur25.java
@@ -21,14 +21,16 @@
 import android.renderscript.Allocation;
 import android.renderscript.Element;
 import android.renderscript.RenderScript;
-import android.renderscript.Script;
-import android.renderscript.ScriptC;
+import android.renderscript.ScriptIntrinsicBlur;
 import android.renderscript.Type;
 import android.util.Log;
 import android.widget.SeekBar;
 import android.widget.TextView;
 
 public class Blur25 extends TestBase {
+    private boolean mUseIntrinsic = false;
+    private ScriptIntrinsicBlur mIntrinsic;
+
     private int MAX_RADIUS = 25;
     private ScriptC_threshold mScript;
     private ScriptC_vertical_blur mScriptVBlur;
@@ -39,6 +41,10 @@
     private Allocation mScratchPixelsAllocation2;
 
 
+    public Blur25(boolean useIntrinsic) {
+        mUseIntrinsic = useIntrinsic;
+    }
+
     public boolean onBar1Setup(SeekBar b, TextView t) {
         t.setText("Radius");
         b.setProgress(100);
@@ -67,40 +73,59 @@
         int width = mInPixelsAllocation.getType().getX();
         int height = mInPixelsAllocation.getType().getY();
 
-        Type.Builder tb = new Type.Builder(mRS, Element.F32_4(mRS));
-        tb.setX(width);
-        tb.setY(height);
-        mScratchPixelsAllocation1 = Allocation.createTyped(mRS, tb.create());
-        mScratchPixelsAllocation2 = Allocation.createTyped(mRS, tb.create());
+        if (mUseIntrinsic) {
+            mIntrinsic = ScriptIntrinsicBlur.create(mRS, Element.U8_4(mRS));
+            mIntrinsic.setRadius(25.f);
+            mIntrinsic.setInput(mInPixelsAllocation);
+        } else {
 
-        mScriptVBlur = new ScriptC_vertical_blur(mRS, res, R.raw.vertical_blur);
-        mScriptHBlur = new ScriptC_horizontal_blur(mRS, res, R.raw.horizontal_blur);
+            Type.Builder tb = new Type.Builder(mRS, Element.F32_4(mRS));
+            tb.setX(width);
+            tb.setY(height);
+            mScratchPixelsAllocation1 = Allocation.createTyped(mRS, tb.create());
+            mScratchPixelsAllocation2 = Allocation.createTyped(mRS, tb.create());
 
-        mScript = new ScriptC_threshold(mRS, res, R.raw.threshold);
-        mScript.set_width(width);
-        mScript.set_height(height);
-        mScript.set_radius(mRadius);
+            mScriptVBlur = new ScriptC_vertical_blur(mRS, res, R.raw.vertical_blur);
+            mScriptHBlur = new ScriptC_horizontal_blur(mRS, res, R.raw.horizontal_blur);
 
-        mScriptVBlur.invoke_setSaturation(mSaturation);
+            mScript = new ScriptC_threshold(mRS, res, R.raw.threshold);
+            mScript.set_width(width);
+            mScript.set_height(height);
+            mScript.set_radius(mRadius);
 
-        mScript.bind_InPixel(mInPixelsAllocation);
-        mScript.bind_OutPixel(mOutPixelsAllocation);
-        mScript.bind_ScratchPixel1(mScratchPixelsAllocation1);
-        mScript.bind_ScratchPixel2(mScratchPixelsAllocation2);
+            mScriptVBlur.invoke_setSaturation(mSaturation);
 
-        mScript.set_vBlurScript(mScriptVBlur);
-        mScript.set_hBlurScript(mScriptHBlur);
+            mScript.bind_InPixel(mInPixelsAllocation);
+            mScript.bind_OutPixel(mOutPixelsAllocation);
+            mScript.bind_ScratchPixel1(mScratchPixelsAllocation1);
+            mScript.bind_ScratchPixel2(mScratchPixelsAllocation2);
+
+            mScript.set_vBlurScript(mScriptVBlur);
+            mScript.set_hBlurScript(mScriptHBlur);
+        }
     }
 
     public void runTest() {
-        mScript.invoke_filter();
+        if (mUseIntrinsic) {
+            mIntrinsic.forEach(mOutPixelsAllocation);
+        } else {
+            mScript.invoke_filter();
+        }
     }
 
     public void setupBenchmark() {
-        mScript.set_radius(MAX_RADIUS);
+        if (mUseIntrinsic) {
+            mIntrinsic.setRadius(MAX_RADIUS);
+        } else {
+            mScript.set_radius(MAX_RADIUS);
+        }
     }
 
     public void exitBenchmark() {
-        mScript.set_radius(mRadius);
+        if (mUseIntrinsic) {
+            mIntrinsic.setRadius(mRadius);
+        } else {
+            mScript.set_radius(mRadius);
+        }
     }
 }
diff --git a/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/Convolve5x5.java b/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/Convolve5x5.java
new file mode 100644
index 0000000..b3914d1
--- /dev/null
+++ b/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/Convolve5x5.java
@@ -0,0 +1,76 @@
+/*
+ * 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.rs.image;
+
+import java.lang.Math;
+
+import android.renderscript.Allocation;
+import android.renderscript.Element;
+import android.renderscript.Matrix4f;
+import android.renderscript.RenderScript;
+import android.renderscript.Script;
+import android.renderscript.ScriptC;
+import android.renderscript.ScriptGroup;
+import android.renderscript.ScriptIntrinsicConvolve5x5;
+import android.renderscript.Type;
+import android.util.Log;
+
+public class Convolve5x5 extends TestBase {
+    private ScriptC_convolve5x5 mScript;
+    private ScriptIntrinsicConvolve5x5 mIntrinsic;
+
+    private int mWidth;
+    private int mHeight;
+    private boolean mUseIntrinsic;
+
+    public Convolve5x5(boolean useIntrinsic) {
+        mUseIntrinsic = useIntrinsic;
+    }
+
+    public void createTest(android.content.res.Resources res) {
+        mWidth = mInPixelsAllocation.getType().getX();
+        mHeight = mInPixelsAllocation.getType().getY();
+
+        float f[] = new float[25];
+        f[0] = 0.012f; f[1] = 0.025f; f[2] = 0.031f; f[3] = 0.025f; f[4] = 0.012f;
+        f[5] = 0.025f; f[6] = 0.057f; f[7] = 0.075f; f[8] = 0.057f; f[9] = 0.025f;
+        f[10]= 0.031f; f[11]= 0.075f; f[12]= 0.095f; f[13]= 0.075f; f[14]= 0.031f;
+        f[15]= 0.025f; f[16]= 0.057f; f[17]= 0.075f; f[18]= 0.057f; f[19]= 0.025f;
+        f[20]= 0.012f; f[21]= 0.025f; f[22]= 0.031f; f[23]= 0.025f; f[24]= 0.012f;
+
+        if (mUseIntrinsic) {
+            mIntrinsic = ScriptIntrinsicConvolve5x5.create(mRS, Element.U8_4(mRS));
+            mIntrinsic.setCoefficients(f);
+            mIntrinsic.setInput(mInPixelsAllocation);
+        } else {
+            mScript = new ScriptC_convolve5x5(mRS, res, R.raw.convolve5x5);
+            mScript.set_gCoeffs(f);
+            mScript.set_gIn(mInPixelsAllocation);
+            mScript.set_gWidth(mWidth);
+            mScript.set_gHeight(mHeight);
+        }
+    }
+
+    public void runTest() {
+        if (mUseIntrinsic) {
+            mIntrinsic.forEach(mOutPixelsAllocation);
+        } else {
+            mScript.forEach_root(mOutPixelsAllocation);
+        }
+    }
+
+}
diff --git a/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/CrossProcess.java b/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/CrossProcess.java
new file mode 100644
index 0000000..b9e3524
--- /dev/null
+++ b/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/CrossProcess.java
@@ -0,0 +1,63 @@
+/*
+ * 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.rs.image;
+
+import java.lang.Math;
+
+import android.renderscript.Allocation;
+import android.renderscript.Element;
+import android.renderscript.RenderScript;
+import android.renderscript.ScriptIntrinsicLUT;
+import android.util.Log;
+
+public class CrossProcess extends TestBase {
+    private ScriptIntrinsicLUT mIntrinsic;
+
+    public void createTest(android.content.res.Resources res) {
+        mIntrinsic = ScriptIntrinsicLUT.create(mRS, Element.U8_4(mRS));
+        for (int ct=0; ct < 256; ct++) {
+            float f = ((float)ct) / 255.f;
+
+            float r = f;
+            if (r < 0.5f) {
+                r = 4.0f * r * r * r;
+            } else {
+                r = 1.0f - r;
+                r = 1.0f - (4.0f * r * r * r);
+            }
+            mIntrinsic.setRed(ct, (int)(r * 255.f + 0.5f));
+
+            float g = f;
+            if (g < 0.5f) {
+                g = 2.0f * g * g;
+            } else {
+                g = 1.0f - g;
+                g = 1.0f - (2.0f * g * g);
+            }
+            mIntrinsic.setGreen(ct, (int)(g * 255.f + 0.5f));
+
+            float b = f * 0.5f + 0.25f;
+            mIntrinsic.setBlue(ct, (int)(b * 255.f + 0.5f));
+        }
+
+    }
+
+    public void runTest() {
+        mIntrinsic.forEach(mInPixelsAllocation, mOutPixelsAllocation);
+    }
+
+}
diff --git a/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/ImageProcessingActivity.java b/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/ImageProcessingActivity.java
index 37577eb..7b84355 100644
--- a/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/ImageProcessingActivity.java
+++ b/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/ImageProcessingActivity.java
@@ -135,62 +135,74 @@
             mTest = new LevelsV4(true, true);
             break;
         case 4:
-            mTest = new Blur25();
+            mTest = new Blur25(false);
             break;
         case 5:
-            mTest = new Greyscale();
+            mTest = new Blur25(true);
             break;
         case 6:
-            mTest = new Grain();
+            mTest = new Greyscale();
             break;
         case 7:
-            mTest = new Fisheye(false, false);
+            mTest = new Grain();
             break;
         case 8:
-            mTest = new Fisheye(false, true);
+            mTest = new Fisheye(false, false);
             break;
         case 9:
-            mTest = new Fisheye(true, false);
+            mTest = new Fisheye(false, true);
             break;
         case 10:
-            mTest = new Fisheye(true, true);
+            mTest = new Fisheye(true, false);
             break;
         case 11:
-            mTest = new Vignette(false, false);
+            mTest = new Fisheye(true, true);
             break;
         case 12:
-            mTest = new Vignette(false, true);
+            mTest = new Vignette(false, false);
             break;
         case 13:
-            mTest = new Vignette(true, false);
+            mTest = new Vignette(false, true);
             break;
         case 14:
-            mTest = new Vignette(true, true);
+            mTest = new Vignette(true, false);
             break;
         case 15:
-            mTest = new GroupTest(false);
+            mTest = new Vignette(true, true);
             break;
         case 16:
-            mTest = new GroupTest(true);
+            mTest = new GroupTest(false);
             break;
         case 17:
-            mTest = new Convolve3x3(false);
+            mTest = new GroupTest(true);
             break;
         case 18:
-            mTest = new Convolve3x3(true);
+            mTest = new Convolve3x3(false);
             break;
         case 19:
-            mTest = new ColorMatrix(false, false);
+            mTest = new Convolve3x3(true);
             break;
         case 20:
-            mTest = new ColorMatrix(true, false);
+            mTest = new ColorMatrix(false, false);
             break;
         case 21:
-            mTest = new ColorMatrix(true, true);
+            mTest = new ColorMatrix(true, false);
             break;
         case 22:
+            mTest = new ColorMatrix(true, true);
+            break;
+        case 23:
             mTest = new Copy();
             break;
+        case 24:
+            mTest = new CrossProcess();
+            break;
+        case 25:
+            mTest = new Convolve5x5(false);
+            break;
+        case 26:
+            mTest = new Convolve5x5(true);
+            break;
         }
 
         mTest.createBaseTest(this, mBitmapIn);
@@ -203,30 +215,34 @@
     }
 
     void setupTests() {
-        mTestNames = new String[23];
+        mTestNames = new String[27];
         mTestNames[0] = "Levels Vec3 Relaxed";
         mTestNames[1] = "Levels Vec4 Relaxed";
         mTestNames[2] = "Levels Vec3 Full";
         mTestNames[3] = "Levels Vec4 Full";
         mTestNames[4] = "Blur radius 25";
-        mTestNames[5] = "Greyscale";
-        mTestNames[6] = "Grain";
-        mTestNames[7] = "Fisheye Full";
-        mTestNames[8] = "Fisheye Relaxed";
-        mTestNames[9] = "Fisheye Approximate Full";
-        mTestNames[10] = "Fisheye Approximate Relaxed";
-        mTestNames[11] = "Vignette Full";
-        mTestNames[12] = "Vignette Relaxed";
-        mTestNames[13] = "Vignette Approximate Full";
-        mTestNames[14] = "Vignette Approximate Relaxed";
-        mTestNames[15] = "Group Test (emulated)";
-        mTestNames[16] = "Group Test (native)";
-        mTestNames[17] = "Convolve 3x3";
-        mTestNames[18] = "Intrinsics Convolve 3x3";
-        mTestNames[19] = "ColorMatrix";
-        mTestNames[20] = "Intrinsics ColorMatrix";
-        mTestNames[21] = "Intrinsics ColorMatrix Grey";
-        mTestNames[22] = "Copy";
+        mTestNames[5] = "Intrinsic Blur radius 25";
+        mTestNames[6] = "Greyscale";
+        mTestNames[7] = "Grain";
+        mTestNames[8] = "Fisheye Full";
+        mTestNames[9] = "Fisheye Relaxed";
+        mTestNames[10] = "Fisheye Approximate Full";
+        mTestNames[11] = "Fisheye Approximate Relaxed";
+        mTestNames[12] = "Vignette Full";
+        mTestNames[13] = "Vignette Relaxed";
+        mTestNames[14] = "Vignette Approximate Full";
+        mTestNames[15] = "Vignette Approximate Relaxed";
+        mTestNames[16] = "Group Test (emulated)";
+        mTestNames[17] = "Group Test (native)";
+        mTestNames[18] = "Convolve 3x3";
+        mTestNames[19] = "Intrinsics Convolve 3x3";
+        mTestNames[20] = "ColorMatrix";
+        mTestNames[21] = "Intrinsics ColorMatrix";
+        mTestNames[22] = "Intrinsics ColorMatrix Grey";
+        mTestNames[23] = "Copy";
+        mTestNames[24] = "CrossProcess (using LUT)";
+        mTestNames[25] = "Convolve 5x5";
+        mTestNames[26] = "Intrinsics Convolve 5x5";
         mTestSpinner.setAdapter(new ArrayAdapter<String>(
             this, R.layout.spinner_layout, mTestNames));
     }
diff --git a/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/convolve5x5.rs b/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/convolve5x5.rs
new file mode 100644
index 0000000..fe6cf31
--- /dev/null
+++ b/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/convolve5x5.rs
@@ -0,0 +1,74 @@
+/*
+ * 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.
+ */
+
+#pragma version(1)
+#pragma rs java_package_name(com.android.rs.image)
+#pragma rs_fp_relaxed
+
+int32_t gWidth;
+int32_t gHeight;
+rs_allocation gIn;
+
+float gCoeffs[25];
+
+void root(uchar4 *out, uint32_t x, uint32_t y) {
+    uint32_t x0 = max((int32_t)x-2, 0);
+    uint32_t x1 = max((int32_t)x-1, 0);
+    uint32_t x2 = x;
+    uint32_t x3 = min((int32_t)x+1, gWidth-1);
+    uint32_t x4 = min((int32_t)x+2, gWidth-1);
+
+    uint32_t y0 = max((int32_t)y-2, 0);
+    uint32_t y1 = max((int32_t)y-1, 0);
+    uint32_t y2 = y;
+    uint32_t y3 = min((int32_t)y+1, gHeight-1);
+    uint32_t y4 = min((int32_t)y+2, gHeight-1);
+
+    float4 p0 = convert_float4(((uchar4 *)rsGetElementAt(gIn, x0, y0))[0]) * gCoeffs[0]
+              + convert_float4(((uchar4 *)rsGetElementAt(gIn, x1, y0))[0]) * gCoeffs[1]
+              + convert_float4(((uchar4 *)rsGetElementAt(gIn, x2, y0))[0]) * gCoeffs[2]
+              + convert_float4(((uchar4 *)rsGetElementAt(gIn, x3, y0))[0]) * gCoeffs[3]
+              + convert_float4(((uchar4 *)rsGetElementAt(gIn, x4, y0))[0]) * gCoeffs[4];
+
+    float4 p1 = convert_float4(((uchar4 *)rsGetElementAt(gIn, x0, y1))[0]) * gCoeffs[5]
+              + convert_float4(((uchar4 *)rsGetElementAt(gIn, x1, y1))[0]) * gCoeffs[6]
+              + convert_float4(((uchar4 *)rsGetElementAt(gIn, x2, y1))[0]) * gCoeffs[7]
+              + convert_float4(((uchar4 *)rsGetElementAt(gIn, x3, y1))[0]) * gCoeffs[8]
+              + convert_float4(((uchar4 *)rsGetElementAt(gIn, x4, y1))[0]) * gCoeffs[9];
+
+    float4 p2 = convert_float4(((uchar4 *)rsGetElementAt(gIn, x0, y2))[0]) * gCoeffs[10]
+              + convert_float4(((uchar4 *)rsGetElementAt(gIn, x1, y2))[0]) * gCoeffs[11]
+              + convert_float4(((uchar4 *)rsGetElementAt(gIn, x2, y2))[0]) * gCoeffs[12]
+              + convert_float4(((uchar4 *)rsGetElementAt(gIn, x3, y2))[0]) * gCoeffs[13]
+              + convert_float4(((uchar4 *)rsGetElementAt(gIn, x4, y2))[0]) * gCoeffs[14];
+
+    float4 p3 = convert_float4(((uchar4 *)rsGetElementAt(gIn, x0, y3))[0]) * gCoeffs[15]
+              + convert_float4(((uchar4 *)rsGetElementAt(gIn, x1, y3))[0]) * gCoeffs[16]
+              + convert_float4(((uchar4 *)rsGetElementAt(gIn, x2, y3))[0]) * gCoeffs[17]
+              + convert_float4(((uchar4 *)rsGetElementAt(gIn, x3, y3))[0]) * gCoeffs[18]
+              + convert_float4(((uchar4 *)rsGetElementAt(gIn, x4, y3))[0]) * gCoeffs[19];
+
+    float4 p4 = convert_float4(((uchar4 *)rsGetElementAt(gIn, x0, y4))[0]) * gCoeffs[20]
+              + convert_float4(((uchar4 *)rsGetElementAt(gIn, x1, y4))[0]) * gCoeffs[21]
+              + convert_float4(((uchar4 *)rsGetElementAt(gIn, x2, y4))[0]) * gCoeffs[22]
+              + convert_float4(((uchar4 *)rsGetElementAt(gIn, x3, y4))[0]) * gCoeffs[23]
+              + convert_float4(((uchar4 *)rsGetElementAt(gIn, x4, y4))[0]) * gCoeffs[24];
+
+    p0 = clamp(p0 + p1 + p2 + p3 + p4, 0.f, 255.f);
+    *out = convert_uchar4(p0);
+}
+
+
diff --git a/wifi/java/android/net/wifi/ScanResult.java b/wifi/java/android/net/wifi/ScanResult.java
index 3e20756..9977419 100644
--- a/wifi/java/android/net/wifi/ScanResult.java
+++ b/wifi/java/android/net/wifi/ScanResult.java
@@ -28,6 +28,10 @@
 public class ScanResult implements Parcelable {
     /** The network name. */
     public String SSID;
+
+    /** Ascii encoded SSID. This will replace SSID when we deprecate it. @hide */
+    public WifiSsid wifiSsid;
+
     /** The address of the access point. */
     public String BSSID;
     /**
@@ -52,15 +56,11 @@
      */
      public long timestamp;
 
-    /**
-     * We'd like to obtain the following attributes,
-     * but they are not reported via the socket
-     * interface, even though they are known
-     * internally by wpa_supplicant.
-     * {@hide}
-     */
-    public ScanResult(String SSID, String BSSID, String caps, int level, int frequency, long tsf) {
-        this.SSID = SSID;
+    /** {@hide} */
+    public ScanResult(WifiSsid wifiSsid, String BSSID, String caps, int level, int frequency,
+            long tsf) {
+        this.wifiSsid = wifiSsid;
+        this.SSID = (wifiSsid != null) ? wifiSsid.toString() : WifiSsid.NONE;
         this.BSSID = BSSID;
         this.capabilities = caps;
         this.level = level;
@@ -68,9 +68,11 @@
         this.timestamp = tsf;
     }
 
+
     /** copy constructor {@hide} */
     public ScanResult(ScanResult source) {
         if (source != null) {
+            wifiSsid = source.wifiSsid;
             SSID = source.SSID;
             BSSID = source.BSSID;
             capabilities = source.capabilities;
@@ -86,7 +88,7 @@
         String none = "<none>";
 
         sb.append("SSID: ").
-            append(SSID == null ? none : SSID).
+            append(wifiSsid == null ? WifiSsid.NONE : wifiSsid).
             append(", BSSID: ").
             append(BSSID == null ? none : BSSID).
             append(", capabilities: ").
@@ -108,7 +110,12 @@
 
     /** Implement the Parcelable interface {@hide} */
     public void writeToParcel(Parcel dest, int flags) {
-        dest.writeString(SSID);
+        if (wifiSsid != null) {
+            dest.writeInt(1);
+            wifiSsid.writeToParcel(dest, flags);
+        } else {
+            dest.writeInt(0);
+        }
         dest.writeString(BSSID);
         dest.writeString(capabilities);
         dest.writeInt(level);
@@ -120,8 +127,12 @@
     public static final Creator<ScanResult> CREATOR =
         new Creator<ScanResult>() {
             public ScanResult createFromParcel(Parcel in) {
+                WifiSsid wifiSsid = null;
+                if (in.readInt() == 1) {
+                    wifiSsid = WifiSsid.CREATOR.createFromParcel(in);
+                }
                 return new ScanResult(
-                    in.readString(),
+                    wifiSsid,
                     in.readString(),
                     in.readString(),
                     in.readInt(),
diff --git a/wifi/java/android/net/wifi/StateChangeResult.java b/wifi/java/android/net/wifi/StateChangeResult.java
index b15c4a6..c334b91 100644
--- a/wifi/java/android/net/wifi/StateChangeResult.java
+++ b/wifi/java/android/net/wifi/StateChangeResult.java
@@ -23,15 +23,16 @@
  * @hide
  */
 public class StateChangeResult {
-    StateChangeResult(int networkId, String SSID, String BSSID, SupplicantState state) {
+    StateChangeResult(int networkId, WifiSsid wifiSsid, String BSSID,
+            SupplicantState state) {
         this.state = state;
-        this.SSID = SSID;
+        this.wifiSsid= wifiSsid;
         this.BSSID = BSSID;
         this.networkId = networkId;
     }
 
     int networkId;
-    String SSID;
+    WifiSsid wifiSsid;
     String BSSID;
     SupplicantState state;
 }
diff --git a/wifi/java/android/net/wifi/WifiConfigStore.java b/wifi/java/android/net/wifi/WifiConfigStore.java
index a2332e3..84506b6 100644
--- a/wifi/java/android/net/wifi/WifiConfigStore.java
+++ b/wifi/java/android/net/wifi/WifiConfigStore.java
@@ -1318,7 +1318,13 @@
 
         value = mWifiNative.getNetworkVariable(netId, WifiConfiguration.ssidVarName);
         if (!TextUtils.isEmpty(value)) {
-            config.SSID = value;
+            if (value.charAt(0) != '"') {
+                config.SSID = "\"" + WifiSsid.createFromHex(value).toString() + "\"";
+                //TODO: convert a hex string that is not UTF-8 decodable to a P-formatted
+                //supplicant string
+            } else {
+                config.SSID = value;
+            }
         } else {
             config.SSID = null;
         }
diff --git a/wifi/java/android/net/wifi/WifiConfiguration.java b/wifi/java/android/net/wifi/WifiConfiguration.java
index 0a846fd..c4fe1b4 100644
--- a/wifi/java/android/net/wifi/WifiConfiguration.java
+++ b/wifi/java/android/net/wifi/WifiConfiguration.java
@@ -524,6 +524,27 @@
     }
     */
 
+    /** {@hide} */
+    public String getPrintableSsid() {
+        if (SSID == null) return "";
+        final int length = SSID.length();
+        if (length > 2 && (SSID.charAt(0) == '"') && SSID.charAt(length - 1) == '"') {
+            return SSID.substring(1, length - 1);
+        }
+
+        /** The ascii-encoded string format is P"<ascii-encoded-string>"
+         * The decoding is implemented in the supplicant for a newly configured
+         * network.
+         */
+        if (length > 3 && (SSID.charAt(0) == 'P') && (SSID.charAt(1) == '"') &&
+                (SSID.charAt(length-1) == '"')) {
+            WifiSsid wifiSsid = WifiSsid.createFromAsciiEncoded(
+                    SSID.substring(2, length - 1));
+            return wifiSsid.toString();
+        }
+        return SSID;
+    }
+
     private static BitSet readBitSet(Parcel src) {
         int cardinality = src.readInt();
 
diff --git a/wifi/java/android/net/wifi/WifiInfo.java b/wifi/java/android/net/wifi/WifiInfo.java
index 1f1cfdd..05db571 100644
--- a/wifi/java/android/net/wifi/WifiInfo.java
+++ b/wifi/java/android/net/wifi/WifiInfo.java
@@ -20,6 +20,7 @@
 import android.os.Parcel;
 import android.net.NetworkInfo.DetailedState;
 import android.net.NetworkUtils;
+import android.text.TextUtils;
 
 import java.net.InetAddress;
 import java.net.Inet6Address;
@@ -31,6 +32,7 @@
  * is in the process of being set up.
  */
 public class WifiInfo implements Parcelable {
+    private static final String TAG = "WifiInfo";
     /**
      * This is the map described in the Javadoc comment above. The positions
      * of the elements of the array must correspond to the ordinal values
@@ -57,7 +59,7 @@
 
     private SupplicantState mSupplicantState;
     private String mBSSID;
-    private String mSSID;
+    private WifiSsid mWifiSsid;
     private int mNetworkId;
     private boolean mHiddenSSID;
     /** Received Signal Strength Indicator */
@@ -77,7 +79,7 @@
     private boolean mMeteredHint;
 
     WifiInfo() {
-        mSSID = null;
+        mWifiSsid = null;
         mBSSID = null;
         mNetworkId = -1;
         mSupplicantState = SupplicantState.UNINITIALIZED;
@@ -94,7 +96,7 @@
         if (source != null) {
             mSupplicantState = source.mSupplicantState;
             mBSSID = source.mBSSID;
-            mSSID = source.mSSID;
+            mWifiSsid = source.mWifiSsid;
             mNetworkId = source.mNetworkId;
             mHiddenSSID = source.mHiddenSSID;
             mRssi = source.mRssi;
@@ -105,21 +107,34 @@
         }
     }
 
-    void setSSID(String SSID) {
-        mSSID = SSID;
+    void setSSID(WifiSsid wifiSsid) {
+        mWifiSsid = wifiSsid;
         // network is considered not hidden by default
         mHiddenSSID = false;
     }
 
     /**
      * Returns the service set identifier (SSID) of the current 802.11 network.
-     * If the SSID is an ASCII string, it will be returned surrounded by double
-     * quotation marks.Otherwise, it is returned as a string of hex digits. The
+     * If the SSID can be decoded as UTF-8, it will be returned surrounded by double
+     * quotation marks. Otherwise, it is returned as a string of hex digits. The
      * SSID may be {@code null} if there is no network currently connected.
      * @return the SSID
      */
     public String getSSID() {
-        return mSSID;
+        if (mWifiSsid != null) {
+            String unicode = mWifiSsid.toString();
+            if (!TextUtils.isEmpty(unicode)) {
+                return "\"" + unicode + "\"";
+            } else {
+                return mWifiSsid.getHexString();
+            }
+        }
+        return WifiSsid.NONE;
+    }
+
+    /** @hide */
+    public WifiSsid getWifiSsid() {
+        return mWifiSsid;
     }
 
     void setBSSID(String BSSID) {
@@ -279,7 +294,7 @@
         StringBuffer sb = new StringBuffer();
         String none = "<none>";
 
-        sb.append("SSID: ").append(mSSID == null ? none : mSSID).
+        sb.append("SSID: ").append(mWifiSsid == null ? WifiSsid.NONE : mWifiSsid).
             append(", BSSID: ").append(mBSSID == null ? none : mBSSID).
             append(", MAC: ").append(mMacAddress == null ? none : mMacAddress).
             append(", Supplicant state: ").
@@ -308,7 +323,12 @@
         } else {
             dest.writeByte((byte)0);
         }
-        dest.writeString(getSSID());
+        if (mWifiSsid != null) {
+            dest.writeInt(1);
+            mWifiSsid.writeToParcel(dest, flags);
+        } else {
+            dest.writeInt(0);
+        }
         dest.writeString(mBSSID);
         dest.writeString(mMacAddress);
         dest.writeInt(mMeteredHint ? 1 : 0);
@@ -328,7 +348,9 @@
                         info.setInetAddress(InetAddress.getByAddress(in.createByteArray()));
                     } catch (UnknownHostException e) {}
                 }
-                info.setSSID(in.readString());
+                if (in.readInt() == 1) {
+                    info.mWifiSsid = WifiSsid.CREATOR.createFromParcel(in);
+                }
                 info.mBSSID = in.readString();
                 info.mMacAddress = in.readString();
                 info.mMeteredHint = in.readInt() != 0;
diff --git a/wifi/java/android/net/wifi/WifiMonitor.java b/wifi/java/android/net/wifi/WifiMonitor.java
index 17c930b..ab54a15 100644
--- a/wifi/java/android/net/wifi/WifiMonitor.java
+++ b/wifi/java/android/net/wifi/WifiMonitor.java
@@ -645,9 +645,12 @@
          * id=network-id state=new-state
          */
         private void handleSupplicantStateChange(String dataString) {
-            String SSID = null;
+            WifiSsid wifiSsid = null;
             int index = dataString.lastIndexOf("SSID=");
-            if (index != -1) SSID = dataString.substring(index + 5);
+            if (index != -1) {
+                wifiSsid = WifiSsid.createFromAsciiEncoded(
+                        dataString.substring(index + 5));
+            }
             String[] dataTokens = dataString.split(" ");
 
             String BSSID = null;
@@ -690,7 +693,7 @@
             if (newSupplicantState == SupplicantState.INVALID) {
                 Log.w(TAG, "Invalid supplicant state: " + newState);
             }
-            notifySupplicantStateChange(networkId, SSID, BSSID, newSupplicantState);
+            notifySupplicantStateChange(networkId, wifiSsid, BSSID, newSupplicantState);
         }
     }
 
@@ -739,13 +742,14 @@
      * Send the state machine a notification that the state of the supplicant
      * has changed.
      * @param networkId the configured network on which the state change occurred
-     * @param SSID network name
+     * @param wifiSsid network name
      * @param BSSID network address
      * @param newState the new {@code SupplicantState}
      */
-    void notifySupplicantStateChange(int networkId, String SSID, String BSSID, SupplicantState newState) {
+    void notifySupplicantStateChange(int networkId, WifiSsid wifiSsid, String BSSID,
+            SupplicantState newState) {
         mStateMachine.sendMessage(mStateMachine.obtainMessage(SUPPLICANT_STATE_CHANGE_EVENT,
-                new StateChangeResult(networkId, SSID, BSSID, newState)));
+                new StateChangeResult(networkId, wifiSsid, BSSID, newState)));
     }
 
     /**
diff --git a/wifi/java/android/net/wifi/WifiSsid.java b/wifi/java/android/net/wifi/WifiSsid.java
new file mode 100644
index 0000000..6f36111
--- /dev/null
+++ b/wifi/java/android/net/wifi/WifiSsid.java
@@ -0,0 +1,217 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.wifi;
+
+import android.os.Parcelable;
+import android.os.Parcel;
+import android.util.Log;
+
+import java.io.ByteArrayOutputStream;
+import java.nio.ByteBuffer;
+import java.nio.CharBuffer;
+import java.nio.charset.Charset;
+import java.nio.charset.CharsetDecoder;
+import java.nio.charset.CoderResult;
+import java.nio.charset.CodingErrorAction;
+
+/**
+ * Stores SSID octets and handles conversion.
+ *
+ * For Ascii encoded string, any octet < 32 or > 127 is encoded as
+ * a "\x" followed by the hex representation of the octet.
+ * Exception chars are ", \, \e, \n, \r, \t which are escaped by a \
+ * See src/utils/common.c for the implementation in the supplicant.
+ *
+ * @hide
+ */
+public class WifiSsid implements Parcelable {
+    private static final String TAG = "WifiSsid";
+
+    public ByteArrayOutputStream octets = new ByteArrayOutputStream(32);
+
+    private static final int HEX_RADIX = 16;
+    public static final String NONE = "<unknown ssid>";
+
+    private WifiSsid() {
+    }
+
+    public static WifiSsid createFromAsciiEncoded(String asciiEncoded) {
+        WifiSsid a = new WifiSsid();
+        a.convertToBytes(asciiEncoded);
+        return a;
+    }
+
+    public static WifiSsid createFromHex(String hexStr) {
+        WifiSsid a = new WifiSsid();
+        int length = 0;
+        if (hexStr == null) return a;
+
+        if (hexStr.startsWith("0x") || hexStr.startsWith("0X")) {
+            hexStr = hexStr.substring(2);
+        }
+
+        for (int i = 0; i < hexStr.length()-1; i += 2) {
+            int val;
+            try {
+                val = Integer.parseInt(hexStr.substring(i, i + 2), HEX_RADIX);
+            } catch(NumberFormatException e) {
+                val = 0;
+            }
+            a.octets.write(val);
+        }
+        return a;
+    }
+
+    /* This function is equivalent to printf_decode() at src/utils/common.c in
+     * the supplicant */
+    private void convertToBytes(String asciiEncoded) {
+        int i = 0;
+        int val = 0;
+        while (i< asciiEncoded.length()) {
+            char c = asciiEncoded.charAt(i);
+            switch (c) {
+                case '\\':
+                    i++;
+                    switch(asciiEncoded.charAt(i)) {
+                        case '\\':
+                            octets.write('\\');
+                            break;
+                        case '"':
+                            octets.write('"');
+                            break;
+                        case 'n':
+                            octets.write('\n');
+                            break;
+                        case 'r':
+                            octets.write('\r');
+                            break;
+                        case 't':
+                            octets.write('\t');
+                            break;
+                        case 'e':
+                            octets.write(27); //escape char
+                            break;
+                        case 'x':
+                            i++;
+                            try {
+                                val = Integer.parseInt(asciiEncoded.substring(i, i + 2), HEX_RADIX);
+                            } catch (NumberFormatException e) {
+                                val = -1;
+                            }
+                            if (val < 0) {
+                                val = Character.digit(asciiEncoded.charAt(i), HEX_RADIX);
+                                if (val < 0) break;
+                                octets.write(val);
+                                i++;
+                            } else {
+                                octets.write(val);
+                                i += 2;
+                            }
+                            break;
+                        case '0':
+                        case '1':
+                        case '2':
+                        case '3':
+                        case '4':
+                        case '5':
+                        case '6':
+                        case '7':
+                            val = asciiEncoded.charAt(i) - '0';
+                            i++;
+                            if (asciiEncoded.charAt(i) >= '0' && asciiEncoded.charAt(i) <= '7') {
+                                val = val * 8 + asciiEncoded.charAt(i) - '0';
+                                i++;
+                            }
+                            if (asciiEncoded.charAt(i) >= '0' && asciiEncoded.charAt(i) <= '7') {
+                                val = val * 8 + asciiEncoded.charAt(i) - '0';
+                                i++;
+                            }
+                            octets.write(val);
+                            break;
+                        default:
+                            break;
+                    }
+                    break;
+                default:
+                    octets.write(c);
+                    i++;
+                    break;
+            }
+        }
+    }
+
+    @Override
+    public String toString() {
+        if (octets.size() <= 0) return "";
+        // TODO: Handle conversion to other charsets upon failure
+        Charset charset = Charset.forName("UTF-8");
+        CharsetDecoder decoder = charset.newDecoder()
+                .onMalformedInput(CodingErrorAction.REPLACE)
+                .onUnmappableCharacter(CodingErrorAction.REPLACE);
+        CharBuffer out = CharBuffer.allocate(32);
+
+        CoderResult result = decoder.decode(ByteBuffer.wrap(octets.toByteArray()), out, true);
+        out.flip();
+        if (result.isError()) {
+            return NONE;
+        }
+        return out.toString();
+    }
+
+    /** @hide */
+    public byte[] getOctets() {
+        return  octets.toByteArray();
+    }
+
+    /** @hide */
+    public String getHexString() {
+        String out = "0x";
+        byte[] ssidbytes = getOctets();
+        for (int i = 0; i < octets.size(); i++) {
+            out += String.format("%02x", ssidbytes[i]);
+        }
+        return out;
+    }
+
+    /** Implement the Parcelable interface {@hide} */
+    public int describeContents() {
+        return 0;
+    }
+
+    /** Implement the Parcelable interface {@hide} */
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeInt(octets.size());
+        dest.writeByteArray(octets.toByteArray());
+    }
+
+    /** Implement the Parcelable interface {@hide} */
+    public static final Creator<WifiSsid> CREATOR =
+        new Creator<WifiSsid>() {
+            public WifiSsid createFromParcel(Parcel in) {
+                WifiSsid ssid = new WifiSsid();
+                int length = in.readInt();
+                byte b[] = new byte[length];
+                in.readByteArray(b);
+                ssid.octets.write(b, 0, length);
+                return ssid;
+            }
+
+            public WifiSsid[] newArray(int size) {
+                return new WifiSsid[size];
+            }
+        };
+}
diff --git a/wifi/java/android/net/wifi/WifiStateMachine.java b/wifi/java/android/net/wifi/WifiStateMachine.java
index 40111fa..4cbb824 100644
--- a/wifi/java/android/net/wifi/WifiStateMachine.java
+++ b/wifi/java/android/net/wifi/WifiStateMachine.java
@@ -138,10 +138,6 @@
     private boolean mScanResultIsPending = false;
     /* Tracks if the current scan settings are active */
     private boolean mSetScanActive = false;
-    /* High perf mode is true if an app has held a high perf Wifi Lock */
-    private boolean mHighPerfMode = false;
-    /* Tracks if user has disabled suspend optimizations through settings */
-    private AtomicBoolean mSuspendOptEnabled = new AtomicBoolean(true);
 
     private boolean mBluetoothConnectionActive = false;
 
@@ -338,10 +334,8 @@
     static final int CMD_START_PACKET_FILTERING           = BASE + 84;
     /* Clear packet filter */
     static final int CMD_STOP_PACKET_FILTERING            = BASE + 85;
-    /* Set suspend mode optimizations in the driver */
-    static final int CMD_SET_SUSPEND_OPTIMIZATIONS        = BASE + 86;
-    /* Clear suspend mode optimizations in the driver */
-    static final int CMD_CLEAR_SUSPEND_OPTIMIZATIONS      = BASE + 87;
+    /* Enable suspend mode optimizations in the driver */
+    static final int CMD_SET_SUSPEND_OPT_ENABLED          = BASE + 86;
     /* When there are no saved networks, we do a periodic scan to notify user of
      * an open network */
     static final int CMD_NO_NETWORKS_PERIODIC_SCAN        = BASE + 88;
@@ -386,8 +380,19 @@
      */
     private static final int DEFAULT_MAX_DHCP_RETRIES = 9;
 
-    /* Tracks if power save is enabled in driver */
-    private boolean mPowerSaveEnabled = true;;
+    /* Tracks if suspend optimizations need to be disabled by DHCP,
+     * screen or due to high perf mode.
+     * When any of them needs to disable it, we keep the suspend optimizations
+     * disabled
+     */
+    private int mSuspendOptNeedsDisabled = 0;
+
+    private static final int SUSPEND_DUE_TO_DHCP       = 1;
+    private static final int SUSPEND_DUE_TO_HIGH_PERF  = 1<<1;
+    private static final int SUSPEND_DUE_TO_SCREEN     = 1<<2;
+
+    /* Tracks if user has enabled suspend optimizations through settings */
+    private AtomicBoolean mUserWantsSuspendOpt = new AtomicBoolean(true);
 
     /**
      * Default framework scan interval in milliseconds. This is used in the scenario in which
@@ -599,7 +604,7 @@
         mPrimaryDeviceType = mContext.getResources().getString(
                 com.android.internal.R.string.config_wifi_p2p_device_type);
 
-        mSuspendOptEnabled.set(Settings.Secure.getInt(mContext.getContentResolver(),
+        mUserWantsSuspendOpt.set(Settings.Secure.getInt(mContext.getContentResolver(),
                     Settings.Secure.WIFI_SUSPEND_OPTIMIZATIONS_ENABLED, 1) == 1);
 
         mContext.registerReceiver(
@@ -637,20 +642,20 @@
                         enableBackgroundScanCommand(false);
                     }
                     enableAllNetworks();
-                    if (mSuspendOptEnabled.get()) {
+                    if (mUserWantsSuspendOpt.get()) {
                         if (DBG) log("Clear suspend optimizations");
-                        sendMessage(CMD_CLEAR_SUSPEND_OPTIMIZATIONS);
+                        sendMessage(obtainMessage(CMD_SET_SUSPEND_OPT_ENABLED, 0, 0));
                     }
                 } else if (action.equals(Intent.ACTION_SCREEN_OFF)) {
                     enableRssiPolling(false);
                     if (mBackgroundScanSupported) {
                         enableBackgroundScanCommand(true);
                     }
-                    if (mSuspendOptEnabled.get()) {
+                    if (mUserWantsSuspendOpt.get()) {
                         if (DBG) log("Enable suspend optimizations");
                         //Allow 2s for suspend optimizations to be set
                         mSuspendWakeLock.acquire(2000);
-                        sendMessage(CMD_SET_SUSPEND_OPTIMIZATIONS);
+                        sendMessage(obtainMessage(CMD_SET_SUSPEND_OPT_ENABLED, 1, 0));
                     }
                 }
             }
@@ -671,7 +676,7 @@
                 new ContentObserver(getHandler()) {
                     @Override
                     public void onChange(boolean selfChange) {
-                        mSuspendOptEnabled.set(Settings.Secure.getInt(mContext.getContentResolver(),
+                        mUserWantsSuspendOpt.set(Settings.Secure.getInt(mContext.getContentResolver(),
                                 Settings.Secure.WIFI_SUSPEND_OPTIMIZATIONS_ENABLED, 1) == 1);
                     }
                 });
@@ -1172,8 +1177,8 @@
         sb.append("mLastNetworkId ").append(mLastNetworkId).append(LS);
         sb.append("mReconnectCount ").append(mReconnectCount).append(LS);
         sb.append("mIsScanMode ").append(mIsScanMode).append(LS);
-        sb.append("mHighPerfMode").append(mHighPerfMode).append(LS);
-        sb.append("mSuspendOptEnabled").append(mSuspendOptEnabled).append(LS);
+        sb.append("mUserWantsSuspendOpt ").append(mUserWantsSuspendOpt).append(LS);
+        sb.append("mSuspendOptNeedsDisabled ").append(mSuspendOptNeedsDisabled).append(LS);
         sb.append("Supplicant status").append(LS)
                 .append(mWifiNative.status()).append(LS).append(LS);
 
@@ -1191,8 +1196,7 @@
                 case CMD_START_DRIVER:
                 case CMD_SET_SCAN_MODE:
                 case CMD_SET_HIGH_PERF_MODE:
-                case CMD_SET_SUSPEND_OPTIMIZATIONS:
-                case CMD_CLEAR_SUSPEND_OPTIMIZATIONS:
+                case CMD_SET_SUSPEND_OPT_ENABLED:
                 case CMD_ENABLE_BACKGROUND_SCAN:
                 case CMD_ENABLE_ALL_NETWORKS:
                 return false;
@@ -1324,6 +1328,32 @@
         setFrequencyBand(band, false);
     }
 
+    private void setSuspendOptimizationsNative(int reason, boolean enabled) {
+        if (DBG) log("setSuspendOptimizationsNative: " + reason + " " + enabled);
+        if (enabled) {
+            mSuspendOptNeedsDisabled &= ~reason;
+            /* None of dhcp, screen or highperf need it disabled and user wants it enabled */
+            if (mSuspendOptNeedsDisabled == 0 && mUserWantsSuspendOpt.get()) {
+                mWifiNative.setSuspendOptimizations(true);
+                if (DBG) log("Enabled, mSuspendOptNeedsDisabled " + mSuspendOptNeedsDisabled);
+            }
+        } else {
+            mSuspendOptNeedsDisabled |= reason;
+            mWifiNative.setSuspendOptimizations(false);
+            if (DBG) log("Disabled, mSuspendOptNeedsDisabled " + mSuspendOptNeedsDisabled);
+        }
+    }
+
+    private void setSuspendOptimizations(int reason, boolean enabled) {
+        if (DBG) log("setSuspendOptimizations: " + reason + " " + enabled);
+        if (enabled) {
+            mSuspendOptNeedsDisabled &= ~reason;
+        } else {
+            mSuspendOptNeedsDisabled |= reason;
+        }
+        if (DBG) log("mSuspendOptNeedsDisabled " + mSuspendOptNeedsDisabled);
+    }
+
     private void setWifiState(int wifiState) {
         final int previousWifiState = mWifiState.get();
 
@@ -1403,7 +1433,7 @@
         int freq = 0;
         long tsf = 0;
         String flags = "";
-        String ssid = "";
+        WifiSsid wifiSsid = null;
 
         if (scanResults == null) {
             return;
@@ -1441,22 +1471,26 @@
                 } else if (line.startsWith(FLAGS_STR)) {
                     flags = line.substring(FLAGS_STR.length());
                 } else if (line.startsWith(SSID_STR)) {
-                    ssid = line.substring(SSID_STR.length());
-                    if (ssid == null) ssid = "";
+                    wifiSsid = WifiSsid.createFromAsciiEncoded(
+                            line.substring(SSID_STR.length()));
                 } else if (line.startsWith(DELIMITER_STR)) {
                     if (bssid != null) {
+                        String ssid = (wifiSsid != null) ? wifiSsid.toString() : WifiSsid.NONE;
                         String key = bssid + ssid;
                         ScanResult scanResult = mScanResultCache.get(key);
                         if (scanResult != null) {
                             scanResult.level = level;
-                            scanResult.SSID = ssid;
+                            scanResult.wifiSsid = wifiSsid;
+                            // Keep existing API
+                            scanResult.SSID = (wifiSsid != null) ? wifiSsid.toString() :
+                                    WifiSsid.NONE;
                             scanResult.capabilities = flags;
                             scanResult.frequency = freq;
                             scanResult.timestamp = tsf;
                         } else {
                             scanResult =
                                 new ScanResult(
-                                        ssid, bssid, flags, level, freq, tsf);
+                                        wifiSsid, bssid, flags, level, freq, tsf);
                             mScanResultCache.put(key, scanResult);
                         }
                         mScanResults.add(scanResult);
@@ -1466,7 +1500,7 @@
                     freq = 0;
                     tsf = 0;
                     flags = "";
-                    ssid = "";
+                    wifiSsid = null;
                 }
             }
         }
@@ -1652,7 +1686,7 @@
         }
 
         mWifiInfo.setBSSID(stateChangeResult.BSSID);
-        mWifiInfo.setSSID(stateChangeResult.SSID);
+        mWifiInfo.setSSID(stateChangeResult.wifiSsid);
 
         mSupplicantStateTracker.sendMessage(Message.obtain(message));
 
@@ -1732,18 +1766,16 @@
                     mWifiNative.BLUETOOTH_COEXISTENCE_MODE_DISABLED);
         }
 
-        /* Disable power save during DHCP */
-        if (mPowerSaveEnabled) {
-            mPowerSaveEnabled = false;
-            mWifiNative.setPowerSave(mPowerSaveEnabled);
-        }
+        /* Disable power save and suspend optimizations during DHCP */
+        mWifiNative.setPowerSave(false);
+        setSuspendOptimizationsNative(SUSPEND_DUE_TO_DHCP, false);
     }
 
 
     void handlePostDhcpSetup() {
-        /* Restore power save */
-        mPowerSaveEnabled = true;
-        mWifiNative.setPowerSave(mPowerSaveEnabled);
+        /* Restore power save and suspend optimizations */
+        mWifiNative.setPowerSave(true);
+        setSuspendOptimizationsNative(SUSPEND_DUE_TO_DHCP, true);
 
         // Set the coexistence mode back to its default value
         mWifiNative.setBluetoothCoexistenceMode(
@@ -1876,7 +1908,11 @@
                     mEnableBackgroundScan = (message.arg1 == 1);
                     break;
                 case CMD_SET_HIGH_PERF_MODE:
-                    mHighPerfMode = (message.arg1 == 1);
+                    if (message.arg1 == 1) {
+                        setSuspendOptimizations(SUSPEND_DUE_TO_HIGH_PERF, false);
+                    } else {
+                        setSuspendOptimizations(SUSPEND_DUE_TO_HIGH_PERF, true);
+                    }
                     break;
                     /* Discard */
                 case CMD_LOAD_DRIVER:
@@ -1923,14 +1959,18 @@
                 case CMD_RESPONSE_AP_CONFIG:
                 case WifiWatchdogStateMachine.POOR_LINK_DETECTED:
                 case WifiWatchdogStateMachine.GOOD_LINK_DETECTED:
-                case CMD_CLEAR_SUSPEND_OPTIMIZATIONS:
                 case CMD_NO_NETWORKS_PERIODIC_SCAN:
                     break;
                 case DhcpStateMachine.CMD_ON_QUIT:
                     mDhcpStateMachine = null;
                     break;
-                case CMD_SET_SUSPEND_OPTIMIZATIONS:
-                    mSuspendWakeLock.release();
+                case CMD_SET_SUSPEND_OPT_ENABLED:
+                    if (message.arg1 == 1) {
+                        mSuspendWakeLock.release();
+                        setSuspendOptimizations(SUSPEND_DUE_TO_SCREEN, true);
+                    } else {
+                        setSuspendOptimizations(SUSPEND_DUE_TO_SCREEN, false);
+                    }
                     break;
                 case WifiMonitor.DRIVER_HUNG_EVENT:
                     setWifiEnabled(false);
@@ -2666,7 +2706,7 @@
                 mWifiNative.stopFilteringMulticastV4Packets();
             }
 
-            mWifiNative.setPowerSave(mPowerSaveEnabled);
+            mWifiNative.setPowerSave(true);
 
             if (mIsScanMode) {
                 mWifiNative.setScanResultHandling(SCAN_ONLY_MODE);
@@ -2793,20 +2833,19 @@
                         loge("Illegal arugments to CMD_STOP_PACKET_FILTERING");
                     }
                     break;
-                case CMD_SET_SUSPEND_OPTIMIZATIONS:
-                    if (!mHighPerfMode) {
-                        mWifiNative.setSuspendOptimizations(true);
+                case CMD_SET_SUSPEND_OPT_ENABLED:
+                    if (message.arg1 == 1) {
+                        setSuspendOptimizationsNative(SUSPEND_DUE_TO_SCREEN, true);
+                        mSuspendWakeLock.release();
+                    } else {
+                        setSuspendOptimizationsNative(SUSPEND_DUE_TO_SCREEN, false);
                     }
-                    mSuspendWakeLock.release();
-                    break;
-                case CMD_CLEAR_SUSPEND_OPTIMIZATIONS:
-                    mWifiNative.setSuspendOptimizations(false);
                     break;
                 case CMD_SET_HIGH_PERF_MODE:
-                    mHighPerfMode = (message.arg1 == 1);
-                    if (mHighPerfMode) {
-                        //Disable any suspend optimizations
-                        mWifiNative.setSuspendOptimizations(false);
+                    if (message.arg1 == 1) {
+                        setSuspendOptimizationsNative(SUSPEND_DUE_TO_HIGH_PERF, false);
+                    } else {
+                        setSuspendOptimizationsNative(SUSPEND_DUE_TO_HIGH_PERF, true);
                     }
                     break;
                 default: