Merge "Make home stack move like another stack."
diff --git a/Android.mk b/Android.mk
index f1e16b8..9029f4e 100644
--- a/Android.mk
+++ b/Android.mk
@@ -483,6 +483,7 @@
 	frameworks/base/wifi/java/android/net/wifi/p2p/nsd/WifiP2pServiceInfo.aidl \
 	frameworks/base/wifi/java/android/net/wifi/WpsInfo.aidl \
 	frameworks/base/wifi/java/android/net/wifi/ScanResult.aidl \
+        frameworks/base/wifi/java/android/net/wifi/ScanInfo.aidl \
 	frameworks/base/wifi/java/android/net/wifi/WifiEnterpriseConfig.aidl \
 	frameworks/base/wifi/java/android/net/wifi/WifiConfiguration.aidl \
 	frameworks/base/wifi/java/android/net/wifi/WifiInfo.aidl \
diff --git a/api/current.txt b/api/current.txt
index 00b5ec0..c9e9f9a 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -779,6 +779,7 @@
     field public static final int layout_y = 16843136; // 0x1010180
     field public static final int left = 16843181; // 0x10101ad
     field public static final int letterSpacing = 16843958; // 0x10104b6
+    field public static final int level = 16844031; // 0x10104ff
     field public static final int lineSpacingExtra = 16843287; // 0x1010217
     field public static final int lineSpacingMultiplier = 16843288; // 0x1010218
     field public static final int lines = 16843092; // 0x1010154
@@ -18250,6 +18251,7 @@
     field public static final java.lang.String CONNECTIVITY_ACTION = "android.net.conn.CONNECTIVITY_CHANGE";
     field public static final deprecated int DEFAULT_NETWORK_PREFERENCE = 1; // 0x1
     field public static final java.lang.String EXTRA_CAPTIVE_PORTAL = "android.net.extra.CAPTIVE_PORTAL";
+    field public static final java.lang.String EXTRA_CAPTIVE_PORTAL_URL = "android.net.extra.CAPTIVE_PORTAL_URL";
     field public static final java.lang.String EXTRA_EXTRA_INFO = "extraInfo";
     field public static final java.lang.String EXTRA_IS_FAILOVER = "isFailover";
     field public static final java.lang.String EXTRA_NETWORK = "android.net.extra.NETWORK";
@@ -19156,6 +19158,22 @@
 
 package android.net.wifi {
 
+  public class ScanInfo implements android.os.Parcelable {
+    ctor public ScanInfo(android.net.wifi.ScanResult);
+    ctor public ScanInfo(long, int, java.lang.String, java.lang.String, java.lang.String, java.lang.String, byte[], int);
+    method public int describeContents();
+    method public long getBssid();
+    method public byte[] getIconData();
+    method public java.lang.String getIconType();
+    method public java.lang.String getName();
+    method public int getOsuIdentity();
+    method public int getRssi();
+    method public android.net.wifi.ScanResult getScanResult();
+    method public java.lang.String getServiceDescription();
+    method public java.lang.String getSsid();
+    method public void writeToParcel(android.os.Parcel, int);
+  }
+
   public class ScanResult implements android.os.Parcelable {
     method public int describeContents();
     method public boolean is80211mcResponder();
@@ -19356,6 +19374,7 @@
     method public java.util.List<android.net.wifi.WifiConfiguration> getConfiguredNetworks();
     method public android.net.wifi.WifiInfo getConnectionInfo();
     method public android.net.DhcpInfo getDhcpInfo();
+    method public java.util.List<android.net.wifi.ScanInfo> getScanInfos();
     method public java.util.List<android.net.wifi.ScanResult> getScanResults();
     method public int getWifiState();
     method public boolean is5GHzBandSupported();
@@ -19371,6 +19390,7 @@
     method public boolean reconnect();
     method public boolean removeNetwork(int);
     method public boolean saveConfiguration();
+    method public void setOsuSelection(int);
     method public void setTdlsEnabled(java.net.InetAddress, boolean);
     method public void setTdlsEnabledWithMacAddress(java.lang.String, boolean);
     method public boolean setWifiEnabled(boolean);
@@ -40972,6 +40992,7 @@
     method public void setInterpolator(android.view.animation.Interpolator);
     method public synchronized void setMax(int);
     method public synchronized void setProgress(int);
+    method public void setProgress(int, boolean);
     method public void setProgressBackgroundTintList(android.content.res.ColorStateList);
     method public void setProgressBackgroundTintMode(android.graphics.PorterDuff.Mode);
     method public void setProgressDrawable(android.graphics.drawable.Drawable);
diff --git a/api/system-current.txt b/api/system-current.txt
index b2803b7..f39dc9a 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -871,6 +871,7 @@
     field public static final int layout_y = 16843136; // 0x1010180
     field public static final int left = 16843181; // 0x10101ad
     field public static final int letterSpacing = 16843958; // 0x10104b6
+    field public static final int level = 16844031; // 0x10104ff
     field public static final int lineSpacingExtra = 16843287; // 0x1010217
     field public static final int lineSpacingMultiplier = 16843288; // 0x1010218
     field public static final int lines = 16843092; // 0x1010154
@@ -19762,6 +19763,7 @@
     field public static final java.lang.String CONNECTIVITY_ACTION = "android.net.conn.CONNECTIVITY_CHANGE";
     field public static final deprecated int DEFAULT_NETWORK_PREFERENCE = 1; // 0x1
     field public static final java.lang.String EXTRA_CAPTIVE_PORTAL = "android.net.extra.CAPTIVE_PORTAL";
+    field public static final java.lang.String EXTRA_CAPTIVE_PORTAL_URL = "android.net.extra.CAPTIVE_PORTAL_URL";
     field public static final java.lang.String EXTRA_EXTRA_INFO = "extraInfo";
     field public static final java.lang.String EXTRA_IS_FAILOVER = "isFailover";
     field public static final java.lang.String EXTRA_NETWORK = "android.net.extra.NETWORK";
@@ -20909,6 +20911,22 @@
     field public byte id;
   }
 
+  public class ScanInfo implements android.os.Parcelable {
+    ctor public ScanInfo(android.net.wifi.ScanResult);
+    ctor public ScanInfo(long, int, java.lang.String, java.lang.String, java.lang.String, java.lang.String, byte[], int);
+    method public int describeContents();
+    method public long getBssid();
+    method public byte[] getIconData();
+    method public java.lang.String getIconType();
+    method public java.lang.String getName();
+    method public int getOsuIdentity();
+    method public int getRssi();
+    method public android.net.wifi.ScanResult getScanResult();
+    method public java.lang.String getServiceDescription();
+    method public java.lang.String getSsid();
+    method public void writeToParcel(android.os.Parcel, int);
+  }
+
   public class ScanResult implements android.os.Parcelable {
     method public int describeContents();
     method public boolean is80211mcResponder();
@@ -21134,6 +21152,7 @@
     method public android.net.wifi.WifiConnectionStatistics getConnectionStatistics();
     method public android.net.DhcpInfo getDhcpInfo();
     method public java.util.List<android.net.wifi.WifiConfiguration> getPrivilegedConfiguredNetworks();
+    method public java.util.List<android.net.wifi.ScanInfo> getScanInfos();
     method public java.util.List<android.net.wifi.ScanResult> getScanResults();
     method public int getWifiState();
     method public boolean is5GHzBandSupported();
@@ -21153,6 +21172,7 @@
     method public boolean reconnect();
     method public boolean removeNetwork(int);
     method public boolean saveConfiguration();
+    method public void setOsuSelection(int);
     method public void setTdlsEnabled(java.net.InetAddress, boolean);
     method public void setTdlsEnabledWithMacAddress(java.lang.String, boolean);
     method public boolean setWifiEnabled(boolean);
@@ -25667,8 +25687,8 @@
     ctor public UserHandle(android.os.Parcel);
     method public int describeContents();
     method public int getIdentifier();
-    method public final boolean isOwner();
-    method public static final int myUserId();
+    method public boolean isOwner();
+    method public static int myUserId();
     method public static android.os.UserHandle readFromParcel(android.os.Parcel);
     method public void writeToParcel(android.os.Parcel, int);
     method public static void writeToParcel(android.os.UserHandle, android.os.Parcel);
@@ -43580,6 +43600,7 @@
     method public void setInterpolator(android.view.animation.Interpolator);
     method public synchronized void setMax(int);
     method public synchronized void setProgress(int);
+    method public void setProgress(int, boolean);
     method public void setProgressBackgroundTintList(android.content.res.ColorStateList);
     method public void setProgressBackgroundTintMode(android.graphics.PorterDuff.Mode);
     method public void setProgressDrawable(android.graphics.drawable.Drawable);
diff --git a/core/java/android/app/admin/DeviceAdminInfo.java b/core/java/android/app/admin/DeviceAdminInfo.java
index d1e40ae..4e9adf0 100644
--- a/core/java/android/app/admin/DeviceAdminInfo.java
+++ b/core/java/android/app/admin/DeviceAdminInfo.java
@@ -20,6 +20,7 @@
 import org.xmlpull.v1.XmlPullParserException;
 import org.xmlpull.v1.XmlSerializer;
 
+import android.annotation.NonNull;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.pm.ActivityInfo;
@@ -360,6 +361,7 @@
     /**
      * Return the component of the receiver that implements this device admin.
      */
+    @NonNull
     public ComponentName getComponent() {
         return new ComponentName(mReceiver.activityInfo.packageName,
                 mReceiver.activityInfo.name);
diff --git a/core/java/android/content/pm/UserInfo.java b/core/java/android/content/pm/UserInfo.java
index 7392563..d7c2215 100644
--- a/core/java/android/content/pm/UserInfo.java
+++ b/core/java/android/content/pm/UserInfo.java
@@ -136,7 +136,16 @@
      * the method always returns false.
      */
     public boolean isSystemOnly() {
-        return id == UserHandle.USER_SYSTEM && UserManager.isSplitSystemUser();
+        return isSystemOnly(id);
+    }
+
+    /**
+     * Returns true if the given user is a split system user.
+     * <p>If {@link UserManager#isSplitSystemUser split system user mode} is not enabled,
+     * the method always returns false.
+     */
+    public static boolean isSystemOnly(int userId) {
+        return userId == UserHandle.USER_SYSTEM && UserManager.isSplitSystemUser();
     }
 
     /**
diff --git a/core/java/android/content/res/Configuration.java b/core/java/android/content/res/Configuration.java
index 927c02f..477b62c 100644
--- a/core/java/android/content/res/Configuration.java
+++ b/core/java/android/content/res/Configuration.java
@@ -61,7 +61,7 @@
      * resource qualifier.  0 if undefined.
      */
     public int mcc;
-    
+
     /**
      * IMSI MNC (Mobile Network Code), corresponding to
      * <a href="{@docRoot}guide/topics/resources/providing-resources.html#MccQualifier">mnc</a>
@@ -199,7 +199,7 @@
      * @hide
      */
     public static final int SCREENLAYOUT_COMPAT_NEEDED = 0x10000000;
-    
+
     /**
      * Bit mask of overall layout of the screen.  Currently there are two
      * fields:
@@ -207,11 +207,11 @@
      * of the screen.  They may be one of
      * {@link #SCREENLAYOUT_SIZE_SMALL}, {@link #SCREENLAYOUT_SIZE_NORMAL},
      * {@link #SCREENLAYOUT_SIZE_LARGE}, or {@link #SCREENLAYOUT_SIZE_XLARGE}.</p>
-     * 
+     *
      * <p>The {@link #SCREENLAYOUT_LONG_MASK} defines whether the screen
      * is wider/taller than normal.  They may be one of
      * {@link #SCREENLAYOUT_LONG_NO} or {@link #SCREENLAYOUT_LONG_YES}.</p>
-     * 
+     *
      * <p>The {@link #SCREENLAYOUT_LAYOUTDIR_MASK} defines whether the screen layout
      * is either LTR or RTL.  They may be one of
      * {@link #SCREENLAYOUT_LAYOUTDIR_LTR} or {@link #SCREENLAYOUT_LAYOUTDIR_RTL}.</p>
@@ -295,6 +295,62 @@
         return curLayout;
     }
 
+    /** @hide */
+    public static String configurationDiffToString(int diff) {
+        ArrayList<String> list = new ArrayList<>();
+        if ((diff & ActivityInfo.CONFIG_MCC) != 0) {
+            list.add("CONFIG_MCC");
+        }
+        if ((diff & ActivityInfo.CONFIG_MNC) != 0) {
+            list.add("CONFIG_MNC");
+        }
+        if ((diff & ActivityInfo.CONFIG_LOCALE) != 0) {
+            list.add("CONFIG_LOCALE");
+        }
+        if ((diff & ActivityInfo.CONFIG_TOUCHSCREEN) != 0) {
+            list.add("CONFIG_TOUCHSCREEN");
+        }
+        if ((diff & ActivityInfo.CONFIG_KEYBOARD) != 0) {
+            list.add("CONFIG_KEYBOARD");
+        }
+        if ((diff & ActivityInfo.CONFIG_KEYBOARD_HIDDEN) != 0) {
+            list.add("CONFIG_KEYBOARD_HIDDEN");
+        }
+        if ((diff & ActivityInfo.CONFIG_NAVIGATION) != 0) {
+            list.add("CONFIG_NAVIGATION");
+        }
+        if ((diff & ActivityInfo.CONFIG_ORIENTATION) != 0) {
+            list.add("CONFIG_ORIENTATION");
+        }
+        if ((diff & ActivityInfo.CONFIG_SCREEN_LAYOUT) != 0) {
+            list.add("CONFIG_SCREEN_LAYOUT");
+        }
+        if ((diff & ActivityInfo.CONFIG_UI_MODE) != 0) {
+            list.add("CONFIG_UI_MODE");
+        }
+        if ((diff & ActivityInfo.CONFIG_SCREEN_SIZE) != 0) {
+            list.add("CONFIG_SCREEN_SIZE");
+        }
+        if ((diff & ActivityInfo.CONFIG_SMALLEST_SCREEN_SIZE) != 0) {
+            list.add("CONFIG_SMALLEST_SCREEN_SIZE");
+        }
+        if ((diff & ActivityInfo.CONFIG_LAYOUT_DIRECTION) != 0) {
+            list.add("CONFIG_LAYOUT_DIRECTION");
+        }
+        if ((diff & ActivityInfo.CONFIG_FONT_SCALE) != 0) {
+            list.add("CONFIG_FONT_SCALE");
+        }
+        StringBuilder builder = new StringBuilder("{");
+        for (int i = 0, n = list.size(); i < n; i++) {
+            builder.append(list.get(i));
+            if (i != n - 1) {
+                builder.append(", ");
+            }
+        }
+        builder.append("}");
+        return builder.toString();
+    }
+
     /**
      * Check if the Configuration's current {@link #screenLayout} is at
      * least the given size.
@@ -323,7 +379,7 @@
      * <a href="{@docRoot}guide/topics/resources/providing-resources.html#TouchscreenQualifier">finger</a>
      * resource qualifier. */
     public static final int TOUCHSCREEN_FINGER = 3;
-    
+
     /**
      * The kind of touch screen attached to the device.
      * One of: {@link #TOUCHSCREEN_NOTOUCH}, {@link #TOUCHSCREEN_FINGER}.
@@ -344,7 +400,7 @@
      * <a href="{@docRoot}guide/topics/resources/providing-resources.html#ImeQualifier">12key</a>
      * resource qualifier. */
     public static final int KEYBOARD_12KEY = 3;
-    
+
     /**
      * The kind of keyboard attached to the device.
      * One of: {@link #KEYBOARD_NOKEYS}, {@link #KEYBOARD_QWERTY},
@@ -364,7 +420,7 @@
     public static final int KEYBOARDHIDDEN_YES = 2;
     /** Constant matching actual resource implementation. {@hide} */
     public static final int KEYBOARDHIDDEN_SOFT = 3;
-    
+
     /**
      * A flag indicating whether any keyboard is available.  Unlike
      * {@link #hardKeyboardHidden}, this also takes into account a soft
@@ -373,7 +429,7 @@
      * {@link #KEYBOARDHIDDEN_NO}, {@link #KEYBOARDHIDDEN_YES}.
      */
     public int keyboardHidden;
-    
+
     /** Constant for {@link #hardKeyboardHidden}: a value indicating that no value has been set. */
     public static final int HARDKEYBOARDHIDDEN_UNDEFINED = 0;
     /** Constant for {@link #hardKeyboardHidden}, value corresponding to the
@@ -382,7 +438,7 @@
     /** Constant for {@link #hardKeyboardHidden}, value corresponding to the
      * physical keyboard being hidden. */
     public static final int HARDKEYBOARDHIDDEN_YES = 2;
-    
+
     /**
      * A flag indicating whether the hard keyboard has been hidden.  This will
      * be set on a device with a mechanism to hide the keyboard from the
@@ -390,7 +446,7 @@
      * {@link #HARDKEYBOARDHIDDEN_NO}, {@link #HARDKEYBOARDHIDDEN_YES}.
      */
     public int hardKeyboardHidden;
-    
+
     /** Constant for {@link #navigation}: a value indicating that no value has been set. */
     public static final int NAVIGATION_UNDEFINED = 0;
     /** Constant for {@link #navigation}, value corresponding to the
@@ -409,14 +465,14 @@
      * <a href="{@docRoot}guide/topics/resources/providing-resources.html#NavigationQualifier">wheel</a>
      * resource qualifier. */
     public static final int NAVIGATION_WHEEL = 4;
-    
+
     /**
      * The kind of navigation method available on the device.
      * One of: {@link #NAVIGATION_NONAV}, {@link #NAVIGATION_DPAD},
      * {@link #NAVIGATION_TRACKBALL}, {@link #NAVIGATION_WHEEL}.
      */
     public int navigation;
-    
+
     /** Constant for {@link #navigationHidden}: a value indicating that no value has been set. */
     public static final int NAVIGATIONHIDDEN_UNDEFINED = 0;
     /** Constant for {@link #navigationHidden}, value corresponding to the
@@ -427,7 +483,7 @@
      * <a href="{@docRoot}guide/topics/resources/providing-resources.html#NavAvailQualifier">navhidden</a>
      * resource qualifier. */
     public static final int NAVIGATIONHIDDEN_YES = 2;
-    
+
     /**
      * A flag indicating whether any 5-way or DPAD navigation available.
      * This will be set on a device with a mechanism to hide the navigation
@@ -435,7 +491,7 @@
      * {@link #NAVIGATIONHIDDEN_NO}, {@link #NAVIGATIONHIDDEN_YES}.
      */
     public int navigationHidden;
-    
+
     /** Constant for {@link #orientation}: a value indicating that no value has been set. */
     public static final int ORIENTATION_UNDEFINED = 0;
     /** Constant for {@link #orientation}, value corresponding to the
@@ -448,7 +504,7 @@
     public static final int ORIENTATION_LANDSCAPE = 2;
     /** @deprecated Not currently supported or used. */
     @Deprecated public static final int ORIENTATION_SQUARE = 3;
-    
+
     /**
      * Overall orientation of the screen.  May be one of
      * {@link #ORIENTATION_LANDSCAPE}, {@link #ORIENTATION_PORTRAIT}.
@@ -692,7 +748,7 @@
         compatSmallestScreenWidthDp = o.compatSmallestScreenWidthDp;
         seq = o.seq;
     }
-    
+
     public String toString() {
         StringBuilder sb = new StringBuilder(128);
         sb.append("{");
@@ -861,7 +917,7 @@
     @Deprecated public void makeDefault() {
         setToDefaults();
     }
-    
+
     /**
      * Copy the fields from delta into this Configuration object, keeping
      * track of which ones have changed.  Any undefined fields in
@@ -1001,7 +1057,7 @@
         if (delta.seq != 0) {
             seq = delta.seq;
         }
-        
+
         return changed;
     }
 
@@ -1119,12 +1175,12 @@
     /**
      * Determine if a new resource needs to be loaded from the bit set of
      * configuration changes returned by {@link #updateFrom(Configuration)}.
-     * 
+     *
      * @param configChanges The mask of changes configurations as returned by
      * {@link #updateFrom(Configuration)}.
      * @param interestingChanges The configuration changes that the resource
      * can handled, as given in {@link android.util.TypedValue#changingConfigurations}.
-     * 
+     *
      * @return Return true if the resource needs to be loaded, else false.
      */
     public static boolean needNewResources(int configChanges, int interestingChanges) {
@@ -1159,7 +1215,7 @@
         }
         return diff > 0;
     }
-    
+
     /**
      * Parcelable methods
      */
@@ -1236,7 +1292,7 @@
         compatSmallestScreenWidthDp = source.readInt();
         seq = source.readInt();
     }
-    
+
     public static final Parcelable.Creator<Configuration> CREATOR
             = new Parcelable.Creator<Configuration>() {
         public Configuration createFromParcel(Parcel source) {
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index 9a2a241..444548f 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -208,6 +208,12 @@
      * {@link android.content.Intent#getParcelableExtra(String)}.
      */
     public static final String EXTRA_CAPTIVE_PORTAL = "android.net.extra.CAPTIVE_PORTAL";
+
+    /**
+     * Key for passing a URL to the captive portal login activity.
+     */
+    public static final String EXTRA_CAPTIVE_PORTAL_URL = "android.net.extra.CAPTIVE_PORTAL_URL";
+
     /**
      * Broadcast action to indicate the change of data activity status
      * (idle or active) on a network in a recent period.
diff --git a/core/java/android/net/NetworkScoreManager.java b/core/java/android/net/NetworkScoreManager.java
index a939cce..3f36d65 100644
--- a/core/java/android/net/NetworkScoreManager.java
+++ b/core/java/android/net/NetworkScoreManager.java
@@ -245,7 +245,8 @@
         intent.putExtra(EXTRA_NETWORKS_TO_SCORE, networks);
         // A scorer should never become active if its package doesn't hold SCORE_NETWORKS, but
         // ensure the package still holds it to be extra safe.
-        mContext.sendBroadcastAsUser(intent, UserHandle.OWNER, Manifest.permission.SCORE_NETWORKS);
+        // TODO: http://b/23422763
+        mContext.sendBroadcastAsUser(intent, UserHandle.SYSTEM, Manifest.permission.SCORE_NETWORKS);
         return true;
     }
 
diff --git a/core/java/android/os/RecoverySystem.java b/core/java/android/os/RecoverySystem.java
index 8c544f4..41de579 100644
--- a/core/java/android/os/RecoverySystem.java
+++ b/core/java/android/os/RecoverySystem.java
@@ -410,7 +410,7 @@
 
         Intent intent = new Intent("android.intent.action.MASTER_CLEAR_NOTIFICATION");
         intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
-        context.sendOrderedBroadcastAsUser(intent, UserHandle.OWNER,
+        context.sendOrderedBroadcastAsUser(intent, UserHandle.SYSTEM,
                 android.Manifest.permission.MASTER_CLEAR,
                 new BroadcastReceiver() {
                     @Override
diff --git a/core/java/android/os/StrictMode.java b/core/java/android/os/StrictMode.java
index 87ce12c..8b2c74f 100644
--- a/core/java/android/os/StrictMode.java
+++ b/core/java/android/os/StrictMode.java
@@ -1923,7 +1923,7 @@
             if (LOG_V) Log.d(TAG, "strict mode violation stacks read from binder call.  i=" + i);
             ViolationInfo info = new ViolationInfo(p, !currentlyGathering);
             if (info.crashInfo.stackTrace != null && info.crashInfo.stackTrace.length() > 30000) {
-                String front = info.crashInfo.stackTrace.substring(256);
+                String front = info.crashInfo.stackTrace.substring(0, 256);
                 // 30000 characters is way too large for this to be any sane kind of
                 // strict mode collection of stacks.  We've had a problem where we leave
                 // strict mode violations associated with the thread, and it keeps tacking
diff --git a/core/java/android/os/storage/VolumeInfo.java b/core/java/android/os/storage/VolumeInfo.java
index 6888594..c368e5a 100644
--- a/core/java/android/os/storage/VolumeInfo.java
+++ b/core/java/android/os/storage/VolumeInfo.java
@@ -438,6 +438,8 @@
         final Intent intent = new Intent(DocumentsContract.ACTION_BROWSE_DOCUMENT_ROOT);
         intent.addCategory(Intent.CATEGORY_DEFAULT);
         intent.setData(uri);
+        intent.putExtra(DocumentsContract.EXTRA_SHOW_FILESIZE, true);
+        intent.putExtra(DocumentsContract.EXTRA_SHOW_ADVANCED, true);
         return intent;
     }
 
diff --git a/core/java/android/provider/DocumentsContract.java b/core/java/android/provider/DocumentsContract.java
index 59609f9..1a83cd5 100644
--- a/core/java/android/provider/DocumentsContract.java
+++ b/core/java/android/provider/DocumentsContract.java
@@ -93,6 +93,9 @@
     public static final String EXTRA_SHOW_ADVANCED = "android.content.extra.SHOW_ADVANCED";
 
     /** {@hide} */
+    public static final String EXTRA_SHOW_FILESIZE = "android.content.extra.SHOW_FILESIZE";
+
+    /** {@hide} */
     public static final String EXTRA_TARGET_URI = "android.content.extra.TARGET_URI";
 
     /**
@@ -266,7 +269,7 @@
          * writability of a document may change over time, for example due to
          * remote access changes. This flag indicates that a document client can
          * expect {@link ContentResolver#openOutputStream(Uri)} to succeed.
-         * 
+         *
          * @see #COLUMN_FLAGS
          */
         public static final int FLAG_SUPPORTS_WRITE = 1 << 1;
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index 45bc1df..92e473d 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -226,6 +226,7 @@
             @ViewDebug.IntToString(from = TYPE_PRIVATE_PRESENTATION, to = "TYPE_PRIVATE_PRESENTATION"),
             @ViewDebug.IntToString(from = TYPE_VOICE_INTERACTION, to = "TYPE_VOICE_INTERACTION"),
             @ViewDebug.IntToString(from = TYPE_VOICE_INTERACTION_STARTING, to = "TYPE_VOICE_INTERACTION_STARTING"),
+            @ViewDebug.IntToString(from = TYPE_DOCK_DIVIDER, to = "TYPE_DOCK_DIVIDER"),
         })
         public int type;
 
@@ -565,6 +566,13 @@
         public static final int TYPE_VOICE_INTERACTION_STARTING = FIRST_SYSTEM_WINDOW+33;
 
         /**
+         * Window for displaying a handle used for resizing docked stacks. This window is owned
+         * by the system process.
+         * @hide
+         */
+        public static final int TYPE_DOCK_DIVIDER = FIRST_SYSTEM_WINDOW+34;
+
+        /**
          * End of types of system windows.
          */
         public static final int LAST_SYSTEM_WINDOW      = 2999;
diff --git a/core/java/android/widget/AbsSeekBar.java b/core/java/android/widget/AbsSeekBar.java
index 6883db2..68855ff 100644
--- a/core/java/android/widget/AbsSeekBar.java
+++ b/core/java/android/widget/AbsSeekBar.java
@@ -385,17 +385,19 @@
     }
 
     @Override
-    void onProgressRefresh(float scale, boolean fromUser, int progress) {
-        super.onProgressRefresh(scale, fromUser, progress);
+    void onVisualProgressChanged(int id, float scale) {
+        super.onVisualProgressChanged(id, scale);
 
-        final Drawable thumb = mThumb;
-        if (thumb != null) {
-            setThumbPos(getWidth(), thumb, scale, Integer.MIN_VALUE);
+        if (id == R.id.progress) {
+            final Drawable thumb = mThumb;
+            if (thumb != null) {
+                setThumbPos(getWidth(), thumb, scale, Integer.MIN_VALUE);
 
-            // Since we draw translated, the drawable's bounds that it signals
-            // for invalidation won't be the actual bounds we want invalidated,
-            // so just invalidate this whole view.
-            invalidate();
+                // Since we draw translated, the drawable's bounds that it signals
+                // for invalidation won't be the actual bounds we want invalidated,
+                // so just invalidate this whole view.
+                invalidate();
+            }
         }
     }
 
@@ -709,8 +711,7 @@
                 case KeyEvent.KEYCODE_DPAD_RIGHT:
                     increment = isLayoutRtl() ? -increment : increment;
 
-                    // Let progress bar handle clamping values.
-                    if (setProgress(getProgress() + increment, true)) {
+                    if (setProgressInternal(getProgress() + increment, true, true)) {
                         onKeyChange();
                         return true;
                     }
@@ -764,7 +765,7 @@
                 }
                 float value = arguments.getFloat(
                         AccessibilityNodeInfo.ACTION_ARGUMENT_PROGRESS_VALUE);
-                return setProgress((int) value, true);
+                return setProgressInternal((int) value, true, true);
             }
             case AccessibilityNodeInfo.ACTION_SCROLL_FORWARD:
             case AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD: {
@@ -777,7 +778,7 @@
                 }
 
                 // Let progress bar handle clamping values.
-                if (setProgress(getProgress() + increment, true)) {
+                if (setProgressInternal(getProgress() + increment, true, true)) {
                     onKeyChange();
                     return true;
                 }
diff --git a/core/java/android/widget/ProgressBar.java b/core/java/android/widget/ProgressBar.java
index fce3754..04c68ae 100644
--- a/core/java/android/widget/ProgressBar.java
+++ b/core/java/android/widget/ProgressBar.java
@@ -16,10 +16,13 @@
 
 package android.widget;
 
+import android.animation.ObjectAnimator;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.graphics.PorterDuff;
 
+import android.util.FloatProperty;
+import android.util.IntProperty;
 import android.view.accessibility.AccessibilityNodeInfo;
 import com.android.internal.R;
 
@@ -28,7 +31,6 @@
 import android.content.res.ColorStateList;
 import android.content.res.TypedArray;
 import android.graphics.Bitmap;
-import android.graphics.BitmapShader;
 import android.graphics.Canvas;
 import android.graphics.Rect;
 import android.graphics.Shader;
@@ -38,7 +40,6 @@
 import android.graphics.drawable.ClipDrawable;
 import android.graphics.drawable.Drawable;
 import android.graphics.drawable.LayerDrawable;
-import android.graphics.drawable.ShapeDrawable;
 import android.graphics.drawable.StateListDrawable;
 import android.graphics.drawable.shapes.RoundRectShape;
 import android.graphics.drawable.shapes.Shape;
@@ -57,6 +58,7 @@
 import android.view.animation.AlphaAnimation;
 import android.view.animation.Animation;
 import android.view.animation.AnimationUtils;
+import android.view.animation.DecelerateInterpolator;
 import android.view.animation.Interpolator;
 import android.view.animation.LinearInterpolator;
 import android.view.animation.Transformation;
@@ -198,9 +200,17 @@
  */
 @RemoteView
 public class ProgressBar extends View {
+
     private static final int MAX_LEVEL = 10000;
     private static final int TIMEOUT_SEND_ACCESSIBILITY_EVENT = 200;
 
+    /** Interpolator used for smooth progress animations. */
+    private static final DecelerateInterpolator PROGRESS_ANIM_INTERPOLATOR =
+            new DecelerateInterpolator();
+
+    /** Duration of smooth progress animations. */
+    private static final int PROGRESS_ANIM_DURATION = 80;
+
     int mMinWidth;
     int mMaxWidth;
     int mMinHeight;
@@ -234,6 +244,9 @@
     private boolean mAttached;
     private boolean mRefreshIsPosted;
 
+    /** Value used to track progress animation, in the range [0...1]. */
+    private float mVisualProgress;
+
     boolean mMirrorForRtl = false;
 
     private final ArrayList<RefreshData> mRefreshData = new ArrayList<RefreshData>();
@@ -814,8 +827,8 @@
             updateDrawableBounds(getWidth(), getHeight());
             updateDrawableState();
 
-            doRefreshProgress(R.id.progress, mProgress, false, false);
-            doRefreshProgress(R.id.secondaryProgress, mSecondaryProgress, false, false);
+            doRefreshProgress(R.id.progress, mProgress, false, false, false);
+            doRefreshProgress(R.id.secondaryProgress, mSecondaryProgress, false, false, false);
         }
     }
 
@@ -1246,7 +1259,7 @@
                 final int count = mRefreshData.size();
                 for (int i = 0; i < count; i++) {
                     final RefreshData rd = mRefreshData.get(i);
-                    doRefreshProgress(rd.id, rd.progress, rd.fromUser, true);
+                    doRefreshProgress(rd.id, rd.progress, rd.fromUser, true, rd.animate);
                     rd.recycle();
                 }
                 mRefreshData.clear();
@@ -1263,8 +1276,9 @@
         public int id;
         public int progress;
         public boolean fromUser;
+        public boolean animate;
 
-        public static RefreshData obtain(int id, int progress, boolean fromUser) {
+        public static RefreshData obtain(int id, int progress, boolean fromUser, boolean animate) {
             RefreshData rd = sPool.acquire();
             if (rd == null) {
                 rd = new RefreshData();
@@ -1272,6 +1286,7 @@
             rd.id = id;
             rd.progress = progress;
             rd.fromUser = fromUser;
+            rd.animate = animate;
             return rd;
         }
 
@@ -1281,26 +1296,21 @@
     }
 
     private synchronized void doRefreshProgress(int id, int progress, boolean fromUser,
-            boolean callBackToApp) {
-        float scale = mMax > 0 ? (float) progress / (float) mMax : 0;
-        final Drawable d = mCurrentDrawable;
-        if (d != null) {
-            Drawable progressDrawable = null;
+            boolean callBackToApp, boolean animate) {
+        final float scale = mMax > 0 ? progress / (float) mMax : 0;
+        final boolean isPrimary = id == R.id.progress;
 
-            if (d instanceof LayerDrawable) {
-                progressDrawable = ((LayerDrawable) d).findDrawableByLayerId(id);
-                if (progressDrawable != null && canResolveLayoutDirection()) {
-                    progressDrawable.setLayoutDirection(getLayoutDirection());
-                }
-            }
-
-            final int level = (int) (scale * MAX_LEVEL);
-            (progressDrawable != null ? progressDrawable : d).setLevel(level);
+        if (isPrimary && animate) {
+            final ObjectAnimator animator = ObjectAnimator.ofFloat(this, VISUAL_PROGRESS, scale);
+            animator.setAutoCancel(true);
+            animator.setDuration(PROGRESS_ANIM_DURATION);
+            animator.setInterpolator(PROGRESS_ANIM_INTERPOLATOR);
+            animator.start();
         } else {
-            invalidate();
+            setVisualProgress(id, scale);
         }
 
-        if (callBackToApp && id == R.id.progress) {
+        if (isPrimary && callBackToApp) {
             onProgressRefresh(scale, fromUser, progress);
         }
     }
@@ -1311,15 +1321,51 @@
         }
     }
 
-    private synchronized void refreshProgress(int id, int progress, boolean fromUser) {
+    /**
+     * Sets the visual state of a progress indicator.
+     *
+     * @param id the identifier of the progress indicator
+     * @param progress the visual progress in the range [0...1]
+     */
+    private void setVisualProgress(int id, float progress) {
+        mVisualProgress = progress;
+
+        Drawable d = mCurrentDrawable;
+
+        if (d instanceof LayerDrawable) {
+            d = ((LayerDrawable) d).findDrawableByLayerId(id);
+        }
+
+        if (d != null) {
+            final int level = (int) (progress * MAX_LEVEL);
+            d.setLevel(level);
+        } else {
+            invalidate();
+        }
+
+        onVisualProgressChanged(id, progress);
+    }
+
+    /**
+     * Called when the visual state of a progress indicator changes.
+     *
+     * @param id the identifier of the progress indicator
+     * @param progress the visual progress in the range [0...1]
+     */
+    void onVisualProgressChanged(int id, float progress) {
+        // Stub method.
+    }
+
+    private synchronized void refreshProgress(int id, int progress, boolean fromUser,
+            boolean animate) {
         if (mUiThreadId == Thread.currentThread().getId()) {
-            doRefreshProgress(id, progress, fromUser, true);
+            doRefreshProgress(id, progress, fromUser, true, animate);
         } else {
             if (mRefreshProgressRunnable == null) {
                 mRefreshProgressRunnable = new RefreshProgressRunnable();
             }
 
-            final RefreshData rd = RefreshData.obtain(id, progress, fromUser);
+            final RefreshData rd = RefreshData.obtain(id, progress, fromUser, animate);
             mRefreshData.add(rd);
             if (mAttached && !mRefreshIsPosted) {
                 post(mRefreshProgressRunnable);
@@ -1329,8 +1375,8 @@
     }
 
     /**
-     * <p>Set the current progress to the specified value. Does not do anything
-     * if the progress bar is in indeterminate mode.</p>
+     * Sets the current progress to the specified value. Does not do anything
+     * if the progress bar is in indeterminate mode.
      *
      * @param progress the new progress, between 0 and {@link #getMax()}
      *
@@ -1341,11 +1387,26 @@
      */
     @android.view.RemotableViewMethod
     public synchronized void setProgress(int progress) {
-        setProgress(progress, false);
+        setProgressInternal(progress, false, false);
+    }
+
+    /**
+     * Sets the current progress to the specified value, optionally animating
+     * between the current and target values.
+     * <p>
+     * Animation does not affect the result of {@link #getProgress()}, which
+     * will return the target value immediately after this method is called.
+     *
+     * @param progress the new progress value, between 0 and {@link #getMax()}
+     * @param animate {@code true} to animate between the current and target
+     *                values or {@code false} to not animate
+     */
+    public void setProgress(int progress, boolean animate) {
+        setProgressInternal(progress, false, animate);
     }
 
     @android.view.RemotableViewMethod
-    synchronized boolean setProgress(int progress, boolean fromUser) {
+    synchronized boolean setProgressInternal(int progress, boolean fromUser, boolean animate) {
         if (mIndeterminate) {
             // Not applicable.
             return false;
@@ -1359,7 +1420,7 @@
         }
 
         mProgress = progress;
-        refreshProgress(R.id.progress, mProgress, fromUser);
+        refreshProgress(R.id.progress, mProgress, fromUser, animate);
         return true;
     }
 
@@ -1391,7 +1452,7 @@
 
         if (secondaryProgress != mSecondaryProgress) {
             mSecondaryProgress = secondaryProgress;
-            refreshProgress(R.id.secondaryProgress, mSecondaryProgress, false);
+            refreshProgress(R.id.secondaryProgress, mSecondaryProgress, false, false);
         }
     }
 
@@ -1464,7 +1525,7 @@
             if (mProgress > max) {
                 mProgress = max;
             }
-            refreshProgress(R.id.progress, mProgress, false);
+            refreshProgress(R.id.progress, mProgress, false, false);
         }
     }
 
@@ -1847,7 +1908,7 @@
                 final int count = mRefreshData.size();
                 for (int i = 0; i < count; i++) {
                     final RefreshData rd = mRefreshData.get(i);
-                    doRefreshProgress(rd.id, rd.progress, rd.fromUser, true);
+                    doRefreshProgress(rd.id, rd.progress, rd.fromUser, true, rd.animate);
                     rd.recycle();
                 }
                 mRefreshData.clear();
@@ -1956,4 +2017,23 @@
         boolean mHasSecondaryProgressTint;
         boolean mHasSecondaryProgressTintMode;
     }
+
+    /**
+     * Property wrapper around the visual state of the {@code progress} functionality
+     * handled by the {@link ProgressBar#setProgress(int, boolean)} method. This does
+     * not correspond directly to the actual progress -- only the visual state.
+     */
+    private final FloatProperty<ProgressBar> VISUAL_PROGRESS =
+            new FloatProperty<ProgressBar>("visual_progress") {
+                @Override
+                public void setValue(ProgressBar object, float value) {
+                    object.setVisualProgress(R.id.progress, value);
+                    object.mVisualProgress = value;
+                }
+
+                @Override
+                public Float get(ProgressBar object) {
+                    return object.mVisualProgress;
+                }
+            };
 }
diff --git a/core/java/android/widget/Switch.java b/core/java/android/widget/Switch.java
index 6f3a711..434516d 100644
--- a/core/java/android/widget/Switch.java
+++ b/core/java/android/widget/Switch.java
@@ -1387,7 +1387,7 @@
             mTrackDrawable.jumpToCurrentState();
         }
 
-        if (mPositionAnimator != null && mPositionAnimator.isRunning()) {
+        if (mPositionAnimator != null && mPositionAnimator.isStarted()) {
             mPositionAnimator.end();
             mPositionAnimator = null;
         }
diff --git a/core/res/res/layout/docked_stack_divider.xml b/core/res/res/layout/docked_stack_divider.xml
new file mode 100644
index 0000000..aa6e68d
--- /dev/null
+++ b/core/res/res/layout/docked_stack_divider.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2015 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+        android:layout_width="@dimen/docked_stack_divider_thickness"
+        android:layout_height="match_parent"
+        android:background="@android:color/black"
+        />
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 902d40d..dc96df4 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -5550,6 +5550,8 @@
             <!-- Push object to the end of its container, not changing its size. -->
             <flag name="end" value="0x00800005" />
         </attr>
+        <!-- Specifies the initial drawable level in the range 0 to 10000. -->
+        <attr name="level" format="integer" />
         <!-- Reference to a drawable resource to draw with the specified scale. -->
         <attr name="drawable" />
         <!-- Use the drawable's intrinsic width and height as minimum size values.
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index a19bc20..9b1a613 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -410,6 +410,9 @@
          radio is unable to find any MCC information to infer wifi country code from -->
     <bool translatable="false" name="config_wifi_revert_country_code_on_cellular_loss">false</bool>
 
+    <!-- Boolean indicating whether or not wifi firmware debugging is enabled -->
+    <bool translatable="false" name="config_wifi_enable_wifi_firmware_debugging">false</bool>
+
     <!-- Integer specifying the basic autojoin parameters -->
     <integer translatable="false" name="config_wifi_framework_5GHz_preference_boost_threshold">-65</integer>
     <integer translatable="false" name="config_wifi_framework_5GHz_preference_boost_factor">5</integer>
diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml
index 8635a4f..2621bc9c 100644
--- a/core/res/res/values/dimens.xml
+++ b/core/res/res/values/dimens.xml
@@ -45,6 +45,9 @@
     <!-- Margin at the edge of the screen to ignore touch events for in the windowshade. -->
     <dimen name="status_bar_edge_ignore">5dp</dimen>
 
+    <!-- Width of a divider bar used to resize docked stacks. -->
+    <dimen name="docked_stack_divider_thickness">24dp</dimen>
+
     <!-- Min width for a tablet device -->
     <dimen name="min_xlarge_screen_width">800dp</dimen>
 
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index b6284c9..89d9bab 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -2673,6 +2673,7 @@
     <public type="attr" name="maxButtonHeight" />
     <public type="attr" name="buttonGravity" />
     <public type="attr" name="collapseIcon" />
+    <public type="attr" name="level" />
 
     <public type="style" name="Theme.Material.DayNight" />
     <public type="style" name="Theme.Material.DayNight.DarkActionBar" />
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 4edc847..b5efbfd 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -307,6 +307,7 @@
   <java-symbol type="bool" name="config_wifi_enable_disconnection_debounce" />
   <java-symbol type="bool" name="config_wifi_enable_5GHz_preference" />
   <java-symbol type="bool" name="config_wifi_revert_country_code_on_cellular_loss" />
+  <java-symbol type="bool" name="config_wifi_enable_wifi_firmware_debugging" />
   <java-symbol type="bool" name="config_supportMicNearUltrasound" />
   <java-symbol type="bool" name="config_supportSpeakerNearUltrasound" />
   <java-symbol type="integer" name="config_wifi_framework_5GHz_preference_boost_threshold" />
@@ -1477,6 +1478,7 @@
   <java-symbol type="bool" name="config_showNavigationBar" />
   <java-symbol type="bool" name="config_supportAutoRotation" />
   <java-symbol type="bool" name="target_honeycomb_needs_options_menu" />
+  <java-symbol type="dimen" name="docked_stack_divider_thickness" />
   <java-symbol type="dimen" name="navigation_bar_height" />
   <java-symbol type="dimen" name="navigation_bar_height_landscape" />
   <java-symbol type="dimen" name="navigation_bar_width" />
@@ -1719,6 +1721,7 @@
   <java-symbol type="integer" name="config_undockedHdmiRotation" />
   <java-symbol type="integer" name="config_virtualKeyQuietTimeMillis" />
   <java-symbol type="layout" name="am_compat_mode_dialog" />
+  <java-symbol type="layout" name="docked_stack_divider" />
   <java-symbol type="layout" name="launch_warning" />
   <java-symbol type="layout" name="safe_mode" />
   <java-symbol type="layout" name="simple_list_item_2_single_choice" />
diff --git a/docs/html/training/location/geofencing.jd b/docs/html/training/location/geofencing.jd
index 59fc4c6..556329c 100644
--- a/docs/html/training/location/geofencing.jd
+++ b/docs/html/training/location/geofencing.jd
@@ -100,8 +100,8 @@
 <h3>Create geofence objects</h3>
 
 <p>
-    First, use <code><a href="{@docRoot}reference/com/google/android/gms/location/Geofence.Builder.
-    html">Geofence.Builder</a></code> to create a geofence, setting the desired radius, duration, and
+    First, use <code><a href="{@docRoot}reference/com/google/android/gms/location/Geofence.Builder.html">
+    Geofence.Builder</a></code> to create a geofence, setting the desired radius, duration, and
     transition types for the geofence. For example, to populate a list object named
     {@code mGeofenceList}:
     </p>
diff --git a/graphics/java/android/graphics/drawable/ScaleDrawable.java b/graphics/java/android/graphics/drawable/ScaleDrawable.java
index 0acbeda..f9206b7 100644
--- a/graphics/java/android/graphics/drawable/ScaleDrawable.java
+++ b/graphics/java/android/graphics/drawable/ScaleDrawable.java
@@ -35,19 +35,29 @@
 
 /**
  * A Drawable that changes the size of another Drawable based on its current
- * level value.  You can control how much the child Drawable changes in width
+ * level value. You can control how much the child Drawable changes in width
  * and height based on the level, as well as a gravity to control where it is
- * placed in its overall container.  Most often used to implement things like
+ * placed in its overall container. Most often used to implement things like
  * progress bars.
- *
- * <p>It can be defined in an XML file with the <code>&lt;scale></code> element. For more
- * information, see the guide to <a
- * href="{@docRoot}guide/topics/resources/drawable-resource.html">Drawable Resources</a>.</p>
+ * <p>
+ * The default level may be specified from XML using the
+ * {@link android.R.styleable#ScaleDrawable_level android:level} property. When
+ * this property is not specified, the default level is 0, which corresponds to
+ * zero height and/or width depending on the values specified for
+ * {@code android.R.styleable#ScaleDrawable_scaleWidth scaleWidth} and
+ * {@code android.R.styleable#ScaleDrawable_scaleHeight scaleHeight}. At run
+ * time, the level may be set via {@link #setLevel(int)}.
+ * <p>
+ * A scale drawable may be defined in an XML file with the {@code &lt;scale>}
+ * element. For more information, see the guide to
+ * <a href="{@docRoot}guide/topics/resources/drawable-resource.html">Drawable
+ * Resources</a>.
  *
  * @attr ref android.R.styleable#ScaleDrawable_scaleWidth
  * @attr ref android.R.styleable#ScaleDrawable_scaleHeight
  * @attr ref android.R.styleable#ScaleDrawable_scaleGravity
  * @attr ref android.R.styleable#ScaleDrawable_drawable
+ * @attr ref android.R.styleable#ScaleDrawable_level
  */
 public class ScaleDrawable extends DrawableWrapper {
     private static final int MAX_LEVEL = 10000;
@@ -92,6 +102,8 @@
         inflateChildDrawable(r, parser, attrs, theme);
         verifyRequiredAttributes(a);
         a.recycle();
+
+        updateLocalState();
     }
 
     private void verifyRequiredAttributes(TypedArray a) throws XmlPullParserException {
@@ -117,6 +129,8 @@
                 R.styleable.ScaleDrawable_scaleGravity, state.mGravity);
         state.mUseIntrinsicSizeAsMin = a.getBoolean(
                 R.styleable.ScaleDrawable_useIntrinsicSizeAsMinimum, state.mUseIntrinsicSizeAsMin);
+        state.mInitialLevel = a.getInt(
+                R.styleable.ScaleDrawable_level, state.mInitialLevel);
 
         final Drawable dr = a.getDrawable(R.styleable.ScaleDrawable_drawable);
         if (dr != null) {
@@ -165,6 +179,8 @@
         // The drawable may have changed as a result of applying the theme, so
         // apply the theme to the wrapped drawable last.
         super.applyTheme(t);
+
+        updateLocalState();
     }
 
     @Override
@@ -239,6 +255,7 @@
         float mScaleHeight = DO_NOT_SCALE;
         int mGravity = Gravity.LEFT;
         boolean mUseIntrinsicSizeAsMin = false;
+        int mInitialLevel = 0;
 
         ScaleState(ScaleState orig) {
             super(orig);
@@ -248,6 +265,7 @@
                 mScaleHeight = orig.mScaleHeight;
                 mGravity = orig.mGravity;
                 mUseIntrinsicSizeAsMin = orig.mUseIntrinsicSizeAsMin;
+                mInitialLevel = orig.mInitialLevel;
             }
         }
 
@@ -257,10 +275,23 @@
         }
     }
 
+    /**
+     * Creates a new ScaleDrawable based on the specified constant state.
+     * <p>
+     * The resulting drawable is guaranteed to have a new constant state.
+     *
+     * @param state constant state from which the drawable inherits
+     */
     private ScaleDrawable(ScaleState state, Resources res) {
         super(state, res);
 
         mState = state;
+
+        updateLocalState();
+    }
+
+    private void updateLocalState() {
+        setLevel(mState.mInitialLevel);
     }
 }
 
diff --git a/libs/hwui/Caches.cpp b/libs/hwui/Caches.cpp
index ff71313..7c63e31 100644
--- a/libs/hwui/Caches.cpp
+++ b/libs/hwui/Caches.cpp
@@ -54,7 +54,6 @@
         , mInitialized(false) {
     INIT_LOGD("Creating OpenGL renderer caches");
     init();
-    initFont();
     initConstraints();
     initStaticProperties();
     initExtensions();
@@ -78,10 +77,6 @@
     return true;
 }
 
-void Caches::initFont() {
-    fontRenderer = GammaFontRenderer::createRenderer();
-}
-
 void Caches::initExtensions() {
     if (mExtensions.hasDebugMarker()) {
         eventMark = glInsertEventMarkerEXT;
@@ -100,15 +95,9 @@
 }
 
 void Caches::initStaticProperties() {
-    gpuPixelBuffersEnabled = false;
-
     // OpenGL ES 3.0+ specific features
-    if (mExtensions.hasPixelBufferObjects()) {
-        char property[PROPERTY_VALUE_MAX];
-        if (property_get(PROPERTY_ENABLE_GPU_PIXEL_BUFFERS, property, "true") > 0) {
-            gpuPixelBuffersEnabled = !strcmp(property, "true");
-        }
-    }
+    gpuPixelBuffersEnabled = mExtensions.hasPixelBufferObjects()
+            && property_get_bool(PROPERTY_ENABLE_GPU_PIXEL_BUFFERS, true);
 }
 
 void Caches::terminate() {
@@ -203,14 +192,14 @@
             dropShadowCache.getMaxSize());
     log.appendFormat("  PatchCache           %8d / %8d\n",
             patchCache.getSize(), patchCache.getMaxSize());
-    for (uint32_t i = 0; i < fontRenderer->getFontRendererCount(); i++) {
-        const uint32_t sizeA8 = fontRenderer->getFontRendererSize(i, GL_ALPHA);
-        const uint32_t sizeRGBA = fontRenderer->getFontRendererSize(i, GL_RGBA);
-        log.appendFormat("  FontRenderer %d A8    %8d / %8d\n", i, sizeA8, sizeA8);
-        log.appendFormat("  FontRenderer %d RGBA  %8d / %8d\n", i, sizeRGBA, sizeRGBA);
-        log.appendFormat("  FontRenderer %d total %8d / %8d\n", i, sizeA8 + sizeRGBA,
-                sizeA8 + sizeRGBA);
-    }
+
+    const uint32_t sizeA8 = fontRenderer.getFontRendererSize(GL_ALPHA);
+    const uint32_t sizeRGBA = fontRenderer.getFontRendererSize(GL_RGBA);
+    log.appendFormat("  FontRenderer A8    %8d / %8d\n", sizeA8, sizeA8);
+    log.appendFormat("  FontRenderer RGBA  %8d / %8d\n", sizeRGBA, sizeRGBA);
+    log.appendFormat("  FontRenderer total %8d / %8d\n", sizeA8 + sizeRGBA,
+            sizeA8 + sizeRGBA);
+
     log.appendFormat("Other:\n");
     log.appendFormat("  FboCache             %8d / %8d\n",
             fboCache.getSize(), fboCache.getMaxSize());
@@ -222,10 +211,8 @@
     total += tessellationCache.getSize();
     total += dropShadowCache.getSize();
     total += patchCache.getSize();
-    for (uint32_t i = 0; i < fontRenderer->getFontRendererCount(); i++) {
-        total += fontRenderer->getFontRendererSize(i, GL_ALPHA);
-        total += fontRenderer->getFontRendererSize(i, GL_RGBA);
-    }
+    total += fontRenderer.getFontRendererSize(GL_ALPHA);
+    total += fontRenderer.getFontRendererSize(GL_RGBA);
 
     log.appendFormat("Total memory usage:\n");
     log.appendFormat("  %d bytes, %.2f MB\n", total, total / 1024.0f / 1024.0f);
@@ -250,12 +237,12 @@
             patchCache.clear();
             dropShadowCache.clear();
             gradientCache.clear();
-            fontRenderer->clear();
+            fontRenderer.clear();
             fboCache.clear();
             dither.clear();
             // fall through
         case FlushMode::Moderate:
-            fontRenderer->flush();
+            fontRenderer.flush();
             textureCache.flush();
             pathCache.clear();
             tessellationCache.clear();
diff --git a/libs/hwui/Caches.h b/libs/hwui/Caches.h
index 929db17..61e958d 100644
--- a/libs/hwui/Caches.h
+++ b/libs/hwui/Caches.h
@@ -21,6 +21,7 @@
 #include "Dither.h"
 #include "Extensions.h"
 #include "FboCache.h"
+#include "GammaFontRenderer.h"
 #include "GradientCache.h"
 #include "LayerCache.h"
 #include "PatchCache.h"
@@ -53,8 +54,6 @@
 namespace android {
 namespace uirenderer {
 
-class GammaFontRenderer;
-
 ///////////////////////////////////////////////////////////////////////////////
 // Caches
 ///////////////////////////////////////////////////////////////////////////////
@@ -156,7 +155,7 @@
     TextDropShadowCache dropShadowCache;
     FboCache fboCache;
 
-    GammaFontRenderer* fontRenderer;
+    GammaFontRenderer fontRenderer;
 
     TaskManager tasks;
 
@@ -178,8 +177,6 @@
     TextureState& textureState() { return *mTextureState; }
 
 private:
-
-    void initFont();
     void initExtensions();
     void initConstraints();
     void initStaticProperties();
diff --git a/libs/hwui/DeferredDisplayList.cpp b/libs/hwui/DeferredDisplayList.cpp
index 9fb1e75..a81ffb9 100644
--- a/libs/hwui/DeferredDisplayList.cpp
+++ b/libs/hwui/DeferredDisplayList.cpp
@@ -631,7 +631,7 @@
 
 void DeferredDisplayList::flush(OpenGLRenderer& renderer, Rect& dirty) {
     ATRACE_NAME("flush drawing commands");
-    Caches::getInstance().fontRenderer->endPrecaching();
+    Caches::getInstance().fontRenderer.endPrecaching();
 
     if (isEmpty()) return; // nothing to flush
     renderer.restoreToCount(1);
diff --git a/libs/hwui/DisplayListOp.h b/libs/hwui/DisplayListOp.h
index 14126a9..dc5cb8b 100644
--- a/libs/hwui/DisplayListOp.h
+++ b/libs/hwui/DisplayListOp.h
@@ -1246,7 +1246,7 @@
 
     virtual void onDefer(OpenGLRenderer& renderer, DeferInfo& deferInfo,
             const DeferredDisplayState& state) override {
-        FontRenderer& fontRenderer = renderer.getCaches().fontRenderer->getFontRenderer(mPaint);
+        FontRenderer& fontRenderer = renderer.getCaches().fontRenderer.getFontRenderer();
         fontRenderer.precache(mPaint, mText, mCount, SkMatrix::I());
 
         deferInfo.batchId = mPaint->getColor() == SK_ColorBLACK ?
@@ -1311,7 +1311,7 @@
 
     virtual void onDefer(OpenGLRenderer& renderer, DeferInfo& deferInfo,
             const DeferredDisplayState& state) override {
-        FontRenderer& fontRenderer = renderer.getCaches().fontRenderer->getFontRenderer(mPaint);
+        FontRenderer& fontRenderer = renderer.getCaches().fontRenderer.getFontRenderer();
         SkMatrix transform;
         renderer.findBestFontTransform(state.mMatrix, &transform);
         if (mPrecacheTransform != transform) {
diff --git a/libs/hwui/FontRenderer.cpp b/libs/hwui/FontRenderer.cpp
index 75c3ead..4b9d4f9 100644
--- a/libs/hwui/FontRenderer.cpp
+++ b/libs/hwui/FontRenderer.cpp
@@ -75,8 +75,8 @@
 
 static bool sLogFontRendererCreate = true;
 
-FontRenderer::FontRenderer()
-        : mGammaTable(nullptr)
+FontRenderer::FontRenderer(const uint8_t* gammaTable)
+        : mGammaTable(gammaTable)
         , mCurrentFont(nullptr)
         , mActiveFonts(LruCache<Font::FontDescription, Font*>::kUnlimitedCapacity)
         , mCurrentCacheTexture(nullptr)
@@ -92,27 +92,15 @@
         INIT_LOGD("Creating FontRenderer");
     }
 
-    mSmallCacheWidth = DEFAULT_TEXT_SMALL_CACHE_WIDTH;
-    mSmallCacheHeight = DEFAULT_TEXT_SMALL_CACHE_HEIGHT;
-    mLargeCacheWidth = DEFAULT_TEXT_LARGE_CACHE_WIDTH;
-    mLargeCacheHeight = DEFAULT_TEXT_LARGE_CACHE_HEIGHT;
+    mSmallCacheWidth = property_get_int32(PROPERTY_TEXT_SMALL_CACHE_WIDTH,
+            DEFAULT_TEXT_SMALL_CACHE_WIDTH);
+    mSmallCacheHeight = property_get_int32(PROPERTY_TEXT_SMALL_CACHE_HEIGHT,
+            DEFAULT_TEXT_SMALL_CACHE_HEIGHT);
 
-    char property[PROPERTY_VALUE_MAX];
-    if (property_get(PROPERTY_TEXT_SMALL_CACHE_WIDTH, property, nullptr) > 0) {
-        mSmallCacheWidth = atoi(property);
-    }
-
-    if (property_get(PROPERTY_TEXT_SMALL_CACHE_HEIGHT, property, nullptr) > 0) {
-        mSmallCacheHeight = atoi(property);
-    }
-
-    if (property_get(PROPERTY_TEXT_LARGE_CACHE_WIDTH, property, nullptr) > 0) {
-        mLargeCacheWidth = atoi(property);
-    }
-
-    if (property_get(PROPERTY_TEXT_LARGE_CACHE_HEIGHT, property, nullptr) > 0) {
-        mLargeCacheHeight = atoi(property);
-    }
+    mLargeCacheWidth = property_get_int32(PROPERTY_TEXT_LARGE_CACHE_WIDTH,
+            DEFAULT_TEXT_LARGE_CACHE_WIDTH);
+    mLargeCacheHeight = property_get_int32(PROPERTY_TEXT_LARGE_CACHE_HEIGHT,
+            DEFAULT_TEXT_LARGE_CACHE_HEIGHT);
 
     uint32_t maxTextureSize = (uint32_t) Caches::getInstance().maxTextureSize;
 
diff --git a/libs/hwui/FontRenderer.h b/libs/hwui/FontRenderer.h
index 2954975..936c838 100644
--- a/libs/hwui/FontRenderer.h
+++ b/libs/hwui/FontRenderer.h
@@ -72,16 +72,12 @@
 
 class FontRenderer {
 public:
-    FontRenderer();
+    FontRenderer(const uint8_t* gammaTable);
     ~FontRenderer();
 
     void flushLargeCaches(std::vector<CacheTexture*>& cacheTextures);
     void flushLargeCaches();
 
-    void setGammaTable(const uint8_t* gammaTable) {
-        mGammaTable = gammaTable;
-    }
-
     void setFont(const SkPaint* paint, const SkMatrix& matrix);
 
     void precache(const SkPaint* paint, const char* text, int numGlyphs, const SkMatrix& matrix);
diff --git a/libs/hwui/GammaFontRenderer.cpp b/libs/hwui/GammaFontRenderer.cpp
index 6c3d04e..96cac86 100644
--- a/libs/hwui/GammaFontRenderer.cpp
+++ b/libs/hwui/GammaFontRenderer.cpp
@@ -21,183 +21,22 @@
 namespace android {
 namespace uirenderer {
 
-///////////////////////////////////////////////////////////////////////////////
-// Utils
-///////////////////////////////////////////////////////////////////////////////
-
-static int luminance(const SkPaint* paint) {
-    uint32_t c = paint->getColor();
-    const int r = (c >> 16) & 0xFF;
-    const int g = (c >>  8) & 0xFF;
-    const int b = (c      ) & 0xFF;
-    return (r * 2 + g * 5 + b) >> 3;
-}
-
-///////////////////////////////////////////////////////////////////////////////
-// Base class GammaFontRenderer
-///////////////////////////////////////////////////////////////////////////////
-
-GammaFontRenderer* GammaFontRenderer::createRenderer() {
-    // Choose the best renderer
-    char property[PROPERTY_VALUE_MAX];
-    if (property_get(PROPERTY_TEXT_GAMMA_METHOD, property, DEFAULT_TEXT_GAMMA_METHOD) > 0) {
-        if (!strcasecmp(property, "lookup")) {
-            return new LookupGammaFontRenderer();
-        }
-    }
-
-    return new Lookup3GammaFontRenderer();
-}
-
 GammaFontRenderer::GammaFontRenderer() {
-    // Get the renderer properties
-    char property[PROPERTY_VALUE_MAX];
-
-    // Get the gamma
-    mGamma = DEFAULT_TEXT_GAMMA;
-    if (property_get(PROPERTY_TEXT_GAMMA, property, nullptr) > 0) {
-        INIT_LOGD("  Setting text gamma to %s", property);
-        mGamma = atof(property);
-    } else {
-        INIT_LOGD("  Using default text gamma of %.2f", DEFAULT_TEXT_GAMMA);
-    }
-
-    // Get the black gamma threshold
-    mBlackThreshold = DEFAULT_TEXT_BLACK_GAMMA_THRESHOLD;
-    if (property_get(PROPERTY_TEXT_BLACK_GAMMA_THRESHOLD, property, nullptr) > 0) {
-        INIT_LOGD("  Setting text black gamma threshold to %s", property);
-        mBlackThreshold = atoi(property);
-    } else {
-        INIT_LOGD("  Using default text black gamma threshold of %d",
-                DEFAULT_TEXT_BLACK_GAMMA_THRESHOLD);
-    }
-
-    // Get the white gamma threshold
-    mWhiteThreshold = DEFAULT_TEXT_WHITE_GAMMA_THRESHOLD;
-    if (property_get(PROPERTY_TEXT_WHITE_GAMMA_THRESHOLD, property, nullptr) > 0) {
-        INIT_LOGD("  Setting text white gamma threshold to %s", property);
-        mWhiteThreshold = atoi(property);
-    } else {
-        INIT_LOGD("  Using default white black gamma threshold of %d",
-                DEFAULT_TEXT_WHITE_GAMMA_THRESHOLD);
-    }
-}
-
-GammaFontRenderer::~GammaFontRenderer() {
-}
-
-///////////////////////////////////////////////////////////////////////////////
-// Lookup-based renderer
-///////////////////////////////////////////////////////////////////////////////
-
-LookupGammaFontRenderer::LookupGammaFontRenderer()
-        : GammaFontRenderer() {
     INIT_LOGD("Creating lookup gamma font renderer");
 
     // Compute the gamma tables
-    const float gamma = 1.0f / mGamma;
+    const float gamma = 1.0f / Properties::textGamma;
 
     for (uint32_t i = 0; i <= 255; i++) {
         mGammaTable[i] = uint8_t((float)::floor(pow(i / 255.0f, gamma) * 255.0f + 0.5f));
     }
-
-    mRenderer = nullptr;
 }
 
-void LookupGammaFontRenderer::endPrecaching() {
+void GammaFontRenderer::endPrecaching() {
     if (mRenderer) {
         mRenderer->endPrecaching();
     }
 }
 
-///////////////////////////////////////////////////////////////////////////////
-// Lookup-based renderer, using 3 different correction tables
-///////////////////////////////////////////////////////////////////////////////
-
-Lookup3GammaFontRenderer::Lookup3GammaFontRenderer()
-        : GammaFontRenderer() {
-    INIT_LOGD("Creating lookup3 gamma font renderer");
-
-    // Compute the gamma tables
-    const float blackGamma = mGamma;
-    const float whiteGamma = 1.0f / mGamma;
-
-    for (uint32_t i = 0; i <= 255; i++) {
-        const float v = i / 255.0f;
-        const float black = pow(v, blackGamma);
-        const float white = pow(v, whiteGamma);
-
-        mGammaTable[i] = i;
-        mGammaTable[256 + i] = uint8_t((float)::floor(black * 255.0f + 0.5f));
-        mGammaTable[512 + i] = uint8_t((float)::floor(white * 255.0f + 0.5f));
-    }
-
-    memset(mRenderers, 0, sizeof(FontRenderer*) * kGammaCount);
-    memset(mRenderersUsageCount, 0, sizeof(uint32_t) * kGammaCount);
-}
-
-void Lookup3GammaFontRenderer::endPrecaching() {
-    for (int i = 0; i < kGammaCount; i++) {
-        if (mRenderers[i]) {
-            mRenderers[i]->endPrecaching();
-        }
-    }
-}
-
-void Lookup3GammaFontRenderer::clear() {
-    for (int i = 0; i < kGammaCount; i++) {
-        mRenderers[i].release();
-    }
-}
-
-void Lookup3GammaFontRenderer::flush() {
-    int count = 0;
-    int min = -1;
-    uint32_t minCount = UINT_MAX;
-
-    for (int i = 0; i < kGammaCount; i++) {
-        if (mRenderers[i]) {
-            count++;
-            if (mRenderersUsageCount[i] < minCount) {
-                minCount = mRenderersUsageCount[i];
-                min = i;
-            }
-        }
-    }
-
-    if (count <= 1 || min < 0) return;
-
-    mRenderers[min].release();
-
-    // Also eliminate the caches for large glyphs, as they consume significant memory
-    for (int i = 0; i < kGammaCount; ++i) {
-        if (mRenderers[i]) {
-            mRenderers[i]->flushLargeCaches();
-        }
-    }
-}
-
-FontRenderer* Lookup3GammaFontRenderer::getRenderer(Gamma gamma) {
-    if (!mRenderers[gamma]) {
-        mRenderers[gamma].reset(new FontRenderer());
-        mRenderers[gamma]->setGammaTable(&mGammaTable[gamma * 256]);
-    }
-    mRenderersUsageCount[gamma]++;
-    return mRenderers[gamma].get();
-}
-
-FontRenderer& Lookup3GammaFontRenderer::getFontRenderer(const SkPaint* paint) {
-    if (paint->getShader() == nullptr) {
-        const int l = luminance(paint);
-
-        if (l <= mBlackThreshold) {
-            return *getRenderer(kGammaBlack);
-        } else if (l >= mWhiteThreshold) {
-            return *getRenderer(kGammaWhite);
-        }
-    }
-    return *getRenderer(kGammaDefault);
-}
-
 }; // namespace uirenderer
 }; // namespace android
diff --git a/libs/hwui/GammaFontRenderer.h b/libs/hwui/GammaFontRenderer.h
index 03f2510..146d385 100644
--- a/libs/hwui/GammaFontRenderer.h
+++ b/libs/hwui/GammaFontRenderer.h
@@ -17,122 +17,44 @@
 #ifndef ANDROID_HWUI_GAMMA_FONT_RENDERER_H
 #define ANDROID_HWUI_GAMMA_FONT_RENDERER_H
 
-#include <SkPaint.h>
-
 #include "FontRenderer.h"
 #include "Program.h"
 
+#include <SkPaint.h>
+
 namespace android {
 namespace uirenderer {
 
 class GammaFontRenderer {
 public:
-    virtual ~GammaFontRenderer();
-
-    virtual void clear() = 0;
-    virtual void flush() = 0;
-
-    virtual FontRenderer& getFontRenderer(const SkPaint* paint) = 0;
-
-    virtual uint32_t getFontRendererCount() const = 0;
-    virtual uint32_t getFontRendererSize(uint32_t fontRenderer, GLenum format) const = 0;
-
-    virtual void endPrecaching() = 0;
-
-    static GammaFontRenderer* createRenderer();
-
-protected:
     GammaFontRenderer();
 
-    int mBlackThreshold;
-    int mWhiteThreshold;
-
-    float mGamma;
-};
-
-class LookupGammaFontRenderer: public GammaFontRenderer {
-public:
-    ~LookupGammaFontRenderer() {
-        delete mRenderer;
+    void clear() {
+        mRenderer.release();
     }
 
-    void clear() override {
-        delete mRenderer;
-        mRenderer = nullptr;
-    }
-
-    void flush() override {
+    void flush() {
         if (mRenderer) {
             mRenderer->flushLargeCaches();
         }
     }
 
-    FontRenderer& getFontRenderer(const SkPaint* paint) override {
+    FontRenderer& getFontRenderer() {
         if (!mRenderer) {
-            mRenderer = new FontRenderer;
-            mRenderer->setGammaTable(&mGammaTable[0]);
+            mRenderer.reset(new FontRenderer(&mGammaTable[0]));
         }
         return *mRenderer;
     }
 
-    uint32_t getFontRendererCount() const override {
-        return 1;
-    }
-
-    uint32_t getFontRendererSize(uint32_t fontRenderer, GLenum format) const override {
+    uint32_t getFontRendererSize(GLenum format) const {
         return mRenderer ? mRenderer->getCacheSize(format) : 0;
     }
 
-    void endPrecaching() override;
+    void endPrecaching();
 
 private:
-    LookupGammaFontRenderer();
-
-    FontRenderer* mRenderer;
+    std::unique_ptr<FontRenderer> mRenderer;
     uint8_t mGammaTable[256];
-
-    friend class GammaFontRenderer;
-};
-
-class Lookup3GammaFontRenderer: public GammaFontRenderer {
-public:
-    void clear() override;
-    void flush() override;
-
-    FontRenderer& getFontRenderer(const SkPaint* paint) override;
-
-    uint32_t getFontRendererCount() const override {
-        return kGammaCount;
-    }
-
-    uint32_t getFontRendererSize(uint32_t fontRenderer, GLenum format) const override {
-        if (fontRenderer >= kGammaCount) return 0;
-
-        if (!mRenderers[fontRenderer]) return 0;
-
-        return mRenderers[fontRenderer]->getCacheSize(format);
-    }
-
-    void endPrecaching() override;
-
-private:
-    Lookup3GammaFontRenderer();
-
-    enum Gamma {
-        kGammaDefault = 0,
-        kGammaBlack = 1,
-        kGammaWhite = 2,
-        kGammaCount = 3
-    };
-
-    FontRenderer* getRenderer(Gamma gamma);
-
-    uint32_t mRenderersUsageCount[kGammaCount];
-    std::unique_ptr<FontRenderer> mRenderers[kGammaCount];
-
-    uint8_t mGammaTable[256 * kGammaCount];
-
-    friend class GammaFontRenderer;
 };
 
 }; // namespace uirenderer
diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp
index e06e348..a401ce1 100644
--- a/libs/hwui/OpenGLRenderer.cpp
+++ b/libs/hwui/OpenGLRenderer.cpp
@@ -2014,7 +2014,7 @@
         y = floorf(y + currentTransform()->getTranslateY() + 0.5f);
     }
 
-    FontRenderer& fontRenderer = mCaches.fontRenderer->getFontRenderer(paint);
+    FontRenderer& fontRenderer = mCaches.fontRenderer.getFontRenderer();
     fontRenderer.setFont(paint, SkMatrix::I());
 
     int alpha;
@@ -2166,7 +2166,7 @@
     SkXfermode::Mode mode;
     getAlphaAndMode(paint, &alpha, &mode);
 
-    FontRenderer& fontRenderer = mCaches.fontRenderer->getFontRenderer(paint);
+    FontRenderer& fontRenderer = mCaches.fontRenderer.getFontRenderer();
 
     if (CC_UNLIKELY(hasTextShadow(paint))) {
         fontRenderer.setFont(paint, SkMatrix::I());
@@ -2234,7 +2234,7 @@
     // TODO: avoid scissor by calculating maximum bounds using path bounds + font metrics
     mRenderState.scissor().setEnabled(true);
 
-    FontRenderer& fontRenderer = mCaches.fontRenderer->getFontRenderer(paint);
+    FontRenderer& fontRenderer = mCaches.fontRenderer.getFontRenderer();
     fontRenderer.setFont(paint, SkMatrix::I());
     fontRenderer.setTextureFiltering(true);
 
diff --git a/libs/hwui/Properties.cpp b/libs/hwui/Properties.cpp
index b8f8585..36a8dac 100644
--- a/libs/hwui/Properties.cpp
+++ b/libs/hwui/Properties.cpp
@@ -33,6 +33,8 @@
 bool Properties::useBufferAge = true;
 bool Properties::enablePartialUpdates = true;
 
+float Properties::textGamma = DEFAULT_TEXT_GAMMA;
+
 DebugLevel Properties::debugLevel = kDebugDisabled;
 OverdrawColorSet Properties::overdrawColorSet = OverdrawColorSet::Default;
 StencilClipDebug Properties::debugStencilClip = StencilClipDebug::Hide;
@@ -47,6 +49,15 @@
 ProfileType Properties::sProfileType = ProfileType::None;
 bool Properties::sDisableProfileBars = false;
 
+static float property_get_float(const char* key, float defaultValue) {
+    char buf[PROPERTY_VALUE_MAX] = {'\0',};
+
+    if (property_get(PROPERTY_PROFILE, buf, "") > 0) {
+        return atof(buf);
+    }
+    return defaultValue;
+}
+
 bool Properties::load() {
     char property[PROPERTY_VALUE_MAX];
     bool prevDebugLayersUpdates = debugLayersUpdates;
@@ -110,6 +121,8 @@
     useBufferAge = property_get_bool(PROPERTY_USE_BUFFER_AGE, true);
     enablePartialUpdates = property_get_bool(PROPERTY_ENABLE_PARTIAL_UPDATES, true);
 
+    textGamma = property_get_float(PROPERTY_TEXT_GAMMA, DEFAULT_TEXT_GAMMA);
+
     return (prevDebugLayersUpdates != debugLayersUpdates)
             || (prevDebugOverdraw != debugOverdraw)
             || (prevDebugStencilClip != debugStencilClip);
diff --git a/libs/hwui/Properties.h b/libs/hwui/Properties.h
index 98834b8..3512c36 100644
--- a/libs/hwui/Properties.h
+++ b/libs/hwui/Properties.h
@@ -208,30 +208,8 @@
 #define PROPERTY_TEXT_LARGE_CACHE_WIDTH "ro.hwui.text_large_cache_width"
 #define PROPERTY_TEXT_LARGE_CACHE_HEIGHT "ro.hwui.text_large_cache_height"
 
-// Indicates whether gamma correction should be applied in the shaders
-// or in lookup tables. Accepted values:
-//
-//     - "lookup3", correction based on lookup tables. Gamma correction
-//        is different for black and white text (see thresholds below)
-//
-//     - "lookup", correction based on a single lookup table
-//
-//     - "shader3", correction applied by a GLSL shader. Gamma correction
-//        is different for black and white text (see thresholds below)
-//
-//     - "shader", correction applied by a GLSL shader
-//
-// See PROPERTY_TEXT_GAMMA, PROPERTY_TEXT_BLACK_GAMMA_THRESHOLD and
-// PROPERTY_TEXT_WHITE_GAMMA_THRESHOLD for more control.
-#define PROPERTY_TEXT_GAMMA_METHOD "hwui.text_gamma_correction"
-#define DEFAULT_TEXT_GAMMA_METHOD "lookup"
-
 // Gamma (>= 1.0, <= 10.0)
 #define PROPERTY_TEXT_GAMMA "hwui.text_gamma"
-// Luminance threshold below which black gamma correction is applied. Range: [0..255]
-#define PROPERTY_TEXT_BLACK_GAMMA_THRESHOLD "hwui.text_gamma.black_threshold"
-// Lumincance threshold above which white gamma correction is applied. Range: [0..255]
-#define PROPERTY_TEXT_WHITE_GAMMA_THRESHOLD "hwui.text_gamma.white_threshold"
 
 ///////////////////////////////////////////////////////////////////////////////
 // Default property values
@@ -250,8 +228,6 @@
 #define DEFAULT_TEXTURE_CACHE_FLUSH_RATE 0.6f
 
 #define DEFAULT_TEXT_GAMMA 1.4f
-#define DEFAULT_TEXT_BLACK_GAMMA_THRESHOLD 64
-#define DEFAULT_TEXT_WHITE_GAMMA_THRESHOLD 192
 
 ///////////////////////////////////////////////////////////////////////////////
 // Misc
@@ -300,6 +276,8 @@
     static bool useBufferAge;
     static bool enablePartialUpdates;
 
+    static float textGamma;
+
     static DebugLevel debugLevel;
     static OverdrawColorSet overdrawColorSet;
     static StencilClipDebug debugStencilClip;
diff --git a/packages/CaptivePortalLogin/src/com/android/captiveportallogin/CaptivePortalLoginActivity.java b/packages/CaptivePortalLogin/src/com/android/captiveportallogin/CaptivePortalLoginActivity.java
index 0fe5509..abb464e 100644
--- a/packages/CaptivePortalLogin/src/com/android/captiveportallogin/CaptivePortalLoginActivity.java
+++ b/packages/CaptivePortalLogin/src/com/android/captiveportallogin/CaptivePortalLoginActivity.java
@@ -76,8 +76,9 @@
         String server = Settings.Global.getString(getContentResolver(), "captive_portal_server");
         if (server == null) server = DEFAULT_SERVER;
         mCm = ConnectivityManager.from(this);
+        String url = getIntent().getStringExtra(ConnectivityManager.EXTRA_CAPTIVE_PORTAL_URL);
         try {
-            mURL = new URL("http", server, "/generate_204");
+            mURL = url != null ? new URL(url) : new URL("http", server, "/generate_204");
         } catch (MalformedURLException e) {
             // System misconfigured, bail out in a way that at least provides network access.
             Log.e(TAG, "Invalid captive portal URL, server=" + server);
diff --git a/packages/DocumentsUI/AndroidManifest.xml b/packages/DocumentsUI/AndroidManifest.xml
index 97bc8fd..ac6f950 100644
--- a/packages/DocumentsUI/AndroidManifest.xml
+++ b/packages/DocumentsUI/AndroidManifest.xml
@@ -35,11 +35,6 @@
                 <action android:name="android.intent.action.OPEN_DOCUMENT_TREE" />
                 <category android:name="android.intent.category.DEFAULT" />
             </intent-filter>
-            <intent-filter>
-                <action android:name="android.provider.action.BROWSE_DOCUMENT_ROOT" />
-                <category android:name="android.intent.category.DEFAULT" />
-                <data android:mimeType="vnd.android.document/root" />
-            </intent-filter>
         </activity>
 
         <activity
@@ -63,6 +58,11 @@
                 <action android:name="android.intent.action.MAIN" />
                 <category android:name="android.intent.category.LAUNCHER" />
             </intent-filter>
+            <intent-filter>
+                <action android:name="android.provider.action.BROWSE_DOCUMENT_ROOT" />
+                <category android:name="android.intent.category.DEFAULT" />
+                <data android:mimeType="vnd.android.document/root" />
+            </intent-filter>
         </activity>
 
         <provider
diff --git a/packages/DocumentsUI/src/com/android/documentsui/BaseActivity.java b/packages/DocumentsUI/src/com/android/documentsui/BaseActivity.java
index 4d0a7eb..1585908 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/BaseActivity.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/BaseActivity.java
@@ -16,13 +16,6 @@
 
 package com.android.documentsui;
 
-import static com.android.documentsui.BaseActivity.State.ACTION_BROWSE;
-import static com.android.documentsui.BaseActivity.State.ACTION_CREATE;
-import static com.android.documentsui.BaseActivity.State.ACTION_GET_CONTENT;
-import static com.android.documentsui.BaseActivity.State.ACTION_MANAGE;
-import static com.android.documentsui.BaseActivity.State.ACTION_OPEN;
-import static com.android.documentsui.BaseActivity.State.ACTION_OPEN_COPY_DESTINATION;
-import static com.android.documentsui.BaseActivity.State.ACTION_OPEN_TREE;
 import static com.android.documentsui.DirectoryFragment.ANIM_DOWN;
 import static com.android.documentsui.DirectoryFragment.ANIM_NONE;
 import static com.android.documentsui.DirectoryFragment.ANIM_SIDE;
@@ -125,22 +118,6 @@
     }
 
     @Override
-    public void onResume() {
-        super.onResume();
-
-        final State state = getDisplayState();
-        final RootInfo root = getCurrentRoot();
-
-        // If we're browsing a specific root, and that root went away, then we
-        // have no reason to hang around
-        if (state.action == State.ACTION_BROWSE && root != null) {
-            if (mRoots.getRootBlocking(root.authority, root.rootId) == null) {
-                finish();
-            }
-        }
-    }
-
-    @Override
     public boolean onCreateOptionsMenu(Menu menu) {
         boolean showMenu = super.onCreateOptionsMenu(menu);
 
@@ -178,8 +155,10 @@
         State state = getDisplayState();
 
         sortSize.setVisible(state.showSize); // Only sort by size when visible
+        fileSize.setVisible(!state.showSize);
         grid.setVisible(state.derivedMode != State.MODE_GRID);
         list.setVisible(state.derivedMode != State.MODE_LIST);
+        advanced.setVisible(!mState.showAdvanced);
         settings.setVisible((root.flags & Root.FLAG_HAS_SETTINGS) != 0);
 
         return shown;
@@ -189,13 +168,17 @@
         State state = new State();
 
         final Intent intent = getIntent();
-        final String action = intent.getAction();
 
         state.localOnly = intent.getBooleanExtra(Intent.EXTRA_LOCAL_ONLY, false);
-        state.forceAdvanced = intent.getBooleanExtra(DocumentsContract.EXTRA_SHOW_ADVANCED, false);
-        state.showAdvanced = state.forceAdvanced ||
-                LocalPreferences.getDisplayAdvancedDevices(this);
 
+        state.forceSize = intent.getBooleanExtra(DocumentsContract.EXTRA_SHOW_FILESIZE, false);
+        state.showSize = state.forceSize || LocalPreferences.getDisplayFileSize(this);
+
+        state.forceAdvanced = intent.getBooleanExtra(DocumentsContract.EXTRA_SHOW_ADVANCED, false);
+        state.showAdvanced = state.forceAdvanced
+                || LocalPreferences.getDisplayAdvancedDevices(this);
+
+        state.initAcceptMimes(intent);
         state.excludedAuthorities = getExcludedAuthorities();
 
         return state;
@@ -219,7 +202,7 @@
         if (mRoots.isRecentsRoot(root)) {
             onCurrentDirectoryChanged(ANIM_SIDE);
         } else {
-            new PickRootTask(root).executeOnExecutor(getCurrentExecutor());
+            new PickRootTask(root).executeOnExecutor(getExecutorForCurrentDirectory());
         }
     }
 
@@ -399,6 +382,7 @@
         public int derivedSortOrder = SORT_ORDER_DISPLAY_NAME;
 
         public boolean allowMultiple;
+        public boolean forceSize ;
         public boolean showSize;
         public boolean localOnly ;
         public boolean forceAdvanced ;
@@ -429,7 +413,6 @@
         public static final int ACTION_OPEN_TREE = 4;
         public static final int ACTION_MANAGE = 5;
         public static final int ACTION_BROWSE = 6;
-        public static final int ACTION_BROWSE_ALL = 7;
         public static final int ACTION_OPEN_COPY_DESTINATION = 8;
 
         public static final int MODE_UNKNOWN = 0;
@@ -441,6 +424,15 @@
         public static final int SORT_ORDER_LAST_MODIFIED = 2;
         public static final int SORT_ORDER_SIZE = 3;
 
+        public void initAcceptMimes(Intent intent) {
+            if (intent.hasExtra(Intent.EXTRA_MIME_TYPES)) {
+                acceptMimes = intent.getStringArrayExtra(Intent.EXTRA_MIME_TYPES);
+            } else {
+                String glob = intent.getType();
+                acceptMimes = new String[] { glob != null ? glob : "*/*" };
+            }
+        }
+
         @Override
         public int describeContents() {
             return 0;
@@ -453,6 +445,7 @@
             out.writeStringArray(acceptMimes);
             out.writeInt(userSortOrder);
             out.writeInt(allowMultiple ? 1 : 0);
+            out.writeInt(forceSize ? 1 : 0);
             out.writeInt(showSize ? 1 : 0);
             out.writeInt(localOnly ? 1 : 0);
             out.writeInt(forceAdvanced ? 1 : 0);
@@ -475,6 +468,7 @@
                 state.acceptMimes = in.readStringArray();
                 state.userSortOrder = in.readInt();
                 state.allowMultiple = in.readInt() != 0;
+                state.forceSize = in.readInt() != 0;
                 state.showSize = in.readInt() != 0;
                 state.localOnly = in.readInt() != 0;
                 state.forceAdvanced = in.readInt() != 0;
@@ -562,7 +556,7 @@
         return getDisplayState().stack.peek();
     }
 
-    public Executor getCurrentExecutor() {
+    public Executor getExecutorForCurrentDirectory() {
         final DocumentInfo cwd = getCurrentDirectory();
         if (cwd != null && cwd.authority != null) {
             return ProviderExecutor.forAuthority(cwd.authority);
diff --git a/packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java b/packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java
index 1a17b6c..ea8ecf5 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java
@@ -17,7 +17,6 @@
 package com.android.documentsui;
 
 import static com.android.documentsui.BaseActivity.State.ACTION_BROWSE;
-import static com.android.documentsui.BaseActivity.State.ACTION_BROWSE_ALL;
 import static com.android.documentsui.BaseActivity.State.ACTION_CREATE;
 import static com.android.documentsui.BaseActivity.State.ACTION_MANAGE;
 import static com.android.documentsui.BaseActivity.State.MODE_GRID;
@@ -101,7 +100,6 @@
 import com.android.documentsui.model.DocumentInfo;
 import com.android.documentsui.model.DocumentStack;
 import com.android.documentsui.model.RootInfo;
-import com.android.internal.util.Preconditions;
 
 import com.google.common.collect.Lists;
 
@@ -1554,9 +1552,9 @@
     }
 
     private FragmentTuner pickFragmentTuner(final State state) {
-        return state.action == ACTION_BROWSE_ALL
+        return state.action == ACTION_BROWSE
                 ? new FilesTuner()
-                : new DefaultTuner(state);
+                : new DefaultTuner(state.action);
     }
 
     /**
@@ -1593,15 +1591,14 @@
      */
     private static final class DefaultTuner implements FragmentTuner {
 
-        private final State mState;
+        private final boolean mManaging;
 
-        public DefaultTuner(State state) {
-            mState = state;
+        public DefaultTuner(int action) {
+            mManaging = (action == ACTION_MANAGE);
         }
 
         @Override
         public void updateActionMenu(Menu menu, int dirType, boolean canDelete) {
-            Preconditions.checkState(mState.action != ACTION_BROWSE_ALL);
 
             final MenuItem open = menu.findItem(R.id.menu_open);
             final MenuItem share = menu.findItem(R.id.menu_share);
@@ -1610,14 +1607,11 @@
             final MenuItem moveTo = menu.findItem(R.id.menu_move_to);
             final MenuItem copyToClipboard = menu.findItem(R.id.menu_copy_to_clipboard);
 
-            final boolean manageOrBrowse = (mState.action == ACTION_MANAGE
-                    || mState.action == ACTION_BROWSE);
-
-            open.setVisible(!manageOrBrowse);
-            share.setVisible(manageOrBrowse);
-            delete.setVisible(manageOrBrowse && canDelete);
+            open.setVisible(!mManaging);
+            share.setVisible(mManaging);
+            delete.setVisible(mManaging && canDelete);
             // Disable copying from the Recents view.
-            copyTo.setVisible(manageOrBrowse && dirType != TYPE_RECENT_OPEN);
+            copyTo.setVisible(mManaging && dirType != TYPE_RECENT_OPEN);
             moveTo.setVisible(SystemProperties.getBoolean("debug.documentsui.enable_move", false));
 
             // Only shown in files mode.
@@ -1634,6 +1628,7 @@
     private static final class FilesTuner implements FragmentTuner {
         @Override
         public void updateActionMenu(Menu menu, int dirType, boolean canDelete) {
+
             menu.findItem(R.id.menu_share).setVisible(true);
             menu.findItem(R.id.menu_delete).setVisible(canDelete);
             menu.findItem(R.id.menu_copy_to_clipboard).setVisible(true);
diff --git a/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java b/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java
index 8b2b4f2..dbfcf40 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java
@@ -16,7 +16,6 @@
 
 package com.android.documentsui;
 
-import static com.android.documentsui.BaseActivity.State.ACTION_BROWSE;
 import static com.android.documentsui.BaseActivity.State.ACTION_CREATE;
 import static com.android.documentsui.BaseActivity.State.ACTION_GET_CONTENT;
 import static com.android.documentsui.BaseActivity.State.ACTION_OPEN;
@@ -28,7 +27,6 @@
 import android.app.Activity;
 import android.app.Fragment;
 import android.app.FragmentManager;
-import android.content.ActivityNotFoundException;
 import android.content.ClipData;
 import android.content.ComponentName;
 import android.content.ContentProviderClient;
@@ -44,7 +42,6 @@
 import android.os.Bundle;
 import android.os.Parcelable;
 import android.provider.DocumentsContract;
-import android.provider.DocumentsContract.Root;
 import android.util.Log;
 import android.view.Menu;
 import android.view.MenuItem;
@@ -87,7 +84,7 @@
         super.onCreate(icicle);
 
         final Resources res = getResources();
-        mShowAsDialog = res.getBoolean(R.bool.show_as_dialog) && mState.action != ACTION_BROWSE;
+        mShowAsDialog = res.getBoolean(R.bool.show_as_dialog);
 
         if (!mShowAsDialog) {
             setTheme(R.style.DocumentsNonDialogTheme);
@@ -123,14 +120,6 @@
 
         setActionBar(mToolbar);
 
-        // Hide roots when we're managing a specific root
-        if (mState.action == ACTION_BROWSE) {
-            mDrawer.lockClosed();
-            if (mShowAsDialog) {
-                findViewById(R.id.container_roots).setVisibility(View.GONE);
-            }
-        }
-
         if (mState.action == ACTION_CREATE) {
             final String mimeType = getIntent().getType();
             final String title = getIntent().getStringExtra(Intent.EXTRA_TITLE);
@@ -156,12 +145,7 @@
             // In this case, we set the activity title in AsyncTask.onPostExecute().  To prevent
             // talkback from reading aloud the default title, we clear it here.
             setTitle("");
-            if (mState.action == ACTION_BROWSE) {
-                final Uri rootUri = getIntent().getData();
-                new RestoreRootTask(rootUri).executeOnExecutor(getCurrentExecutor());
-            } else {
-                new RestoreStackTask().execute();
-            }
+            new RestoreStackTask().execute();
         } else {
             onCurrentDirectoryChanged(ANIM_NONE);
         }
@@ -181,8 +165,6 @@
             state.action = ACTION_GET_CONTENT;
         } else if (Intent.ACTION_OPEN_DOCUMENT_TREE.equals(action)) {
             state.action = ACTION_OPEN_TREE;
-        } else if (DocumentsContract.ACTION_BROWSE_DOCUMENT_ROOT.equals(action)) {
-            state.action = ACTION_BROWSE;
         } else if (DocumentsIntent.ACTION_OPEN_COPY_DESTINATION.equals(action)) {
             state.action = ACTION_OPEN_COPY_DESTINATION;
         }
@@ -192,20 +174,6 @@
                     Intent.EXTRA_ALLOW_MULTIPLE, false);
         }
 
-        if (state.action == ACTION_BROWSE) {
-            state.acceptMimes = new String[] { "*/*" };
-            state.allowMultiple = true;
-        } else if (intent.hasExtra(Intent.EXTRA_MIME_TYPES)) {
-            state.acceptMimes = intent.getStringArrayExtra(Intent.EXTRA_MIME_TYPES);
-        } else {
-            state.acceptMimes = new String[] { intent.getType() };
-        }
-
-        if (state.action == ACTION_BROWSE) {
-            state.showSize = true;
-        } else {
-            state.showSize = LocalPreferences.getDisplayFileSize(this);
-        }
         if (state.action == ACTION_OPEN_COPY_DESTINATION) {
             state.directoryCopy = intent.getBooleanExtra(
                     BaseActivity.DocumentsIntent.EXTRA_DIRECTORY_COPY, false);
@@ -357,11 +325,7 @@
         final MenuItem fileSize = menu.findItem(R.id.menu_file_size);
         final MenuItem settings = menu.findItem(R.id.menu_settings);
 
-        // File size is locked visible for browse because that is the action triggered by Settings,
-        // where the user is trying to find large files to clean up.
-        // TODO: instead of setting this according to the action, use a local preference, but
-        // provide a @hide extra to let callers like Settings force-enable size visibility.
-        boolean fileSizeVisible = mState.action != ACTION_BROWSE;
+        boolean fileSizeVisible = mState.showSize && !mState.forceSize;
         if (mState.action == ACTION_CREATE
                 || mState.action == ACTION_OPEN_TREE
                 || mState.action == ACTION_OPEN_COPY_DESTINATION) {
@@ -383,11 +347,9 @@
             createDir.setVisible(false);
         }
 
-        advanced.setVisible(mState.action != ACTION_BROWSE && !mState.forceAdvanced);
+        advanced.setVisible(!mState.forceAdvanced);
         fileSize.setVisible(fileSizeVisible);
-
-        settings.setVisible(mState.action == ACTION_BROWSE
-                && (root.flags & Root.FLAG_HAS_SETTINGS) != 0);
+        settings.setVisible(false);
 
         return true;
     }
@@ -446,11 +408,11 @@
     }
 
     void onSaveRequested(DocumentInfo replaceTarget) {
-        new ExistingFinishTask(replaceTarget.derivedUri).executeOnExecutor(getCurrentExecutor());
+        new ExistingFinishTask(replaceTarget.derivedUri).executeOnExecutor(getExecutorForCurrentDirectory());
     }
 
     void onSaveRequested(String mimeType, String displayName) {
-        new CreateFinishTask(mimeType, displayName).executeOnExecutor(getCurrentExecutor());
+        new CreateFinishTask(mimeType, displayName).executeOnExecutor(getExecutorForCurrentDirectory());
     }
 
     @Override
@@ -466,21 +428,10 @@
             openDirectory(doc);
         } else if (mState.action == ACTION_OPEN || mState.action == ACTION_GET_CONTENT) {
             // Explicit file picked, return
-            new ExistingFinishTask(doc.derivedUri).executeOnExecutor(getCurrentExecutor());
+            new ExistingFinishTask(doc.derivedUri).executeOnExecutor(getExecutorForCurrentDirectory());
         } else if (mState.action == ACTION_CREATE) {
             // Replace selected file
             SaveFragment.get(fm).setReplaceTarget(doc);
-        } else if (mState.action == ACTION_BROWSE) {
-            // Go straight to viewing
-            final Intent view = new Intent(Intent.ACTION_VIEW);
-            view.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
-            view.setData(doc.derivedUri);
-
-            try {
-                startActivity(view);
-            } catch (ActivityNotFoundException ex) {
-                Toast.makeText(this, R.string.toast_no_application, Toast.LENGTH_SHORT).show();
-            }
         }
     }
 
@@ -492,7 +443,7 @@
             for (int i = 0; i < size; i++) {
                 uris[i] = docs.get(i).derivedUri;
             }
-            new ExistingFinishTask(uris).executeOnExecutor(getCurrentExecutor());
+            new ExistingFinishTask(uris).executeOnExecutor(getExecutorForCurrentDirectory());
         }
     }
 
@@ -507,7 +458,7 @@
             // Should not be reached.
             throw new IllegalStateException("Invalid mState.action.");
         }
-        new PickFinishTask(result).executeOnExecutor(getCurrentExecutor());
+        new PickFinishTask(result).executeOnExecutor(getExecutorForCurrentDirectory());
     }
 
     @Override
diff --git a/packages/DocumentsUI/src/com/android/documentsui/FilesActivity.java b/packages/DocumentsUI/src/com/android/documentsui/FilesActivity.java
index c1362c8..7e9531b 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/FilesActivity.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/FilesActivity.java
@@ -16,8 +16,9 @@
 
 package com.android.documentsui;
 
-import static com.android.documentsui.DirectoryFragment.ANIM_DOWN;
 import static com.android.documentsui.DirectoryFragment.ANIM_NONE;
+import static com.android.documentsui.Shared.DEBUG;
+import static com.android.internal.util.Preconditions.checkArgument;
 
 import android.app.Activity;
 import android.app.FragmentManager;
@@ -25,11 +26,9 @@
 import android.content.ClipData;
 import android.content.ContentResolver;
 import android.content.ContentValues;
-import android.content.Context;
 import android.content.Intent;
 import android.net.Uri;
 import android.os.Bundle;
-import android.provider.DocumentsContract;
 import android.support.annotation.Nullable;
 import android.util.Log;
 import android.view.KeyEvent;
@@ -46,7 +45,6 @@
 import com.android.documentsui.model.DocumentStack;
 import com.android.documentsui.model.DurableUtils;
 import com.android.documentsui.model.RootInfo;
-import com.android.internal.util.Preconditions;
 
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -58,7 +56,6 @@
 public class FilesActivity extends BaseActivity {
 
     public static final String TAG = "FilesActivity";
-    static final boolean DEBUG = false;
 
     private Toolbar mToolbar;
     private Spinner mToolbarStack;
@@ -74,8 +71,6 @@
     public void onCreate(Bundle icicle) {
         super.onCreate(icicle);
 
-        final Context context = this;
-
         mToolbar = (Toolbar) findViewById(R.id.toolbar);
 
         mStackAdapter = new StackAdapter();
@@ -90,7 +85,16 @@
 
         RootsFragment.show(getFragmentManager(), null);
         if (!mState.restored) {
-            new RestoreStackTask().execute();
+            Uri rootUri = getIntent().getData();
+
+            // If we've got a specific root to display, restore that root using a dedicated
+            // authority. That way a misbehaving provider won't result in an ANR.
+            if (rootUri != null) {
+                new RestoreRootTask(rootUri).executeOnExecutor(
+                        ProviderExecutor.forAuthority(rootUri.getAuthority()));
+            } else {
+                new RestoreStackTask().execute();
+            }
 
             // Show a failure dialog if there was a failed operation.
             final Intent intent = getIntent();
@@ -115,22 +119,16 @@
 
         final Intent intent = getIntent();
 
-        state.action = State.ACTION_BROWSE_ALL;
-        state.acceptMimes = new String[] { intent.getType() };
+        state.action = State.ACTION_BROWSE;
         state.allowMultiple = true;
 
-        // These options are specific to the DocumentsActivity.
-        Preconditions.checkArgument(
-                !intent.hasExtra(Intent.EXTRA_LOCAL_ONLY));
-        Preconditions.checkArgument(
-                !intent.hasExtra(DocumentsContract.EXTRA_SHOW_ADVANCED));
-
-        state.showAdvanced = LocalPreferences.getDisplayAdvancedDevices(this);
-        state.showSize = LocalPreferences.getDisplayFileSize(this);
+        // Options specific to the DocumentsActivity.
+        checkArgument(!intent.hasExtra(Intent.EXTRA_LOCAL_ONLY));
 
         final DocumentStack stack = intent.getParcelableExtra(CopyService.EXTRA_STACK);
-        if (stack != null)
+        if (stack != null) {
             state.stack = stack;
+        }
 
         return state;
     }
@@ -142,6 +140,21 @@
     }
 
     @Override
+    public void onResume() {
+        super.onResume();
+
+        final RootInfo root = getCurrentRoot();
+
+        // If we're browsing a specific root, and that root went away, then we
+        // have no reason to hang around.
+        // TODO: Rather than just disappearing, maybe we should inform
+        // the user what has happened, let them close us. Less surprising.
+        if (mRoots.getRootBlocking(root.authority, root.rootId) == null) {
+            finish();
+        }
+    }
+
+    @Override
     public void updateActionBar() {
         final RootInfo root = getCurrentRoot();
 
@@ -194,12 +207,8 @@
     public boolean onPrepareOptionsMenu(Menu menu) {
         boolean shown = super.onPrepareOptionsMenu(menu);
 
-        menu.findItem(R.id.menu_file_size).setVisible(true);
-        menu.findItem(R.id.menu_advanced).setVisible(true);
-
         final MenuItem pasteFromCb = menu.findItem(R.id.menu_paste_from_clipboard);
         final MenuItem createDir = menu.findItem(R.id.menu_create_dir);
-        final MenuItem settings = menu.findItem(R.id.menu_settings);
 
         boolean canCreateDir = canCreateDirectory();
 
diff --git a/packages/DocumentsUI/src/com/android/documentsui/ManageRootActivity.java b/packages/DocumentsUI/src/com/android/documentsui/ManageRootActivity.java
index 798992b..f5b1d8e 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/ManageRootActivity.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/ManageRootActivity.java
@@ -87,7 +87,7 @@
             // talkback from reading aloud the default title, we clear it here.
             setTitle("");
             final Uri rootUri = getIntent().getData();
-            new RestoreRootTask(rootUri).executeOnExecutor(getCurrentExecutor());
+            new RestoreRootTask(rootUri).executeOnExecutor(getExecutorForCurrentDirectory());
         } else {
             onCurrentDirectoryChanged(ANIM_NONE);
         }
diff --git a/packages/DocumentsUI/src/com/android/documentsui/MultiSelectManager.java b/packages/DocumentsUI/src/com/android/documentsui/MultiSelectManager.java
index 5930056..5839943 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/MultiSelectManager.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/MultiSelectManager.java
@@ -45,6 +45,8 @@
 import com.android.documentsui.Events.InputEvent;
 import com.android.documentsui.Events.MotionInputEvent;
 
+import com.google.android.collect.Lists;
+
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
@@ -113,8 +115,11 @@
                     }
                 };
 
-        CompositeOnGestureListener<? extends Object> compositeListener =
-                new CompositeOnGestureListener<>(listener, gestureDelegate);
+        CompositeOnGestureListener compositeListener =
+                new CompositeOnGestureListener(
+                        Lists.<OnGestureListener>newArrayList(listener, gestureDelegate),
+                        Lists.<OnDoubleTapListener>newArrayList(listener, gestureDelegate));
+
         final GestureDetector detector =
                 new GestureDetector(recyclerView.getContext(), compositeListener);
 
@@ -1060,21 +1065,23 @@
      * @template A gestureDelegate that implements both {@link OnGestureListener}
      *     and {@link OnDoubleTapListener}
      */
-    private static final class
-            CompositeOnGestureListener<L extends OnGestureListener & OnDoubleTapListener>
+    private static final class CompositeOnGestureListener
             implements OnGestureListener, OnDoubleTapListener {
 
-        private L[] mListeners;
+        private List<OnGestureListener> mGestureListeners;
+        private List<OnDoubleTapListener> mTapListeners;
 
-        @SafeVarargs
-        public CompositeOnGestureListener(L... listeners) {
-            mListeners = listeners;
+        public CompositeOnGestureListener(
+                List<OnGestureListener> gestureListeners,
+                List<OnDoubleTapListener> tapListeners) {
+            mGestureListeners = gestureListeners;
+            mTapListeners = tapListeners;
         }
 
         @Override
         public boolean onDown(MotionEvent e) {
-            for (int i = 0; i < mListeners.length; i++) {
-                if (mListeners[i].onDown(e)) {
+            for (OnGestureListener l : mGestureListeners) {
+                if (l.onDown(e)) {
                     return true;
                 }
             }
@@ -1083,15 +1090,15 @@
 
         @Override
         public void onShowPress(MotionEvent e) {
-            for (int i = 0; i < mListeners.length; i++) {
-                mListeners[i].onShowPress(e);
+            for (OnGestureListener l : mGestureListeners) {
+                l.onShowPress(e);
             }
         }
 
         @Override
         public boolean onSingleTapUp(MotionEvent e) {
-            for (int i = 0; i < mListeners.length; i++) {
-                if (mListeners[i].onSingleTapUp(e)) {
+            for (OnGestureListener l : mGestureListeners) {
+                if (l.onSingleTapUp(e)) {
                     return true;
                 }
             }
@@ -1100,8 +1107,8 @@
 
         @Override
         public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
-            for (int i = 0; i < mListeners.length; i++) {
-                if (mListeners[i].onScroll(e1, e2, distanceX, distanceY)) {
+            for (OnGestureListener l : mGestureListeners) {
+                if (l.onScroll(e1, e2, distanceX, distanceY)) {
                     return true;
                 }
             }
@@ -1110,15 +1117,15 @@
 
         @Override
         public void onLongPress(MotionEvent e) {
-            for (int i = 0; i < mListeners.length; i++) {
-                mListeners[i].onLongPress(e);
+            for (OnGestureListener l : mGestureListeners) {
+                l.onLongPress(e);
             }
         }
 
         @Override
         public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
-            for (int i = 0; i < mListeners.length; i++) {
-                if (mListeners[i].onFling(e1, e2, velocityX, velocityY)) {
+            for (OnGestureListener l : mGestureListeners) {
+                if (l.onFling(e1, e2, velocityX, velocityY)) {
                     return true;
                 }
             }
@@ -1127,8 +1134,8 @@
 
         @Override
         public boolean onSingleTapConfirmed(MotionEvent e) {
-            for (int i = 0; i < mListeners.length; i++) {
-                if (mListeners[i].onSingleTapConfirmed(e)) {
+            for (OnDoubleTapListener listener : mTapListeners) {
+                if (listener.onSingleTapConfirmed(e)) {
                     return true;
                 }
             }
@@ -1137,8 +1144,8 @@
 
         @Override
         public boolean onDoubleTap(MotionEvent e) {
-            for (int i = 0; i < mListeners.length; i++) {
-                if (mListeners[i].onDoubleTap(e)) {
+            for (OnDoubleTapListener listener : mTapListeners) {
+                if (listener.onDoubleTap(e)) {
                     return true;
                 }
             }
@@ -1147,8 +1154,8 @@
 
         @Override
         public boolean onDoubleTapEvent(MotionEvent e) {
-            for (int i = 0; i < mListeners.length; i++) {
-                if (mListeners[i].onDoubleTapEvent(e)) {
+            for (OnDoubleTapListener listener : mTapListeners) {
+                if (listener.onDoubleTapEvent(e)) {
                     return true;
                 }
             }
diff --git a/packages/DocumentsUI/src/com/android/documentsui/RootsCache.java b/packages/DocumentsUI/src/com/android/documentsui/RootsCache.java
index 05f7d8d..cb46bca 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/RootsCache.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/RootsCache.java
@@ -17,6 +17,7 @@
 package com.android.documentsui;
 
 import static com.android.documentsui.Shared.TAG;
+import static com.android.documentsui.Shared.DEBUG;
 
 import android.content.ContentProviderClient;
 import android.content.ContentResolver;
@@ -58,8 +59,6 @@
  * Cache of known storage backends and their roots.
  */
 public class RootsCache {
-    private static final boolean LOGD = false;
-
     public static final Uri sNotificationUri = Uri.parse(
             "content://com.android.documentsui.roots/");
 
@@ -91,7 +90,7 @@
 
         @Override
         public void onChange(boolean selfChange, Uri uri) {
-            if (LOGD) Log.d(TAG, "Updating roots due to change at " + uri);
+            if (DEBUG) Log.d(TAG, "Updating roots due to change at " + uri);
             updateAuthorityAsync(uri.getAuthority());
         }
     }
@@ -148,7 +147,7 @@
         final ContentResolver resolver = mContext.getContentResolver();
         synchronized (mLock) {
             for (String authority : mStoppedAuthorities) {
-                if (LOGD) Log.d(TAG, "Loading stopped authority " + authority);
+                if (DEBUG) Log.d(TAG, "Loading stopped authority " + authority);
                 mRoots.putAll(authority, loadRootsForAuthority(resolver, authority));
             }
             mStoppedAuthorities.clear();
@@ -199,7 +198,8 @@
             }
 
             final long delta = SystemClock.elapsedRealtime() - start;
-            Log.d(TAG, "Update found " + mTaskRoots.size() + " roots in " + delta + "ms");
+            if (DEBUG)
+                Log.d(TAG, "Update found " + mTaskRoots.size() + " roots in " + delta + "ms");
             synchronized (mLock) {
                 mRoots = mTaskRoots;
                 mStoppedAuthorities = mTaskStoppedAuthorities;
@@ -213,7 +213,7 @@
             // Ignore stopped packages for now; we might query them
             // later during UI interaction.
             if ((info.applicationInfo.flags & ApplicationInfo.FLAG_STOPPED) != 0) {
-                if (LOGD) Log.d(TAG, "Ignoring stopped authority " + info.authority);
+                if (DEBUG) Log.d(TAG, "Ignoring stopped authority " + info.authority);
                 mTaskStoppedAuthorities.add(info.authority);
                 return;
             }
@@ -223,7 +223,7 @@
             if (mFilterPackage != null && !mFilterPackage.equals(info.packageName)) {
                 synchronized (mLock) {
                     if (mTaskRoots.putAll(info.authority, mRoots.get(info.authority))) {
-                        if (LOGD) Log.d(TAG, "Used cached roots for " + info.authority);
+                        if (DEBUG) Log.d(TAG, "Used cached roots for " + info.authority);
                         cacheHit = true;
                     }
                 }
@@ -241,7 +241,7 @@
      * Bring up requested provider and query for all active roots.
      */
     private Collection<RootInfo> loadRootsForAuthority(ContentResolver resolver, String authority) {
-        if (LOGD) Log.d(TAG, "Loading roots for " + authority);
+        if (DEBUG) Log.d(TAG, "Loading roots for " + authority);
 
         synchronized (mObservedAuthorities) {
             if (mObservedAuthorities.add(authority)) {
@@ -370,10 +370,15 @@
             // Exclude downloads roots that don't support directory creation
             // TODO: Add flag to check the root supports directory creation or not.
             if (state.directoryCopy && root.isDownloads()) continue;
-            // Only show empty roots when creating
-            if ((state.action != State.ACTION_CREATE ||
+
+            // Only show empty roots when creating, or in browse mode.
+            if (empty && (state.action != State.ACTION_BROWSE ||
+                 state.action != State.ACTION_CREATE ||
                  state.action != State.ACTION_OPEN_TREE ||
-                 state.action != State.ACTION_OPEN_COPY_DESTINATION) && empty) continue;
+                 state.action != State.ACTION_OPEN_COPY_DESTINATION)) {
+                if (DEBUG) Log.i(TAG, "Skipping empty root: " + root);
+                continue;
+            }
 
             // Only include roots that serve requested content
             final boolean overlap =
@@ -385,7 +390,7 @@
 
             // Exclude roots from the calling package.
             if (state.excludedAuthorities.contains(root.authority)) {
-                if (LOGD) Log.d(TAG, "Excluding root " + root.authority + " from calling package.");
+                if (DEBUG) Log.d(TAG, "Excluding root " + root.authority + " from calling package.");
                 continue;
             }
 
diff --git a/packages/DocumentsUI/src/com/android/documentsui/Shared.java b/packages/DocumentsUI/src/com/android/documentsui/Shared.java
index 0c1ebc1..9c884d4 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/Shared.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/Shared.java
@@ -22,7 +22,7 @@
  * @hide
  */
 public final class Shared {
-    public static final boolean DEBUG = false;
+    public static final boolean DEBUG = true;
     public static final String TAG = "Documents";
 
     /**
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardSimPinView.java b/packages/Keyguard/src/com/android/keyguard/KeyguardSimPinView.java
index aeac912..2033159 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardSimPinView.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardSimPinView.java
@@ -23,6 +23,7 @@
 
 import android.content.Context;
 import android.content.res.ColorStateList;
+import android.content.res.Configuration;
 import android.content.res.Resources;
 import android.app.AlertDialog;
 import android.app.AlertDialog.Builder;
@@ -96,6 +97,12 @@
     }
 
     @Override
+    protected void onConfigurationChanged(Configuration newConfig) {
+        super.onConfigurationChanged(newConfig);
+        resetState();
+    }
+
+    @Override
     protected int getPromtReasonStringRes(int reason) {
         // No message on SIM Pin
         return 0;
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitor.java
index e342865..57ee319 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -121,7 +121,6 @@
     private static final int MSG_DEVICE_PROVISIONED = 308;
     private static final int MSG_DPM_STATE_CHANGED = 309;
     private static final int MSG_USER_SWITCHING = 310;
-    private static final int MSG_KEYGUARD_VISIBILITY_CHANGED = 311;
     private static final int MSG_KEYGUARD_RESET = 312;
     private static final int MSG_BOOT_COMPLETED = 313;
     private static final int MSG_USER_SWITCH_COMPLETE = 314;
@@ -233,9 +232,6 @@
                 case MSG_USER_SWITCH_COMPLETE:
                     handleUserSwitchComplete(msg.arg1);
                     break;
-                case MSG_KEYGUARD_VISIBILITY_CHANGED:
-                    handleKeyguardVisibilityChanged(msg.arg1);
-                    break;
                 case MSG_KEYGUARD_RESET:
                     handleKeyguardReset();
                     break;
@@ -1344,19 +1340,20 @@
     }
 
     /**
-     * Handle {@link #MSG_KEYGUARD_VISIBILITY_CHANGED}
+     * Notifies that the visibility state of Keyguard has changed.
+     *
+     * <p>Needs to be called from the main thread.
      */
-    private void handleKeyguardVisibilityChanged(int showing) {
-        if (DEBUG) Log.d(TAG, "handleKeyguardVisibilityChanged(" + showing + ")");
-        boolean isShowing = (showing == 1);
-        mKeyguardIsVisible = isShowing;
+    public void onKeyguardVisibilityChanged(boolean showing) {
+        if (DEBUG) Log.d(TAG, "onKeyguardVisibilityChanged(" + showing + ")");
+        mKeyguardIsVisible = showing;
         for (int i = 0; i < mCallbacks.size(); i++) {
             KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
             if (cb != null) {
-                cb.onKeyguardVisibilityChangedRaw(isShowing);
+                cb.onKeyguardVisibilityChangedRaw(showing);
             }
         }
-        if (!isShowing) {
+        if (!showing) {
             mFingerprintAlreadyAuthenticated = false;
         }
         updateFingerprintListeningState();
@@ -1477,13 +1474,6 @@
         }
     }
 
-    public void sendKeyguardVisibilityChanged(boolean showing) {
-        if (DEBUG) Log.d(TAG, "sendKeyguardVisibilityChanged(" + showing + ")");
-        Message message = mHandler.obtainMessage(MSG_KEYGUARD_VISIBILITY_CHANGED);
-        message.arg1 = showing ? 1 : 0;
-        message.sendToTarget();
-    }
-
     public void sendKeyguardReset() {
         mHandler.obtainMessage(MSG_KEYGUARD_RESET).sendToTarget();
     }
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java
index 1d71346..e12e3a5 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java
@@ -166,7 +166,7 @@
                         .putExtra(Intent.EXTRA_SETTING_NAME, name)
                         .putExtra(Intent.EXTRA_SETTING_NEW_VALUE, value)
                         .putExtra(Intent.EXTRA_SETTING_PREVIOUS_VALUE, oldValue);
-                context.sendBroadcastAsUser(intent, UserHandle.OWNER, null);
+                context.sendBroadcastAsUser(intent, UserHandle.SYSTEM, null);
             }
         }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java b/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
index 7449009..5bf251b 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
@@ -159,7 +159,9 @@
         if (mServicesStarted) {
             int len = mServices.length;
             for (int i = 0; i < len; i++) {
-                mServices[i].onConfigurationChanged(newConfig);
+                if (mServices[i] != null) {
+                    mServices[i].onConfigurationChanged(newConfig);
+                }
             }
         }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/AnglesVarianceClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/AnglesVarianceClassifier.java
index 5cd914f..8c681fc 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/AnglesVarianceClassifier.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/AnglesVarianceClassifier.java
@@ -16,7 +16,6 @@
 
 package com.android.systemui.classifier;
 
-import android.hardware.SensorEvent;
 import android.view.MotionEvent;
 
 import java.lang.Math;
@@ -27,27 +26,19 @@
 
 /**
  * A classifier which calculates the variance of differences between successive angles in a stroke.
- * For each stroke it keeps its last three points. If some successive points are the same, it ignores
- * the repetitions. If a new point is added, the classifier calculates the angle between the last
- * three points. After that it calculates the difference between this angle and the previously
- * calculated angle. The return value of the classifier is the variance of the differences
- * from a stroke. If there are multiple strokes created at once, the classifier sums up the
- * variances of all the strokes. Also the value is multiplied by HISTORY_FACTOR after each
- * INTERVAL milliseconds.
+ * For each stroke it keeps its last three points. If some successive points are the same, it
+ * ignores the repetitions. If a new point is added, the classifier calculates the angle between
+ * the last three points. After that, it calculates the difference between this angle and the
+ * previously calculated angle. The return value of the classifier is the variance of the
+ * differences from a stroke. To the differences there is artificially added value 0.0 and the
+ * difference between the first angle and PI (angles are in radians). It helps with strokes which
+ * have few points and punishes more strokes which are not smooth.
  */
-public class AnglesVarianceClassifier extends Classifier {
-    private final float INTERVAL = 10.0f;
-    private final float CLEAR_HISTORY = 500f;
-    private final float HISTORY_FACTOR = 0.9f;
-
+public class AnglesVarianceClassifier extends StrokeClassifier {
     private HashMap<Stroke, Data> mStrokeMap = new HashMap<>();
-    private float mValue;
-    private long mLastUpdate;
 
     public AnglesVarianceClassifier(ClassifierData classifierData) {
         mClassifierData = classifierData;
-        mValue = 0.0f;
-        mLastUpdate = System.currentTimeMillis();
     }
 
     @Override
@@ -65,40 +56,12 @@
                 mStrokeMap.put(stroke, new Data());
             }
             mStrokeMap.get(stroke).addPoint(stroke.getPoints().get(stroke.getPoints().size() - 1));
-
-            if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL
-                    || (action == MotionEvent.ACTION_POINTER_UP && i == event.getActionIndex())) {
-                decayValue();
-                mValue += mStrokeMap.get(stroke).getAnglesVariance();
-            }
         }
     }
 
-    /**
-     * Decreases mValue through time
-     */
-    private void decayValue() {
-        long currentTimeMillis = System.currentTimeMillis();
-        if (currentTimeMillis - mLastUpdate > CLEAR_HISTORY) {
-            mValue = 0.0f;
-        } else {
-            mValue *= Math.pow(HISTORY_FACTOR, (float) (currentTimeMillis - mLastUpdate) / INTERVAL);
-        }
-        mLastUpdate = currentTimeMillis;
-    }
-
     @Override
-    public void onSensorChanged(SensorEvent event) {
-    }
-
-    @Override
-    public float getFalseTouchEvaluation(int type) {
-        decayValue();
-        float currentValue = 0.0f;
-        for (Data data: mStrokeMap.values()) {
-            currentValue += data.getAnglesVariance();
-        }
-        return (float) (mValue + currentValue);
+    public float getFalseTouchEvaluation(int type, Stroke stroke) {
+        return mStrokeMap.get(stroke).getAnglesVariance();
     }
 
     private class Data {
@@ -150,7 +113,7 @@
         }
 
         public float getAnglesVariance() {
-            return mSumSquares / mCount + (mSum / mCount) * (mSum / mCount);
+            return mSumSquares / mCount - (mSum / mCount) * (mSum / mCount);
         }
     }
 }
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/Classifier.java b/packages/SystemUI/src/com/android/systemui/classifier/Classifier.java
index b76be14..89d20de 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/Classifier.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/Classifier.java
@@ -20,7 +20,7 @@
 import android.view.MotionEvent;
 
 /**
- * An interface for classifiers for touch and sensor events.
+ * An abstract class for classifiers for touch and sensor events.
  */
 public abstract class Classifier {
     public static final int QUICK_SETTINGS = 0;
@@ -30,6 +30,7 @@
     public static final int UNLOCK = 4;
     public static final int LEFT_AFFORDANCE = 5;
     public static final int RIGHT_AFFORDANCE = 6;
+    public static final int GENERIC = 7;
 
     /**
      * Contains all the information about touch events from which the classifier can query
@@ -47,11 +48,4 @@
      */
     public void onSensorChanged(SensorEvent event) {
     }
-
-    /**
-     * @param type the type of action for which this method is called
-     * @return a nonnegative value which is used to determine whether this a false touch. The
-     *         bigger the value the greater the chance that this a false touch.
-     */
-    public abstract float getFalseTouchEvaluation(int type);
 }
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/ClassifierData.java b/packages/SystemUI/src/com/android/systemui/classifier/ClassifierData.java
index 77b81d2..bccad4e 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/ClassifierData.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/ClassifierData.java
@@ -19,21 +19,26 @@
 import android.util.SparseArray;
 import android.view.MotionEvent;
 
+import java.util.ArrayList;
+
 /**
  * Contains data which is used to classify interaction sequences on the lockscreen. It does, for
  * example, provide information on the current touch state.
  */
 public class ClassifierData {
     private SparseArray<Stroke> mCurrentStrokes = new SparseArray<>();
+    private ArrayList<Stroke> mEndingStrokes = new ArrayList<>();
 
     public ClassifierData() {
     }
 
     public void update(MotionEvent event) {
+        mEndingStrokes.clear();
         int action = event.getActionMasked();
         if (action == MotionEvent.ACTION_DOWN) {
             mCurrentStrokes.clear();
         }
+
         for (int i = 0; i < event.getPointerCount(); i++) {
             int id = event.getPointerId(i);
             if (mCurrentStrokes.get(id) == null) {
@@ -41,10 +46,16 @@
             }
             mCurrentStrokes.get(id).addPoint(event.getX(i), event.getY(i),
                     event.getEventTimeNano());
+
+            if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL
+                    || (action == MotionEvent.ACTION_POINTER_UP && i == event.getActionIndex())) {
+                mEndingStrokes.add(getStroke(id));
+            }
         }
     }
 
     public void cleanUp(MotionEvent event) {
+        mEndingStrokes.clear();
         int action = event.getActionMasked();
         for (int i = 0; i < event.getPointerCount(); i++) {
             int id = event.getPointerId(i);
@@ -56,6 +67,13 @@
     }
 
     /**
+     * @return the list of Strokes which are ending in the recently added MotionEvent
+     */
+    public ArrayList<Stroke> getEndingStrokes() {
+        return mEndingStrokes;
+    }
+
+    /**
      * @param id the id from MotionEvent
      * @return the Stroke assigned to the id
      */
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/FalsingManager.java b/packages/SystemUI/src/com/android/systemui/classifier/FalsingManager.java
index 347273a..c68fff8 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/FalsingManager.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/FalsingManager.java
@@ -126,11 +126,10 @@
     }
 
     /**
-     * @param type the type of action for which this method is called
      * @return true if the classifier determined that this is not a human interacting with the phone
      */
-    public boolean isFalseTouch(int type) {
-        return mHumanInteractionClassifier.getFalseTouchEvaluation(type) > 0.5;
+    public boolean isFalseTouch() {
+        return mHumanInteractionClassifier.isFalseTouch();
     }
 
     @Override
@@ -189,6 +188,7 @@
     }
 
     public void onQsDown() {
+        mHumanInteractionClassifier.setType(Classifier.QUICK_SETTINGS);
         mDataCollector.onQsDown();
     }
 
@@ -197,6 +197,7 @@
     }
 
     public void onTrackingStarted() {
+        mHumanInteractionClassifier.setType(Classifier.UNLOCK);
         mDataCollector.onTrackingStarted();
     }
 
@@ -217,6 +218,7 @@
     }
 
     public void onNotificatonStartDraggingDown() {
+        mHumanInteractionClassifier.setType(Classifier.NOTIFICATION_DRAG_DOWN);
         mDataCollector.onNotificatonStartDraggingDown();
     }
 
@@ -229,6 +231,7 @@
     }
 
     public void onNotificatonStartDismissing() {
+        mHumanInteractionClassifier.setType(Classifier.NOTIFICATION_DISMISS);
         mDataCollector.onNotificatonStartDismissing();
     }
 
@@ -245,6 +248,11 @@
     }
 
     public void onAffordanceSwipingStarted(boolean rightCorner) {
+        if (rightCorner) {
+            mHumanInteractionClassifier.setType(Classifier.RIGHT_AFFORDANCE);
+        } else {
+            mHumanInteractionClassifier.setType(Classifier.LEFT_AFFORDANCE);
+        }
         mDataCollector.onAffordanceSwipingStarted(rightCorner);
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/GestureClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/GestureClassifier.java
new file mode 100644
index 0000000..e7f4c35
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/classifier/GestureClassifier.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2015 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.systemui.classifier;
+
+/**
+ * An abstract class for classifiers which classify the whole gesture (all the strokes which
+ * occurred from DOWN event to UP/CANCEL event)
+ */
+public abstract class GestureClassifier extends Classifier {
+
+    /**
+     * @param type the type of action for which this method is called
+     * @return a non-negative value which is used to determine whether the most recent gesture is a
+     *         false interaction. The bigger the value the greater the chance that this a false
+     *         interaction.
+     */
+    public abstract float getFalseTouchEvaluation(int type);
+}
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/HistoryEvaluator.java b/packages/SystemUI/src/com/android/systemui/classifier/HistoryEvaluator.java
new file mode 100644
index 0000000..b057bda
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/classifier/HistoryEvaluator.java
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2015 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.systemui.classifier;
+
+import java.util.ArrayList;
+
+/**
+ * Holds the evaluations for ended strokes and gestures. These values are decreased through time.
+ */
+public class HistoryEvaluator {
+    private static final float INTERVAL = 50.0f;
+    private static final float HISTORY_FACTOR = 0.9f;
+    private static final float EPSILON = 1e-5f;
+
+    private final ArrayList<Data> mStrokes = new ArrayList<>();
+    private final ArrayList<Data> mGestureWeights = new ArrayList<>();
+    private long mLastUpdate;
+
+    public HistoryEvaluator() {
+        mLastUpdate = System.currentTimeMillis();
+    }
+
+    public void addStroke(float evaluation) {
+        decayValue();
+        mStrokes.add(new Data(evaluation));
+    }
+
+    public void addGesture(float evaluation) {
+        decayValue();
+        mGestureWeights.add(new Data(evaluation));
+    }
+
+    /**
+     * Calculates the weighted average of strokes and adds to it the weighted average of gestures
+     */
+    public float getEvaluation() {
+        return weightedAverage(mStrokes) + weightedAverage(mGestureWeights);
+    }
+
+    private float weightedAverage(ArrayList<Data> list) {
+        float sumValue = 0.0f;
+        float sumWeight = 0.0f;
+        int size = list.size();
+        for (int i = 0; i < size; i++) {
+            Data data = list.get(i);
+            sumValue += data.evaluation * data.weight;
+            sumWeight += data.weight;
+        }
+
+        if (sumWeight == 0.0f) {
+            return 0.0f;
+        }
+
+        return sumValue / sumWeight;
+    }
+
+    private void decayValue() {
+        long currentTimeMillis = System.currentTimeMillis();
+
+        // All weights are multiplied by HISTORY_FACTOR after each INTERVAL milliseconds.
+        float factor = (float) Math.pow(HISTORY_FACTOR,
+                (float) (currentTimeMillis - mLastUpdate) / INTERVAL);
+
+        decayValue(mStrokes, factor);
+        decayValue(mGestureWeights, factor);
+        mLastUpdate = currentTimeMillis;
+    }
+
+    private void decayValue(ArrayList<Data> list, float factor) {
+        int size = list.size();
+        for (int i = 0; i < size; i++) {
+            list.get(i).weight *= factor;
+        }
+
+        // Removing evaluations with such small weights that they do not matter anymore
+        while (!list.isEmpty() && isZero(list.get(0).weight)) {
+            list.remove(0);
+        }
+    }
+
+    private boolean isZero(float x) {
+        return x <= EPSILON && x >= -EPSILON;
+    }
+
+    /**
+     * For each stroke it holds its initial value and the current weight. Initially the
+     * weight is set to 1.0
+     */
+    private class Data {
+        public float evaluation;
+        public float weight;
+
+        public Data(float evaluation) {
+            this.evaluation = evaluation;
+            weight = 1.0f;
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/HumanInteractionClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/HumanInteractionClassifier.java
index a5f6df85..86ea640 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/HumanInteractionClassifier.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/HumanInteractionClassifier.java
@@ -25,6 +25,8 @@
 import android.provider.Settings;
 import android.view.MotionEvent;
 
+import java.util.ArrayList;
+
 /**
  * An classifier trying to determine whether it is a human interacting with the phone or not.
  */
@@ -35,8 +37,14 @@
     private final Handler mHandler = new Handler();
     private final Context mContext;
 
-    private AnglesVarianceClassifier mAnglesVarianceClassifier;
+    private ArrayList<StrokeClassifier> mStrokeClassifiers = new ArrayList<>();
+    private ArrayList<GestureClassifier> mGestureClassifiers = new ArrayList<>();
+    private final int mStrokeClassifiersSize;
+    private final int mGestureClassifiersSize;
+
+    private HistoryEvaluator mHistoryEvaluator;
     private boolean mEnableClassifier = false;
+    private int mCurrentType = Classifier.GENERIC;
 
     protected final ContentObserver mSettingsObserver = new ContentObserver(mHandler) {
         @Override
@@ -48,7 +56,12 @@
     private HumanInteractionClassifier(Context context) {
         mContext = context;
         mClassifierData = new ClassifierData();
-        mAnglesVarianceClassifier = new AnglesVarianceClassifier(mClassifierData);
+        mHistoryEvaluator = new HistoryEvaluator();
+
+        mStrokeClassifiers.add(new AnglesVarianceClassifier(mClassifierData));
+
+        mStrokeClassifiersSize = mStrokeClassifiers.size();
+        mGestureClassifiersSize = mGestureClassifiers.size();
 
         mContext.getContentResolver().registerContentObserver(
                 Settings.Global.getUriFor(HIC_ENABLE), false,
@@ -71,11 +84,44 @@
                 HIC_ENABLE, 0);
     }
 
+    public void setType(int type) {
+        mCurrentType = type;
+    }
+
     @Override
     public void onTouchEvent(MotionEvent event) {
         if (mEnableClassifier) {
             mClassifierData.update(event);
-            mAnglesVarianceClassifier.onTouchEvent(event);
+
+            for (int i = 0; i < mStrokeClassifiersSize; i++) {
+                mStrokeClassifiers.get(i).onTouchEvent(event);
+            }
+
+            for (int i = 0; i < mGestureClassifiersSize; i++) {
+                mGestureClassifiers.get(i).onTouchEvent(event);
+            }
+
+            int size = mClassifierData.getEndingStrokes().size();
+            for (int i = 0; i < size; i++) {
+                Stroke stroke = mClassifierData.getEndingStrokes().get(i);
+                float evaluation = 0.0f;
+                for (int j = 0; j < mStrokeClassifiersSize; j++) {
+                    evaluation += mStrokeClassifiers.get(j).getFalseTouchEvaluation(
+                            mCurrentType, stroke);
+                }
+                mHistoryEvaluator.addStroke(evaluation);
+            }
+
+            int action = event.getActionMasked();
+            if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL) {
+                float evaluation = 0.0f;
+                for (int i = 0; i < mGestureClassifiersSize; i++) {
+                    evaluation += mGestureClassifiers.get(i).getFalseTouchEvaluation(mCurrentType);
+                }
+                mHistoryEvaluator.addGesture(evaluation);
+                setType(Classifier.GENERIC);
+            }
+
             mClassifierData.cleanUp(event);
         }
     }
@@ -84,12 +130,8 @@
     public void onSensorChanged(SensorEvent event) {
     }
 
-    @Override
-    public float getFalseTouchEvaluation(int type) {
-        if (mEnableClassifier) {
-            return mAnglesVarianceClassifier.getFalseTouchEvaluation(type);
-        }
-        return 0.0f;
+    public boolean isFalseTouch() {
+        return mHistoryEvaluator.getEvaluation() >= 5.0f;
     }
 
     public boolean isEnabled() {
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/Stroke.java b/packages/SystemUI/src/com/android/systemui/classifier/Stroke.java
index f386cbe4..8c3fdd4 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/Stroke.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/Stroke.java
@@ -19,7 +19,8 @@
 import java.util.ArrayList;
 
 /**
- * Contains data about movement traces (pointers)
+ * Contains data about a stroke (a single trace, all the events from a given id from the
+ * DOWN/POINTER_DOWN event till the UP/POINTER_UP/CANCEL event.)
  */
 public class Stroke {
     private ArrayList<Point> mPoints = new ArrayList<>();
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/StrokeClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/StrokeClassifier.java
new file mode 100644
index 0000000..d561f46
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/classifier/StrokeClassifier.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2015 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.systemui.classifier;
+
+/**
+ * An abstract class for classifiers which classify each stroke separately.
+ */
+public abstract class StrokeClassifier extends Classifier {
+
+    /**
+     * @param type the type of action for which this method is called
+     * @param stroke the stroke for which the evaluation will be calculated
+     * @return a non-negative value which is used to determine whether this a false touch. The
+     *         bigger the value the greater the chance that this a false touch.
+     */
+    public abstract float getFalseTouchEvaluation(int type, Stroke stroke);
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index 3de5001..2ea1e43 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -921,9 +921,27 @@
                 } catch (RemoteException e) {
                     Slog.w(TAG, "Failed to call onKeyguardExitResult(false)", e);
                 }
+            } else if (!isSecure()) {
+
+                // Keyguard is not secure, no need to do anything, and we don't need to reshow
+                // the Keyguard after the client releases the Keyguard lock.
+                mExternallyEnabled = true;
+                mNeedToReshowWhenReenabled = false;
+                updateInputRestricted();
+                try {
+                    callback.onKeyguardExitResult(true);
+                } catch (RemoteException e) {
+                    Slog.w(TAG, "Failed to call onKeyguardExitResult(false)", e);
+                }
             } else {
-                mExitSecureCallback = callback;
-                verifyUnlockLocked();
+
+                // Since we prevent apps from hiding the Keyguard if we are secure, this should be
+                // a no-op as well.
+                try {
+                    callback.onKeyguardExitResult(false);
+                } catch (RemoteException e) {
+                    Slog.w(TAG, "Failed to call onKeyguardExitResult(false)", e);
+                }
             }
         }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/media/RingtonePlayer.java b/packages/SystemUI/src/com/android/systemui/media/RingtonePlayer.java
index 7f68e29..f39f302 100644
--- a/packages/SystemUI/src/com/android/systemui/media/RingtonePlayer.java
+++ b/packages/SystemUI/src/com/android/systemui/media/RingtonePlayer.java
@@ -160,7 +160,7 @@
                 throw new SecurityException("Async playback only available from system UID.");
             }
             if (UserHandle.ALL.equals(user)) {
-                user = UserHandle.OWNER;
+                user = UserHandle.SYSTEM;
             }
             mAsyncPlayer.play(getContextForUser(user), uri, looping, aa);
         }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/QWifiTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/QWifiTile.java
index 87194fb..b157275 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/QWifiTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/QWifiTile.java
@@ -17,6 +17,7 @@
 package com.android.systemui.qs.tiles;
 
 import com.android.systemui.qs.QSTileView;
+import com.android.systemui.statusbar.policy.WifiIcons;
 
 /** Quick settings tile: Wifi **/
 public class QWifiTile extends WifiTile {
@@ -29,4 +30,23 @@
     public int getTileType() {
         return QSTileView.QS_TYPE_QUICK;
     }
+
+    @Override
+    protected void handleUpdateState(SignalState state, Object arg) {
+        super.handleUpdateState(state, arg);
+
+        CallbackInfo cb = (CallbackInfo) arg;
+        if (cb == null) {
+            cb = mSignalCallback.mInfo;
+        }
+        boolean wifiConnected = cb.enabled && (cb.wifiSignalIconId > 0) && (cb.enabledDesc != null);
+
+        if (state.enabled && wifiConnected) {
+            // Only show full signal here.
+            state.icon = ResourceIcon.get(WifiIcons.QS_WIFI_SIGNAL_STRENGTH[1][4]);
+        }
+        // No activity in the quick toggle.
+        state.activityIn = false;
+        state.activityOut = false;
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java
index 3295e14..91e0218 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java
@@ -48,7 +48,7 @@
     private final WifiDetailAdapter mDetailAdapter;
     private final QSTile.SignalState mStateBeforeClick = newTileState();
 
-    private final WifiSignalCallback mSignalCallback = new WifiSignalCallback();
+    protected final WifiSignalCallback mSignalCallback = new WifiSignalCallback();
 
     private final boolean mAlwaysDetail;
 
@@ -200,7 +200,7 @@
         return string;
     }
 
-    private static final class CallbackInfo {
+    protected static final class CallbackInfo {
         boolean enabled;
         boolean connected;
         int wifiSignalIconId;
@@ -223,7 +223,7 @@
         }
     }
 
-    private final class WifiSignalCallback extends SignalCallbackAdapter {
+    protected final class WifiSignalCallback extends SignalCallbackAdapter {
         final CallbackInfo mInfo = new CallbackInfo();
 
         @Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java
index 5a2fa3b..1d890d0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java
@@ -33,7 +33,7 @@
     private static final String TAG = "DozeParameters";
     private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
 
-    private static final int MAX_DURATION = 10 * 1000;
+    private static final int MAX_DURATION = 60 * 1000;
 
     private final Context mContext;
 
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 13d0e1e..3feead8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
@@ -610,7 +610,7 @@
         if (!mStatusBar.isFalsingThresholdNeeded()) {
             return false;
         }
-        if (mFalsingManager.isFalseTouch(Classifier.UNLOCK)) {
+        if (mFalsingManager.isFalseTouch()) {
             return true;
         }
         if (!mTouchAboveFalsingThreshold) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
index 394ff3f..05f6e57 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -466,7 +466,7 @@
 
         KeyguardUpdateMonitor updateMonitor = KeyguardUpdateMonitor.getInstance(mContext);
         if ((showing && !occluded) != (mLastShowing && !mLastOccluded) || mFirstUpdate) {
-            updateMonitor.sendKeyguardVisibilityChanged(showing && !occluded);
+            updateMonitor.onKeyguardVisibilityChanged(showing && !occluded);
         }
         if (bouncerShowing != mLastBouncerShowing || mFirstUpdate) {
             updateMonitor.sendKeyguardBouncerChanged(bouncerShowing);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiIcons.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiIcons.java
index c56646f..374408d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiIcons.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiIcons.java
@@ -18,7 +18,7 @@
 
 import com.android.systemui.R;
 
-class WifiIcons {
+public class WifiIcons {
     static final int[][] WIFI_SIGNAL_STRENGTH = {
             { R.drawable.stat_sys_wifi_signal_0,
               R.drawable.stat_sys_wifi_signal_1,
@@ -32,7 +32,7 @@
               R.drawable.stat_sys_wifi_signal_4_fully }
         };
 
-    static final int[][] QS_WIFI_SIGNAL_STRENGTH = {
+    public static final int[][] QS_WIFI_SIGNAL_STRENGTH = {
             { R.drawable.ic_qs_wifi_0,
               R.drawable.ic_qs_wifi_1,
               R.drawable.ic_qs_wifi_2,
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialog.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialog.java
index b2f527e..da19b06 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialog.java
@@ -89,7 +89,6 @@
 
     private static final long USER_ATTEMPT_GRACE_PERIOD = 1000;
     private static final int WAIT_FOR_RIPPLE = 200;
-    private static final int UPDATE_ANIMATION_DURATION = 80;
 
     private final Context mContext;
     private final H mHandler = new H();
@@ -353,14 +352,6 @@
         writer.println(mAccessibility.mFeedbackEnabled);
     }
 
-    private static int getImpliedLevel(SeekBar seekBar, int progress) {
-        final int m = seekBar.getMax();
-        final int n = m / 100 - 1;
-        final int level = progress == 0 ? 0
-                : progress == m ? (m / 100) : (1 + (int)((progress / (float) m) * n));
-        return level;
-    }
-
     @SuppressLint("InflateParams")
     private VolumeRow initRow(final int stream, int iconRes, int iconMuteRes, boolean important) {
         final VolumeRow row = new VolumeRow();
@@ -690,7 +681,7 @@
                 : false;
 
         // update slider max
-        final int max = ss.levelMax * 100;
+        final int max = ss.levelMax;
         if (max != row.slider.getMax()) {
             row.slider.setMax(max);
         }
@@ -777,8 +768,7 @@
         if (row.tracking) {
             return;  // don't update if user is sliding
         }
-        final int progress = row.slider.getProgress();
-        final int level = getImpliedLevel(row.slider, progress);
+        final int level = row.slider.getProgress();
         final boolean rowVisible = row.view.getVisibility() == View.VISIBLE;
         final boolean inGracePeriod = (SystemClock.uptimeMillis() - row.userAttempt)
                 < USER_ATTEMPT_GRACE_PERIOD;
@@ -794,33 +784,7 @@
                 return;  // don't clamp if visible
             }
         }
-        final int newProgress = vlevel * 100;
-        if (progress != newProgress) {
-            if (mShowing && rowVisible) {
-                // animate!
-                if (row.anim != null && row.anim.isRunning()
-                        && row.animTargetProgress == newProgress) {
-                    return;  // already animating to the target progress
-                }
-                // start/update animation
-                if (row.anim == null) {
-                    row.anim = ObjectAnimator.ofInt(row.slider, "progress", progress, newProgress);
-                    row.anim.setInterpolator(new DecelerateInterpolator());
-                } else {
-                    row.anim.cancel();
-                    row.anim.setIntValues(progress, newProgress);
-                }
-                row.animTargetProgress = newProgress;
-                row.anim.setDuration(UPDATE_ANIMATION_DURATION);
-                row.anim.start();
-            } else {
-                // update slider directly to clamped value
-                if (row.anim != null) {
-                    row.anim.cancel();
-                }
-                row.slider.setProgress(newProgress);
-            }
-        }
+        row.slider.setProgress(vlevel, true);
     }
 
     private void recheckH(VolumeRow row) {
@@ -1025,20 +989,19 @@
                     + " onProgressChanged " + progress + " fromUser=" + fromUser);
             if (!fromUser) return;
             if (mRow.ss.levelMin > 0) {
-                final int minProgress = mRow.ss.levelMin * 100;
+                final int minProgress = mRow.ss.levelMin;
                 if (progress < minProgress) {
                     seekBar.setProgress(minProgress);
                     progress = minProgress;
                 }
             }
-            final int userLevel = getImpliedLevel(seekBar, progress);
-            if (mRow.ss.level != userLevel || mRow.ss.muted && userLevel > 0) {
+            if (mRow.ss.level != progress || mRow.ss.muted && progress > 0) {
                 mRow.userAttempt = SystemClock.uptimeMillis();
-                if (mRow.requestedLevel != userLevel) {
-                    mController.setStreamVolume(mRow.stream, userLevel);
-                    mRow.requestedLevel = userLevel;
+                if (mRow.requestedLevel != progress) {
+                    mController.setStreamVolume(mRow.stream, progress);
+                    mRow.requestedLevel = progress;
                     Events.writeEvent(mContext, Events.EVENT_TOUCH_LEVEL_CHANGED, mRow.stream,
-                            userLevel);
+                            progress);
                 }
             }
         }
@@ -1055,7 +1018,7 @@
             if (D.BUG) Log.d(TAG, "onStopTrackingTouch"+ " " + mRow.stream);
             mRow.tracking = false;
             mRow.userAttempt = SystemClock.uptimeMillis();
-            int userLevel = getImpliedLevel(seekBar, seekBar.getProgress());
+            final int userLevel = seekBar.getProgress();
             Events.writeEvent(mContext, Events.EVENT_TOUCH_LEVEL_DONE, mRow.stream, userLevel);
             if (mRow.ss.level != userLevel) {
                 mHandler.sendMessageDelayed(mHandler.obtainMessage(H.RECHECK, mRow),
@@ -1137,8 +1100,6 @@
         private int iconState;  // from Events
         private boolean cachedShowHeaders = VolumePrefs.DEFAULT_SHOW_HEADERS;
         private int cachedExpandButtonRes;
-        private ObjectAnimator anim;  // slider progress animation for non-touch-related updates
-        private int animTargetProgress;
         private int lastAudibleLevel = 1;
     }
 
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index 91c3d48..ff2a2ee 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -967,8 +967,8 @@
 
         List<ResolveInfo> installedServices = mPackageManager.queryIntentServicesAsUser(
                 new Intent(AccessibilityService.SERVICE_INTERFACE),
-                PackageManager.GET_SERVICES 
-                  | PackageManager.GET_META_DATA 
+                PackageManager.GET_SERVICES
+                  | PackageManager.GET_META_DATA
                   | PackageManager.GET_DISABLED_UNTIL_USED_COMPONENTS,
                 mCurrentUserId);
 
@@ -3217,7 +3217,8 @@
                 case WindowManager.LayoutParams.TYPE_SYSTEM_ALERT:
                 case WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG:
                 case WindowManager.LayoutParams.TYPE_SYSTEM_ERROR:
-                case WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY: {
+                case WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY:
+                case WindowManager.LayoutParams.TYPE_DOCK_DIVIDER: {
                     return AccessibilityWindowInfo.TYPE_SYSTEM;
                 }
 
diff --git a/services/core/java/com/android/server/DeviceIdleController.java b/services/core/java/com/android/server/DeviceIdleController.java
index 2f4ad3f..0c6eb40 100644
--- a/services/core/java/com/android/server/DeviceIdleController.java
+++ b/services/core/java/com/android/server/DeviceIdleController.java
@@ -1523,13 +1523,13 @@
     private void reportPowerSaveWhitelistChangedLocked() {
         Intent intent = new Intent(PowerManager.ACTION_POWER_SAVE_WHITELIST_CHANGED);
         intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
-        getContext().sendBroadcastAsUser(intent, UserHandle.OWNER);
+        getContext().sendBroadcastAsUser(intent, UserHandle.SYSTEM);
     }
 
     private void reportTempWhitelistChangedLocked() {
         Intent intent = new Intent(PowerManager.ACTION_POWER_SAVE_TEMP_WHITELIST_CHANGED);
         intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
-        getContext().sendBroadcastAsUser(intent, UserHandle.OWNER);
+        getContext().sendBroadcastAsUser(intent, UserHandle.SYSTEM);
     }
 
     void readConfigFileLocked() {
diff --git a/services/core/java/com/android/server/DropBoxManagerService.java b/services/core/java/com/android/server/DropBoxManagerService.java
index b9db89ec..2454487 100644
--- a/services/core/java/com/android/server/DropBoxManagerService.java
+++ b/services/core/java/com/android/server/DropBoxManagerService.java
@@ -170,7 +170,7 @@
             @Override
             public void handleMessage(Message msg) {
                 if (msg.what == MSG_SEND_BROADCAST) {
-                    getContext().sendBroadcastAsUser((Intent)msg.obj, UserHandle.OWNER,
+                    getContext().sendBroadcastAsUser((Intent)msg.obj, UserHandle.SYSTEM,
                             android.Manifest.permission.READ_LOGS);
                 }
             }
@@ -488,7 +488,7 @@
 
     ///////////////////////////////////////////////////////////////////////////
 
-    /** Chronologically sorted list of {@link #EntryFile} */
+    /** Chronologically sorted list of {@link EntryFile} */
     private static final class FileList implements Comparable<FileList> {
         public int blocks = 0;
         public final TreeSet<EntryFile> contents = new TreeSet<EntryFile>();
@@ -613,7 +613,7 @@
 
         /**
          * Creates a EntryFile object with only a timestamp for comparison purposes.
-         * @param timestampMillis to compare with.
+         * @param millis to compare with.
          */
         public EntryFile(long millis) {
             this.tag = null;
diff --git a/services/core/java/com/android/server/GestureLauncherService.java b/services/core/java/com/android/server/GestureLauncherService.java
index bd7d4b2..7c85001 100644
--- a/services/core/java/com/android/server/GestureLauncherService.java
+++ b/services/core/java/com/android/server/GestureLauncherService.java
@@ -101,8 +101,7 @@
      * Whether camera double tap power button gesture is currently enabled;
      */
     private boolean mCameraDoubleTapPowerEnabled;
-    private long mLastPowerDownWhileNonInteractive;
-    private long mLastPowerDownWhileInteractive;
+    private long mLastPowerDown;
 
     public GestureLauncherService(Context context) {
         super(context);
@@ -252,35 +251,32 @@
     public boolean interceptPowerKeyDown(KeyEvent event, boolean interactive) {
         boolean launched = false;
         boolean intercept = false;
+        long doubleTapInterval;
         synchronized (this) {
-            if (!mCameraDoubleTapPowerEnabled) {
-                mLastPowerDownWhileNonInteractive = 0;
-                mLastPowerDownWhileInteractive = 0;
-                return false;
-            }
-            if (event.getEventTime() - mLastPowerDownWhileNonInteractive
-                    < CAMERA_POWER_DOUBLE_TAP_TIME_MS) {
+            doubleTapInterval = event.getEventTime() - mLastPowerDown;
+            if (mCameraDoubleTapPowerEnabled
+                    && doubleTapInterval < CAMERA_POWER_DOUBLE_TAP_TIME_MS) {
                 launched = true;
-                intercept = true;
-            } else if (event.getEventTime() - mLastPowerDownWhileInteractive
-                    < CAMERA_POWER_DOUBLE_TAP_TIME_MS) {
-                launched = true;
+                intercept = interactive;
             }
-            mLastPowerDownWhileNonInteractive = interactive ? 0 : event.getEventTime();
-            mLastPowerDownWhileInteractive = interactive ? event.getEventTime() : 0;
+            mLastPowerDown = event.getEventTime();
         }
         if (launched) {
             Slog.i(TAG, "Power button double tap gesture detected, launching camera.");
-            launched = handleCameraLaunchGesture(false /* useWakelock */,
-                    MetricsLogger.ACTION_DOUBLE_TAP_POWER_CAMERA_GESTURE);
+            launched = handleCameraLaunchGesture(false /* useWakelock */);
+            if (launched) {
+                MetricsLogger.action(mContext, MetricsLogger.ACTION_DOUBLE_TAP_POWER_CAMERA_GESTURE,
+                        (int) doubleTapInterval);
+            }
         }
+        MetricsLogger.histogram(mContext, "power_double_tap_interval", (int) doubleTapInterval);
         return intercept && launched;
     }
 
     /**
      * @return true if camera was launched, false otherwise.
      */
-    private boolean handleCameraLaunchGesture(boolean useWakelock, int logCategory) {
+    private boolean handleCameraLaunchGesture(boolean useWakelock) {
         boolean userSetupComplete = Settings.Secure.getInt(mContext.getContentResolver(),
                 Settings.Secure.USER_SETUP_COMPLETE, 0) != 0;
         if (!userSetupComplete) {
@@ -300,7 +296,6 @@
         StatusBarManagerInternal service = LocalServices.getService(
                 StatusBarManagerInternal.class);
         service.onCameraLaunchGestureDetected();
-        MetricsLogger.action(mContext, logCategory);
         return true;
     }
 
@@ -339,8 +334,8 @@
                     Slog.d(TAG, String.format("Received a camera launch event: " +
                             "values=[%.4f, %.4f, %.4f].", values[0], values[1], values[2]));
                 }
-                if (handleCameraLaunchGesture(true /* useWakelock */,
-                        MetricsLogger.ACTION_WIGGLE_CAMERA_GESTURE)) {
+                if (handleCameraLaunchGesture(true /* useWakelock */)) {
+                    MetricsLogger.action(mContext, MetricsLogger.ACTION_WIGGLE_CAMERA_GESTURE);
                     trackCameraLaunchEvent(event);
                 }
                 return;
diff --git a/services/core/java/com/android/server/LocationManagerService.java b/services/core/java/com/android/server/LocationManagerService.java
index 885c765..087ddd6 100644
--- a/services/core/java/com/android/server/LocationManagerService.java
+++ b/services/core/java/com/android/server/LocationManagerService.java
@@ -211,8 +211,8 @@
             new ArrayList<LocationProviderProxy>();
 
     // current active user on the device - other users are denied location data
-    private int mCurrentUserId = UserHandle.USER_OWNER;
-    private int[] mCurrentUserProfiles = new int[] { UserHandle.USER_OWNER };
+    private int mCurrentUserId = UserHandle.USER_SYSTEM;
+    private int[] mCurrentUserProfiles = new int[] { UserHandle.USER_SYSTEM };
 
     public LocationManagerService(Context context) {
         super();
@@ -1832,7 +1832,8 @@
 
         // geo-fence manager uses the public location API, need to clear identity
         int uid = Binder.getCallingUid();
-        if (UserHandle.getUserId(uid) != UserHandle.USER_OWNER) {
+        // TODO: http://b/23822629
+        if (UserHandle.getUserId(uid) != UserHandle.USER_SYSTEM) {
             // temporary measure until geofences work for secondary users
             Log.w(TAG, "proximity alerts are currently available only to the primary user");
             return;
diff --git a/services/core/java/com/android/server/MountService.java b/services/core/java/com/android/server/MountService.java
index 540f8cb..c3d32c2 100644
--- a/services/core/java/com/android/server/MountService.java
+++ b/services/core/java/com/android/server/MountService.java
@@ -2981,7 +2981,7 @@
 
             Intent service = new Intent().setComponent(DEFAULT_CONTAINER_COMPONENT);
             if (mContext.bindServiceAsUser(service, mDefContainerConn, Context.BIND_AUTO_CREATE,
-                    UserHandle.OWNER)) {
+                    UserHandle.SYSTEM)) {
                 mBound = true;
                 return true;
             }
@@ -3014,7 +3014,6 @@
                     Slog.w(TAG, "Failed to invoke remote methods on default container service. Giving up");
                     mObbActionHandler.sendEmptyMessage(OBB_MCS_UNBIND);
                     handleError();
-                    return;
                 } else {
                     handleExecute();
                     if (DEBUG_OBB)
@@ -3176,8 +3175,6 @@
             waitForReady();
             warnOnNotMounted();
 
-            final ObbInfo obbInfo = getObbInfo();
-
             final ObbState existingState;
             synchronized (mObbMounts) {
                 existingState = mObbPathToStateMap.get(mObbState.rawPath);
diff --git a/services/core/java/com/android/server/NetworkScoreService.java b/services/core/java/com/android/server/NetworkScoreService.java
index b05a690..b984e19 100644
--- a/services/core/java/com/android/server/NetworkScoreService.java
+++ b/services/core/java/com/android/server/NetworkScoreService.java
@@ -133,8 +133,8 @@
                 filter.addDataSchemeSpecificPart(scorer.mPackageName,
                         PatternMatcher.PATTERN_LITERAL);
                 mReceiver = new ScorerChangedReceiver(scorer.mPackageName);
-                // TODO: Need to update when we support per-user scorers.
-                mContext.registerReceiverAsUser(mReceiver, UserHandle.OWNER, filter, null, null);
+                // TODO: Need to update when we support per-user scorers. http://b/23422763
+                mContext.registerReceiverAsUser(mReceiver, UserHandle.SYSTEM, filter, null, null);
                 if (Log.isLoggable(TAG, Log.VERBOSE)) {
                     Log.v(TAG, "Registered receiver for " + scorer.mPackageName);
                 }
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 6728b83..07a7af4 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -33,6 +33,7 @@
 import static com.android.server.am.ActivityManagerDebugConfig.*;
 import static com.android.server.am.ActivityStackSupervisor.FORCE_FOCUS;
 import static com.android.server.am.ActivityStackSupervisor.ON_TOP;
+import static com.android.server.am.ActivityStackSupervisor.PRESERVE_WINDOWS;
 import static com.android.server.am.TaskRecord.INVALID_TASK_ID;
 import static com.android.server.am.TaskRecord.LOCK_TASK_AUTH_DONT_LOCK;
 import static com.android.server.am.TaskRecord.LOCK_TASK_AUTH_LAUNCHABLE_PRIV;
@@ -2020,7 +2021,7 @@
                                 intent, PendingIntent.FLAG_CANCEL_CURRENT, null,
                                 new UserHandle(userId)))
                         .setDeleteIntent(PendingIntent.getBroadcastAsUser(mContext, 0,
-                                deleteIntent, 0, UserHandle.OWNER))
+                                deleteIntent, 0, UserHandle.SYSTEM))
                         .build();
 
                 try {
@@ -2378,8 +2379,8 @@
         mGrantFile = new AtomicFile(new File(systemDir, "urigrants.xml"));
 
         // User 0 is the first and only user that runs at boot.
-        mStartedUsers.put(UserHandle.USER_OWNER, new UserState(UserHandle.OWNER, true));
-        mUserLru.add(UserHandle.USER_OWNER);
+        mStartedUsers.put(UserHandle.USER_SYSTEM, new UserState(UserHandle.SYSTEM, true));
+        mUserLru.add(UserHandle.USER_SYSTEM);
         updateStartedUserArrayLocked();
 
         GL_ES_VERSION = SystemProperties.getInt("ro.opengles.version",
@@ -4626,7 +4627,7 @@
             // is hosted by the process...  then make sure all visible
             // activities are running, taking care of restarting this
             // process.
-            mStackSupervisor.ensureActivitiesVisibleLocked(null, 0);
+            mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
         }
     }
 
@@ -5646,7 +5647,7 @@
                 try {
                     // Entire package setting changed
                     enabled = pm.getApplicationEnabledSetting(packageName,
-                            (userId != UserHandle.USER_ALL) ? userId : UserHandle.USER_OWNER);
+                            (userId != UserHandle.USER_ALL) ? userId : UserHandle.USER_SYSTEM);
                 } catch (Exception e) {
                     // No such package/component; probably racing with uninstall.  In any
                     // event it means we have nothing further to do here.
@@ -5664,7 +5665,7 @@
                 try {
                     enabled = pm.getComponentEnabledSetting(
                             new ComponentName(packageName, changedClass),
-                            (userId != UserHandle.USER_ALL) ? userId : UserHandle.USER_OWNER);
+                            (userId != UserHandle.USER_ALL) ? userId : UserHandle.USER_SYSTEM);
                 } catch (Exception e) {
                     // As above, probably racing with uninstall.
                     return;
@@ -8664,7 +8665,7 @@
             }
             if (task.mResizeable != resizeable) {
                 task.mResizeable = resizeable;
-                mStackSupervisor.ensureActivitiesVisibleLocked(null, 0);
+                mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
                 mStackSupervisor.resumeTopActivitiesLocked();
             }
         }
@@ -9108,7 +9109,7 @@
         long ident = Binder.clearCallingIdentity();
         try {
             synchronized (this) {
-                mStackSupervisor.resizeStackLocked(stackId, bounds);
+                mStackSupervisor.resizeStackLocked(stackId, bounds, !PRESERVE_WINDOWS);
             }
         } finally {
             Binder.restoreCallingIdentity(ident);
@@ -9391,7 +9392,7 @@
                     (ProviderInfo)providers.get(i);
                 boolean singleton = isSingleton(cpi.processName, cpi.applicationInfo,
                         cpi.name, cpi.flags);
-                if (singleton && UserHandle.getUserId(app.uid) != UserHandle.USER_OWNER) {
+                if (singleton && UserHandle.getUserId(app.uid) != UserHandle.USER_SYSTEM) {
                     // This is a singleton provider, but a user besides the
                     // default user is asking to initialize a process it runs
                     // in...  well, no, it doesn't actually run in this process,
@@ -9613,7 +9614,7 @@
         }
     }
 
-    private final ContentProviderHolder getContentProviderImpl(IApplicationThread caller,
+    private ContentProviderHolder getContentProviderImpl(IApplicationThread caller,
             String name, IBinder token, boolean stable, int userId) {
         ContentProviderRecord cpr;
         ContentProviderConnection conn = null;
@@ -9641,14 +9642,14 @@
             cpr = mProviderMap.getProviderByName(name, userId);
             // If that didn't work, check if it exists for user 0 and then
             // verify that it's a singleton provider before using it.
-            if (cpr == null && userId != UserHandle.USER_OWNER) {
-                cpr = mProviderMap.getProviderByName(name, UserHandle.USER_OWNER);
+            if (cpr == null && userId != UserHandle.USER_SYSTEM) {
+                cpr = mProviderMap.getProviderByName(name, UserHandle.USER_SYSTEM);
                 if (cpr != null) {
                     cpi = cpr.info;
                     if (isSingleton(cpi.processName, cpi.applicationInfo,
                             cpi.name, cpi.flags)
                             && isValidSingletonCall(r.uid, cpi.applicationInfo.uid)) {
-                        userId = UserHandle.USER_OWNER;
+                        userId = UserHandle.USER_SYSTEM;
                         checkCrossUser = false;
                     } else {
                         cpr = null;
@@ -9741,7 +9742,6 @@
                 Binder.restoreCallingIdentity(origId);
             }
 
-            boolean singleton;
             if (!providerRunning) {
                 try {
                     checkTime(startTime, "getContentProviderImpl: before resolveContentProvider");
@@ -9758,11 +9758,11 @@
                 // (it's a call within the same user || the provider is a
                 // privileged app)
                 // Then allow connecting to the singleton provider
-                singleton = isSingleton(cpi.processName, cpi.applicationInfo,
+                boolean singleton = isSingleton(cpi.processName, cpi.applicationInfo,
                         cpi.name, cpi.flags)
                         && isValidSingletonCall(r.uid, cpi.applicationInfo.uid);
                 if (singleton) {
-                    userId = UserHandle.USER_OWNER;
+                    userId = UserHandle.USER_SYSTEM;
                 }
                 cpi.applicationInfo = getAppInfoForUser(cpi.applicationInfo, userId);
                 checkTime(startTime, "getContentProviderImpl: got app info for user");
@@ -10365,7 +10365,7 @@
         }
         final ProcessRecord r = new ProcessRecord(stats, info, proc, uid);
         if (!mBooted && !mBooting
-                && userId == UserHandle.USER_OWNER
+                && userId == UserHandle.USER_SYSTEM
                 && (info.flags & PERSISTENT_MASK) == PERSISTENT_MASK) {
             r.persistent = true;
         }
@@ -11188,7 +11188,7 @@
                 final boolean translucentChanged = r.changeWindowTranslucency(true);
                 if (translucentChanged) {
                     r.task.stack.releaseBackgroundResources(r);
-                    mStackSupervisor.ensureActivitiesVisibleLocked(null, 0);
+                    mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
                 }
                 mWindowManager.setAppFullscreen(token, true);
                 return translucentChanged;
@@ -11216,7 +11216,7 @@
                 if (translucentChanged) {
                     r.task.stack.convertActivityToTranslucent(r);
                 }
-                mStackSupervisor.ensureActivitiesVisibleLocked(null, 0);
+                mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
                 mWindowManager.setAppFullscreen(token, false);
                 return translucentChanged;
             }
@@ -11822,12 +11822,12 @@
     }
 
     private boolean deliverPreBootCompleted(final Runnable onFinishCallback,
-            ArrayList<ComponentName> doneReceivers, int userId) {
+            ArrayList<ComponentName> doneReceivers) {
         Intent intent = new Intent(Intent.ACTION_PRE_BOOT_COMPLETED);
         List<ResolveInfo> ris = null;
         try {
             ris = AppGlobals.getPackageManager().queryIntentReceivers(
-                    intent, null, 0, userId);
+                    intent, null, 0, UserHandle.USER_SYSTEM);
         } catch (RemoteException e) {
         }
         if (ris == null) {
@@ -11841,22 +11841,18 @@
         }
         intent.addFlags(Intent.FLAG_RECEIVER_BOOT_UPGRADE);
 
-        // For User 0, load the version number. When delivering to a new user, deliver
-        // to all receivers.
-        if (userId == UserHandle.USER_OWNER) {
-            ArrayList<ComponentName> lastDoneReceivers = readLastDonePreBootReceivers();
-            for (int i=0; i<ris.size(); i++) {
-                ActivityInfo ai = ris.get(i).activityInfo;
-                ComponentName comp = new ComponentName(ai.packageName, ai.name);
-                if (lastDoneReceivers.contains(comp)) {
-                    // We already did the pre boot receiver for this app with the current
-                    // platform version, so don't do it again...
-                    ris.remove(i);
-                    i--;
-                    // ...however, do keep it as one that has been done, so we don't
-                    // forget about it when rewriting the file of last done receivers.
-                    doneReceivers.add(comp);
-                }
+        ArrayList<ComponentName> lastDoneReceivers = readLastDonePreBootReceivers();
+        for (int i=0; i<ris.size(); i++) {
+            ActivityInfo ai = ris.get(i).activityInfo;
+            ComponentName comp = new ComponentName(ai.packageName, ai.name);
+            if (lastDoneReceivers.contains(comp)) {
+                // We already did the pre boot receiver for this app with the current
+                // platform version, so don't do it again...
+                ris.remove(i);
+                i--;
+                // ...however, do keep it as one that has been done, so we don't
+                // forget about it when rewriting the file of last done receivers.
+                doneReceivers.add(comp);
             }
         }
 
@@ -11864,9 +11860,8 @@
             return false;
         }
 
-        // If primary user, send broadcast to all available users, else just to userId
-        final int[] users = userId == UserHandle.USER_OWNER ? getUsersLocked()
-                : new int[] { userId };
+        // TODO: can we still do this with per user encryption?
+        final int[] users = getUsersLocked();
         if (users.length <= 0) {
             return false;
         }
@@ -11917,7 +11912,7 @@
                         writeLastDonePreBootReceivers(doneReceivers);
                         systemReady(goingCallback);
                     }
-                }, doneReceivers, UserHandle.USER_OWNER);
+                }, doneReceivers);
 
                 if (mWaitingUpdate) {
                     return;
@@ -17699,7 +17694,8 @@
                 kept = mainStack.ensureActivityConfigurationLocked(starting, changes, false);
                 // And we need to make sure at this point that all other activities
                 // are made visible with the correct configuration.
-                mStackSupervisor.ensureActivitiesVisibleLocked(starting, changes);
+                mStackSupervisor.ensureActivitiesVisibleLocked(starting, changes,
+                        !PRESERVE_WINDOWS);
             }
         }
 
@@ -20170,7 +20166,7 @@
                 }
 
                 if ((userInfo.flags&UserInfo.FLAG_INITIALIZED) == 0) {
-                    if (userId != UserHandle.USER_OWNER) {
+                    if (userId != UserHandle.USER_SYSTEM) {
                         Intent intent = new Intent(Intent.ACTION_USER_INITIALIZE);
                         intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
                         broadcastIntentLocked(null, null, intent, null,
@@ -20408,7 +20404,7 @@
             for (int i = 0; i < num; i++) {
                 Integer oldUserId = mUserLru.get(i);
                 UserState oldUss = mStartedUsers.get(oldUserId);
-                if (oldUserId == UserHandle.USER_OWNER || oldUserId == mCurrentUserId
+                if (oldUserId == UserHandle.USER_SYSTEM || oldUserId == mCurrentUserId
                         || oldUss.mState == UserState.STATE_STOPPING
                         || oldUss.mState == UserState.STATE_SHUTDOWN) {
                     continue;
@@ -20493,8 +20489,12 @@
                     i++;
                     continue;
                 }
-                if (oldUserId == UserHandle.USER_OWNER || oldUserId == mCurrentUserId) {
-                    // Owner and current can't be stopped, but count as running.
+                if (oldUserId == UserHandle.USER_SYSTEM || oldUserId == mCurrentUserId) {
+                    // Owner/System user and current user can't be stopped. We count it as running
+                    // when it is not a pure system user.
+                    if (UserInfo.isSystemOnly(oldUserId)) {
+                        num--;
+                    }
                     i++;
                     continue;
                 }
@@ -20517,8 +20517,8 @@
             Slog.w(TAG, msg);
             throw new SecurityException(msg);
         }
-        if (userId < 0 || userId == UserHandle.USER_OWNER) {
-            throw new IllegalArgumentException("Can't stop primary user " + userId);
+        if (userId < 0 || userId == UserHandle.USER_SYSTEM) {
+            throw new IllegalArgumentException("Can't stop system user " + userId);
         }
         enforceShellRestriction(UserManager.DISALLOW_DEBUGGING_FEATURES, userId);
         synchronized (this) {
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index b6d5d896..9809c2e 100644
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -30,6 +30,7 @@
 import static com.android.server.am.ActivityRecord.APPLICATION_ACTIVITY_TYPE;
 
 import static com.android.server.am.ActivityStackSupervisor.MOVING;
+import static com.android.server.am.ActivityStackSupervisor.PRESERVE_WINDOWS;
 
 import android.graphics.Rect;
 import android.util.ArraySet;
@@ -809,7 +810,7 @@
     }
 
     void goToSleep() {
-        ensureActivitiesVisibleLocked(null, 0);
+        ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
 
         // Make sure any stopped but visible activities are now sleeping.
         // This ensures that the activity's onStop() is called.
@@ -1329,17 +1330,28 @@
             return topHomeActivity == null || !topHomeActivity.isHomeActivity();
         }
 
-        if (focusedStackId == DOCKED_STACK_ID
-                && stackIndex == (mStacks.indexOf(focusedStack) - 1)) {
+        final int belowFocusedIndex = mStacks.indexOf(focusedStack) - 1;
+        if (focusedStackId == DOCKED_STACK_ID && stackIndex == belowFocusedIndex) {
             // Stacks directly behind the docked stack are always visible.
             return true;
         }
 
-        if (mStackId == HOME_STACK_ID && focusedStackId == FULLSCREEN_WORKSPACE_STACK_ID) {
-            // Home stack is always visible behind the fullscreen stack with a translucent activity.
-            // This is done so that the home stack can act as a background to the translucent
-            // activity.
-            return hasTranslucentActivity(focusedStack);
+        if (focusedStackId == FULLSCREEN_WORKSPACE_STACK_ID
+                && hasTranslucentActivity(focusedStack)) {
+            // Stacks behind the fullscreen stack with a translucent activity are always
+            // visible so they can act as a backdrop to the translucent activity.
+            // For example, dialog activities
+            if (stackIndex == belowFocusedIndex) {
+                return true;
+            }
+            if (belowFocusedIndex >= 0) {
+                final ActivityStack stack = mStacks.get(belowFocusedIndex);
+                if (stack.mStackId == DOCKED_STACK_ID && stackIndex == (belowFocusedIndex - 1)) {
+                    // The stack behind the docked stack is also visible so we can have a complete
+                    // backdrop to the translucent activity when the docked stack is up.
+                    return true;
+                }
+            }
         }
 
         if (mStackId >= FIRST_STATIC_STACK_ID && mStackId <= LAST_STATIC_STACK_ID) {
@@ -1373,7 +1385,8 @@
      * Make sure that all activities that need to be visible (that is, they
      * currently can be seen by the user) actually are.
      */
-    final void ensureActivitiesVisibleLocked(ActivityRecord starting, int configChanges) {
+    final void ensureActivitiesVisibleLocked(ActivityRecord starting, int configChanges,
+            boolean preserveWindows) {
         ActivityRecord top = topRunningActivityLocked(null);
         if (top == null) {
             return;
@@ -1422,7 +1435,7 @@
                     // First: if this is not the current activity being started, make
                     // sure it matches the current configuration.
                     if (r != starting) {
-                        ensureActivityConfigurationLocked(r, 0, false);
+                        ensureActivityConfigurationLocked(r, 0, preserveWindows);
                     }
 
                     if (r.app == null || r.app.thread == null) {
@@ -2336,7 +2349,7 @@
                 // Don't do a starting window for mLaunchTaskBehind. More importantly make sure we
                 // tell WindowManager that r is visible even though it is at the back of the stack.
                 mWindowManager.setAppVisibility(r.appToken, true);
-                ensureActivitiesVisibleLocked(null, 0);
+                ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
             } else if (SHOW_APP_STARTING_PREVIEW && doShow) {
                 // Figure out if we are transitioning from another activity that is
                 // "has the same starting icon" as the next one.  This allows the
@@ -4018,6 +4031,11 @@
             return true;
         }
 
+        if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION,
+                "Configuration changes for " + r + " ; taskChanges="
+                        + Configuration.configurationDiffToString(taskChanges) + ", allChanges="
+                        + Configuration.configurationDiffToString(changes));
+
         // If the activity isn't currently running, just leave the new
         // configuration and it will pick that up next time it starts.
         if (r.app == null || r.app.thread == null) {
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index 7f7638b..17a4472 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -175,6 +175,9 @@
     // should be created if it doesn't exist already.
     private static final boolean CREATE_IF_NEEDED = true;
 
+    // Used to indicate that windows of activities should be preserved during the resize.
+    static final boolean PRESERVE_WINDOWS = true;
+
     // Used to indicate if an object (e.g. task) should be moved/created
     // at the top of its container (e.g. stack).
     static final boolean ON_TOP = true;
@@ -189,6 +192,7 @@
     // Activity actions an app cannot start if it uses a permission which is not granted.
     private static final ArrayMap<String, String> ACTION_TO_RUNTIME_PERMISSION =
             new ArrayMap<>();
+
     static {
         ACTION_TO_RUNTIME_PERMISSION.put(MediaStore.ACTION_IMAGE_CAPTURE,
                 Manifest.permission.CAMERA);
@@ -651,7 +655,7 @@
             }
         }
         if (!didSomething) {
-            ensureActivitiesVisibleLocked(null, 0);
+            ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
         }
         return didSomething;
     }
@@ -1794,8 +1798,11 @@
                 return container.mStack;
             }
 
-            if (mFocusedStack != mHomeStack && (!newTask ||
-                    mFocusedStack.mActivityContainer.isEligibleForNewTasks())) {
+            // The fullscreen stack is the only stack that can contain any task regardless of if
+            // the task is resizeable or not. So, we let the task go in the fullscreen task if it
+            // is the focus stack.
+            if (mFocusedStack.mStackId == FULLSCREEN_WORKSPACE_STACK_ID
+                    && (!newTask || mFocusedStack.mActivityContainer.isEligibleForNewTasks())) {
                 if (DEBUG_FOCUS || DEBUG_STACK) Slog.d(TAG_FOCUS,
                         "computeStackFocus: Have a focused stack=" + mFocusedStack);
                 return mFocusedStack;
@@ -1814,7 +1821,7 @@
             }
 
             // If there is no suitable dynamic stack then we figure out which static stack to use.
-            int stackId = task != null ? task.getLaunchStackId() :
+            final int stackId = task != null ? task.getLaunchStackId() :
                         bounds != null ? FREEFORM_WORKSPACE_STACK_ID :
                                          FULLSCREEN_WORKSPACE_STACK_ID;
             stack = getStack(stackId, CREATE_IF_NEEDED, ON_TOP);
@@ -2620,7 +2627,7 @@
                 }
                 mLaunchingActivity.release();
             }
-            ensureActivitiesVisibleLocked(null, 0);
+            ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
         }
 
         // Atomically retrieve all of the other things to do.
@@ -2954,7 +2961,7 @@
         }
     }
 
-    void resizeStackLocked(int stackId, Rect bounds) {
+    void resizeStackLocked(int stackId, Rect bounds, boolean preserveWindows) {
         final ActivityStack stack = getStack(stackId);
         if (stack == null) {
             Slog.w(TAG, "resizeStack: stackId " + stackId + " not found.");
@@ -2993,7 +3000,7 @@
                 // docked stack tasks to the fullscreen stack.
                 for (int i = FIRST_STATIC_STACK_ID; i <= LAST_STATIC_STACK_ID; i++) {
                     if (i != DOCKED_STACK_ID) {
-                        resizeStackLocked(i, null);
+                        resizeStackLocked(i, null, preserveWindows);
                     }
                 }
 
@@ -3024,20 +3031,22 @@
                             tempRect.right -= leftChange;
                             tempRect.top -= bottomChange;
                             tempRect.bottom -= topChange;
-                            resizeStackLocked(i, tempRect);
+                            resizeStackLocked(i, tempRect, PRESERVE_WINDOWS);
                         }
                     }
                 }
-
             }
+            // Since we are resizing the stack, all other operations should strive to preserve
+            // windows.
+            preserveWindows = true;
         }
         stack.setBounds(bounds);
 
         if (r != null) {
-            final boolean updated = stack.ensureActivityConfigurationLocked(r, 0, false);
+            final boolean updated = stack.ensureActivityConfigurationLocked(r, 0, preserveWindows);
             // And we need to make sure at this point that all other activities
             // are made visible with the correct configuration.
-            ensureActivitiesVisibleLocked(r, 0);
+            ensureActivitiesVisibleLocked(r, 0, preserveWindows);
             if (!updated) {
                 resumeTopActivitiesLocked(stack, null, null);
             }
@@ -3098,7 +3107,7 @@
                 final boolean preserveWindow = resizedByUser && !changedStacks;
                 kept = stack.ensureActivityConfigurationLocked(r, 0, preserveWindow);
                 // All other activities must be made visible with their correct configuration.
-                ensureActivitiesVisibleLocked(r, 0);
+                ensureActivitiesVisibleLocked(r, 0, !PRESERVE_WINDOWS);
                 if (!kept) {
                     resumeTopActivitiesLocked(stack, null, null);
                     if (changedStacks && stackId == FULLSCREEN_WORKSPACE_STACK_ID) {
@@ -3201,7 +3210,13 @@
         final boolean wasFocused = isFrontStack(task.stack) && (topRunningActivityLocked() == r);
         final boolean wasResumed = wasFocused && (task.stack.mResumedActivity == r);
 
+        final boolean resizeable = task.mResizeable;
+        // Temporarily disable resizeablility of task we are moving. We don't want it to be resized
+        // if a docked stack is created below which will lead to the stack we are moving from and
+        // its resizeable tasks being resized.
+        task.mResizeable = false;
         final ActivityStack stack = getStack(stackId, CREATE_IF_NEEDED, toTop);
+        task.mResizeable = resizeable;
         mWindowManager.moveTaskToStack(task.taskId, stack.mStackId, toTop);
         if (task.stack != null) {
             task.stack.removeTask(task, reason, MOVING);
@@ -3246,7 +3261,7 @@
 
         // The task might have already been running and its visibility needs to be synchronized with
         // the visibility of the stack / windows.
-        ensureActivitiesVisibleLocked(null, 0);
+        ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
         resumeTopActivitiesLocked();
     }
 
@@ -3266,7 +3281,7 @@
         stack.positionTask(task, position, stackChanged);
         // The task might have already been running and its visibility needs to be synchronized with
         // the visibility of the stack / windows.
-        stack.ensureActivitiesVisibleLocked(null, 0);
+        stack.ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
         resumeTopActivitiesLocked();
     }
 
@@ -3441,7 +3456,7 @@
             mService.updateUsageStats(r, true);
         }
         if (allResumedActivitiesComplete()) {
-            ensureActivitiesVisibleLocked(null, 0);
+            ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
             mWindowManager.executeAppTransition();
             return true;
         }
@@ -3527,14 +3542,15 @@
         mHandler.obtainMessage(LAUNCH_TASK_BEHIND_COMPLETE, token).sendToTarget();
     }
 
-    void ensureActivitiesVisibleLocked(ActivityRecord starting, int configChanges) {
+    void ensureActivitiesVisibleLocked(ActivityRecord starting, int configChanges,
+            boolean preserveWindows) {
         // First the front stacks. In case any are not fullscreen and are in front of home.
         for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
             final ArrayList<ActivityStack> stacks = mActivityDisplays.valueAt(displayNdx).mStacks;
             final int topStackNdx = stacks.size() - 1;
             for (int stackNdx = topStackNdx; stackNdx >= 0; --stackNdx) {
                 final ActivityStack stack = stacks.get(stackNdx);
-                stack.ensureActivitiesVisibleLocked(starting, configChanges);
+                stack.ensureActivitiesVisibleLocked(starting, configChanges, preserveWindows);
             }
         }
     }
diff --git a/services/core/java/com/android/server/am/CompatModePackages.java b/services/core/java/com/android/server/am/CompatModePackages.java
index 424ceb1..c36fd06 100644
--- a/services/core/java/com/android/server/am/CompatModePackages.java
+++ b/services/core/java/com/android/server/am/CompatModePackages.java
@@ -17,6 +17,7 @@
 package com.android.server.am;
 
 import static com.android.server.am.ActivityManagerDebugConfig.*;
+import static com.android.server.am.ActivityStackSupervisor.PRESERVE_WINDOWS;
 
 import java.io.File;
 import java.io.FileInputStream;
@@ -347,7 +348,7 @@
                 stack.ensureActivityConfigurationLocked(starting, 0, false);
                 // And we need to make sure at this point that all other activities
                 // are made visible with the correct configuration.
-                stack.ensureActivitiesVisibleLocked(starting, 0);
+                stack.ensureActivitiesVisibleLocked(starting, 0, !PRESERVE_WINDOWS);
             }
         }
     }
diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java
index 452378f..533f425 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController.java
@@ -837,6 +837,7 @@
         if (mPendingScreenOff && target != Display.STATE_OFF) {
             setScreenState(Display.STATE_OFF);
             mPendingScreenOff = false;
+            mPowerState.dismissColorFade();
         }
 
         if (target == Display.STATE_ON) {
@@ -910,6 +911,7 @@
                 // A black surface is already hiding the contents of the screen.
                 setScreenState(Display.STATE_OFF);
                 mPendingScreenOff = false;
+                mPowerState.dismissColorFade();
             } else if (performScreenOffTransition
                     && mPowerState.prepareColorFade(mContext,
                             mColorFadeFadesConfig ?
diff --git a/services/core/java/com/android/server/location/GeofenceProxy.java b/services/core/java/com/android/server/location/GeofenceProxy.java
index d1bb8db..b3a0010 100644
--- a/services/core/java/com/android/server/location/GeofenceProxy.java
+++ b/services/core/java/com/android/server/location/GeofenceProxy.java
@@ -94,7 +94,7 @@
 
     private void bindHardwareGeofence() {
         mContext.bindServiceAsUser(new Intent(mContext, GeofenceHardwareService.class),
-                mServiceConnection, Context.BIND_AUTO_CREATE, UserHandle.OWNER);
+                mServiceConnection, Context.BIND_AUTO_CREATE, UserHandle.SYSTEM);
     }
 
     private ServiceConnection mServiceConnection = new ServiceConnection() {
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 04d382d..3974205 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -2003,6 +2003,7 @@
             case TYPE_SYSTEM_DIALOG:
             case TYPE_VOLUME_OVERLAY:
             case TYPE_PRIVATE_PRESENTATION:
+            case TYPE_DOCK_DIVIDER:
                 break;
         }
 
@@ -2134,54 +2135,56 @@
         case TYPE_INPUT_METHOD_DIALOG:
             // on-screen keyboards and other such input method user interfaces go here.
             return 13;
+        case TYPE_DOCK_DIVIDER:
+            return 14;
         case TYPE_KEYGUARD_SCRIM:
             // the safety window that shows behind keyguard while keyguard is starting
-            return 14;
-        case TYPE_STATUS_BAR_SUB_PANEL:
             return 15;
-        case TYPE_STATUS_BAR:
+        case TYPE_STATUS_BAR_SUB_PANEL:
             return 16;
-        case TYPE_STATUS_BAR_PANEL:
+        case TYPE_STATUS_BAR:
             return 17;
-        case TYPE_KEYGUARD_DIALOG:
+        case TYPE_STATUS_BAR_PANEL:
             return 18;
+        case TYPE_KEYGUARD_DIALOG:
+            return 19;
         case TYPE_VOLUME_OVERLAY:
             // the on-screen volume indicator and controller shown when the user
             // changes the device volume
-            return 19;
+            return 20;
         case TYPE_SYSTEM_OVERLAY:
             // the on-screen volume indicator and controller shown when the user
             // changes the device volume
-            return 20;
+            return 21;
         case TYPE_NAVIGATION_BAR:
             // the navigation bar, if available, shows atop most things
-            return 21;
+            return 22;
         case TYPE_NAVIGATION_BAR_PANEL:
             // some panels (e.g. search) need to show on top of the navigation bar
-            return 22;
+            return 23;
         case TYPE_SYSTEM_ERROR:
             // system-level error dialogs
-            return 23;
+            return 24;
         case TYPE_MAGNIFICATION_OVERLAY:
             // used to highlight the magnified portion of a display
-            return 24;
+            return 25;
         case TYPE_DISPLAY_OVERLAY:
             // used to simulate secondary display devices
-            return 25;
+            return 26;
         case TYPE_DRAG:
             // the drag layer: input for drag-and-drop is associated with this window,
             // which sits above all other focusable windows
-            return 26;
+            return 27;
         case TYPE_ACCESSIBILITY_OVERLAY:
             // overlay put by accessibility services to intercept user interaction
-            return 27;
-        case TYPE_SECURE_SYSTEM_OVERLAY:
             return 28;
-        case TYPE_BOOT_PROGRESS:
+        case TYPE_SECURE_SYSTEM_OVERLAY:
             return 29;
+        case TYPE_BOOT_PROGRESS:
+            return 30;
         case TYPE_POINTER:
             // the (mouse) pointer layer
-            return 30;
+            return 31;
         }
         Log.e(TAG, "Unknown window type: " + type);
         return 2;
@@ -2271,6 +2274,7 @@
             case TYPE_WALLPAPER:
             case TYPE_DREAM:
             case TYPE_KEYGUARD_SCRIM:
+            case TYPE_DOCK_DIVIDER:
                 return false;
             default:
                 // Hide only windows below the keyguard host window.
diff --git a/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java b/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java
index 7ae3c79..c0dfbcb 100644
--- a/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java
+++ b/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java
@@ -129,7 +129,7 @@
         intent.setComponent(keyguardComponent);
 
         if (!context.bindServiceAsUser(intent, mKeyguardConnection,
-                Context.BIND_AUTO_CREATE, UserHandle.OWNER)) {
+                Context.BIND_AUTO_CREATE, UserHandle.SYSTEM)) {
             Log.v(TAG, "*** Keyguard: can't bind to " + keyguardComponent);
             mKeyguardState.showing = false;
             mKeyguardState.showingAndNotOccluded = false;
diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
index 87dc6c4..11a1639 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
@@ -305,9 +305,8 @@
                 throw new SecurityException("invalid status bar icon slot: " + slot);
             }
 
-            StatusBarIcon icon = new StatusBarIcon(iconPackage, UserHandle.OWNER, iconId,
-                    iconLevel, 0,
-                    contentDescription);
+            StatusBarIcon icon = new StatusBarIcon(iconPackage, UserHandle.SYSTEM, iconId,
+                    iconLevel, 0, contentDescription);
             //Slog.d(TAG, "setIcon slot=" + slot + " index=" + index + " icon=" + icon);
             mIcons.setIcon(index, icon);
 
diff --git a/services/core/java/com/android/server/telecom/TelecomLoaderService.java b/services/core/java/com/android/server/telecom/TelecomLoaderService.java
index f4bd61f..6c5452a 100644
--- a/services/core/java/com/android/server/telecom/TelecomLoaderService.java
+++ b/services/core/java/com/android/server/telecom/TelecomLoaderService.java
@@ -188,7 +188,7 @@
                     | Context.BIND_AUTO_CREATE;
 
             // Bind to Telecom and register the service
-            if (mContext.bindServiceAsUser(intent, serviceConnection, flags, UserHandle.OWNER)) {
+            if (mContext.bindServiceAsUser(intent, serviceConnection, flags, UserHandle.SYSTEM)) {
                 mServiceConnection = serviceConnection;
             }
         }
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index cb0dba8..4392ab4 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -16,6 +16,7 @@
 
 package com.android.server.wm;
 
+import static android.app.ActivityManager.DOCKED_STACK_ID;
 import static android.app.ActivityManager.HOME_STACK_ID;
 
 import static com.android.server.wm.WindowManagerService.DEBUG_VISIBILITY;
@@ -111,6 +112,8 @@
     /** Remove this display when animation on it has completed. */
     boolean mDeferredRemoval;
 
+    final DockedStackDividerController mDividerControllerLocked;
+
     /**
      * @param display May not be null.
      * @param service You know.
@@ -122,6 +125,7 @@
         display.getMetrics(mDisplayMetrics);
         isDefaultDisplay = mDisplayId == Display.DEFAULT_DISPLAY;
         mService = service;
+        mDividerControllerLocked = new DockedStackDividerController(service.mContext, this);
     }
 
     int getDisplayId() {
@@ -552,4 +556,14 @@
     public String toString() {
         return "Display " + mDisplayId + " info=" + mDisplayInfo + " stacks=" + mStacks;
     }
+
+    TaskStack getDockedStack() {
+        for (int i = mStacks.size() - 1; i >= 0; i--) {
+            TaskStack stack = mStacks.get(i);
+            if (stack.mStackId == DOCKED_STACK_ID) {
+                return stack;
+            }
+        }
+        return null;
+    }
 }
diff --git a/services/core/java/com/android/server/wm/DockedStackDividerController.java b/services/core/java/com/android/server/wm/DockedStackDividerController.java
new file mode 100644
index 0000000..ad207d4
--- /dev/null
+++ b/services/core/java/com/android/server/wm/DockedStackDividerController.java
@@ -0,0 +1,117 @@
+/*
+ * 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.server.wm;
+
+import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
+import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
+import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
+import static android.view.WindowManager.LayoutParams.FLAG_SPLIT_TOUCH;
+import static android.view.WindowManager.LayoutParams.FLAG_TOUCHABLE_WHEN_WAKING;
+import static android.view.WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH;
+import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER;
+
+import android.content.Context;
+import android.graphics.PixelFormat;
+import android.graphics.Rect;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.WindowManager;
+import android.view.WindowManagerGlobal;
+
+/**
+ * Controls showing and hiding of a docked stack divider on the display.
+ */
+public class DockedStackDividerController {
+    private static final String TAG = "DockedStackDivider";
+    private final Context mContext;
+    private final int mDividerWidth;
+    private final DisplayContent mDisplayContent;
+    private View mView;
+    private Rect mTmpRect = new Rect();
+
+    DockedStackDividerController(Context context, DisplayContent displayContent) {
+        mContext = context;
+        mDisplayContent = displayContent;
+        mDividerWidth = context.getResources().getDimensionPixelSize(
+                com.android.internal.R.dimen.docked_stack_divider_thickness);
+    }
+
+    private void addDivider() {
+        View view = LayoutInflater.from(mContext).inflate(
+                com.android.internal.R.layout.docked_stack_divider, null);
+        WindowManagerGlobal manager = WindowManagerGlobal.getInstance();
+        WindowManager.LayoutParams params = new WindowManager.LayoutParams(
+                mDividerWidth, MATCH_PARENT, TYPE_DOCK_DIVIDER,
+                FLAG_TOUCHABLE_WHEN_WAKING | FLAG_NOT_FOCUSABLE | FLAG_NOT_TOUCH_MODAL
+                        | FLAG_WATCH_OUTSIDE_TOUCH | FLAG_SPLIT_TOUCH,
+                PixelFormat.OPAQUE);
+        params.setTitle(TAG);
+        manager.addView(view, params, mDisplayContent.getDisplay(), null);
+        mView = view;
+    }
+
+    private void removeDivider() {
+        WindowManagerGlobal manager = WindowManagerGlobal.getInstance();
+        manager.removeView(mView, true /* immediate */);
+        mView = null;
+    }
+
+    boolean hasDivider() {
+        return mView != null;
+    }
+
+    void update() {
+        TaskStack stack = mDisplayContent.getDockedStack();
+        if (stack != null && mView == null) {
+            addDivider();
+        } else if (stack == null && mView != null) {
+            removeDivider();
+        }
+    }
+
+    int getWidth() {
+        return mDividerWidth;
+    }
+
+
+    void positionDockedStackedDivider(Rect frame) {
+        TaskStack stack = mDisplayContent.getDockedStack();
+        if (stack == null) {
+            // Unfortunately we might end up with still having a divider, even though the underlying
+            // stack was already removed. This is because we are on AM thread and the removal of the
+            // divider was deferred to WM thread and hasn't happened yet.
+            return;
+        }
+        final @TaskStack.DockSide int side = stack.getDockSide();
+        stack.getBounds(mTmpRect);
+        switch (side) {
+            case TaskStack.DOCKED_LEFT:
+                frame.set(mTmpRect.right, frame.top, mTmpRect.right + frame.width(), frame.bottom);
+                break;
+            case TaskStack.DOCKED_TOP:
+                frame.set(frame.left, mTmpRect.bottom, mTmpRect.right,
+                        mTmpRect.bottom + frame.height());
+                break;
+            case TaskStack.DOCKED_RIGHT:
+                frame.set(mTmpRect.left - frame.width(), frame.top, mTmpRect.left, frame.bottom);
+                break;
+            case TaskStack.DOCKED_BOTTOM:
+                frame.set(frame.left, mTmpRect.top - frame.height(), frame.right, mTmpRect.top);
+                break;
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 4541dd6..6ebff42 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -16,6 +16,7 @@
 
 package com.android.server.wm;
 
+import static android.app.ActivityManager.DOCKED_STACK_ID;
 import static com.android.server.wm.WindowManagerService.TAG;
 import static com.android.server.wm.WindowManagerService.DEBUG_RESIZE;
 import static com.android.server.wm.WindowManagerService.DEBUG_STACK;
@@ -432,6 +433,10 @@
         return mStack != null && mStack.mStackId == FREEFORM_WORKSPACE_STACK_ID;
     }
 
+    boolean inDockedWorkspace() {
+        return mStack != null && mStack.mStackId == DOCKED_STACK_ID;
+    }
+
     @Override
     public boolean isFullscreen() {
         return mFullscreen;
diff --git a/services/core/java/com/android/server/wm/TaskStack.java b/services/core/java/com/android/server/wm/TaskStack.java
index 1362555..96fcf93 100644
--- a/services/core/java/com/android/server/wm/TaskStack.java
+++ b/services/core/java/com/android/server/wm/TaskStack.java
@@ -352,7 +352,8 @@
             // the docked stack occupies a dedicated region on screen.
             bounds = new Rect();
             displayContent.getLogicalDisplayRect(mTmpRect);
-            getInitialDockedStackBounds(mTmpRect, bounds, mStackId);
+            getInitialDockedStackBounds(mTmpRect, bounds, mStackId,
+                    mDisplayContent.mDividerControllerLocked.getWidth() / 2);
         }
 
         updateDisplayInfo(bounds);
@@ -371,27 +372,29 @@
      * @param displayRect The bounds of the display the docked stack is on.
      * @param outBounds Output bounds that should be used for the stack.
      * @param stackId Id of stack we are calculating the bounds for.
+     * @param adjustment
      */
-    private static void getInitialDockedStackBounds(
-            Rect displayRect, Rect outBounds, int stackId) {
+    private static void getInitialDockedStackBounds(Rect displayRect, Rect outBounds, int stackId,
+            int adjustment) {
         // Docked stack start off occupying half the screen space.
+        final boolean dockedStack = stackId == DOCKED_STACK_ID;
         final boolean splitHorizontally = displayRect.width() > displayRect.height();
         final boolean topOrLeftCreateMode =
                 WindowManagerService.sDockedStackCreateMode == DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT;
-        final boolean placeTopOrLeft = (stackId == DOCKED_STACK_ID && topOrLeftCreateMode)
-                || (stackId != DOCKED_STACK_ID && !topOrLeftCreateMode);
+        final boolean placeTopOrLeft = (dockedStack && topOrLeftCreateMode)
+                || (!dockedStack && !topOrLeftCreateMode);
         outBounds.set(displayRect);
         if (placeTopOrLeft) {
             if (splitHorizontally) {
-                outBounds.right = displayRect.centerX();
+                outBounds.right = displayRect.centerX() - adjustment;
             } else {
-                outBounds.bottom = displayRect.centerY();
+                outBounds.bottom = displayRect.centerY() - adjustment;
             }
         } else {
             if (splitHorizontally) {
-                outBounds.left = displayRect.centerX();
+                outBounds.left = displayRect.centerX() + adjustment;
             } else {
-                outBounds.top = displayRect.centerY();
+                outBounds.top = displayRect.centerY() + adjustment;
             }
         }
     }
@@ -404,7 +407,8 @@
     private void resizeNonDockedStacks(boolean fullscreen) {
         mDisplayContent.getLogicalDisplayRect(mTmpRect);
         if (!fullscreen) {
-            getInitialDockedStackBounds(mTmpRect, mTmpRect, FULLSCREEN_WORKSPACE_STACK_ID);
+            getInitialDockedStackBounds(mTmpRect, mTmpRect, FULLSCREEN_WORKSPACE_STACK_ID,
+                    mDisplayContent.mDividerControllerLocked.getWidth());
         }
 
         final int count = mService.mStackIdToStack.size();
@@ -546,14 +550,14 @@
         final int orientation = mService.mCurConfiguration.orientation;
         if (orientation == Configuration.ORIENTATION_PORTRAIT) {
             // Portrait mode, docked either at the top or the bottom.
-            if (mTmpRect.top - mBounds.top < mTmpRect.bottom - mBounds.bottom) {
+            if (mBounds.top - mTmpRect.top < mTmpRect.bottom - mBounds.bottom) {
                 return DOCKED_TOP;
             } else {
                 return DOCKED_BOTTOM;
             }
         } else if (orientation == Configuration.ORIENTATION_LANDSCAPE) {
             // Landscape mode, docked either on the left or on the right.
-            if (mTmpRect.left - mBounds.left < mTmpRect.right - mBounds.right) {
+            if (mBounds.left - mTmpRect.left < mTmpRect.right - mBounds.right) {
                 return DOCKED_LEFT;
             } else {
                 return DOCKED_RIGHT;
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 29598ab..d510c4a 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -17,6 +17,7 @@
 package com.android.server.wm;
 
 import static android.app.ActivityManager.DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT;
+import static android.app.ActivityManager.DOCKED_STACK_ID;
 import static android.view.WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW;
 import static android.view.WindowManager.LayoutParams.FIRST_SUB_WINDOW;
 import static android.view.WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
@@ -33,6 +34,7 @@
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
 import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
 import static android.view.WindowManager.LayoutParams.TYPE_BOOT_PROGRESS;
+import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER;
 import static android.view.WindowManager.LayoutParams.TYPE_DREAM;
 import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
 import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD_DIALOG;
@@ -921,6 +923,7 @@
         mAllowTheaterModeWakeFromLayout = context.getResources().getBoolean(
                 com.android.internal.R.bool.config_allowTheaterModeWakeFromWindowLayout);
 
+
         LocalServices.addService(WindowManagerInternal.class, new LocalService());
         initPolicy();
 
@@ -1854,6 +1857,11 @@
                             + attrs.token + ".  Aborting.");
                     return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
                 }
+            } else if (type == TYPE_DOCK_DIVIDER) {
+                if (displayContent.mDividerControllerLocked.hasDivider()) {
+                    Slog.w(TAG, "Attempted to add docked stack divider twice. Aborting.");
+                    return WindowManagerGlobal.ADD_MULTIPLE_SINGLETON;
+                }
             } else if (token.appWindowToken != null) {
                 Slog.w(TAG, "Non-null appWindowToken for system window of type=" + type);
                 // It is not valid to use an app token with other system types; we will
@@ -4521,7 +4529,10 @@
                     }
                     stack.attachDisplayContent(displayContent);
                     displayContent.attachStack(stack, onTop);
-
+                    if (stack.mStackId == DOCKED_STACK_ID) {
+                        mH.obtainMessage(H.UPDATE_DOCKED_STACK_DIVIDER,
+                                displayContent).sendToTarget();
+                    }
                     moveStackWindowsLocked(displayContent);
                     final WindowList windows = displayContent.getWindowList();
                     for (int winNdx = windows.size() - 1; winNdx >= 0; --winNdx) {
@@ -4544,6 +4555,11 @@
     void detachStackLocked(DisplayContent displayContent, TaskStack stack) {
         displayContent.detachStack(stack);
         stack.detachDisplay();
+        // We can't directly remove the divider, because only the WM thread can do these operations
+        // and we can be on AM thread.
+        if (stack.mStackId == DOCKED_STACK_ID) {
+            mH.obtainMessage(H.UPDATE_DOCKED_STACK_DIVIDER, displayContent).sendToTarget();
+        }
     }
 
     public void detachStack(int stackId) {
@@ -7220,6 +7236,8 @@
         public static final int TAP_DOWN_OUTSIDE_TASK = 40;
         public static final int FINISH_TASK_POSITIONING = 41;
 
+        public static final int UPDATE_DOCKED_STACK_DIVIDER = 42;
+
         @Override
         public void handleMessage(Message msg) {
             if (DEBUG_WINDOW_TRACE) {
@@ -7759,6 +7777,13 @@
                     }
                 }
                 break;
+                case UPDATE_DOCKED_STACK_DIVIDER: {
+                    DisplayContent content = (DisplayContent) msg.obj;
+                    synchronized (mWindowMap) {
+                        content.mDividerControllerLocked.update();
+                    }
+                }
+                break;
             }
             if (DEBUG_WINDOW_TRACE) {
                 Slog.v(TAG, "handleMessage: exit");
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index a6478a0..64440d3 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -23,6 +23,7 @@
 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_COMPATIBLE_WINDOW;
 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_KEYGUARD;
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
+import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER;
 import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
 import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD_DIALOG;
 import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
@@ -707,6 +708,8 @@
             mContentFrame.set(mFrame);
             mVisibleFrame.set(mContentFrame);
             mStableFrame.set(mContentFrame);
+        } else if (mAttrs.type == TYPE_DOCK_DIVIDER) {
+            mDisplayContent.mDividerControllerLocked.positionDockedStackedDivider(mFrame);
         } else {
             mContentFrame.set(Math.max(mContentFrame.left, mFrame.left),
                     Math.max(mContentFrame.top, mFrame.top),
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index e11e317..b0a04c7 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -1226,7 +1226,7 @@
 
     void removeUserData(int userHandle) {
         synchronized (this) {
-            if (userHandle == UserHandle.USER_OWNER) {
+            if (userHandle == UserHandle.USER_SYSTEM) {
                 Slog.w(LOG_TAG, "Tried to remove device policy file for user 0! Ignoring.");
                 return;
             }
@@ -1372,12 +1372,12 @@
                 && !hasUserSetupCompleted(userId);
 
         if (reqPolicy == DeviceAdminInfo.USES_POLICY_DEVICE_OWNER) {
-            if ((userId == UserHandle.USER_OWNER && (ownsDevice || ownsInitialization))
+            if ((userId == UserHandle.USER_SYSTEM && (ownsDevice || ownsInitialization))
                     || (ownsDevice && ownsProfile)) {
                 return true;
             }
         } else if (reqPolicy == DeviceAdminInfo.USES_POLICY_PROFILE_OWNER) {
-            if ((userId == UserHandle.USER_OWNER && ownsDevice) || ownsProfile
+            if ((userId == UserHandle.USER_SYSTEM && ownsDevice) || ownsProfile
                     || ownsInitialization) {
                 return true;
             }
@@ -1892,7 +1892,7 @@
     }
 
     private void onLockSettingsReady() {
-        getUserData(UserHandle.USER_OWNER);
+        getUserData(UserHandle.USER_SYSTEM);
         loadOwners();
         cleanUpOldUsers();
         // Register an observer for watching for user setup complete.
@@ -3107,10 +3107,10 @@
 
     private void setDoNotAskCredentialsOnBoot() {
         synchronized (this) {
-            DevicePolicyData policyData = getUserData(UserHandle.USER_OWNER);
+            DevicePolicyData policyData = getUserData(UserHandle.USER_SYSTEM);
             if (!policyData.doNotAskCredentialsOnBoot) {
                 policyData.doNotAskCredentialsOnBoot = true;
-                saveSettingsLocked(UserHandle.USER_OWNER);
+                saveSettingsLocked(UserHandle.USER_SYSTEM);
             }
         }
     }
@@ -3120,7 +3120,7 @@
         mContext.enforceCallingOrSelfPermission(
                 android.Manifest.permission.QUERY_DO_NOT_ASK_CREDENTIALS_ON_BOOT, null);
         synchronized (this) {
-            DevicePolicyData policyData = getUserData(UserHandle.USER_OWNER);
+            DevicePolicyData policyData = getUserData(UserHandle.USER_SYSTEM);
             return policyData.doNotAskCredentialsOnBoot;
         }
     }
@@ -3475,20 +3475,14 @@
             final ActiveAdmin admin = getActiveAdminForCallerLocked(null,
                     DeviceAdminInfo.USES_POLICY_WIPE_DATA);
 
-            final String source;
-            final ComponentName cname = admin.info.getComponent();
-            if (cname != null) {
-                source = cname.flattenToShortString();
-            } else {
-                source = admin.info.getPackageName();
-            }
+            final String source = admin.info.getComponent().flattenToShortString();
 
             long ident = binderClearCallingIdentity();
             try {
                 if ((flags & WIPE_RESET_PROTECTION_DATA) != 0) {
                     boolean ownsInitialization = isDeviceInitializer(admin.info.getPackageName())
                             && !hasUserSetupCompleted(userHandle);
-                    if (userHandle != UserHandle.USER_OWNER
+                    if (userHandle != UserHandle.USER_SYSTEM
                             || !(isDeviceOwner(admin.info.getPackageName())
                                     || ownsInitialization)) {
                         throw new SecurityException(
@@ -3510,7 +3504,7 @@
     }
 
     private void wipeDeviceOrUserLocked(boolean wipeExtRequested, final int userHandle, String reason) {
-        if (userHandle == UserHandle.USER_OWNER) {
+        if (userHandle == UserHandle.USER_SYSTEM) {
             wipeDataLocked(wipeExtRequested, reason);
         } else {
             mHandler.post(new Runnable() {
@@ -3519,7 +3513,7 @@
                     try {
                         IActivityManager am = getIActivityManager();
                         if (am.getCurrentUser().id == userHandle) {
-                            am.switchUser(UserHandle.USER_OWNER);
+                            am.switchUser(UserHandle.USER_SYSTEM);
                         }
 
                         boolean isManagedProfile = isManagedProfile(userHandle);
@@ -3733,8 +3727,8 @@
         synchronized(this) {
             Preconditions.checkNotNull(who, "ComponentName is null");
 
-            // Only check if owner has set global proxy. We don't allow other users to set it.
-            DevicePolicyData policy = getUserData(UserHandle.USER_OWNER);
+            // Only check if system user has set global proxy. We don't allow other users to set it.
+            DevicePolicyData policy = getUserData(UserHandle.USER_SYSTEM);
             ActiveAdmin admin = getActiveAdminForCallerLocked(who,
                     DeviceAdminInfo.USES_POLICY_SETS_GLOBAL_PROXY);
 
@@ -3750,8 +3744,8 @@
                 }
             }
 
-            // If the user is not the owner, don't set the global proxy. Fail silently.
-            if (UserHandle.getCallingUserId() != UserHandle.USER_OWNER) {
+            // If the user is not system, don't set the global proxy. Fail silently.
+            if (UserHandle.getCallingUserId() != UserHandle.USER_SYSTEM) {
                 Slog.w(LOG_TAG, "Only the owner is allowed to set the global proxy. User "
                         + UserHandle.getCallingUserId() + " is not permitted.");
                 return null;
@@ -3786,7 +3780,7 @@
         }
         enforceCrossUserPermission(userHandle);
         synchronized(this) {
-            DevicePolicyData policy = getUserData(UserHandle.USER_OWNER);
+            DevicePolicyData policy = getUserData(UserHandle.USER_SYSTEM);
             // Scan through active admins and find if anyone has already
             // set the global proxy.
             final int N = policy.mAdminList.size();
@@ -3874,10 +3868,9 @@
         final int userHandle = UserHandle.getCallingUserId();
         synchronized (this) {
             // Check for permissions
-            // Only owner can set storage encryption
-            if (userHandle != UserHandle.USER_OWNER
-                    || UserHandle.getCallingUserId() != UserHandle.USER_OWNER) {
-                Slog.w(LOG_TAG, "Only owner is allowed to set storage encryption. User "
+            // Only system user can set storage encryption
+            if (userHandle != UserHandle.USER_SYSTEM) {
+                Slog.w(LOG_TAG, "Only owner/system user is allowed to set storage encryption. User "
                         + UserHandle.getCallingUserId() + " is not permitted.");
                 return 0;
             }
@@ -3896,7 +3889,7 @@
                 saveSettingsLocked(userHandle);
             }
 
-            DevicePolicyData policy = getUserData(UserHandle.USER_OWNER);
+            DevicePolicyData policy = getUserData(UserHandle.USER_SYSTEM);
             // (2) Compute "max" for all admins
             boolean newRequested = false;
             final int N = policy.mAdminList.size();
@@ -4259,7 +4252,7 @@
             try {
                 IBackupManager ibm = IBackupManager.Stub.asInterface(
                         ServiceManager.getService(Context.BACKUP_SERVICE));
-                ibm.setBackupServiceActive(UserHandle.USER_OWNER, false);
+                ibm.setBackupServiceActive(UserHandle.USER_SYSTEM, false);
             } catch (RemoteException e) {
                 throw new IllegalStateException("Failed deactivating backup service.", e);
             } finally {
@@ -4314,7 +4307,7 @@
                 return null;
             }
             String deviceOwnerPackage = mOwners.getDeviceOwnerPackageName();
-            return getApplicationLabel(deviceOwnerPackage, UserHandle.USER_OWNER);
+            return getApplicationLabel(deviceOwnerPackage, UserHandle.USER_SYSTEM);
         }
     }
 
@@ -4325,7 +4318,7 @@
             return null;
         }
 
-        DevicePolicyData policy = getUserData(UserHandle.USER_OWNER);
+        DevicePolicyData policy = getUserData(UserHandle.USER_SYSTEM);
         final int n = policy.mAdminList.size();
         for (int i = 0; i < n; i++) {
             ActiveAdmin admin = policy.mAdminList.get(i);
@@ -4351,7 +4344,7 @@
             throw new SecurityException("clearDeviceOwner can only be called by the device owner");
         }
         synchronized (this) {
-            clearUserPoliciesLocked(new UserHandle(UserHandle.USER_OWNER));
+            clearUserPoliciesLocked(new UserHandle(UserHandle.USER_SYSTEM));
 
             mOwners.clearDeviceOwner();
             mOwners.writeDeviceOwner();
@@ -4361,7 +4354,7 @@
             try {
                 IBackupManager ibm = IBackupManager.Stub.asInterface(
                         ServiceManager.getService(Context.BACKUP_SERVICE));
-                ibm.setBackupServiceActive(UserHandle.USER_OWNER, true);
+                ibm.setBackupServiceActive(UserHandle.USER_SYSTEM, true);
             } catch (RemoteException e) {
                 throw new IllegalStateException("Failed reactivating backup service.", e);
             } finally {
@@ -4611,7 +4604,7 @@
                     removeActiveAdmin(who, userId);
                 }
 
-                if (userId == UserHandle.USER_OWNER) {
+                if (userId == UserHandle.USER_SYSTEM) {
                     Settings.Global.putInt(mContext.getContentResolver(),
                             Settings.Global.DEVICE_PROVISIONED, 1);
                 }
@@ -4794,7 +4787,7 @@
 
         int callingUid = binderGetCallingUid();
         if (callingUid == Process.SHELL_UID || callingUid == Process.ROOT_UID) {
-            if (!hasUserSetupCompleted(UserHandle.USER_OWNER)) {
+            if (!hasUserSetupCompleted(UserHandle.USER_SYSTEM)) {
                 return;
             }
             // STOPSHIP Do proper check in split user mode
@@ -4818,7 +4811,7 @@
                 android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS, null);
         // STOPSHIP Do proper check in split user mode
         if (!UserManager.isSplitSystemUser()) {
-            if (hasUserSetupCompleted(UserHandle.USER_OWNER)) {
+            if (hasUserSetupCompleted(UserHandle.USER_SYSTEM)) {
                 throw new IllegalStateException("Cannot set the device owner if the device is "
                         + "already set-up");
             }
@@ -5526,7 +5519,7 @@
 
             long id = binderClearCallingIdentity();
             try {
-                int userId = UserHandle.USER_OWNER;
+                int userId = UserHandle.USER_SYSTEM;
                 if (userHandle != null) {
                     userId = userHandle.getIdentifier();
                 }
@@ -5563,13 +5556,13 @@
     @Override
     public void setUserRestriction(ComponentName who, String key, boolean enabled) {
         Preconditions.checkNotNull(who, "ComponentName is null");
-        final UserHandle user = new UserHandle(UserHandle.getCallingUserId());
-        final int userHandle = user.getIdentifier();
+        final int userHandle = UserHandle.getCallingUserId();
+        final UserHandle user = new UserHandle(userHandle);
         synchronized (this) {
             ActiveAdmin activeAdmin =
                     getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
             boolean isDeviceOwner = isDeviceOwner(activeAdmin.info.getPackageName());
-            if (!isDeviceOwner && userHandle != UserHandle.USER_OWNER
+            if (!isDeviceOwner && userHandle != UserHandle.USER_SYSTEM
                     && DEVICE_OWNER_USER_RESTRICTIONS.contains(key)) {
                 throw new SecurityException("Profile owners cannot set user restriction " + key);
             }
@@ -5594,8 +5587,7 @@
                     } else if (UserManager.DISALLOW_ADJUST_VOLUME.equals(key)) {
                         iAudioService.setMasterMute(true, 0, mContext.getPackageName(),
                                 userHandle);
-                    }
-                    if (UserManager.DISALLOW_CONFIG_WIFI.equals(key)) {
+                    } else if (UserManager.DISALLOW_CONFIG_WIFI.equals(key)) {
                         Settings.Secure.putIntForUser(mContext.getContentResolver(),
                                 Settings.Secure.WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON, 0,
                                 userHandle);
@@ -5607,8 +5599,9 @@
                                 Settings.Secure.LOCATION_PROVIDERS_ALLOWED, "",
                                 userHandle);
                     } else if (UserManager.DISALLOW_DEBUGGING_FEATURES.equals(key)) {
-                        // Only disable adb if changing for primary user, since it is global
-                        if (userHandle == UserHandle.USER_OWNER) {
+                        // Only disable adb if changing for system user, since it is global
+                        // TODO: should this be admin user?
+                        if (userHandle == UserHandle.USER_SYSTEM) {
                             Settings.Global.putStringForUser(mContext.getContentResolver(),
                                     Settings.Global.ADB_ENABLED, "0", userHandle);
                         }
@@ -6497,8 +6490,8 @@
         mContext.enforceCallingOrSelfPermission(permission.NOTIFY_PENDING_SYSTEM_UPDATE,
                 "Only the system update service can broadcast update information");
 
-        if (UserHandle.getCallingUserId() != UserHandle.USER_OWNER) {
-            Slog.w(LOG_TAG, "Only the system update service in the primary user " +
+        if (UserHandle.getCallingUserId() != UserHandle.USER_SYSTEM) {
+            Slog.w(LOG_TAG, "Only the system update service in the system user " +
                     "can broadcast update information.");
             return;
         }
diff --git a/core/java/android/net/IpReachabilityMonitor.java b/services/net/java/android/net/ip/IpReachabilityMonitor.java
similarity index 99%
rename from core/java/android/net/IpReachabilityMonitor.java
rename to services/net/java/android/net/ip/IpReachabilityMonitor.java
index 2283004c..3acd565 100644
--- a/core/java/android/net/IpReachabilityMonitor.java
+++ b/services/net/java/android/net/ip/IpReachabilityMonitor.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package android.net;
+package android.net.ip;
 
 import com.android.internal.annotations.GuardedBy;
 
diff --git a/core/java/android/net/netlink/NetlinkConstants.java b/services/net/java/android/net/netlink/NetlinkConstants.java
similarity index 100%
rename from core/java/android/net/netlink/NetlinkConstants.java
rename to services/net/java/android/net/netlink/NetlinkConstants.java
diff --git a/core/java/android/net/netlink/NetlinkErrorMessage.java b/services/net/java/android/net/netlink/NetlinkErrorMessage.java
similarity index 100%
rename from core/java/android/net/netlink/NetlinkErrorMessage.java
rename to services/net/java/android/net/netlink/NetlinkErrorMessage.java
diff --git a/core/java/android/net/netlink/NetlinkMessage.java b/services/net/java/android/net/netlink/NetlinkMessage.java
similarity index 100%
rename from core/java/android/net/netlink/NetlinkMessage.java
rename to services/net/java/android/net/netlink/NetlinkMessage.java
diff --git a/core/java/android/net/netlink/NetlinkSocket.java b/services/net/java/android/net/netlink/NetlinkSocket.java
similarity index 100%
rename from core/java/android/net/netlink/NetlinkSocket.java
rename to services/net/java/android/net/netlink/NetlinkSocket.java
diff --git a/core/java/android/net/netlink/RtNetlinkNeighborMessage.java b/services/net/java/android/net/netlink/RtNetlinkNeighborMessage.java
similarity index 100%
rename from core/java/android/net/netlink/RtNetlinkNeighborMessage.java
rename to services/net/java/android/net/netlink/RtNetlinkNeighborMessage.java
diff --git a/core/java/android/net/netlink/StructNdMsg.java b/services/net/java/android/net/netlink/StructNdMsg.java
similarity index 100%
rename from core/java/android/net/netlink/StructNdMsg.java
rename to services/net/java/android/net/netlink/StructNdMsg.java
diff --git a/core/java/android/net/netlink/StructNdaCacheInfo.java b/services/net/java/android/net/netlink/StructNdaCacheInfo.java
similarity index 100%
rename from core/java/android/net/netlink/StructNdaCacheInfo.java
rename to services/net/java/android/net/netlink/StructNdaCacheInfo.java
diff --git a/core/java/android/net/netlink/StructNlAttr.java b/services/net/java/android/net/netlink/StructNlAttr.java
similarity index 100%
rename from core/java/android/net/netlink/StructNlAttr.java
rename to services/net/java/android/net/netlink/StructNlAttr.java
diff --git a/core/java/android/net/netlink/StructNlMsgErr.java b/services/net/java/android/net/netlink/StructNlMsgErr.java
similarity index 100%
rename from core/java/android/net/netlink/StructNlMsgErr.java
rename to services/net/java/android/net/netlink/StructNlMsgErr.java
diff --git a/core/java/android/net/netlink/StructNlMsgHdr.java b/services/net/java/android/net/netlink/StructNlMsgHdr.java
similarity index 100%
rename from core/java/android/net/netlink/StructNlMsgHdr.java
rename to services/net/java/android/net/netlink/StructNlMsgHdr.java
diff --git a/core/tests/coretests/src/android/net/netlink/NetlinkErrorMessageTest.java b/services/tests/servicestests/src/android/net/netlink/NetlinkErrorMessageTest.java
similarity index 100%
rename from core/tests/coretests/src/android/net/netlink/NetlinkErrorMessageTest.java
rename to services/tests/servicestests/src/android/net/netlink/NetlinkErrorMessageTest.java
diff --git a/core/tests/coretests/src/android/net/netlink/NetlinkSocketTest.java b/services/tests/servicestests/src/android/net/netlink/NetlinkSocketTest.java
similarity index 100%
rename from core/tests/coretests/src/android/net/netlink/NetlinkSocketTest.java
rename to services/tests/servicestests/src/android/net/netlink/NetlinkSocketTest.java
diff --git a/core/tests/coretests/src/android/net/netlink/RtNetlinkNeighborMessageTest.java b/services/tests/servicestests/src/android/net/netlink/RtNetlinkNeighborMessageTest.java
similarity index 100%
rename from core/tests/coretests/src/android/net/netlink/RtNetlinkNeighborMessageTest.java
rename to services/tests/servicestests/src/android/net/netlink/RtNetlinkNeighborMessageTest.java
diff --git a/services/usb/java/com/android/server/usb/UsbSettingsManager.java b/services/usb/java/com/android/server/usb/UsbSettingsManager.java
index 2cf42f0..154cbd3 100644
--- a/services/usb/java/com/android/server/usb/UsbSettingsManager.java
+++ b/services/usb/java/com/android/server/usb/UsbSettingsManager.java
@@ -517,7 +517,7 @@
                 com.android.internal.R.bool.config_disableUsbPermissionDialogs);
 
         synchronized (mLock) {
-            if (UserHandle.OWNER.equals(user)) {
+            if (UserHandle.SYSTEM.equals(user)) {
                 upgradeSingleUserLocked();
             }
             readSettingsLocked();
diff --git a/tests/ActivityTests/src/com/google/android/test/activity/ActivityTestMain.java b/tests/ActivityTests/src/com/google/android/test/activity/ActivityTestMain.java
index 5fbfe8a..ceb3993e 100644
--- a/tests/ActivityTests/src/com/google/android/test/activity/ActivityTestMain.java
+++ b/tests/ActivityTests/src/com/google/android/test/activity/ActivityTestMain.java
@@ -57,6 +57,7 @@
 import android.content.res.Configuration;
 import android.util.Log;
 
+
 public class ActivityTestMain extends Activity {
     static final String TAG = "ActivityTest";
 
@@ -315,7 +316,7 @@
                         Log.i(TAG, "Service disconnected " + name);
                     }
                 };
-                if (bindServiceAsUser(intent, conn, Context.BIND_AUTO_CREATE, UserHandle.OWNER)) {
+                if (bindServiceAsUser(intent, conn, Context.BIND_AUTO_CREATE, UserHandle.SYSTEM)) {
                     mConnections.add(conn);
                 } else {
                     Toast.makeText(ActivityTestMain.this, "Failed to bind",
diff --git a/tools/layoutlib/bridge/src/android/content/res/BridgeResources.java b/tools/layoutlib/bridge/src/android/content/res/BridgeResources.java
index 163fbcb..3b7bf85 100644
--- a/tools/layoutlib/bridge/src/android/content/res/BridgeResources.java
+++ b/tools/layoutlib/bridge/src/android/content/res/BridgeResources.java
@@ -48,9 +48,6 @@
 import java.io.InputStream;
 import java.util.Iterator;
 
-/**
- *
- */
 public final class BridgeResources extends Resources {
 
     private BridgeContext mContext;
@@ -278,7 +275,7 @@
      * always Strings. The ideal signature for the method should be &lt;T super String&gt;, but java
      * generics don't support it.
      */
-    private <T extends CharSequence> T[] fillValues(ArrayResourceValue resValue, T[] values) {
+    <T extends CharSequence> T[] fillValues(ArrayResourceValue resValue, T[] values) {
         int i = 0;
         for (Iterator<String> iterator = resValue.iterator(); iterator.hasNext(); i++) {
             @SuppressWarnings("unchecked")
diff --git a/tools/layoutlib/bridge/src/android/content/res/BridgeTypedArray.java b/tools/layoutlib/bridge/src/android/content/res/BridgeTypedArray.java
index 6a61090..31dd3d9 100644
--- a/tools/layoutlib/bridge/src/android/content/res/BridgeTypedArray.java
+++ b/tools/layoutlib/bridge/src/android/content/res/BridgeTypedArray.java
@@ -16,6 +16,7 @@
 
 package android.content.res;
 
+import com.android.ide.common.rendering.api.ArrayResourceValue;
 import com.android.ide.common.rendering.api.AttrResourceValue;
 import com.android.ide.common.rendering.api.LayoutLog;
 import com.android.ide.common.rendering.api.RenderResources;
@@ -33,6 +34,7 @@
 import org.xmlpull.v1.XmlPullParserException;
 
 import android.annotation.Nullable;
+import android.content.res.Resources.NotFoundException;
 import android.content.res.Resources.Theme;
 import android.graphics.drawable.Drawable;
 import android.util.DisplayMetrics;
@@ -740,12 +742,20 @@
      */
     @Override
     public CharSequence[] getTextArray(int index) {
-        String value = getString(index);
-        if (value != null) {
-            return new CharSequence[] { value };
+        if (!hasValue(index)) {
+            return null;
         }
-
-        return null;
+        ResourceValue resVal = mResourceData[index];
+        if (resVal instanceof ArrayResourceValue) {
+            ArrayResourceValue array = (ArrayResourceValue) resVal;
+            int count = array.getElementCount();
+            return count >= 0 ? mBridgeResources.fillValues(array, new CharSequence[count]) : null;
+        }
+        int id = getResourceId(index, 0);
+        String resIdMessage = id > 0 ? " (resource id 0x" + Integer.toHexString(id) + ')' : "";
+        throw new NotFoundException(
+                String.format("%1$s in %2$s%3$s is not a valid array resource.",
+                        resVal.getValue(), mNames[index], resIdMessage));
     }
 
     @Override
diff --git a/wifi/java/android/net/wifi/IWifiManager.aidl b/wifi/java/android/net/wifi/IWifiManager.aidl
index 0d95b38..23be8e0 100644
--- a/wifi/java/android/net/wifi/IWifiManager.aidl
+++ b/wifi/java/android/net/wifi/IWifiManager.aidl
@@ -23,6 +23,7 @@
 import android.net.wifi.ScanSettings;
 import android.net.wifi.WifiChannel;
 import android.net.wifi.ScanResult;
+import android.net.wifi.ScanInfo;
 import android.net.wifi.WifiConnectionStatistics;
 import android.net.wifi.WifiActivityEnergyInfo;
 import android.net.Network;
@@ -70,6 +71,10 @@
 
     void disconnect();
 
+    List<ScanInfo> getScanInfos(String callingPackage);
+
+    void setOsuSelection(int osuID);
+
     void reconnect();
 
     void reassociate();
diff --git a/wifi/java/android/net/wifi/ScanInfo.aidl b/wifi/java/android/net/wifi/ScanInfo.aidl
new file mode 100644
index 0000000..18ae508
--- /dev/null
+++ b/wifi/java/android/net/wifi/ScanInfo.aidl
@@ -0,0 +1,19 @@
+/**
+ * Copyright (c) 2015, 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;
+
+parcelable ScanInfo;
diff --git a/wifi/java/android/net/wifi/ScanInfo.java b/wifi/java/android/net/wifi/ScanInfo.java
new file mode 100644
index 0000000..39186fa
--- /dev/null
+++ b/wifi/java/android/net/wifi/ScanInfo.java
@@ -0,0 +1,189 @@
+package android.net.wifi;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+public class ScanInfo implements Parcelable {
+    private final ScanResult mScanResult;
+
+    private final long mBSSID;          // The BSSID of the best AP with an SSID matching the OSU
+    private final int mRSSI;            // RSSI of the AP with BSSID
+    private final String mSSID;         // The SSID to connect to for an OSU connection.
+    private final String mName;
+    private final String mServiceDescription;
+    private final String mIconType;
+    private final byte[] mIconData;
+    private final int mOSUIdentity;
+
+    public ScanInfo(ScanResult scanResult) {
+        mScanResult = scanResult;
+
+        mBSSID = -1;
+        mRSSI = -1;
+        mSSID = null;
+        mName = null;
+        mServiceDescription = null;
+        mIconType = null;
+        mIconData = null;
+        mOSUIdentity = -1;
+    }
+
+    public ScanInfo(long BSSID, int rssi, String SSID, String name, String serviceDescription,
+                    String iconType, byte[] iconData, int OSUIdentity) {
+        mBSSID = BSSID;
+        mRSSI = rssi;
+        mSSID = SSID;
+        mName = name;
+        mServiceDescription = serviceDescription;
+        mIconType = iconType;
+        mIconData = iconData;
+        mOSUIdentity = OSUIdentity;
+
+        mScanResult = null;
+    }
+
+    /**
+     * Get the scan result of this ScanInfo.
+     * @return The ScanResult, if this ScanInfo contains a one. If the ScanInfo contains
+     * OSU information getScanResult will return null.
+     */
+    public ScanResult getScanResult() {
+        return mScanResult;
+    }
+
+    /**
+     * OSU only: The BSSID of the AP who advertises the OSU SSID. This value is not guaranteed to
+     * be correct; In the somewhat unlikely case that multiple APs advertise OSU SSIDs that matches
+     * an OSU information element returned through ANQP and one of those is not related to an OSU
+     * there is a (slight) risk that the BSSID is for a "spoof" OSU.
+     * The matching algorithm that produces the ScanInfo objects makes a best effort to get the
+     * matching right though and since it is (a) fair to assume that the OSU SSID resides on the
+     * same AP as the one advertising the OSU information, and (b) BSSIDs for multi-SSID APs are
+     * typically adjacent to each other, matching will prefer the BSSID closest to the advertising
+     * APs BSSID if multiple SSIDs match.
+     * @return The BSSID.
+     */
+    public long getBssid() {
+        return mBSSID;
+    }
+
+    /**
+     * OSU only.
+     * @return The signal level of the AP associated with the BSSID from getBSSID.
+     */
+    public int getRssi() {
+        return mRSSI;
+    }
+
+    /**
+     * OSU only.
+     * @return The SSID of the AP to which to associate to establish an OSU connection.
+     */
+    public String getSsid() {
+        return mSSID;
+    }
+
+    /**
+     * OSU only.
+     * @return The name of the Service Provider of the OSU.
+     */
+    public String getName() {
+        return mName;
+    }
+
+    /**
+     * OSU only.
+     * @return The service description of the OSU.
+     */
+    public String getServiceDescription() {
+        return mServiceDescription;
+    }
+
+    /**
+     * OSU only.
+     * Get the type of icon that icon data represents, e.g. JPG, PNG etc. This field is formatted
+     * using standard MIME encodings per RFC-4288 and IANA MIME media types.
+     * @return The icon type in icon data.
+     */
+    public String getIconType() {
+        return mIconType;
+    }
+
+    /**
+     * OSU only.
+     * @return The binary data of the icon.
+     */
+    public byte[] getIconData() {
+        return mIconData;
+    }
+
+    /**
+     * OSU only.
+     * @return a unique identity for the OSU. This value is generated by the framework and should
+     * be used to uniquely identify a specific OSU. Please note that values may be reused after
+     * a very long time-span (in any normal scenario, likely years) and implementations should make
+     * sure to not rely on any long term persisted values.
+     */
+    public int getOsuIdentity() {
+        return mOSUIdentity;
+    }
+
+    private static final int ScanResultMarker = 0;
+    private static final int OSUMarker = 1;
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    /** Implement the Parcelable interface {@hide} */
+    public static final Creator<ScanInfo> CREATOR =
+            new Creator<ScanInfo>() {
+                @Override
+                public ScanInfo createFromParcel(Parcel source) {
+                    int marker = source.readInt();
+                    if (marker == ScanResultMarker) {
+                        return new ScanInfo(ScanResult.CREATOR.createFromParcel(source));
+                    }
+                    else if (marker == OSUMarker) {
+                        return new ScanInfo(
+                                source.readLong(),
+                                source.readInt(),
+                                source.readString(),
+                                source.readString(),
+                                source.readString(),
+                                source.readString(),
+                                source.createByteArray(),
+                                source.readInt()
+                                );
+                    }
+                    else {
+                        throw new RuntimeException("Bad ScanInfo data");
+                    }
+                }
+
+                @Override
+                public ScanInfo[] newArray(int size) {
+                    return new ScanInfo[0];
+                }
+            };
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        if (mScanResult != null) {
+            dest.writeInt(ScanResultMarker);
+            mScanResult.writeToParcel(dest, flags);
+            return;
+        }
+
+        dest.writeInt(OSUMarker);
+        dest.writeLong(mBSSID);
+        dest.writeInt(mRSSI);
+        dest.writeString(mSSID);
+        dest.writeString(mName);
+        dest.writeString(mServiceDescription);
+        dest.writeString(mIconType);
+        dest.writeByteArray(mIconData);
+        dest.writeInt(mOSUIdentity);
+    }
+}
diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java
index cf88df4..ff8d6d4d 100644
--- a/wifi/java/android/net/wifi/WifiManager.java
+++ b/wifi/java/android/net/wifi/WifiManager.java
@@ -1315,6 +1315,30 @@
     }
 
     /**
+     * An augmented version of getScanResults that returns ScanResults as well as OSU information
+     * wrapped in ScanInfo objects.
+     * @return
+     */
+    public List<ScanInfo> getScanInfos() {
+        try {
+            return mService.getScanInfos(mContext.getOpPackageName());
+        } catch (RemoteException e) {
+            return null;
+        }
+    }
+
+    /**
+     * Notify the OSU framework about the currently selected OSU.
+     * @param osuID The OSU ID from ScanInfo.getOsuIdentity()
+     */
+    public void setOsuSelection(int osuID) {
+        try {
+            mService.setOsuSelection(osuID);
+        } catch (RemoteException e) {
+        }
+    }
+
+    /**
      * Check if scanning is always available.
      *
      * If this return {@code true}, apps can issue {@link #startScan} and fetch scan results