Merge "Add readEvent method to MtpDevice."
diff --git a/Android.mk b/Android.mk
index d48887d..9029f4e 100644
--- a/Android.mk
+++ b/Android.mk
@@ -303,6 +303,7 @@
 	core/java/com/android/internal/textservice/ISpellCheckerSessionListener.aidl \
 	core/java/com/android/internal/textservice/ITextServicesManager.aidl \
 	core/java/com/android/internal/textservice/ITextServicesSessionListener.aidl \
+	core/java/com/android/internal/view/IDropPermissionHolder.aidl \
 	core/java/com/android/internal/view/IInputContext.aidl \
 	core/java/com/android/internal/view/IInputContextCallback.aidl \
 	core/java/com/android/internal/view/IInputMethod.aidl \
@@ -482,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 adcc99a..2d133aa 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -323,6 +323,7 @@
     field public static final int buttonBarNeutralButtonStyle = 16843914; // 0x101048a
     field public static final int buttonBarPositiveButtonStyle = 16843913; // 0x1010489
     field public static final int buttonBarStyle = 16843566; // 0x101032e
+    field public static final int buttonGravity = 16844029; // 0x10104fd
     field public static final int buttonStyle = 16842824; // 0x1010048
     field public static final int buttonStyleInset = 16842826; // 0x101004a
     field public static final int buttonStyleSmall = 16842825; // 0x1010049
@@ -372,6 +373,7 @@
     field public static final int codes = 16843330; // 0x1010242
     field public static final int collapseColumns = 16843083; // 0x101014b
     field public static final int collapseContentDescription = 16843984; // 0x10104d0
+    field public static final int collapseIcon = 16844030; // 0x10104fe
     field public static final int color = 16843173; // 0x10101a5
     field public static final int colorAccent = 16843829; // 0x1010435
     field public static final int colorActivatedHighlight = 16843664; // 0x1010390
@@ -412,6 +414,7 @@
     field public static final int contentInsetRight = 16843862; // 0x1010456
     field public static final int contentInsetStart = 16843859; // 0x1010453
     field public static final int contextClickable = 16844007; // 0x10104e7
+    field public static final int contextPopupMenuStyle = 16844032; // 0x1010500
     field public static final int controlX1 = 16843772; // 0x10103fc
     field public static final int controlX2 = 16843774; // 0x10103fe
     field public static final int controlY1 = 16843773; // 0x10103fd
@@ -777,6 +780,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
@@ -809,6 +813,7 @@
     field public static final int marqueeRepeatLimit = 16843293; // 0x101021d
     field public static final int matchOrder = 16843855; // 0x101044f
     field public static final int max = 16843062; // 0x1010136
+    field public static final int maxButtonHeight = 16844028; // 0x10104fc
     field public static final int maxDate = 16843584; // 0x1010340
     field public static final int maxEms = 16843095; // 0x1010157
     field public static final int maxHeight = 16843040; // 0x1010120
@@ -1216,6 +1221,7 @@
     field public static final int textAppearanceListItemSmall = 16843679; // 0x101039f
     field public static final int textAppearanceMedium = 16842817; // 0x1010041
     field public static final int textAppearanceMediumInverse = 16842820; // 0x1010044
+    field public static final int textAppearancePopupMenuHeader = 16844033; // 0x1010501
     field public static final int textAppearanceSearchResultSubtitle = 16843424; // 0x10102a0
     field public static final int textAppearanceSearchResultTitle = 16843425; // 0x10102a1
     field public static final int textAppearanceSmall = 16842818; // 0x1010042
@@ -1284,6 +1290,11 @@
     field public static final int tintMode = 16843771; // 0x10103fb
     field public static final int title = 16843233; // 0x10101e1
     field public static final int titleCondensed = 16843234; // 0x10101e2
+    field public static final int titleMargin = 16844023; // 0x10104f7
+    field public static final int titleMarginBottom = 16844027; // 0x10104fb
+    field public static final int titleMarginEnd = 16844025; // 0x10104f9
+    field public static final int titleMarginStart = 16844024; // 0x10104f8
+    field public static final int titleMarginTop = 16844026; // 0x10104fa
     field public static final int titleTextAppearance = 16843822; // 0x101042e
     field public static final int titleTextColor = 16844003; // 0x10104e3
     field public static final int titleTextStyle = 16843512; // 0x10102f8
@@ -18248,6 +18259,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";
@@ -19154,6 +19166,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();
@@ -19354,6 +19382,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();
@@ -19369,6 +19398,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);
@@ -34808,6 +34838,7 @@
     method public int getAction();
     method public android.content.ClipData getClipData();
     method public android.content.ClipDescription getClipDescription();
+    method public android.view.DropPermissionHolder getDropPermissionHolder();
     method public java.lang.Object getLocalState();
     method public boolean getResult();
     method public float getX();
@@ -34822,6 +34853,14 @@
     field public static final android.os.Parcelable.Creator<android.view.DragEvent> CREATOR;
   }
 
+  public class DropPermissionHolder implements android.os.Parcelable {
+    method public int describeContents();
+    method public void grant();
+    method public void revoke();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.view.DropPermissionHolder> CREATOR;
+  }
+
   public class FocusFinder {
     method public android.view.View findNearestTouchable(android.view.ViewGroup, int, int, int, int[]);
     method public final android.view.View findNextFocus(android.view.ViewGroup, android.view.View, int);
@@ -36510,6 +36549,7 @@
     method public void setY(float);
     method public void setZ(float);
     method public boolean showContextMenu();
+    method public boolean showContextMenu(float, float);
     method public android.view.ActionMode startActionMode(android.view.ActionMode.Callback);
     method public android.view.ActionMode startActionMode(android.view.ActionMode.Callback, int);
     method public void startAnimation(android.view.animation.Animation);
@@ -36525,6 +36565,11 @@
     field public static final int ACCESSIBILITY_LIVE_REGION_NONE = 0; // 0x0
     field public static final int ACCESSIBILITY_LIVE_REGION_POLITE = 1; // 0x1
     field public static final android.util.Property<android.view.View, java.lang.Float> ALPHA;
+    field public static final int DRAG_FLAG_GLOBAL = 256; // 0x100
+    field public static final int DRAG_FLAG_GLOBAL_PERSISTABLE_URI_PERMISSION = 64; // 0x40
+    field public static final int DRAG_FLAG_GLOBAL_PREFIX_URI_PERMISSION = 128; // 0x80
+    field public static final int DRAG_FLAG_GLOBAL_URI_READ = 1; // 0x1
+    field public static final int DRAG_FLAG_GLOBAL_URI_WRITE = 2; // 0x2
     field public static final int DRAG_FLAG_OPAQUE = 512; // 0x200
     field public static final int DRAWING_CACHE_QUALITY_AUTO = 0; // 0x0
     field public static final int DRAWING_CACHE_QUALITY_HIGH = 1048576; // 0x100000
@@ -36971,6 +37016,7 @@
     method public void setTransitionGroup(boolean);
     method public boolean shouldDelayChildPressedState();
     method public boolean showContextMenuForChild(android.view.View);
+    method public boolean showContextMenuForChild(android.view.View, float, float);
     method public android.view.ActionMode startActionModeForChild(android.view.View, android.view.ActionMode.Callback);
     method public android.view.ActionMode startActionModeForChild(android.view.View, android.view.ActionMode.Callback, int);
     method public void startLayoutAnimation();
@@ -37092,6 +37138,7 @@
     method public abstract boolean requestSendAccessibilityEvent(android.view.View, android.view.accessibility.AccessibilityEvent);
     method public abstract void requestTransparentRegion(android.view.View);
     method public abstract boolean showContextMenuForChild(android.view.View);
+    method public abstract boolean showContextMenuForChild(android.view.View, float, float);
     method public abstract android.view.ActionMode startActionModeForChild(android.view.View, android.view.ActionMode.Callback);
     method public abstract android.view.ActionMode startActionModeForChild(android.view.View, android.view.ActionMode.Callback, int);
   }
@@ -40956,6 +41003,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);
@@ -41952,6 +42000,10 @@
     method public int getPopupTheme();
     method public java.lang.CharSequence getSubtitle();
     method public java.lang.CharSequence getTitle();
+    method public int getTitleMarginBottom();
+    method public int getTitleMarginEnd();
+    method public int getTitleMarginStart();
+    method public int getTitleMarginTop();
     method public boolean hasExpandedActionView();
     method public boolean hideOverflowMenu();
     method public void inflateMenu(int);
@@ -41977,6 +42029,11 @@
     method public void setSubtitleTextColor(int);
     method public void setTitle(int);
     method public void setTitle(java.lang.CharSequence);
+    method public void setTitleMargin(int, int, int, int);
+    method public void setTitleMarginBottom(int);
+    method public void setTitleMarginEnd(int);
+    method public void setTitleMarginStart(int);
+    method public void setTitleMarginTop(int);
     method public void setTitleTextAppearance(android.content.Context, int);
     method public void setTitleTextColor(int);
     method public boolean showOverflowMenu();
diff --git a/api/system-current.txt b/api/system-current.txt
index f09b2bb..93551da 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -415,6 +415,7 @@
     field public static final int buttonBarNeutralButtonStyle = 16843914; // 0x101048a
     field public static final int buttonBarPositiveButtonStyle = 16843913; // 0x1010489
     field public static final int buttonBarStyle = 16843566; // 0x101032e
+    field public static final int buttonGravity = 16844029; // 0x10104fd
     field public static final int buttonStyle = 16842824; // 0x1010048
     field public static final int buttonStyleInset = 16842826; // 0x101004a
     field public static final int buttonStyleSmall = 16842825; // 0x1010049
@@ -464,6 +465,7 @@
     field public static final int codes = 16843330; // 0x1010242
     field public static final int collapseColumns = 16843083; // 0x101014b
     field public static final int collapseContentDescription = 16843984; // 0x10104d0
+    field public static final int collapseIcon = 16844030; // 0x10104fe
     field public static final int color = 16843173; // 0x10101a5
     field public static final int colorAccent = 16843829; // 0x1010435
     field public static final int colorActivatedHighlight = 16843664; // 0x1010390
@@ -504,6 +506,7 @@
     field public static final int contentInsetRight = 16843862; // 0x1010456
     field public static final int contentInsetStart = 16843859; // 0x1010453
     field public static final int contextClickable = 16844007; // 0x10104e7
+    field public static final int contextPopupMenuStyle = 16844032; // 0x1010500
     field public static final int controlX1 = 16843772; // 0x10103fc
     field public static final int controlX2 = 16843774; // 0x10103fe
     field public static final int controlY1 = 16843773; // 0x10103fd
@@ -869,6 +872,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
@@ -901,6 +905,7 @@
     field public static final int marqueeRepeatLimit = 16843293; // 0x101021d
     field public static final int matchOrder = 16843855; // 0x101044f
     field public static final int max = 16843062; // 0x1010136
+    field public static final int maxButtonHeight = 16844028; // 0x10104fc
     field public static final int maxDate = 16843584; // 0x1010340
     field public static final int maxEms = 16843095; // 0x1010157
     field public static final int maxHeight = 16843040; // 0x1010120
@@ -1312,6 +1317,7 @@
     field public static final int textAppearanceListItemSmall = 16843679; // 0x101039f
     field public static final int textAppearanceMedium = 16842817; // 0x1010041
     field public static final int textAppearanceMediumInverse = 16842820; // 0x1010044
+    field public static final int textAppearancePopupMenuHeader = 16844033; // 0x1010501
     field public static final int textAppearanceSearchResultSubtitle = 16843424; // 0x10102a0
     field public static final int textAppearanceSearchResultTitle = 16843425; // 0x10102a1
     field public static final int textAppearanceSmall = 16842818; // 0x1010042
@@ -1380,6 +1386,11 @@
     field public static final int tintMode = 16843771; // 0x10103fb
     field public static final int title = 16843233; // 0x10101e1
     field public static final int titleCondensed = 16843234; // 0x10101e2
+    field public static final int titleMargin = 16844023; // 0x10104f7
+    field public static final int titleMarginBottom = 16844027; // 0x10104fb
+    field public static final int titleMarginEnd = 16844025; // 0x10104f9
+    field public static final int titleMarginStart = 16844024; // 0x10104f8
+    field public static final int titleMarginTop = 16844026; // 0x10104fa
     field public static final int titleTextAppearance = 16843822; // 0x101042e
     field public static final int titleTextColor = 16844003; // 0x10104e3
     field public static final int titleTextStyle = 16843512; // 0x10102f8
@@ -19760,6 +19771,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";
@@ -20907,6 +20919,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();
@@ -21132,6 +21160,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();
@@ -21151,6 +21180,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);
@@ -25665,8 +25695,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);
@@ -32840,6 +32870,7 @@
     field public static final java.lang.String ACTION_DEFAULT_DIALER_CHANGED = "android.telecom.action.DEFAULT_DIALER_CHANGED";
     field public static final java.lang.String ACTION_INCOMING_CALL = "android.telecom.action.INCOMING_CALL";
     field public static final java.lang.String ACTION_PHONE_ACCOUNT_REGISTERED = "android.telecom.action.PHONE_ACCOUNT_REGISTERED";
+    field public static final java.lang.String ACTION_PHONE_ACCOUNT_UNREGISTERED = "android.telecom.action.PHONE_ACCOUNT_UNREGISTERED";
     field public static final java.lang.String ACTION_SHOW_CALL_ACCESSIBILITY_SETTINGS = "android.telecom.action.SHOW_CALL_ACCESSIBILITY_SETTINGS";
     field public static final java.lang.String ACTION_SHOW_CALL_SETTINGS = "android.telecom.action.SHOW_CALL_SETTINGS";
     field public static final java.lang.String ACTION_SHOW_MISSED_CALLS_NOTIFICATION = "android.telecom.action.SHOW_MISSED_CALLS_NOTIFICATION";
@@ -37102,6 +37133,7 @@
     method public int getAction();
     method public android.content.ClipData getClipData();
     method public android.content.ClipDescription getClipDescription();
+    method public android.view.DropPermissionHolder getDropPermissionHolder();
     method public java.lang.Object getLocalState();
     method public boolean getResult();
     method public float getX();
@@ -37116,6 +37148,14 @@
     field public static final android.os.Parcelable.Creator<android.view.DragEvent> CREATOR;
   }
 
+  public class DropPermissionHolder implements android.os.Parcelable {
+    method public int describeContents();
+    method public void grant();
+    method public void revoke();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.view.DropPermissionHolder> CREATOR;
+  }
+
   public class FocusFinder {
     method public android.view.View findNearestTouchable(android.view.ViewGroup, int, int, int, int[]);
     method public final android.view.View findNextFocus(android.view.ViewGroup, android.view.View, int);
@@ -38804,6 +38844,7 @@
     method public void setY(float);
     method public void setZ(float);
     method public boolean showContextMenu();
+    method public boolean showContextMenu(float, float);
     method public android.view.ActionMode startActionMode(android.view.ActionMode.Callback);
     method public android.view.ActionMode startActionMode(android.view.ActionMode.Callback, int);
     method public void startAnimation(android.view.animation.Animation);
@@ -38819,6 +38860,11 @@
     field public static final int ACCESSIBILITY_LIVE_REGION_NONE = 0; // 0x0
     field public static final int ACCESSIBILITY_LIVE_REGION_POLITE = 1; // 0x1
     field public static final android.util.Property<android.view.View, java.lang.Float> ALPHA;
+    field public static final int DRAG_FLAG_GLOBAL = 256; // 0x100
+    field public static final int DRAG_FLAG_GLOBAL_PERSISTABLE_URI_PERMISSION = 64; // 0x40
+    field public static final int DRAG_FLAG_GLOBAL_PREFIX_URI_PERMISSION = 128; // 0x80
+    field public static final int DRAG_FLAG_GLOBAL_URI_READ = 1; // 0x1
+    field public static final int DRAG_FLAG_GLOBAL_URI_WRITE = 2; // 0x2
     field public static final int DRAG_FLAG_OPAQUE = 512; // 0x200
     field public static final int DRAWING_CACHE_QUALITY_AUTO = 0; // 0x0
     field public static final int DRAWING_CACHE_QUALITY_HIGH = 1048576; // 0x100000
@@ -39265,6 +39311,7 @@
     method public void setTransitionGroup(boolean);
     method public boolean shouldDelayChildPressedState();
     method public boolean showContextMenuForChild(android.view.View);
+    method public boolean showContextMenuForChild(android.view.View, float, float);
     method public android.view.ActionMode startActionModeForChild(android.view.View, android.view.ActionMode.Callback);
     method public android.view.ActionMode startActionModeForChild(android.view.View, android.view.ActionMode.Callback, int);
     method public void startLayoutAnimation();
@@ -39386,6 +39433,7 @@
     method public abstract boolean requestSendAccessibilityEvent(android.view.View, android.view.accessibility.AccessibilityEvent);
     method public abstract void requestTransparentRegion(android.view.View);
     method public abstract boolean showContextMenuForChild(android.view.View);
+    method public abstract boolean showContextMenuForChild(android.view.View, float, float);
     method public abstract android.view.ActionMode startActionModeForChild(android.view.View, android.view.ActionMode.Callback);
     method public abstract android.view.ActionMode startActionModeForChild(android.view.View, android.view.ActionMode.Callback, int);
   }
@@ -43564,6 +43612,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);
@@ -44560,6 +44609,10 @@
     method public int getPopupTheme();
     method public java.lang.CharSequence getSubtitle();
     method public java.lang.CharSequence getTitle();
+    method public int getTitleMarginBottom();
+    method public int getTitleMarginEnd();
+    method public int getTitleMarginStart();
+    method public int getTitleMarginTop();
     method public boolean hasExpandedActionView();
     method public boolean hideOverflowMenu();
     method public void inflateMenu(int);
@@ -44585,6 +44638,11 @@
     method public void setSubtitleTextColor(int);
     method public void setTitle(int);
     method public void setTitle(java.lang.CharSequence);
+    method public void setTitleMargin(int, int, int, int);
+    method public void setTitleMarginBottom(int);
+    method public void setTitleMarginEnd(int);
+    method public void setTitleMarginStart(int);
+    method public void setTitleMarginTop(int);
     method public void setTitleTextAppearance(android.content.Context, int);
     method public void setTitleTextColor(int);
     method public boolean showOverflowMenu();
diff --git a/cmds/am/src/com/android/commands/am/Am.java b/cmds/am/src/com/android/commands/am/Am.java
index 9185d7a..5c9fd51 100644
--- a/cmds/am/src/com/android/commands/am/Am.java
+++ b/cmds/am/src/com/android/commands/am/Am.java
@@ -19,6 +19,8 @@
 package com.android.commands.am;
 
 import static android.app.ActivityManager.DOCKED_STACK_ID;
+import static android.app.ActivityManager.RESIZE_MODE_SYSTEM;
+import static android.app.ActivityManager.RESIZE_MODE_USER;
 
 import android.app.ActivityManager;
 import android.app.ActivityManager.StackInfo;
@@ -161,6 +163,7 @@
                 "       am task drag-task-test <TASK_ID> <STEP_SIZE> [DELAY_MS] \n" +
                 "       am task size-task-test <TASK_ID> <STEP_SIZE> [DELAY_MS] \n" +
                 "       am get-config\n" +
+                "       am suppress-resize-config-changes <true|false>\n" +
                 "       am set-inactive [--user <USER_ID>] <PACKAGE> true|false\n" +
                 "       am get-inactive [--user <USER_ID>] <PACKAGE>\n" +
                 "       am send-trim-memory [--user <USER_ID>] <PROCESS>\n" +
@@ -324,6 +327,8 @@
                 "\n" +
                 "am get-config: retrieve the configuration and any recent configurations\n" +
                 "  of the device.\n" +
+                "am suppress-resize-config-changes: suppresses configuration changes due to\n" +
+                "  user resizing an activity/task.\n" +
                 "\n" +
                 "am set-inactive: sets the inactive state of an app.\n" +
                 "\n" +
@@ -451,6 +456,8 @@
             runTask();
         } else if (op.equals("get-config")) {
             runGetConfig();
+        } else if (op.equals("suppress-resize-config-changes")) {
+            runSuppressResizeConfigChanges();
         } else if (op.equals("set-inactive")) {
             runSetInactive();
         } else if (op.equals("get-inactive")) {
@@ -2271,7 +2278,8 @@
 
     private void taskResize(int taskId, Rect bounds, int delay_ms, boolean pretendUserResize) {
         try {
-            mAm.resizeTask(taskId, bounds, pretendUserResize);
+            final int resizeMode = pretendUserResize ? RESIZE_MODE_USER : RESIZE_MODE_SYSTEM;
+            mAm.resizeTask(taskId, bounds, resizeMode);
             Thread.sleep(delay_ms);
         } catch (RemoteException e) {
             System.err.println("Error changing task bounds: " + e);
@@ -2603,6 +2611,16 @@
         }
     }
 
+    private void runSuppressResizeConfigChanges() throws Exception {
+        boolean suppress = Boolean.valueOf(nextArgRequired());
+
+        try {
+            mAm.suppressResizeConfigChanges(suppress);
+        } catch (RemoteException e) {
+            System.err.println("Error suppressing resize config changes: " + e);
+        }
+    }
+
     private void runSetInactive() throws Exception {
         int userId = UserHandle.USER_CURRENT;
 
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index 9ef51c8..4191dce 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -468,6 +468,29 @@
      */
     public static final int DOCKED_STACK_CREATE_MODE_BOTTOM_OR_RIGHT = 1;
 
+
+    /**
+     * Input parameter to {@link android.app.IActivityManager#resizeTask} which indicates
+     * that the resize is from the window manager (instead of the user).
+     * @hide
+     */
+    public static final int RESIZE_MODE_SYSTEM = 0;
+
+    /**
+     * Input parameter to {@link android.app.IActivityManager#resizeTask} which indicates
+     * that the resize is initiated by the user (most likely via a drag action on the
+     * window's edge or corner).
+     * @hide
+     */
+    public static final int RESIZE_MODE_USER   = 1;
+
+    /**
+     * Input parameter to {@link android.app.IActivityManager#resizeTask} which indicates
+     * that the resize should be performed even if the bounds appears unchanged.
+     * @hide
+     */
+    public static final int RESIZE_MODE_FORCED = 2;
+
     /** @hide */
     public int getFrontActivityScreenCompatMode() {
         try {
diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java
index da6fc59..cb1a89f 100644
--- a/core/java/android/app/ActivityManagerNative.java
+++ b/core/java/android/app/ActivityManagerNative.java
@@ -2452,9 +2452,9 @@
         case RESIZE_TASK_TRANSACTION: {
             data.enforceInterface(IActivityManager.descriptor);
             int taskId = data.readInt();
-            final boolean resizedByUser = data.readInt() == 1;
+            int resizeMode = data.readInt();
             Rect r = Rect.CREATOR.createFromParcel(data);
-            resizeTask(taskId, r, resizedByUser);
+            resizeTask(taskId, r, resizeMode);
             reply.writeNoException();
             return true;
         }
@@ -2682,6 +2682,13 @@
             reportSizeConfigurations(token, horizontal, vertical);
             return true;
         }
+        case SUPPRESS_RESIZE_CONFIG_CHANGES_TRANSACTION: {
+            data.enforceInterface(IActivityManager.descriptor);
+            final boolean suppress = data.readInt() == 1;
+            suppressResizeConfigChanges(suppress);
+            reply.writeNoException();
+            return true;
+        }
         }
 
         return super.onTransact(code, data, reply, flags);
@@ -5900,13 +5907,13 @@
     }
 
     @Override
-    public void resizeTask(int taskId, Rect r, boolean resizedByUser) throws RemoteException
+    public void resizeTask(int taskId, Rect r, int resizeMode) throws RemoteException
     {
         Parcel data = Parcel.obtain();
         Parcel reply = Parcel.obtain();
         data.writeInterfaceToken(IActivityManager.descriptor);
         data.writeInt(taskId);
-        data.writeInt(resizedByUser ? 1 : 0);
+        data.writeInt(resizeMode);
         r.writeToParcel(data, 0);
         mRemote.transact(RESIZE_TASK_TRANSACTION, data, reply, 0);
         reply.readException();
@@ -6216,5 +6223,17 @@
         reply.recycle();
     }
 
+    @Override
+    public void suppressResizeConfigChanges(boolean suppress) throws RemoteException {
+        Parcel data = Parcel.obtain();
+        Parcel reply = Parcel.obtain();
+        data.writeInterfaceToken(IActivityManager.descriptor);
+        data.writeInt(suppress ? 1 : 0);
+        mRemote.transact(SUPPRESS_RESIZE_CONFIG_CHANGES_TRANSACTION, data, reply, 0);
+        reply.readException();
+        data.recycle();
+        reply.recycle();
+    }
+
     private IBinder mRemote;
 }
diff --git a/core/java/android/app/ActivityView.java b/core/java/android/app/ActivityView.java
index 9c0d931..371c923 100644
--- a/core/java/android/app/ActivityView.java
+++ b/core/java/android/app/ActivityView.java
@@ -239,6 +239,8 @@
         }
 
         mTextureView.setSurfaceTextureListener(null);
+
+        mThread.quit();
     }
 
     private void attachToSurfaceWhenReady() {
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index 0cd02dd..5544a71 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -93,8 +93,8 @@
 import java.util.Map;
 import java.util.Objects;
 
-/*package*/
-final class ApplicationPackageManager extends PackageManager {
+/** @hide */
+public class ApplicationPackageManager extends PackageManager {
     private static final String TAG = "ApplicationPackageManager";
     private final static boolean DEBUG_ICONS = false;
 
diff --git a/core/java/android/app/Fragment.java b/core/java/android/app/Fragment.java
index f70423f..b44aab7 100644
--- a/core/java/android/app/Fragment.java
+++ b/core/java/android/app/Fragment.java
@@ -63,6 +63,7 @@
     final boolean mRetainInstance;
     final boolean mDetached;
     final Bundle mArguments;
+    final boolean mHidden;
 
     Bundle mSavedFragmentState;
 
@@ -78,6 +79,7 @@
         mRetainInstance = frag.mRetainInstance;
         mDetached = frag.mDetached;
         mArguments = frag.mArguments;
+        mHidden = frag.mHidden;
     }
 
     public FragmentState(Parcel in) {
@@ -90,6 +92,7 @@
         mRetainInstance = in.readInt() != 0;
         mDetached = in.readInt() != 0;
         mArguments = in.readBundle();
+        mHidden = in.readInt() != 0;
         mSavedFragmentState = in.readBundle();
     }
 
@@ -117,6 +120,7 @@
         mInstance.mTag = mTag;
         mInstance.mRetainInstance = mRetainInstance;
         mInstance.mDetached = mDetached;
+        mInstance.mHidden = mHidden;
         mInstance.mFragmentManager = host.mFragmentManager;
         if (FragmentManagerImpl.DEBUG) Log.v(FragmentManagerImpl.TAG,
                 "Instantiated fragment " + mInstance);
@@ -138,6 +142,7 @@
         dest.writeInt(mRetainInstance ? 1 : 0);
         dest.writeInt(mDetached ? 1 : 0);
         dest.writeBundle(mArguments);
+        dest.writeInt(mHidden ? 1 : 0);
         dest.writeBundle(mSavedFragmentState);
     }
 
@@ -460,6 +465,9 @@
     // If set this fragment is being retained across the current config change.
     boolean mRetaining;
 
+    // If set this fragment's loaders are being retained across the current config change.
+    boolean mRetainLoader;
+
     // If set this fragment has menu items to contribute.
     boolean mHasMenu;
 
@@ -2407,7 +2415,7 @@
                 mLoaderManager = mHost.getLoaderManager(mWho, mLoadersStarted, false);
             }
             if (mLoaderManager != null) {
-                if (mRetaining) {
+                if (mRetainLoader) {
                     mLoaderManager.doRetain();
                 } else {
                     mLoaderManager.doStop();
diff --git a/core/java/android/app/FragmentController.java b/core/java/android/app/FragmentController.java
index 28dadfa..1b45137 100644
--- a/core/java/android/app/FragmentController.java
+++ b/core/java/android/app/FragmentController.java
@@ -341,6 +341,7 @@
      */
     public void doLoaderStop(boolean retain) {
         mHost.doLoaderStop(retain);
+        mHost.mFragmentManager.setRetainLoader(retain);
     }
 
     /**
diff --git a/core/java/android/app/FragmentManager.java b/core/java/android/app/FragmentManager.java
index 132ffef..51d6132 100644
--- a/core/java/android/app/FragmentManager.java
+++ b/core/java/android/app/FragmentManager.java
@@ -869,6 +869,17 @@
         }
     }
 
+    void setRetainLoader(boolean retain) {
+        if (mActive != null) {
+            for (int i=0; i<mActive.size(); i++) {
+                Fragment f = mActive.get(i);
+                if (f != null) {
+                    f.mRetainLoader = retain;
+                }
+            }
+        }
+    }
+
     void moveToState(Fragment f, int newState, int transit, int transitionStyle,
             boolean keepActive) {
         if (DEBUG && false) Log.v(TAG, "moveToState: " + f
diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java
index 7bd832b..478fdd1 100644
--- a/core/java/android/app/IActivityManager.java
+++ b/core/java/android/app/IActivityManager.java
@@ -491,7 +491,7 @@
     public void setTaskDescription(IBinder token, ActivityManager.TaskDescription values)
             throws RemoteException;
     public void setTaskResizeable(int taskId, boolean resizeable) throws RemoteException;
-    public void resizeTask(int taskId, Rect bounds, boolean resizedByUser) throws RemoteException;
+    public void resizeTask(int taskId, Rect bounds, int resizeMode) throws RemoteException;
 
     public Rect getTaskBounds(int taskId) throws RemoteException;
     public Bitmap getTaskDescriptionIcon(String filename) throws RemoteException;
@@ -535,6 +535,8 @@
     public int getActivityStackId(IBinder token) throws RemoteException;
     public void moveActivityToStack(IBinder token, int stackId) throws RemoteException;
 
+    public void suppressResizeConfigChanges(boolean suppress) throws RemoteException;
+
     /*
      * Private non-Binder interfaces
      */
@@ -891,4 +893,5 @@
     int MOVE_ACTIVITY_TO_STACK_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 344;
     int REPORT_SIZE_CONFIGURATIONS = IBinder.FIRST_CALL_TRANSACTION + 345;
     int MOVE_TASK_TO_DOCKED_STACK_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 346;
+    int SUPPRESS_RESIZE_CONFIG_CHANGES_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 347;
 }
diff --git a/core/java/android/app/StatusBarManager.java b/core/java/android/app/StatusBarManager.java
index 31d1ab7..5e8ad68 100644
--- a/core/java/android/app/StatusBarManager.java
+++ b/core/java/android/app/StatusBarManager.java
@@ -90,6 +90,9 @@
     public static final int WINDOW_STATE_HIDING = 1;
     public static final int WINDOW_STATE_HIDDEN = 2;
 
+    public static final int CAMERA_LAUNCH_SOURCE_WIGGLE = 0;
+    public static final int CAMERA_LAUNCH_SOURCE_POWER_DOUBLE_TAP = 1;
+
     private Context mContext;
     private IStatusBarService mService;
     private IBinder mToken = new Binder();
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index 3d264c6..288a2cb 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -240,7 +240,7 @@
                 new CachedServiceFetcher<DevicePolicyManager>() {
             @Override
             public DevicePolicyManager createService(ContextImpl ctx) {
-                return DevicePolicyManager.create(ctx, ctx.mMainThread.getHandler());
+                return DevicePolicyManager.create(ctx);
             }});
 
         registerService(Context.DOWNLOAD_SERVICE, DownloadManager.class,
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/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index ac50699..e6484e9 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -32,7 +32,6 @@
 import android.graphics.Bitmap;
 import android.net.ProxyInfo;
 import android.os.Bundle;
-import android.os.Handler;
 import android.os.PersistableBundle;
 import android.os.Process;
 import android.os.RemoteCallback;
@@ -45,6 +44,7 @@
 import android.service.restrictions.RestrictionsReceiver;
 import android.util.Log;
 
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.org.conscrypt.TrustedCertificateStore;
 
 import org.xmlpull.v1.XmlPullParserException;
@@ -87,18 +87,30 @@
     private final Context mContext;
     private final IDevicePolicyManager mService;
 
-    private DevicePolicyManager(Context context, Handler handler) {
-        mContext = context;
-        mService = IDevicePolicyManager.Stub.asInterface(
-                ServiceManager.getService(Context.DEVICE_POLICY_SERVICE));
+    private DevicePolicyManager(Context context) {
+        this(context, IDevicePolicyManager.Stub.asInterface(
+                        ServiceManager.getService(Context.DEVICE_POLICY_SERVICE)));
     }
 
     /** @hide */
-    public static DevicePolicyManager create(Context context, Handler handler) {
-        DevicePolicyManager me = new DevicePolicyManager(context, handler);
+    @VisibleForTesting
+    protected DevicePolicyManager(Context context, IDevicePolicyManager service) {
+        mContext = context;
+        mService = service;
+    }
+
+    /** @hide */
+    public static DevicePolicyManager create(Context context) {
+        DevicePolicyManager me = new DevicePolicyManager(context);
         return me.mService != null ? me : null;
     }
 
+    /** @hide test will override it. */
+    @VisibleForTesting
+    protected int myUserId() {
+        return UserHandle.myUserId();
+    }
+
     /**
      * Activity action: Starts the provisioning flow which sets up a managed profile.
      *
@@ -823,7 +835,7 @@
      * active (enabled) in the system.
      */
     public boolean isAdminActive(@NonNull ComponentName admin) {
-        return isAdminActiveAsUser(admin, UserHandle.myUserId());
+        return isAdminActiveAsUser(admin, myUserId());
     }
 
     /**
@@ -863,7 +875,7 @@
      * returned.
      */
     public List<ComponentName> getActiveAdmins() {
-        return getActiveAdminsAsUser(UserHandle.myUserId());
+        return getActiveAdminsAsUser(myUserId());
     }
 
     /**
@@ -889,7 +901,7 @@
     public boolean packageHasActiveAdmins(String packageName) {
         if (mService != null) {
             try {
-                return mService.packageHasActiveAdmins(packageName, UserHandle.myUserId());
+                return mService.packageHasActiveAdmins(packageName, myUserId());
             } catch (RemoteException e) {
                 Log.w(TAG, "Failed talking with device policy service", e);
             }
@@ -906,7 +918,7 @@
     public void removeActiveAdmin(@NonNull ComponentName admin) {
         if (mService != null) {
             try {
-                mService.removeActiveAdmin(admin, UserHandle.myUserId());
+                mService.removeActiveAdmin(admin, myUserId());
             } catch (RemoteException e) {
                 Log.w(TAG, "Failed talking with device policy service", e);
             }
@@ -925,7 +937,7 @@
     public boolean hasGrantedPolicy(@NonNull ComponentName admin, int usesPolicy) {
         if (mService != null) {
             try {
-                return mService.hasGrantedPolicy(admin, usesPolicy, UserHandle.myUserId());
+                return mService.hasGrantedPolicy(admin, usesPolicy, myUserId());
             } catch (RemoteException e) {
                 Log.w(TAG, "Failed talking with device policy service", e);
             }
@@ -1040,7 +1052,7 @@
      * all admins.
      */
     public int getPasswordQuality(@Nullable ComponentName admin) {
-        return getPasswordQuality(admin, UserHandle.myUserId());
+        return getPasswordQuality(admin, myUserId());
     }
 
     /** @hide per-user version */
@@ -1093,7 +1105,7 @@
      * all admins.
      */
     public int getPasswordMinimumLength(@Nullable ComponentName admin) {
-        return getPasswordMinimumLength(admin, UserHandle.myUserId());
+        return getPasswordMinimumLength(admin, myUserId());
     }
 
     /** @hide per-user version */
@@ -1154,7 +1166,7 @@
      *         password.
      */
     public int getPasswordMinimumUpperCase(@Nullable ComponentName admin) {
-        return getPasswordMinimumUpperCase(admin, UserHandle.myUserId());
+        return getPasswordMinimumUpperCase(admin, myUserId());
     }
 
     /** @hide per-user version */
@@ -1215,7 +1227,7 @@
      *         password.
      */
     public int getPasswordMinimumLowerCase(@Nullable ComponentName admin) {
-        return getPasswordMinimumLowerCase(admin, UserHandle.myUserId());
+        return getPasswordMinimumLowerCase(admin, myUserId());
     }
 
     /** @hide per-user version */
@@ -1273,7 +1285,7 @@
      * @return The minimum number of letters required in the password.
      */
     public int getPasswordMinimumLetters(@Nullable ComponentName admin) {
-        return getPasswordMinimumLetters(admin, UserHandle.myUserId());
+        return getPasswordMinimumLetters(admin, myUserId());
     }
 
     /** @hide per-user version */
@@ -1332,7 +1344,7 @@
      * @return The minimum number of numerical digits required in the password.
      */
     public int getPasswordMinimumNumeric(@Nullable ComponentName admin) {
-        return getPasswordMinimumNumeric(admin, UserHandle.myUserId());
+        return getPasswordMinimumNumeric(admin, myUserId());
     }
 
     /** @hide per-user version */
@@ -1390,7 +1402,7 @@
      * @return The minimum number of symbols required in the password.
      */
     public int getPasswordMinimumSymbols(@Nullable ComponentName admin) {
-        return getPasswordMinimumSymbols(admin, UserHandle.myUserId());
+        return getPasswordMinimumSymbols(admin, myUserId());
     }
 
     /** @hide per-user version */
@@ -1449,7 +1461,7 @@
      * @return The minimum number of letters required in the password.
      */
     public int getPasswordMinimumNonLetter(@Nullable ComponentName admin) {
-        return getPasswordMinimumNonLetter(admin, UserHandle.myUserId());
+        return getPasswordMinimumNonLetter(admin, myUserId());
     }
 
     /** @hide per-user version */
@@ -1540,7 +1552,7 @@
     public long getPasswordExpirationTimeout(@Nullable ComponentName admin) {
         if (mService != null) {
             try {
-                return mService.getPasswordExpirationTimeout(admin, UserHandle.myUserId());
+                return mService.getPasswordExpirationTimeout(admin, myUserId());
             } catch (RemoteException e) {
                 Log.w(TAG, "Failed talking with device policy service", e);
             }
@@ -1561,7 +1573,7 @@
     public long getPasswordExpiration(@Nullable ComponentName admin) {
         if (mService != null) {
             try {
-                return mService.getPasswordExpiration(admin, UserHandle.myUserId());
+                return mService.getPasswordExpiration(admin, myUserId());
             } catch (RemoteException e) {
                 Log.w(TAG, "Failed talking with device policy service", e);
             }
@@ -1577,7 +1589,7 @@
      * @return The length of the password history
      */
     public int getPasswordHistoryLength(@Nullable ComponentName admin) {
-        return getPasswordHistoryLength(admin, UserHandle.myUserId());
+        return getPasswordHistoryLength(admin, myUserId());
     }
 
     /** @hide per-user version */
@@ -1617,7 +1629,7 @@
     public boolean isActivePasswordSufficient() {
         if (mService != null) {
             try {
-                return mService.isActivePasswordSufficient(UserHandle.myUserId());
+                return mService.isActivePasswordSufficient(myUserId());
             } catch (RemoteException e) {
                 Log.w(TAG, "Failed talking with device policy service", e);
             }
@@ -1636,7 +1648,7 @@
     public int getCurrentFailedPasswordAttempts() {
         if (mService != null) {
             try {
-                return mService.getCurrentFailedPasswordAttempts(UserHandle.myUserId());
+                return mService.getCurrentFailedPasswordAttempts(myUserId());
             } catch (RemoteException e) {
                 Log.w(TAG, "Failed talking with device policy service", e);
             }
@@ -1698,7 +1710,7 @@
      * all admins.
      */
     public int getMaximumFailedPasswordsForWipe(@Nullable ComponentName admin) {
-        return getMaximumFailedPasswordsForWipe(admin, UserHandle.myUserId());
+        return getMaximumFailedPasswordsForWipe(admin, myUserId());
     }
 
     /** @hide per-user version */
@@ -1818,7 +1830,7 @@
      * all admins if admin is null. Returns 0 if there are no restrictions.
      */
     public long getMaximumTimeToLock(@Nullable ComponentName admin) {
-        return getMaximumTimeToLock(admin, UserHandle.myUserId());
+        return getMaximumTimeToLock(admin, myUserId());
     }
 
     /** @hide per-user version */
@@ -1881,7 +1893,7 @@
     public void wipeData(int flags) {
         if (mService != null) {
             try {
-                mService.wipeData(flags, UserHandle.myUserId());
+                mService.wipeData(flags, myUserId());
             } catch (RemoteException e) {
                 Log.w(TAG, "Failed talking with device policy service", e);
             }
@@ -1995,7 +2007,7 @@
     public ComponentName getGlobalProxyAdmin() {
         if (mService != null) {
             try {
-                return mService.getGlobalProxyAdmin(UserHandle.myUserId());
+                return mService.getGlobalProxyAdmin(myUserId());
             } catch (RemoteException e) {
                 Log.w(TAG, "Failed talking with device policy service", e);
             }
@@ -2145,7 +2157,7 @@
     public boolean getStorageEncryption(@Nullable ComponentName admin) {
         if (mService != null) {
             try {
-                return mService.getStorageEncryption(admin, UserHandle.myUserId());
+                return mService.getStorageEncryption(admin, myUserId());
             } catch (RemoteException e) {
                 Log.w(TAG, "Failed talking with device policy service", e);
             }
@@ -2173,7 +2185,7 @@
      * or {@link #ENCRYPTION_STATUS_ACTIVE}.
      */
     public int getStorageEncryptionStatus() {
-        return getStorageEncryptionStatus(UserHandle.myUserId());
+        return getStorageEncryptionStatus(myUserId());
     }
 
     /** @hide per-user version */
@@ -2410,7 +2422,7 @@
      * have disabled the camera
      */
     public boolean getCameraDisabled(@Nullable ComponentName admin) {
-        return getCameraDisabled(admin, UserHandle.myUserId());
+        return getCameraDisabled(admin, myUserId());
     }
 
     /** @hide per-user version */
@@ -2457,7 +2469,7 @@
      * have disabled screen capture.
      */
     public boolean getScreenCaptureDisabled(@Nullable ComponentName admin) {
-        return getScreenCaptureDisabled(admin, UserHandle.myUserId());
+        return getScreenCaptureDisabled(admin, myUserId());
     }
 
     /** @hide per-user version */
@@ -2557,7 +2569,7 @@
      * for a list.
      */
     public int getKeyguardDisabledFeatures(@Nullable ComponentName admin) {
-        return getKeyguardDisabledFeatures(admin, UserHandle.myUserId());
+        return getKeyguardDisabledFeatures(admin, myUserId());
     }
 
     /** @hide per-user version */
@@ -2590,7 +2602,7 @@
      * @hide
      */
     public void setActiveAdmin(@NonNull ComponentName policyReceiver, boolean refreshing) {
-        setActiveAdmin(policyReceiver, refreshing, UserHandle.myUserId());
+        setActiveAdmin(policyReceiver, refreshing, myUserId());
     }
 
     /**
@@ -2627,7 +2639,7 @@
     public void getRemoveWarning(@Nullable ComponentName admin, RemoteCallback result) {
         if (mService != null) {
             try {
-                mService.getRemoveWarning(admin, result, UserHandle.myUserId());
+                mService.getRemoveWarning(admin, result, myUserId());
             } catch (RemoteException e) {
                 Log.w(TAG, "Failed talking with device policy service", e);
             }
@@ -2956,7 +2968,7 @@
             throws IllegalArgumentException {
         if (mService != null) {
             try {
-                final int myUserId = UserHandle.myUserId();
+                final int myUserId = myUserId();
                 mService.setActiveAdmin(admin, false, myUserId);
                 return mService.setProfileOwner(admin, ownerName, myUserId);
             } catch (RemoteException re) {
@@ -3301,7 +3313,7 @@
      */
     public List<PersistableBundle> getTrustAgentConfiguration(@Nullable ComponentName admin,
             @NonNull ComponentName agent) {
-        return getTrustAgentConfiguration(admin, agent, UserHandle.myUserId());
+        return getTrustAgentConfiguration(admin, agent, myUserId());
     }
 
     /** @hide per-user version */
@@ -3927,7 +3939,7 @@
      * @see #setAccountManagementDisabled
      */
     public String[] getAccountTypesWithManagementDisabled() {
-        return getAccountTypesWithManagementDisabledAsUser(UserHandle.myUserId());
+        return getAccountTypesWithManagementDisabledAsUser(myUserId());
     }
 
     /**
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index b08db20..670ca80 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -3033,6 +3033,39 @@
     public static final String EXTRA_PROCESS_TEXT_READONLY =
             "android.intent.extra.PROCESS_TEXT_READONLY";
 
+    /**
+     * Broadcast action: reports when a new thermal event has been reached. When the device
+     * is reaching its maximum temperatue, the thermal level reported
+     * {@hide}
+     */
+    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+    public static final String ACTION_THERMAL_EVENT = "android.intent.action.THERMAL_EVENT";
+
+    /** {@hide} */
+    public static final String EXTRA_THERMAL_STATE = "android.intent.extra.THERMAL_STATE";
+
+    /**
+     * Thermal state when the device is normal. This state is sent in the
+     * {@link ACTION_THERMAL_EVENT} broadcast as {@link EXTRA_THERMAL_STATE}.
+     * {@hide}
+     */
+    public static final int EXTRA_THERMAL_STATE_NORMAL = 0;
+
+    /**
+     * Thermal state where the device is approaching its maximum threshold. This state is sent in
+     * the {@link ACTION_THERMAL_EVENT} broadcast as {@link EXTRA_THERMAL_STATE}.
+     * {@hide}
+     */
+    public static final int EXTRA_THERMAL_STATE_WARNING = 1;
+
+    /**
+     * Thermal state where the device has reached its maximum threshold. This state is sent in the
+     * {@link ACTION_THERMAL_EVENT} broadcast as {@link EXTRA_THERMAL_STATE}.
+     * {@hide}
+     */
+    public static final int EXTRA_THERMAL_STATE_EXCEEDED = 2;
+
+
     // ---------------------------------------------------------------------
     // ---------------------------------------------------------------------
     // Standard intent categories (see addCategory()).
diff --git a/core/java/android/content/pm/ActivityInfo.java b/core/java/android/content/pm/ActivityInfo.java
index a59f429..a121b4d 100644
--- a/core/java/android/content/pm/ActivityInfo.java
+++ b/core/java/android/content/pm/ActivityInfo.java
@@ -33,12 +33,16 @@
  */
 public class ActivityInfo extends ComponentInfo
         implements Parcelable {
+
+     // NOTE: When adding new data members be sure to update the copy-constructor, Parcel
+     // constructor, and writeToParcel.
+
     /**
      * A style resource identifier (in the package's resources) of this
      * activity's theme.  From the "theme" attribute or, if not set, 0.
      */
     public int theme;
-    
+
     /**
      * Constant corresponding to <code>standard</code> in
      * the {@link android.R.attr#launchMode} attribute.
@@ -707,6 +711,7 @@
         super(orig);
         theme = orig.theme;
         launchMode = orig.launchMode;
+        documentLaunchMode = orig.documentLaunchMode;
         permission = orig.permission;
         taskAffinity = orig.taskAffinity;
         targetActivity = orig.targetActivity;
@@ -788,6 +793,7 @@
         super.writeToParcel(dest, parcelableFlags);
         dest.writeInt(theme);
         dest.writeInt(launchMode);
+        dest.writeInt(documentLaunchMode);
         dest.writeString(permission);
         dest.writeString(taskAffinity);
         dest.writeString(targetActivity);
@@ -827,6 +833,7 @@
         super(source);
         theme = source.readInt();
         launchMode = source.readInt();
+        documentLaunchMode = source.readInt();
         permission = source.readString();
         taskAffinity = source.readString();
         targetActivity = source.readString();
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index 968f9b2..04b1a3b 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -114,6 +114,12 @@
     /** File name in an APK for the Android manifest. */
     private static final String ANDROID_MANIFEST_FILENAME = "AndroidManifest.xml";
 
+    /**
+     * File name in an APK for bytecode.  There may be additional bytecode files
+     * but this one is always required for an APK that has code.
+     */
+    private static final String BYTECODE_FILENAME = "classes.dex";
+
     /** Path prefix for apps on expanded storage */
     private static final String MNT_EXPAND = "/mnt/expand/";
 
@@ -615,6 +621,7 @@
     public final static int PARSE_IS_PRIVILEGED = 1<<7;
     public final static int PARSE_COLLECT_CERTIFICATES = 1<<8;
     public final static int PARSE_TRUSTED_OVERLAY = 1<<9;
+    public final static int PARSE_ENFORCE_CODE = 1<<10;
 
     private static final Comparator<String> sSplitNameComparator = new SplitNameComparator();
 
@@ -1066,8 +1073,11 @@
 
     private static void collectCertificates(Package pkg, File apkFile, int flags)
             throws PackageParserException {
+        final boolean requireCode = ((flags & PARSE_ENFORCE_CODE) != 0)
+                && ((pkg.applicationInfo.flags & ApplicationInfo.FLAG_HAS_CODE) != 0);
         final String apkPath = apkFile.getAbsolutePath();
 
+        boolean codeFound = false;
         StrictJarFile jarFile = null;
         try {
             jarFile = new StrictJarFile(apkPath);
@@ -1089,13 +1099,23 @@
                     final ZipEntry entry = i.next();
 
                     if (entry.isDirectory()) continue;
-                    if (entry.getName().startsWith("META-INF/")) continue;
-                    if (entry.getName().equals(ANDROID_MANIFEST_FILENAME)) continue;
+
+                    final String entryName = entry.getName();
+                    if (entryName.startsWith("META-INF/")) continue;
+                    if (entryName.equals(ANDROID_MANIFEST_FILENAME)) continue;
+                    if (entryName.equals(BYTECODE_FILENAME)) {
+                        codeFound = true;
+                    }
 
                     toVerify.add(entry);
                 }
             }
 
+            if (!codeFound && requireCode) {
+                throw new PackageParserException(INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
+                        "Package " + apkPath + " code is missing");
+            }
+
             // Verify that entries are signed consistently with the first entry
             // we encountered. Note that for splits, certificates may have
             // already been populated during an earlier parse of a base APK.
@@ -4561,6 +4581,17 @@
             return applicationInfo.isUpdatedSystemApp();
         }
 
+        /**
+         * @hide
+         */
+        public boolean canHaveOatDir() {
+            // The following app types CANNOT have oat directory
+            // - non-updated system apps
+            // - forward-locked apps or apps installed in ASEC containers
+            return (!isSystemApp() || isUpdatedSystemApp())
+                    && !isForwardLocked() && !applicationInfo.isExternalAsec();
+        }
+
         public String toString() {
             return "Package{"
                 + Integer.toHexString(System.identityHashCode(this))
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/hardware/display/DisplayManagerGlobal.java b/core/java/android/hardware/display/DisplayManagerGlobal.java
index 21ba7bd..121a187 100644
--- a/core/java/android/hardware/display/DisplayManagerGlobal.java
+++ b/core/java/android/hardware/display/DisplayManagerGlobal.java
@@ -359,6 +359,14 @@
         }
     }
 
+    public void requestColorTransform(int displayId, int colorTransformId) {
+        try {
+            mDm.requestColorTransform(displayId, colorTransformId);
+        } catch (RemoteException ex) {
+            Log.e(TAG, "Failed to request color transform.", ex);
+        }
+    }
+
     public VirtualDisplay createVirtualDisplay(Context context, MediaProjection projection,
             String name, int width, int height, int densityDpi, Surface surface, int flags,
             VirtualDisplay.Callback callback, Handler handler) {
diff --git a/core/java/android/hardware/display/IDisplayManager.aidl b/core/java/android/hardware/display/IDisplayManager.aidl
index 4486dd4..8a1abf1 100644
--- a/core/java/android/hardware/display/IDisplayManager.aidl
+++ b/core/java/android/hardware/display/IDisplayManager.aidl
@@ -59,6 +59,9 @@
     // No permissions required.
     WifiDisplayStatus getWifiDisplayStatus();
 
+    // Requires CONFIGURE_DISPLAY_COLOR_TRANSFORM
+    void requestColorTransform(int displayId, int colorTransformId);
+
     // Requires CAPTURE_VIDEO_OUTPUT, CAPTURE_SECURE_VIDEO_OUTPUT, or an appropriate
     // MediaProjection token for certain combinations of flags.
     int createVirtualDisplay(in IVirtualDisplayCallback callback,
diff --git a/core/java/android/hardware/fingerprint/FingerprintManager.java b/core/java/android/hardware/fingerprint/FingerprintManager.java
index 122df23..04caa8f 100644
--- a/core/java/android/hardware/fingerprint/FingerprintManager.java
+++ b/core/java/android/hardware/fingerprint/FingerprintManager.java
@@ -772,7 +772,7 @@
             if (mRemovalCallback != null) {
                 int reqFingerId = mRemovalFingerprint.getFingerId();
                 int reqGroupId = mRemovalFingerprint.getGroupId();
-                if (fingerId != reqFingerId) {
+                if (reqFingerId != 0  &&  fingerId != reqFingerId) {
                     Log.w(TAG, "Finger id didn't match: " + fingerId + " != " + reqFingerId);
                 }
                 if (groupId != reqGroupId) {
@@ -962,4 +962,3 @@
     };
 
 }
-
diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java
index 6e28982..8ab8991 100644
--- a/core/java/android/inputmethodservice/InputMethodService.java
+++ b/core/java/android/inputmethodservice/InputMethodService.java
@@ -436,9 +436,12 @@
                 try {
                     showWindow(true);
                 } catch (BadTokenException e) {
-                    if (DEBUG) Log.v(TAG, "BadTokenException: IME is done.");
-                    mWindowVisible = false;
-                    mWindowAdded = false;
+                    // We have ignored BadTokenException here since Jelly Bean MR-2 (API Level 18).
+                    // We could ignore BadTokenException in InputMethodService#showWindow() instead,
+                    // but it may break assumptions for those who override #showWindow() that we can
+                    // detect errors in #showWindow() by checking BadTokenException.
+                    // TODO: Investigate its feasibility.  Update JavaDoc of #showWindow() of
+                    // whether it's OK to override #showWindow() or not.
                 }
             }
             clearInsetOfPreviousIme();
@@ -1445,7 +1448,19 @@
             mWindowWasVisible = mWindowVisible;
             mInShowWindow = true;
             showWindowInner(showInput);
+        } catch (BadTokenException e) {
+            // BadTokenException is a normal consequence in certain situations, e.g., swapping IMEs
+            // while there is a DO_SHOW_SOFT_INPUT message in the IIMethodWrapper queue.
+            if (DEBUG) Log.v(TAG, "BadTokenException: IME is done.");
+            mWindowVisible = false;
+            mWindowAdded = false;
+            // Rethrow the exception to preserve the existing behavior.  Some IMEs may have directly
+            // called this method and relied on this exception for some clean-up tasks.
+            // TODO: Give developers a clear guideline of whether it's OK to call this method or
+            // InputMethodManager#showSoftInputFromInputMethod() should always be used instead.
+            throw e;
         } finally {
+            // TODO: Is it OK to set true when we get BadTokenException?
             mWindowWasVisible = true;
             mInShowWindow = false;
         }
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/BatteryStats.java b/core/java/android/os/BatteryStats.java
index bad94fc..8e86a53 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -463,13 +463,15 @@
         public abstract long getCpuPowerMaUs(int which);
 
         /**
-         * Returns the approximate cpu time (in milliseconds) spent at a certain CPU speed.
+         * Returns the approximate cpu time (in milliseconds) spent at a certain CPU speed for a
+         * given CPU cluster.
+         * @param cluster the index of the CPU cluster.
          * @param step the index of the CPU speed. This is not the actual speed of the CPU.
          * @param which one of STATS_SINCE_CHARGED, STATS_SINCE_UNPLUGGED, or STATS_CURRENT.
-         * @see BatteryStats#getCpuSpeedSteps()
+         * @see PowerProfile.getNumCpuClusters()
+         * @see PowerProfile.getNumSpeedStepsInCpuCluster(int)
          */
-        @Deprecated
-        public abstract long getTimeAtCpuSpeed(int step, int which);
+        public abstract long getTimeAtCpuSpeed(int cluster, int step, int which);
 
         public static abstract class Sensor {
             /*
@@ -2276,9 +2278,6 @@
 
     public abstract Map<String, ? extends Timer> getKernelWakelockStats();
 
-    /** Returns the number of different speeds that the CPU can run at */
-    public abstract int getCpuSpeedSteps();
-
     public abstract void writeToParcelWithoutUids(Parcel out, int flags);
 
     private final static void formatTimeRaw(StringBuilder out, long seconds) {
diff --git a/core/java/android/os/PowerManagerInternal.java b/core/java/android/os/PowerManagerInternal.java
index 70cff00..b6d0fcb 100644
--- a/core/java/android/os/PowerManagerInternal.java
+++ b/core/java/android/os/PowerManagerInternal.java
@@ -53,6 +53,15 @@
      */
     public static final int WAKEFULNESS_DOZING = 3;
 
+
+    /**
+     * Power hint: The user is interacting with the device. The corresponding data field must be
+     * the expected duration of the fling, or 0 if unknown.
+     *
+     * This must be kept in sync with the values in hardware/libhardware/include/hardware/power.h
+     */
+    public static final int POWER_HINT_INTERACTION = 2;
+
     public static String wakefulnessToString(int wakefulness) {
         switch (wakefulness) {
             case WAKEFULNESS_ASLEEP:
@@ -148,4 +157,6 @@
     public abstract void updateUidProcState(int uid, int procState);
 
     public abstract void uidGone(int uid);
+
+    public abstract void powerHint(int hintId, int data);
 }
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/UserHandle.java b/core/java/android/os/UserHandle.java
index 48ede4f..213e083 100644
--- a/core/java/android/os/UserHandle.java
+++ b/core/java/android/os/UserHandle.java
@@ -57,15 +57,15 @@
 
     /**
      * @hide A user id constant to indicate the "owner" user of the device
-     * @deprecated Consider using either USER_SYSTEM constant or
-     * UserInfo.isPrimary().
+     * @deprecated Consider using either {@link UserHandle#USER_SYSTEM} constant or
+     * check the target user's flag {@link android.content.pm.UserInfo#isAdmin}.
      */
     public static final int USER_OWNER = 0;
 
     /**
      * @hide A user handle to indicate the primary/owner user of the device
-     * @deprecated Consider using either SYSTEM constant or
-     * UserInfo.isPrimary().
+     * @deprecated Consider using either {@link UserHandle#SYSTEM} constant or
+     * check the target user's flag {@link android.content.pm.UserInfo#isAdmin}.
      */
     public static final UserHandle OWNER = new UserHandle(USER_OWNER);
 
@@ -90,7 +90,7 @@
      * user.
      * @hide
      */
-    public static final boolean isSameUser(int uid1, int uid2) {
+    public static boolean isSameUser(int uid1, int uid2) {
         return getUserId(uid1) == getUserId(uid2);
     }
 
@@ -102,12 +102,12 @@
      * @return whether the appId is the same for both uids
      * @hide
      */
-    public static final boolean isSameApp(int uid1, int uid2) {
+    public static boolean isSameApp(int uid1, int uid2) {
         return getAppId(uid1) == getAppId(uid2);
     }
 
     /** @hide */
-    public static final boolean isIsolated(int uid) {
+    public static boolean isIsolated(int uid) {
         if (uid > 0) {
             final int appId = getAppId(uid);
             return appId >= Process.FIRST_ISOLATED_UID && appId <= Process.LAST_ISOLATED_UID;
@@ -130,7 +130,7 @@
      * Returns the user id for a given uid.
      * @hide
      */
-    public static final int getUserId(int uid) {
+    public static int getUserId(int uid) {
         if (MU_ENABLED) {
             return uid / PER_USER_RANGE;
         } else {
@@ -139,12 +139,12 @@
     }
 
     /** @hide */
-    public static final int getCallingUserId() {
+    public static int getCallingUserId() {
         return getUserId(Binder.getCallingUid());
     }
 
     /** @hide */
-    public static final UserHandle getCallingUserHandle() {
+    public static UserHandle getCallingUserHandle() {
         int userId = getUserId(Binder.getCallingUid());
         UserHandle userHandle = userHandles.get(userId);
         // Intentionally not synchronized to save time
@@ -159,7 +159,7 @@
      * Returns the uid that is composed from the userId and the appId.
      * @hide
      */
-    public static final int getUid(int userId, int appId) {
+    public static int getUid(int userId, int appId) {
         if (MU_ENABLED) {
             return userId * PER_USER_RANGE + (appId % PER_USER_RANGE);
         } else {
@@ -171,7 +171,7 @@
      * Returns the app id (or base uid) for a given uid, stripping out the user id from it.
      * @hide
      */
-    public static final int getAppId(int uid) {
+    public static int getAppId(int uid) {
         return uid % PER_USER_RANGE;
     }
 
@@ -179,7 +179,7 @@
      * Returns the gid shared between all apps with this userId.
      * @hide
      */
-    public static final int getUserGid(int userId) {
+    public static int getUserGid(int userId) {
         return getUid(userId, Process.SHARED_USER_GID);
     }
 
@@ -187,7 +187,7 @@
      * Returns the shared app gid for a given uid or appId.
      * @hide
      */
-    public static final int getSharedAppGid(int id) {
+    public static int getSharedAppGid(int id) {
         return Process.FIRST_SHARED_APPLICATION_GID + (id % PER_USER_RANGE)
                 - Process.FIRST_APPLICATION_UID;
     }
@@ -196,7 +196,7 @@
      * Returns the app id for a given shared app gid. Returns -1 if the ID is invalid.
      * @hide
      */
-    public static final int getAppIdFromSharedAppGid(int gid) {
+    public static int getAppIdFromSharedAppGid(int gid) {
         final int appId = getAppId(gid) + Process.FIRST_APPLICATION_UID
                 - Process.FIRST_SHARED_APPLICATION_GID;
         if (appId < 0 || appId >= Process.FIRST_SHARED_APPLICATION_GID) {
@@ -272,7 +272,7 @@
      * @hide
      */
     @SystemApi
-    public static final int myUserId() {
+    public static int myUserId() {
         return getUserId(Process.myUid());
     }
 
@@ -280,9 +280,10 @@
      * Returns true if this UserHandle refers to the owner user; false otherwise.
      * @return true if this UserHandle refers to the owner user; false otherwise.
      * @hide
+     * TODO: find an alternative to this Api.
      */
     @SystemApi
-    public final boolean isOwner() {
+    public boolean isOwner() {
         return this.equals(OWNER);
     }
 
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/provider/Settings.java b/core/java/android/provider/Settings.java
index 1cb7284..225f0cf 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -3283,7 +3283,6 @@
             DOCK_SOUNDS_ENABLED,        // moved to global
             LOCKSCREEN_SOUNDS_ENABLED,
             SHOW_WEB_SUGGESTIONS,
-            NOTIFICATION_LIGHT_PULSE,
             SIP_CALL_OPTIONS,
             SIP_RECEIVE_CALLS,
             POINTER_SPEED,
diff --git a/core/java/android/view/BatchedInputEventReceiver.java b/core/java/android/view/BatchedInputEventReceiver.java
new file mode 100644
index 0000000..b1d28e0
--- /dev/null
+++ b/core/java/android/view/BatchedInputEventReceiver.java
@@ -0,0 +1,82 @@
+/*
+ * 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.view;
+
+import android.os.Looper;
+
+/**
+ * Similar to {@link InputEventReceiver}, but batches events to vsync boundaries when possible.
+ * @hide
+ */
+public class BatchedInputEventReceiver extends InputEventReceiver {
+    Choreographer mChoreographer;
+    private boolean mBatchedInputScheduled;
+
+    public BatchedInputEventReceiver(
+            InputChannel inputChannel, Looper looper, Choreographer choreographer) {
+        super(inputChannel, looper);
+        mChoreographer = choreographer;
+    }
+
+    @Override
+    public void onBatchedInputEventPending() {
+        scheduleBatchedInput();
+    }
+
+    @Override
+    public void dispose() {
+        unscheduleBatchedInput();
+        super.dispose();
+    }
+
+    void doConsumeBatchedInput(long frameTimeNanos) {
+        if (mBatchedInputScheduled) {
+            mBatchedInputScheduled = false;
+            if (consumeBatchedInputEvents(frameTimeNanos) && frameTimeNanos != -1) {
+                // If we consumed a batch here, we want to go ahead and schedule the
+                // consumption of batched input events on the next frame. Otherwise, we would
+                // wait until we have more input events pending and might get starved by other
+                // things occurring in the process. If the frame time is -1, however, then
+                // we're in a non-batching mode, so there's no need to schedule this.
+                scheduleBatchedInput();
+            }
+        }
+    }
+
+    private void scheduleBatchedInput() {
+        if (!mBatchedInputScheduled) {
+            mBatchedInputScheduled = true;
+            mChoreographer.postCallback(Choreographer.CALLBACK_INPUT, mBatchedInputRunnable, null);
+        }
+    }
+
+    private void unscheduleBatchedInput() {
+        if (mBatchedInputScheduled) {
+            mBatchedInputScheduled = false;
+            mChoreographer.removeCallbacks(
+                    Choreographer.CALLBACK_INPUT, mBatchedInputRunnable, null);
+        }
+    }
+
+    private final class BatchedInputRunnable implements Runnable {
+        @Override
+        public void run() {
+            doConsumeBatchedInput(mChoreographer.getFrameTimeNanos());
+        }
+    }
+    private final BatchedInputRunnable mBatchedInputRunnable = new BatchedInputRunnable();
+}
diff --git a/core/java/android/view/Display.java b/core/java/android/view/Display.java
index 35c4192..1269ad9 100644
--- a/core/java/android/view/Display.java
+++ b/core/java/android/view/Display.java
@@ -16,7 +16,10 @@
 
 package android.view;
 
+import android.annotation.RequiresPermission;
+import android.content.Context;
 import android.content.res.CompatibilityInfo;
+import android.content.res.Resources;
 import android.graphics.PixelFormat;
 import android.graphics.Point;
 import android.graphics.Rect;
@@ -30,6 +33,8 @@
 
 import java.util.Arrays;
 
+import static android.Manifest.permission.CONFIGURE_DISPLAY_COLOR_TRANSFORM;
+
 /**
  * Provides information about the size and density of a logical display.
  * <p>
@@ -679,6 +684,49 @@
     }
 
     /**
+     * Request the display applies a color transform.
+     * @hide
+     */
+    @RequiresPermission(CONFIGURE_DISPLAY_COLOR_TRANSFORM)
+    public void requestColorTransform(ColorTransform colorTransform) {
+        mGlobal.requestColorTransform(mDisplayId, colorTransform.getId());
+    }
+
+    /**
+     * Returns the active color transform of this display
+     * @hide
+     */
+    public ColorTransform getColorTransform() {
+        synchronized (this) {
+            updateDisplayInfoLocked();
+            return mDisplayInfo.getColorTransform();
+        }
+    }
+
+    /**
+     * Returns the default color transform of this display
+     * @hide
+     */
+    public ColorTransform getDefaultColorTransform() {
+        synchronized (this) {
+            updateDisplayInfoLocked();
+            return mDisplayInfo.getDefaultColorTransform();
+        }
+    }
+
+    /**
+     * Gets the supported color transforms of this device.
+     * @hide
+     */
+    public ColorTransform[] getSupportedColorTransforms() {
+        synchronized (this) {
+            updateDisplayInfoLocked();
+            ColorTransform[] transforms = mDisplayInfo.supportedColorTransforms;
+            return Arrays.copyOf(transforms, transforms.length);
+        }
+    }
+
+    /**
      * Gets the app VSYNC offset, in nanoseconds.  This is a positive value indicating
      * the phase offset of the VSYNC events provided by Choreographer relative to the
      * display refresh.  For example, if Choreographer reports that the refresh occurred
@@ -1054,4 +1102,89 @@
             }
         };
     }
+
+    /**
+     * A color transform supported by a given display.
+     *
+     * @see Display#getSupportedColorTransforms()
+     * @hide
+     */
+    public static final class ColorTransform implements Parcelable {
+        public static final ColorTransform[] EMPTY_ARRAY = new ColorTransform[0];
+
+        private final int mId;
+        private final int mColorTransform;
+
+        public ColorTransform(int id, int colorTransform) {
+            mId = id;
+            mColorTransform = colorTransform;
+        }
+
+        public int getId() {
+            return mId;
+        }
+
+        public int getColorTransform() {
+            return mColorTransform;
+        }
+
+        @Override
+        public boolean equals(Object other) {
+            if (this == other) {
+                return true;
+            }
+            if (!(other instanceof ColorTransform)) {
+                return false;
+            }
+            ColorTransform that = (ColorTransform) other;
+            return mId == that.mId
+                && mColorTransform == that.mColorTransform;
+        }
+
+        @Override
+        public int hashCode() {
+            int hash = 1;
+            hash = hash * 17 + mId;
+            hash = hash * 17 + mColorTransform;
+            return hash;
+        }
+
+        @Override
+        public String toString() {
+            return new StringBuilder("{")
+                    .append("id=").append(mId)
+                    .append(", colorTransform=").append(mColorTransform)
+                    .append("}")
+                    .toString();
+        }
+
+        @Override
+        public int describeContents() {
+            return 0;
+        }
+
+        private ColorTransform(Parcel in) {
+            this(in.readInt(), in.readInt());
+        }
+
+        @Override
+        public void writeToParcel(Parcel out, int parcelableFlags) {
+            out.writeInt(mId);
+            out.writeInt(mColorTransform);
+        }
+
+        @SuppressWarnings("hiding")
+        public static final Parcelable.Creator<ColorTransform> CREATOR
+                = new Parcelable.Creator<ColorTransform>() {
+            @Override
+            public ColorTransform createFromParcel(Parcel in) {
+                return new ColorTransform(in);
+            }
+
+            @Override
+            public ColorTransform[] newArray(int size) {
+                return new ColorTransform[size];
+            }
+        };
+    }
 }
diff --git a/core/java/android/view/DisplayInfo.java b/core/java/android/view/DisplayInfo.java
index cf17990..ee76274 100644
--- a/core/java/android/view/DisplayInfo.java
+++ b/core/java/android/view/DisplayInfo.java
@@ -169,6 +169,15 @@
      */
     public Display.Mode[] supportedModes = Display.Mode.EMPTY_ARRAY;
 
+    /** The active color transform. */
+    public int colorTransformId;
+
+    /** The default color transform. */
+    public int defaultColorTransformId;
+
+    /** The list of supported color transforms */
+    public Display.ColorTransform[] supportedColorTransforms = Display.ColorTransform.EMPTY_ARRAY;
+
     /**
      * The logical display density which is the basis for density-independent
      * pixels.
@@ -279,6 +288,8 @@
                 && rotation == other.rotation
                 && modeId == other.modeId
                 && defaultModeId == other.defaultModeId
+                && colorTransformId == other.colorTransformId
+                && defaultColorTransformId == other.defaultColorTransformId
                 && logicalDensityDpi == other.logicalDensityDpi
                 && physicalXDpi == other.physicalXDpi
                 && physicalYDpi == other.physicalYDpi
@@ -317,6 +328,10 @@
         modeId = other.modeId;
         defaultModeId = other.defaultModeId;
         supportedModes = Arrays.copyOf(other.supportedModes, other.supportedModes.length);
+        colorTransformId = other.colorTransformId;
+        defaultColorTransformId = other.defaultColorTransformId;
+        supportedColorTransforms = Arrays.copyOf(
+                other.supportedColorTransforms, other.supportedColorTransforms.length);
         logicalDensityDpi = other.logicalDensityDpi;
         physicalXDpi = other.physicalXDpi;
         physicalYDpi = other.physicalYDpi;
@@ -353,6 +368,13 @@
         for (int i = 0; i < nModes; i++) {
             supportedModes[i] = Display.Mode.CREATOR.createFromParcel(source);
         }
+        colorTransformId = source.readInt();
+        defaultColorTransformId = source.readInt();
+        int nColorTransforms = source.readInt();
+        supportedColorTransforms = new Display.ColorTransform[nColorTransforms];
+        for (int i = 0; i < nColorTransforms; i++) {
+            supportedColorTransforms[i] = Display.ColorTransform.CREATOR.createFromParcel(source);
+        }
         logicalDensityDpi = source.readInt();
         physicalXDpi = source.readFloat();
         physicalYDpi = source.readFloat();
@@ -390,6 +412,12 @@
         for (int i = 0; i < supportedModes.length; i++) {
             supportedModes[i].writeToParcel(dest, flags);
         }
+        dest.writeInt(colorTransformId);
+        dest.writeInt(defaultColorTransformId);
+        dest.writeInt(supportedColorTransforms.length);
+        for (int i = 0; i < supportedColorTransforms.length; i++) {
+            supportedColorTransforms[i].writeToParcel(dest, flags);
+        }
         dest.writeInt(logicalDensityDpi);
         dest.writeFloat(physicalXDpi);
         dest.writeFloat(physicalYDpi);
@@ -461,6 +489,24 @@
         return result;
     }
 
+    public Display.ColorTransform getColorTransform() {
+        return findColorTransform(colorTransformId);
+    }
+
+    public Display.ColorTransform getDefaultColorTransform() {
+        return findColorTransform(defaultColorTransformId);
+    }
+
+    private Display.ColorTransform findColorTransform(int colorTransformId) {
+        for (int i = 0; i < supportedColorTransforms.length; i++) {
+            Display.ColorTransform colorTransform = supportedColorTransforms[i];
+            if (colorTransform.getId() == colorTransformId) {
+                return colorTransform;
+            }
+        }
+        throw new IllegalStateException("Unable to locate color transform: " + colorTransformId);
+    }
+
     public void getAppMetrics(DisplayMetrics outMetrics) {
         getAppMetrics(outMetrics, CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO, null);
     }
@@ -562,6 +608,12 @@
         sb.append(defaultModeId);
         sb.append(", modes ");
         sb.append(Arrays.toString(supportedModes));
+        sb.append(", colorTransformId ");
+        sb.append(colorTransformId);
+        sb.append(", defaultColorTransformId ");
+        sb.append(defaultColorTransformId);
+        sb.append(", supportedColorTransforms ");
+        sb.append(Arrays.toString(supportedColorTransforms));
         sb.append(", rotation ");
         sb.append(rotation);
         sb.append(", density ");
diff --git a/core/java/android/view/DragEvent.java b/core/java/android/view/DragEvent.java
index f559e21..34835f4 100644
--- a/core/java/android/view/DragEvent.java
+++ b/core/java/android/view/DragEvent.java
@@ -128,6 +128,8 @@
     float mX, mY;
     ClipDescription mClipDescription;
     ClipData mClipData;
+    DropPermissionHolder mDropPermissionHolder;
+
     Object mLocalState;
     boolean mDragResult;
 
@@ -253,28 +255,30 @@
     }
 
     private void init(int action, float x, float y, ClipDescription description, ClipData data,
-            Object localState, boolean result) {
+            DropPermissionHolder dropPermissionHolder, Object localState, boolean result) {
         mAction = action;
         mX = x;
         mY = y;
         mClipDescription = description;
         mClipData = data;
+        mDropPermissionHolder = dropPermissionHolder;
         mLocalState = localState;
         mDragResult = result;
     }
 
     static DragEvent obtain() {
-        return DragEvent.obtain(0, 0f, 0f, null, null, null, false);
+        return DragEvent.obtain(0, 0f, 0f, null, null, null, null, false);
     }
 
     /** @hide */
     public static DragEvent obtain(int action, float x, float y, Object localState,
-            ClipDescription description, ClipData data, boolean result) {
+            ClipDescription description, ClipData data, DropPermissionHolder dropPermissionHolder,
+            boolean result) {
         final DragEvent ev;
         synchronized (gRecyclerLock) {
             if (gRecyclerTop == null) {
                 ev = new DragEvent();
-                ev.init(action, x, y, description, data, localState, result);
+                ev.init(action, x, y, description, data, dropPermissionHolder, localState, result);
                 return ev;
             }
             ev = gRecyclerTop;
@@ -285,7 +289,7 @@
         ev.mRecycled = false;
         ev.mNext = null;
 
-        ev.init(action, x, y, description, data, localState, result);
+        ev.init(action, x, y, description, data, dropPermissionHolder, localState, result);
 
         return ev;
     }
@@ -293,7 +297,8 @@
     /** @hide */
     public static DragEvent obtain(DragEvent source) {
         return obtain(source.mAction, source.mX, source.mY, source.mLocalState,
-                source.mClipDescription, source.mClipData, source.mDragResult);
+                source.mClipDescription, source.mClipData, source.mDropPermissionHolder,
+                source.mDragResult);
     }
 
     /**
@@ -358,6 +363,17 @@
     }
 
     /**
+     * Returns the {@link android.view.DropPermissionHolder} object that can be used by the drag
+     * listener to request and release the permissions for the content URIs contained in the
+     * {@link android.content.ClipData} object associated with this event.
+     * This method only returns valid data if the event action is {@link #ACTION_DROP}.
+     * @return The DropPermissionHolder object used to handle content URI permissions.
+     */
+    public DropPermissionHolder getDropPermissionHolder() {
+        return mDropPermissionHolder;
+    }
+
+    /**
      * Returns the local state object sent to the system as part of the call to
      * {@link android.view.View#startDrag(ClipData,View.DragShadowBuilder,Object,int) startDrag()}.
      * The object is intended to provide local information about the drag and drop operation. For
@@ -477,6 +493,12 @@
             dest.writeInt(1);
             mClipDescription.writeToParcel(dest, flags);
         }
+        if (mDropPermissionHolder == null) {
+            dest.writeInt(0);
+        } else {
+            dest.writeInt(1);
+            mDropPermissionHolder.writeToParcel(dest, flags);
+        }
     }
 
     /**
@@ -496,6 +518,9 @@
             if (in.readInt() != 0) {
                 event.mClipDescription = ClipDescription.CREATOR.createFromParcel(in);
             }
+            if (in.readInt() != 0) {
+                event.mDropPermissionHolder = DropPermissionHolder.CREATOR.createFromParcel(in);
+            }
             return event;
         }
 
diff --git a/core/java/android/view/DropPermissionHolder.java b/core/java/android/view/DropPermissionHolder.java
new file mode 100644
index 0000000..993e67a
--- /dev/null
+++ b/core/java/android/view/DropPermissionHolder.java
@@ -0,0 +1,161 @@
+package android.view;
+
+import android.app.IActivityManager;
+import android.content.ClipData;
+import android.content.Intent;
+import android.net.Uri;
+import android.os.Binder;
+import android.os.IBinder;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.os.RemoteException;
+import com.android.internal.view.IDropPermissionHolder;
+
+import java.util.ArrayList;
+
+public class DropPermissionHolder implements Parcelable {
+
+    IDropPermissionHolder mDropPermissionHolder;
+
+    /**
+     * Create a new DropPermissionHolder to be passed to the client with a DragEvent.
+     *
+     * @hide
+     */
+    public DropPermissionHolder(ClipData clipData, IActivityManager activityManager,
+            int sourceUid, String targetPackage, int mode, int sourceUserId, int targetUserId) {
+        mDropPermissionHolder = new LocalDropPermissionHolder(clipData, activityManager,
+                sourceUid, targetPackage, mode, sourceUserId, targetUserId);
+    }
+
+    private class LocalDropPermissionHolder extends IDropPermissionHolder.Stub {
+
+        private final IActivityManager mActivityManager;
+        private final int mSourceUid;
+        private final String mTargetPackage;
+        private final int mMode;
+        private final int mSourceUserId;
+        private final int mTargetUserId;
+
+        IBinder mPermissionOwner = null;
+
+        final private ArrayList<Uri> mUris = new ArrayList<Uri>();
+
+        LocalDropPermissionHolder(ClipData clipData, IActivityManager activityManager,
+                int sourceUid, String targetPackage, int mode, int sourceUserId, int targetUserId) {
+            mActivityManager = activityManager;
+            mSourceUid = sourceUid;
+            mTargetPackage = targetPackage;
+            mMode = mode;
+            mSourceUserId = sourceUserId;
+            mTargetUserId = targetUserId;
+
+            int N = clipData.getItemCount();
+            for (int i = 0; i != N; ++i) {
+                ClipData.Item item = clipData.getItemAt(i);
+
+                if (item.getUri() != null) {
+                    mUris.add(item.getUri());
+                }
+
+                Intent intent = item.getIntent();
+                if (intent != null && intent.getData() != null) {
+                    mUris.add(intent.getData());
+                }
+            }
+        }
+
+        @Override
+        public void grant() throws RemoteException {
+            if (mPermissionOwner != null) {
+                return;
+            }
+
+            mPermissionOwner = mActivityManager.newUriPermissionOwner("drop");
+
+            long origId = Binder.clearCallingIdentity();
+            try {
+                for (Uri mUri : mUris) {
+                    mActivityManager.grantUriPermissionFromOwner(
+                            mPermissionOwner, mSourceUid, mTargetPackage, mUri, mMode,
+                            mSourceUserId, mTargetUserId);
+                }
+            } finally {
+                Binder.restoreCallingIdentity(origId);
+            }
+
+        }
+
+        @Override
+        public void revoke() throws RemoteException {
+            if (mPermissionOwner == null) {
+                return;
+            }
+
+            for (Uri mUri : mUris) {
+                mActivityManager.revokeUriPermissionFromOwner(
+                        mPermissionOwner, mUri, mMode, mSourceUserId);
+            }
+
+            mPermissionOwner = null;
+        }
+    }
+
+    /**
+     * Request permissions granted by the activity which started the drag.
+     */
+    public void grant() {
+        try {
+            mDropPermissionHolder.grant();
+        } catch (RemoteException e) {
+        }
+    }
+
+    /**
+     * Revoke permissions granted by the {@link #grant()} call.
+     */
+    public void revoke() {
+        try {
+            mDropPermissionHolder.revoke();
+        } catch (RemoteException e) {
+        }
+    }
+
+    /**
+     * Returns information about the {@link android.os.Parcel} representation of this
+     * DropPermissionHolder object.
+     * @return Information about the {@link android.os.Parcel} representation.
+     */
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    /**
+     * Creates a {@link android.os.Parcel} object from this DropPermissionHolder object.
+     * @param dest A {@link android.os.Parcel} object in which to put the DropPermissionHolder
+     *             object.
+     * @param flags Flags to store in the Parcel.
+     */
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeStrongBinder(mDropPermissionHolder.asBinder());
+    }
+
+    DropPermissionHolder(Parcel in) {
+        mDropPermissionHolder = IDropPermissionHolder.Stub.asInterface(in.readStrongBinder());
+    }
+
+    /**
+     * A container for creating a DropPermissionHolder from a Parcel.
+     */
+    public static final Parcelable.Creator<DropPermissionHolder> CREATOR
+            = new Parcelable.Creator<DropPermissionHolder>() {
+        public DropPermissionHolder createFromParcel(Parcel in) {
+            return new DropPermissionHolder(in);
+        }
+        public DropPermissionHolder[] newArray(int size) {
+            return new DropPermissionHolder[size];
+        }
+    };
+}
diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl
index 33c51ff..7adfa8d 100644
--- a/core/java/android/view/IWindowManager.aidl
+++ b/core/java/android/view/IWindowManager.aidl
@@ -98,11 +98,12 @@
      * @param taskBounds Bounds to use when creating a new Task with the input task Id if
      *                   the task doesn't exist yet.
      * @param configuration Configuration that is being used with this task.
+     * @param cropWindowsToStack True if the app windows should be cropped to the stack bounds.
      */
     void addAppToken(int addPos, IApplicationToken token, int taskId, int stackId,
             int requestedOrientation, boolean fullscreen, boolean showWhenLocked, int userId,
             int configChanges, boolean voiceInteraction, boolean launchTaskBehind,
-            in Rect taskBounds, in Configuration configuration);
+            in Rect taskBounds, in Configuration configuration, boolean cropWindowsToStack);
     /**
      *
      * @param token The token we are adding to the input task Id.
@@ -143,7 +144,6 @@
     void setAppStartingWindow(IBinder token, String pkg, int theme,
             in CompatibilityInfo compatInfo, CharSequence nonLocalizedLabel, int labelRes,
             int icon, int logo, int windowFlags, IBinder transferFrom, boolean createIfNeeded);
-    void setAppWillBeHidden(IBinder token);
     void setAppVisibility(IBinder token, boolean visible);
     void startAppFreezingScreen(IBinder token, int configChanges);
     void stopAppFreezingScreen(IBinder token, boolean force);
diff --git a/core/java/android/view/ThreadedRenderer.java b/core/java/android/view/ThreadedRenderer.java
index 420f7a1..dcef142 100644
--- a/core/java/android/view/ThreadedRenderer.java
+++ b/core/java/android/view/ThreadedRenderer.java
@@ -108,6 +108,11 @@
     private Choreographer mChoreographer;
     private boolean mRootNodeNeedsUpdate;
 
+    // In case of multi threaded render nodes, these bounds indicate the content bounds against
+    // which the backdrop needs to be cropped against.
+    private final Rect mCurrentContentBounds = new Rect();
+    private final Rect mStagedContentBounds = new Rect();
+
     ThreadedRenderer(Context context, boolean translucent) {
         final TypedArray a = context.obtainStyledAttributes(null, R.styleable.Lighting, 0, 0);
         mLightY = a.getDimension(R.styleable.Lighting_lightY, 0);
@@ -307,6 +312,47 @@
         Trace.traceEnd(Trace.TRACE_TAG_VIEW);
     }
 
+    /**
+     * Adds a rendernode to the renderer which can be drawn and changed asynchronously to the
+     * rendernode of the UI thread.
+     * @param node The node to add.
+     * @param placeFront If true, the render node will be placed in front of the content node,
+     *                   otherwise behind the content node.
+     */
+    public void addRenderNode(RenderNode node, boolean placeFront) {
+        nAddRenderNode(mNativeProxy, node.mNativeRenderNode, placeFront);
+    }
+
+    /**
+     * Only especially added render nodes can be removed.
+     * @param node The node which was added via addRenderNode which should get removed again.
+     */
+    public void removeRenderNode(RenderNode node) {
+        nRemoveRenderNode(mNativeProxy, node.mNativeRenderNode);
+    }
+
+    /**
+     * Draws a particular render node. If the node is not the content node, only the additional
+     * nodes will get drawn and the content remains untouched.
+     * @param node The node to be drawn.
+     */
+    public void drawRenderNode(RenderNode node) {
+        nDrawRenderNode(mNativeProxy, node.mNativeRenderNode);
+    }
+
+    /**
+     * To avoid unnecessary overdrawing of the main content all additionally passed render nodes
+     * will be prevented to overdraw this area. It will be synchronized with the draw call.
+     * This should be updated in the content view's draw call.
+     * @param left The left side of the protected bounds.
+     * @param top The top side of the protected bounds.
+     * @param right The right side of the protected bounds.
+     * @param bottom The bottom side of the protected bounds.
+     */
+    public void setContentOverdrawProtectionBounds(int left, int top, int right, int bottom) {
+        mStagedContentBounds.set(left, top, right, bottom);
+    }
+
     @Override
     void invalidateRoot() {
         mRootNodeNeedsUpdate = true;
@@ -320,6 +366,14 @@
         choreographer.mFrameInfo.markDrawStart();
 
         updateRootDisplayList(view, callbacks);
+        // The main content view was updating the content bounds and we transfer them to the
+        // renderer.
+        if (!mCurrentContentBounds.equals(mStagedContentBounds)) {
+            mCurrentContentBounds.set(mStagedContentBounds);
+            nSetContentOverdrawProtectionBounds(mNativeProxy, mCurrentContentBounds.left,
+                mCurrentContentBounds.top, mCurrentContentBounds.right,
+            mCurrentContentBounds.bottom);
+        }
 
         attachInfo.mIgnoreDirtyState = false;
 
@@ -541,4 +595,11 @@
     private static native void nDumpProfileInfo(long nativeProxy, FileDescriptor fd,
             @DumpFlags int dumpFlags);
     private static native void nDumpProfileData(byte[] data, FileDescriptor fd);
+
+    private static native void nAddRenderNode(long nativeProxy, long rootRenderNode,
+             boolean placeFront);
+    private static native void nRemoveRenderNode(long nativeProxy, long rootRenderNode);
+    private static native void nDrawRenderNode(long nativeProxy, long rootRenderNode);
+    private static native void nSetContentOverdrawProtectionBounds(long nativeProxy, int left,
+             int top, int right, int bottom);
 }
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index eb591c1..263ec7d 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -3641,9 +3641,44 @@
      * with this flag set, all visible applications will be able to participate
      * in the drag operation and receive the dragged content.
      *
-     * @hide
+     * If this is the only flag set, then the drag recipient will only have access to text data
+     * and intents contained in the {@link ClipData} object. Access to URIs contained in the
+     * {@link ClipData} is determined by other DRAG_FLAG_GLOBAL_* flags.
      */
-    public static final int DRAG_FLAG_GLOBAL = 1;
+    public static final int DRAG_FLAG_GLOBAL = 1 << 8;  // 256
+
+    /**
+     * When this flag is used with {@link #DRAG_FLAG_GLOBAL}, the drag recipient will be able to
+     * request read access to the content URI(s) contained in the {@link ClipData} object.
+     * @see android.content.Intent.FLAG_GRANT_READ_URI_PERMISSION
+     */
+    public static final int DRAG_FLAG_GLOBAL_URI_READ = Intent.FLAG_GRANT_READ_URI_PERMISSION;
+
+    /**
+     * When this flag is used with {@link #DRAG_FLAG_GLOBAL}, the drag recipient will be able to
+     * request write access to the content URI(s) contained in the {@link ClipData} object.
+     * @see android.content.Intent.FLAG_GRANT_WRITE_URI_PERMISSION
+     */
+    public static final int DRAG_FLAG_GLOBAL_URI_WRITE = Intent.FLAG_GRANT_WRITE_URI_PERMISSION;
+
+    /**
+     * When this flag is used with {@link #DRAG_FLAG_GLOBAL_URI_READ} and/or {@link
+     * #DRAG_FLAG_GLOBAL_URI_WRITE}, the URI permission grant can be persisted across device
+     * reboots until explicitly revoked with
+     * {@link android.content.Context#revokeUriPermission(Uri,int) Context.revokeUriPermission}.
+     * @see android.content.Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION
+     */
+    public static final int DRAG_FLAG_GLOBAL_PERSISTABLE_URI_PERMISSION =
+            Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION;
+
+    /**
+     * When this flag is used with {@link #DRAG_FLAG_GLOBAL_URI_READ} and/or {@link
+     * #DRAG_FLAG_GLOBAL_URI_WRITE}, the URI permission grant applies to any URI that is a prefix
+     * match against the original granted URI.
+     * @see android.content.Intent.FLAG_GRANT_PREFIX_URI_PERMISSION
+     */
+    public static final int DRAG_FLAG_GLOBAL_PREFIX_URI_PERMISSION =
+            Intent.FLAG_GRANT_PREFIX_URI_PERMISSION;
 
     /**
      * Flag indicating that the drag shadow will be opaque.  When
@@ -4671,7 +4706,7 @@
             out.append(" #");
             out.append(Integer.toHexString(id));
             final Resources r = mResources;
-            if (Resources.resourceHasPackage(id) && r != null) {
+            if (id > 0 && Resources.resourceHasPackage(id) && r != null) {
                 try {
                     String pkgname;
                     switch (id&0xff000000) {
@@ -5353,7 +5388,7 @@
     protected boolean performButtonActionOnTouchDown(MotionEvent event) {
         if (event.getToolType(0) == MotionEvent.TOOL_TYPE_MOUSE &&
             (event.getButtonState() & MotionEvent.BUTTON_SECONDARY) != 0) {
-            showContextMenu(event.getX(), event.getY(), event.getMetaState());
+            showContextMenu(event.getX(), event.getY());
             mPrivateFlags |= PFLAG_CANCEL_NEXT_UP_EVENT;
             return true;
         }
@@ -5374,13 +5409,10 @@
      *
      * @param x The referenced x coordinate.
      * @param y The referenced y coordinate.
-     * @param metaState The keyboard modifiers that were pressed.
      * @return Whether a context menu was displayed.
-     *
-     * @hide
      */
-    public boolean showContextMenu(float x, float y, int metaState) {
-        return showContextMenu();
+    public boolean showContextMenu(float x, float y) {
+        return getParent().showContextMenuForChild(this, x, y);
     }
 
     /**
@@ -7338,6 +7370,23 @@
     }
 
     /**
+     * Compute the view's coordinate within the surface.
+     *
+     * <p>Computes the coordinates of this view in its surface. The argument
+     * must be an array of two integers. After the method returns, the array
+     * contains the x and y location in that order.</p>
+     * @hide
+     * @param location an array of two integers in which to hold the coordinates
+     */
+    public void getLocationInSurface(@Size(2) int[] location) {
+        getLocationInWindow(location);
+        if (mAttachInfo != null && mAttachInfo.mViewRootImpl != null) {
+            location[0] += mAttachInfo.mViewRootImpl.mWindowAttributes.surfaceInsets.left;
+            location[1] += mAttachInfo.mViewRootImpl.mWindowAttributes.surfaceInsets.top;
+        }
+    }
+
+    /**
      * Provide original WindowInsets that are dispatched to the view hierarchy. The insets are
      * only available if the view is attached.
      *
@@ -8408,11 +8457,14 @@
      */
     public void clearAccessibilityFocus() {
         clearAccessibilityFocusNoCallbacks();
-        // Clear the global reference of accessibility focus if this
-        // view or any of its descendants had accessibility focus.
-        ViewRootImpl viewRootImpl = getViewRootImpl();
+
+        // Clear the global reference of accessibility focus if this view or
+        // any of its descendants had accessibility focus. This will NOT send
+        // an event or update internal state if focus is cleared from a
+        // descendant view, which may leave views in inconsistent states.
+        final ViewRootImpl viewRootImpl = getViewRootImpl();
         if (viewRootImpl != null) {
-            View focusHost = viewRootImpl.getAccessibilityFocusedHost();
+            final View focusHost = viewRootImpl.getAccessibilityFocusedHost();
             if (focusHost != null && ViewRootImpl.isViewDescendantOf(focusHost, this)) {
                 viewRootImpl.setAccessibilityFocus(null, null);
             }
@@ -8688,6 +8740,18 @@
     public void setImportantForAccessibility(int mode) {
         final int oldMode = getImportantForAccessibility();
         if (mode != oldMode) {
+            final boolean hideDescendants =
+                    mode == IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS;
+
+            // If this node or its descendants are no longer important, try to
+            // clear accessibility focus.
+            if (mode == IMPORTANT_FOR_ACCESSIBILITY_NO || hideDescendants) {
+                final View focusHost = findAccessibilityFocusHost(hideDescendants);
+                if (focusHost != null) {
+                    focusHost.clearAccessibilityFocus();
+                }
+            }
+
             // If we're moving between AUTO and another state, we might not need
             // to send a subtree changed notification. We'll store the computed
             // importance, since we'll need to check it later to make sure.
@@ -8707,6 +8771,31 @@
     }
 
     /**
+     * Returns the view within this view's hierarchy that is hosting
+     * accessibility focus.
+     *
+     * @param searchDescendants whether to search for focus in descendant views
+     * @return the view hosting accessibility focus, or {@code null}
+     */
+    private View findAccessibilityFocusHost(boolean searchDescendants) {
+        if (isAccessibilityFocusedViewOrHost()) {
+            return this;
+        }
+
+        if (searchDescendants) {
+            final ViewRootImpl viewRoot = getViewRootImpl();
+            if (viewRoot != null) {
+                final View focusHost = viewRoot.getAccessibilityFocusedHost();
+                if (focusHost != null && ViewRootImpl.isViewDescendantOf(focusHost, this)) {
+                    return focusHost;
+                }
+            }
+        }
+
+        return null;
+    }
+
+    /**
      * Computes whether this view should be exposed for accessibility. In
      * general, views that are interactive or provide information are exposed
      * while views that serve only as containers are hidden.
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index bdcd998..475ce2f 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -767,6 +767,11 @@
         return mParent != null && mParent.showContextMenuForChild(originalView);
     }
 
+    @Override
+    public boolean showContextMenuForChild(View originalView, float x, float y) {
+        return mParent != null && mParent.showContextMenuForChild(originalView, x, y);
+    }
+
     /**
      * {@inheritDoc}
      */
diff --git a/core/java/android/view/ViewParent.java b/core/java/android/view/ViewParent.java
index 15b86d1..07f1e2c 100644
--- a/core/java/android/view/ViewParent.java
+++ b/core/java/android/view/ViewParent.java
@@ -182,6 +182,17 @@
     public boolean showContextMenuForChild(View originalView);
 
     /**
+     * Bring up a context menu for the specified view at the given x/y offset from
+     * the top left corner.
+     *
+     * @param originalView
+     * @param x The x offset at which to open the menu
+     * @param y The y offset at which to open the menu
+     * @return true if a context menu was displayed
+     */
+    public boolean showContextMenuForChild(View originalView, float x, float y);
+
+    /**
      * Have the parent populate the specified context menu if it has anything to
      * add (and then recurse on its parent).
      * 
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 84ba450..f6c60ed 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -169,6 +169,16 @@
     boolean mAppVisible = true;
     int mOrigWindowType = -1;
 
+    /** Whether the window had focus during the most recent traversal. */
+    boolean mHadWindowFocus;
+
+    /**
+     * Whether the window lost focus during a previous traversal and has not
+     * yet gained it back. Used to determine whether a WINDOW_STATE_CHANGE
+     * accessibility events should be sent during traversal.
+     */
+    boolean mLostWindowFocus;
+
     // Set to true if the owner of this window is in the stopped state,
     // so the window should no longer be active.
     boolean mStopped = false;
@@ -191,6 +201,10 @@
     Rect mDirty;
     boolean mIsAnimating;
 
+    private boolean mDragResizing;
+    private int mCanvasOffsetX;
+    private int mCanvasOffsetY;
+
     CompatibilityInfo.Translator mTranslator;
 
     final View.AttachInfo mAttachInfo;
@@ -1506,6 +1520,7 @@
                         frame.width() < desiredWindowWidth && frame.width() != mWidth)
                 || (lp.height == ViewGroup.LayoutParams.WRAP_CONTENT &&
                         frame.height() < desiredWindowHeight && frame.height() != mHeight));
+        windowShouldResize |= mDragResizing;
 
         // Determine whether to compute insets.
         // If there are no inset listeners remaining then we may still need to compute
@@ -1517,10 +1532,11 @@
         boolean insetsPending = false;
         int relayoutResult = 0;
 
+        boolean isViewVisible = viewVisibility == View.VISIBLE;
         if (mFirst || windowShouldResize || insetsChanged ||
                 viewVisibilityChanged || params != null) {
 
-            if (viewVisibility == View.VISIBLE) {
+            if (isViewVisible) {
                 // If this window is giving internal insets to the window
                 // manager, and it is being added or changing its visibility,
                 // then we want to first give the window manager "fake"
@@ -1681,6 +1697,19 @@
                         return;
                     }
                 }
+
+                final boolean dragResizing = (relayoutResult
+                        & WindowManagerGlobal.RELAYOUT_RES_DRAG_RESIZING) != 0;
+                if (mDragResizing != dragResizing) {
+                    mDragResizing = dragResizing;
+                    mFullRedrawNeeded = true;
+                }
+                if (dragResizing) {
+                    mCanvasOffsetX = mWinFrame.left;
+                    mCanvasOffsetY = mWinFrame.top;
+                } else {
+                    mCanvasOffsetX = mCanvasOffsetY = 0;
+                }
             } catch (RemoteException e) {
             }
 
@@ -1758,10 +1787,6 @@
                         || mHeight != hardwareRenderer.getHeight()) {
                     hardwareRenderer.setup(mWidth, mHeight, mAttachInfo,
                             mWindowAttributes.surfaceInsets);
-                    if (!hwInitialized) {
-                        hardwareRenderer.invalidate(mSurface);
-                        mFullRedrawNeeded = true;
-                    }
                 }
             }
 
@@ -1946,19 +1971,33 @@
             mRemainingFrameCount--;
         }
 
+        final boolean changedVisibility = (viewVisibilityChanged || mFirst) && isViewVisible;
+        final boolean hasWindowFocus = mAttachInfo.mHasWindowFocus && isViewVisible;
+        final boolean regainedFocus = hasWindowFocus && mLostWindowFocus;
+        if (regainedFocus) {
+            mLostWindowFocus = false;
+        } else if (!hasWindowFocus && mHadWindowFocus) {
+            mLostWindowFocus = true;
+        }
+
+        if (changedVisibility || regainedFocus) {
+            host.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
+        }
+
         mFirst = false;
         mWillDrawSoon = false;
         mNewSurfaceNeeded = false;
         mViewVisibility = viewVisibility;
+        mHadWindowFocus = hasWindowFocus;
 
-        if (mAttachInfo.mHasWindowFocus && !isInLocalFocusMode()) {
+        if (hasWindowFocus && !isInLocalFocusMode()) {
             final boolean imTarget = WindowManager.LayoutParams
                     .mayUseInputMethod(mWindowAttributes.flags);
             if (imTarget != mLastWasImTarget) {
                 mLastWasImTarget = imTarget;
                 InputMethodManager imm = InputMethodManager.peekInstance();
                 if (imm != null && imTarget) {
-                    imm.onPreWindowFocus(mView, true /* hasWindowFocus */);
+                    imm.onPreWindowFocus(mView, hasWindowFocus);
                     imm.onPostWindowFocus(mView, mView.findFocus(),
                             mWindowAttributes.softInputMode,
                             !mHasHadWindowFocus, mWindowAttributes.flags);
@@ -1971,8 +2010,7 @@
             mReportNextDraw = true;
         }
 
-        boolean cancelDraw = mAttachInfo.mTreeObserver.dispatchOnPreDraw() ||
-                viewVisibility != View.VISIBLE;
+        boolean cancelDraw = mAttachInfo.mTreeObserver.dispatchOnPreDraw() || !isViewVisible;
 
         if (!cancelDraw && !newSurface) {
             if (!skipDraw || mReportNextDraw) {
@@ -1986,7 +2024,7 @@
                 performDraw();
             }
         } else {
-            if (viewVisibility == View.VISIBLE) {
+            if (isViewVisible) {
                 // Try again
                 scheduleTraversals();
             } else if (mPendingTransitions != null && mPendingTransitions.size() > 0) {
@@ -2463,8 +2501,8 @@
 
         mAttachInfo.mTreeObserver.dispatchOnDraw();
 
-        int xOffset = 0;
-        int yOffset = curScrollY;
+        int xOffset = -mCanvasOffsetX;
+        int yOffset = -mCanvasOffsetY + curScrollY;
         final WindowManager.LayoutParams params = mWindowAttributes;
         final Rect surfaceInsets = params != null ? params.surfaceInsets : null;
         if (surfaceInsets != null) {
@@ -3295,13 +3333,6 @@
                                     ~WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION;
                         mHasHadWindowFocus = true;
                     }
-
-                    if (mView != null && mAccessibilityManager.isEnabled()) {
-                        if (hasWindowFocus) {
-                            mView.sendAccessibilityEvent(
-                                    AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
-                        }
-                    }
                 }
             } break;
             case MSG_DIE:
@@ -6209,6 +6240,11 @@
     }
 
     @Override
+    public boolean showContextMenuForChild(View originalView, float x, float y) {
+        return false;
+    }
+
+    @Override
     public ActionMode startActionModeForChild(View originalView, ActionMode.Callback callback) {
         return null;
     }
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/view/WindowManagerGlobal.java b/core/java/android/view/WindowManagerGlobal.java
index 606168c..ab99b9e 100644
--- a/core/java/android/view/WindowManagerGlobal.java
+++ b/core/java/android/view/WindowManagerGlobal.java
@@ -70,6 +70,13 @@
     public static final int RELAYOUT_RES_SURFACE_CHANGED = 0x4;
 
     /**
+     * The window is being resized by dragging one of the window corners,
+     * in this case the surface would be fullsreen-sized. The client should
+     * render to the actual frame location (instead of (0,curScrollY)).
+     */
+    public static final int RELAYOUT_RES_DRAG_RESIZING = 0x8;
+
+    /**
      * Flag for relayout: the client will be later giving
      * internal insets; as a result, the window will not impact other window
      * layouts until the insets are given.
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index 057b701..d0c50c9 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -962,6 +962,9 @@
      * If the base URL uses any other scheme, then the data will be loaded into
      * the WebView as a plain string (i.e. not part of a data URL) and any URL-encoded
      * entities in the string will not be decoded.
+     * <p>
+     * Note that the baseUrl is sent in the 'Referer' HTTP header when
+     * requesting subresources (images, etc.) of the page loaded using this method.
      *
      * @param baseUrl the URL to use as the page's base URL. If null defaults to
      *                'about:blank'.
diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java
index 5724f52..b8faf0c 100644
--- a/core/java/android/widget/AbsListView.java
+++ b/core/java/android/widget/AbsListView.java
@@ -3080,6 +3080,15 @@
     }
 
     private class CheckForLongPress extends WindowRunnnable implements Runnable {
+        private static final int INVALID_COORD = -1;
+        private float mX = INVALID_COORD;
+        private float mY = INVALID_COORD;
+
+        private void setCoords(float x, float y) {
+            mX = x;
+            mY = y;
+        }
+
         @Override
         public void run() {
             final int motionPosition = mMotionPosition;
@@ -3090,7 +3099,11 @@
 
                 boolean handled = false;
                 if (sameWindow() && !mDataChanged) {
-                    handled = performLongPress(child, longPressPosition, longPressId);
+                    if (mX != INVALID_COORD && mY != INVALID_COORD) {
+                        handled = performLongPress(child, longPressPosition, longPressId, mX, mY);
+                    } else {
+                        handled = performLongPress(child, longPressPosition, longPressId);
+                    }
                 }
                 if (handled) {
                     mTouchMode = TOUCH_MODE_REST;
@@ -3146,6 +3159,16 @@
 
     boolean performLongPress(final View child,
             final int longPressPosition, final long longPressId) {
+        return performLongPress(
+                child,
+                longPressPosition,
+                longPressId,
+                CheckForLongPress.INVALID_COORD,
+                CheckForLongPress.INVALID_COORD);
+    }
+
+    boolean performLongPress(final View child,
+            final int longPressPosition, final long longPressId, float x, float y) {
         // CHOICE_MODE_MULTIPLE_MODAL takes over long press.
         if (mChoiceMode == CHOICE_MODE_MULTIPLE_MODAL) {
             if (mChoiceActionMode == null &&
@@ -3163,7 +3186,11 @@
         }
         if (!handled) {
             mContextMenuInfo = createContextMenuInfo(child, longPressPosition, longPressId);
-            handled = super.showContextMenuForChild(AbsListView.this);
+            if (x != CheckForLongPress.INVALID_COORD && y != CheckForLongPress.INVALID_COORD) {
+                handled = super.showContextMenuForChild(AbsListView.this, x, y);
+            } else {
+                handled = super.showContextMenuForChild(AbsListView.this);
+            }
         }
         if (handled) {
             performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
@@ -3178,17 +3205,17 @@
 
     /** @hide */
     @Override
-    public boolean showContextMenu(float x, float y, int metaState) {
+    public boolean showContextMenu(float x, float y) {
         final int position = pointToPosition((int)x, (int)y);
         if (position != INVALID_POSITION) {
             final long id = mAdapter.getItemId(position);
             View child = getChildAt(position - mFirstPosition);
             if (child != null) {
                 mContextMenuInfo = createContextMenuInfo(child, position, id);
-                return super.showContextMenuForChild(AbsListView.this);
+                return super.showContextMenuForChild(AbsListView.this, x, y);
             }
         }
-        return super.showContextMenu(x, y, metaState);
+        return super.showContextMenu(x, y);
     }
 
     @Override
@@ -3341,6 +3368,7 @@
                             if (mPendingCheckForLongPress == null) {
                                 mPendingCheckForLongPress = new CheckForLongPress();
                             }
+                            mPendingCheckForLongPress.setCoords(x, y);
                             mPendingCheckForLongPress.rememberWindowAttachCount();
                             postDelayed(mPendingCheckForLongPress, longPressTimeout);
                         } else {
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/AdapterView.java b/core/java/android/widget/AdapterView.java
index 2cfefba..6ed7ab8 100644
--- a/core/java/android/widget/AdapterView.java
+++ b/core/java/android/widget/AdapterView.java
@@ -815,6 +815,7 @@
     @Override
     protected void dispatchRestoreInstanceState(SparseArray<Parcelable> container) {
         dispatchThawSelfOnly(container);
+        handleDataChanged();
     }
 
     class AdapterDataSetObserver extends DataSetObserver {
diff --git a/core/java/android/widget/DropDownListView.java b/core/java/android/widget/DropDownListView.java
index bcbafc9..c869ccb 100644
--- a/core/java/android/widget/DropDownListView.java
+++ b/core/java/android/widget/DropDownListView.java
@@ -132,11 +132,6 @@
         return selectedView != null && selectedView.isEnabled() || super.shouldShowSelector();
     }
 
-    protected void clearSelection() {
-        setSelectedPositionInt(-1);
-        setNextSelectedPositionInt(-1);
-    }
-
     @Override
     public boolean onHoverEvent(MotionEvent ev) {
         final int action = ev.getActionMasked();
diff --git a/core/java/android/widget/MenuItemHoverListener.java b/core/java/android/widget/MenuItemHoverListener.java
new file mode 100644
index 0000000..87c5c85
--- /dev/null
+++ b/core/java/android/widget/MenuItemHoverListener.java
@@ -0,0 +1,13 @@
+package android.widget;
+
+import com.android.internal.view.menu.MenuBuilder;
+
+/**
+ * An interface notified when a menu item is hovered. Useful for cases when hover should trigger
+ * some behavior at a higher level, like managing the opening and closing of submenus.
+ *
+ * @hide
+ */
+public interface MenuItemHoverListener {
+    public void onItemHovered(MenuBuilder menu, int position);
+}
diff --git a/core/java/android/widget/MenuPopupWindow.java b/core/java/android/widget/MenuPopupWindow.java
index 900aa32..1fb62d0 100644
--- a/core/java/android/widget/MenuPopupWindow.java
+++ b/core/java/android/widget/MenuPopupWindow.java
@@ -22,12 +22,12 @@
 import android.transition.Transition;
 import android.util.AttributeSet;
 import android.view.KeyEvent;
+import android.view.MotionEvent;
 import android.view.View;
-import android.view.ViewGroup;
-import android.view.ViewParent;
 
 import com.android.internal.view.menu.ListMenuItemView;
 import com.android.internal.view.menu.MenuAdapter;
+import com.android.internal.view.menu.MenuBuilder;
 
 /**
  * A MenuPopupWindow represents the popup window for menu.
@@ -37,20 +37,32 @@
  *
  * @hide
  */
-public class MenuPopupWindow extends ListPopupWindow {
+public class MenuPopupWindow extends ListPopupWindow implements MenuItemHoverListener {
+    private MenuItemHoverListener mHoverListener;
+
     public MenuPopupWindow(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
         super(context, attrs, defStyleAttr, defStyleRes);
     }
 
     @Override
     DropDownListView createDropDownListView(Context context, boolean hijackFocus) {
-        return new MenuDropDownListView(context, hijackFocus);
+        MenuDropDownListView view = new MenuDropDownListView(context, hijackFocus);
+        view.setHoverListener(this);
+        return view;
     }
 
     public void setEnterTransition(Transition enterTransition) {
         mPopup.setEnterTransition(enterTransition);
     }
 
+    public void setExitTransition(Transition exitTransition) {
+        mPopup.setExitTransition(exitTransition);
+    }
+
+    public void setHoverListener(MenuItemHoverListener hoverListener) {
+        mHoverListener = hoverListener;
+    }
+
     /**
      * Set whether this window is touch modal or if outside touches will be sent to
      * other windows behind it.
@@ -59,10 +71,23 @@
         mPopup.setTouchModal(touchModal);
     }
 
-    private static class MenuDropDownListView extends DropDownListView {
+    @Override
+    public void onItemHovered(MenuBuilder menu, int position) {
+        // Forward up the chain
+        if (mHoverListener != null) {
+            mHoverListener.onItemHovered(menu, position);
+        }
+    }
+
+    /**
+     * @hide
+     */
+    public static class MenuDropDownListView extends DropDownListView {
         final int mAdvanceKey;
         final int mRetreatKey;
 
+        private MenuItemHoverListener mHoverListener;
+
         public MenuDropDownListView(Context context, boolean hijackFocus) {
             super(context, hijackFocus);
 
@@ -77,6 +102,15 @@
             }
         }
 
+        public void setHoverListener(MenuItemHoverListener hoverListener) {
+            mHoverListener = hoverListener;
+        }
+
+        public void clearSelection() {
+            setSelectedPositionInt(INVALID_POSITION);
+            setNextSelectedPositionInt(INVALID_POSITION);
+        }
+
         @Override
         public boolean onKeyDown(int keyCode, KeyEvent event) {
             ListMenuItemView selectedItem = (ListMenuItemView) getSelectedView();
@@ -90,8 +124,8 @@
                 }
                 return true;
             } else if (selectedItem != null && keyCode == mRetreatKey) {
-                setSelectedPositionInt(-1);
-                setNextSelectedPositionInt(-1);
+                setSelectedPositionInt(INVALID_POSITION);
+                setNextSelectedPositionInt(INVALID_POSITION);
 
                 ((MenuAdapter) getAdapter()).getAdapterMenu().close();
                 return true;
@@ -99,6 +133,38 @@
             return super.onKeyDown(keyCode, event);
         }
 
-    }
+        @Override
+        public boolean onHoverEvent(MotionEvent ev) {
+            boolean dispatchHover = false;
+            final int position = pointToPosition((int) ev.getX(), (int) ev.getY());
 
+            final int action = ev.getActionMasked();
+            if (action == MotionEvent.ACTION_HOVER_ENTER
+                    || action == MotionEvent.ACTION_HOVER_MOVE) {
+                if (position != INVALID_POSITION && position != mSelectedPosition) {
+                    final View hoveredItem = getChildAt(position - getFirstVisiblePosition());
+                    if (hoveredItem.isEnabled()) {
+                        dispatchHover = true;
+                    }
+                }
+            }
+
+            boolean superVal = super.onHoverEvent(ev);
+
+            if (dispatchHover && mHoverListener != null) {
+                ListAdapter adapter = getAdapter();
+                MenuAdapter menuAdapter;
+                if (adapter instanceof HeaderViewListAdapter) {
+                    menuAdapter = (MenuAdapter) ((HeaderViewListAdapter) adapter)
+                            .getWrappedAdapter();
+                } else {
+                    menuAdapter = (MenuAdapter) adapter;
+                }
+
+                mHoverListener.onItemHovered(menuAdapter.getAdapterMenu(), position);
+            }
+
+            return superVal;
+        }
+    }
 }
\ No newline at end of file
diff --git a/core/java/android/widget/PopupMenu.java b/core/java/android/widget/PopupMenu.java
index bd1fbb8..34a8439 100644
--- a/core/java/android/widget/PopupMenu.java
+++ b/core/java/android/widget/PopupMenu.java
@@ -270,25 +270,8 @@
      * @hide
      */
     public boolean onOpenSubMenu(MenuBuilder subMenu) {
-        if (subMenu == null) return false;
-
-        if (!subMenu.hasVisibleItems()) {
-            return true;
-        }
-
-        if (!mShowCascadingMenus) {
-            // Current menu will be dismissed by the normal helper, submenu will be shown in its
-            // place. (If cascading menus are enabled, the cascading implementation will show the
-            // submenu itself).
-            new MenuPopupHelper(mContext, subMenu, mAnchor).show();
-        }
-        return true;
-    }
-
-    /**
-     * @hide
-     */
-    public void onCloseSubMenu(SubMenuBuilder menu) {
+        // The menu presenter will handle opening the submenu itself. Nothing to do here.
+        return false;
     }
 
     /**
diff --git a/core/java/android/widget/PopupWindow.java b/core/java/android/widget/PopupWindow.java
index f9fa027..864a0fe 100644
--- a/core/java/android/widget/PopupWindow.java
+++ b/core/java/android/widget/PopupWindow.java
@@ -1449,11 +1449,13 @@
             anchor.getLocationOnScreen(mScreenLocation);
             onTop = (displayFrame.bottom - mScreenLocation[1] - anchorHeight - yoff) <
                     (mScreenLocation[1] - yoff - displayFrame.top);
-            if (onTop) {
-                p.gravity = Gravity.LEFT | Gravity.BOTTOM;
-                p.y = root.getHeight() - mDrawingLocation[1] + yoff;
-            } else {
-                p.y = mDrawingLocation[1] + anchorHeight + yoff;
+            if (!mOverlapAnchor) {
+                if (onTop) {
+                    p.gravity = Gravity.LEFT | Gravity.BOTTOM;
+                    p.y = root.getHeight() - mDrawingLocation[1] + yoff;
+                } else {
+                    p.y = mDrawingLocation[1] + anchorHeight + yoff;
+                }
             }
         }
 
@@ -1469,13 +1471,21 @@
                 p.width = Math.min(p.width, displayFrameWidth);
             }
 
-            if (onTop) {
-                final int popupTop = mScreenLocation[1] + yoff - mPopupHeight;
-                if (popupTop < 0) {
-                    p.y += popupTop;
+            if (mOverlapAnchor) {
+                final int displayFrameHeight = displayFrame.bottom - displayFrame.top;
+                final int bottom = p.y + p.height;
+                if (bottom > displayFrame.bottom) {
+                    p.y -= bottom - displayFrameHeight;
                 }
             } else {
-                p.y = Math.max(p.y, displayFrame.top);
+                if (onTop) {
+                    final int popupTop = mScreenLocation[1] + yoff - mPopupHeight;
+                    if (popupTop < 0) {
+                        p.y += popupTop;
+                    }
+                } else {
+                    p.y = Math.max(p.y, displayFrame.top);
+                }
             }
         }
 
@@ -1542,7 +1552,13 @@
             Resources res = anchor.getContext().getResources();
             bottomEdge = res.getDisplayMetrics().heightPixels;
         }
-        final int distanceToBottom = bottomEdge - (anchorPos[1] + anchor.getHeight()) - yOffset;
+
+        final int distanceToBottom;
+        if (mOverlapAnchor) {
+            distanceToBottom = bottomEdge - anchorPos[1] - yOffset;
+        } else {
+            distanceToBottom = bottomEdge - (anchorPos[1] + anchor.getHeight()) - yOffset;
+        }
         final int distanceToTop = anchorPos[1] - displayFrame.top + yOffset;
 
         // anchorPos[1] is distance from anchor to top of screen
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/RemoteViews.java b/core/java/android/widget/RemoteViews.java
index 7ca3339..ca1b211 100644
--- a/core/java/android/widget/RemoteViews.java
+++ b/core/java/android/widget/RemoteViews.java
@@ -31,6 +31,7 @@
 import android.content.res.ColorStateList;
 import android.content.res.Configuration;
 import android.content.res.Resources;
+import android.content.res.TypedArray;
 import android.graphics.Bitmap;
 import android.graphics.PorterDuff;
 import android.graphics.Rect;
@@ -55,6 +56,8 @@
 import android.widget.AdapterView.OnItemClickListener;
 import libcore.util.Objects;
 
+import com.android.internal.R;
+
 import java.lang.annotation.ElementType;
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
@@ -206,14 +209,22 @@
 
     /** @hide */
     public static class OnClickHandler {
+
+        private int mEnterAnimationId;
+
         public boolean onClickHandler(View view, PendingIntent pendingIntent,
                 Intent fillInIntent) {
             try {
                 // TODO: Unregister this handler if PendingIntent.FLAG_ONE_SHOT?
                 Context context = view.getContext();
-                ActivityOptions opts = ActivityOptions.makeScaleUpAnimation(view,
-                        0, 0,
-                        view.getMeasuredWidth(), view.getMeasuredHeight());
+                ActivityOptions opts;
+                if (mEnterAnimationId != 0) {
+                    opts = ActivityOptions.makeCustomAnimation(context, mEnterAnimationId, 0);
+                } else {
+                    opts = ActivityOptions.makeScaleUpAnimation(view,
+                            0, 0,
+                            view.getMeasuredWidth(), view.getMeasuredHeight());
+                }
                 context.startIntentSender(
                         pendingIntent.getIntentSender(), fillInIntent,
                         Intent.FLAG_ACTIVITY_NEW_TASK,
@@ -228,6 +239,10 @@
             }
             return true;
         }
+
+        public void setEnterAnimationId(int enterAnimationId) {
+            mEnterAnimationId = enterAnimationId;
+        }
     }
 
     /**
@@ -2761,11 +2776,31 @@
         inflater.setFilter(this);
         result = inflater.inflate(rvToApply.getLayoutId(), parent, false);
 
+        loadTransitionOverride(context, handler);
+
         rvToApply.performApply(result, parent, handler);
 
         return result;
     }
 
+    private static void loadTransitionOverride(Context context,
+            RemoteViews.OnClickHandler handler) {
+        if (handler != null && context.getResources().getBoolean(
+                com.android.internal.R.bool.config_overrideRemoteViewsActivityTransition)) {
+            TypedArray windowStyle = context.getTheme().obtainStyledAttributes(
+                    com.android.internal.R.styleable.Window);
+            int windowAnimations = windowStyle.getResourceId(
+                    com.android.internal.R.styleable.Window_windowAnimationStyle, 0);
+            TypedArray windowAnimationStyle = context.obtainStyledAttributes(
+                    windowAnimations, com.android.internal.R.styleable.WindowAnimation);
+            handler.setEnterAnimationId(windowAnimationStyle.getResourceId(
+                    com.android.internal.R.styleable.
+                            WindowAnimation_activityOpenRemoteViewsEnterAnimation, 0));
+            windowStyle.recycle();
+            windowAnimationStyle.recycle();
+        }
+    }
+
     /**
      * Applies all of the actions to the provided view.
      *
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/java/android/widget/TimePickerClockDelegate.java b/core/java/android/widget/TimePickerClockDelegate.java
index 2365b48..61ef6dc 100644
--- a/core/java/android/widget/TimePickerClockDelegate.java
+++ b/core/java/android/widget/TimePickerClockDelegate.java
@@ -22,7 +22,6 @@
 import android.content.res.Configuration;
 import android.content.res.Resources;
 import android.content.res.TypedArray;
-import android.graphics.drawable.Drawable;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.text.SpannableStringBuilder;
@@ -32,7 +31,6 @@
 import android.util.AttributeSet;
 import android.util.Log;
 import android.util.StateSet;
-import android.util.TypedValue;
 import android.view.HapticFeedbackConstants;
 import android.view.KeyCharacterMap;
 import android.view.KeyEvent;
@@ -66,10 +64,8 @@
     // Also NOT a real index, just used for keyboard mode.
     private static final int ENABLE_PICKER_INDEX = 3;
 
-    private static final int[] ATTRS_TEXT_COLOR = new int[] {
-            com.android.internal.R.attr.textColor};
-    private static final int[] ATTRS_DISABLED_ALPHA = new int[] {
-            com.android.internal.R.attr.disabledAlpha};
+    private static final int[] ATTRS_TEXT_COLOR = new int[] {R.attr.textColor};
+    private static final int[] ATTRS_DISABLED_ALPHA = new int[] {R.attr.disabledAlpha};
 
     // LayoutLib relies on these constants. Change TimePickerClockDelegate_Delegate if
     // modifying these.
diff --git a/core/java/android/widget/Toolbar.java b/core/java/android/widget/Toolbar.java
index 471ea9b..acbf5eb 100644
--- a/core/java/android/widget/Toolbar.java
+++ b/core/java/android/widget/Toolbar.java
@@ -98,6 +98,32 @@
  * <p>In modern Android UIs developers should lean more on a visually distinct color scheme for
  * toolbars than on their application icon. The use of application icon plus title as a standard
  * layout is discouraged on API 21 devices and newer.</p>
+ *
+ * @attr ref android.R.styleable#Toolbar_buttonGravity
+ * @attr ref android.R.styleable#Toolbar_collapseContentDescription
+ * @attr ref android.R.styleable#Toolbar_collapseIcon
+ * @attr ref android.R.styleable#Toolbar_contentInsetEnd
+ * @attr ref android.R.styleable#Toolbar_contentInsetLeft
+ * @attr ref android.R.styleable#Toolbar_contentInsetRight
+ * @attr ref android.R.styleable#Toolbar_contentInsetStart
+ * @attr ref android.R.styleable#Toolbar_gravity
+ * @attr ref android.R.styleable#Toolbar_logo
+ * @attr ref android.R.styleable#Toolbar_logoDescription
+ * @attr ref android.R.styleable#Toolbar_maxButtonHeight
+ * @attr ref android.R.styleable#Toolbar_navigationContentDescription
+ * @attr ref android.R.styleable#Toolbar_navigationIcon
+ * @attr ref android.R.styleable#Toolbar_popupTheme
+ * @attr ref android.R.styleable#Toolbar_subtitle
+ * @attr ref android.R.styleable#Toolbar_subtitleTextAppearance
+ * @attr ref android.R.styleable#Toolbar_subtitleTextColor
+ * @attr ref android.R.styleable#Toolbar_title
+ * @attr ref android.R.styleable#Toolbar_titleMargin
+ * @attr ref android.R.styleable#Toolbar_titleMarginBottom
+ * @attr ref android.R.styleable#Toolbar_titleMarginEnd
+ * @attr ref android.R.styleable#Toolbar_titleMarginStart
+ * @attr ref android.R.styleable#Toolbar_titleMarginTop
+ * @attr ref android.R.styleable#Toolbar_titleTextAppearance
+ * @attr ref android.R.styleable#Toolbar_titleTextColor
  */
 public class Toolbar extends ViewGroup {
     private static final String TAG = "Toolbar";
@@ -203,7 +229,7 @@
         mGravity = a.getInteger(R.styleable.Toolbar_gravity, mGravity);
         mButtonGravity = a.getInteger(R.styleable.Toolbar_buttonGravity, Gravity.TOP);
         mTitleMarginStart = mTitleMarginEnd = mTitleMarginTop = mTitleMarginBottom =
-                a.getDimensionPixelOffset(R.styleable.Toolbar_titleMargins, 0);
+                a.getDimensionPixelOffset(R.styleable.Toolbar_titleMargin, 0);
 
         final int marginStart = a.getDimensionPixelOffset(R.styleable.Toolbar_titleMarginStart, -1);
         if (marginStart >= 0) {
@@ -321,6 +347,116 @@
         return mPopupTheme;
     }
 
+    /**
+     * Sets the title margin.
+     *
+     * @param start the starting title margin in pixels
+     * @param top the top title margin in pixels
+     * @param end the ending title margin in pixels
+     * @param bottom the bottom title margin in pixels
+     * @see #getTitleMarginStart()
+     * @see #getTitleMarginTop()
+     * @see #getTitleMarginEnd()
+     * @see #getTitleMarginBottom()
+     * @attr ref android.R.styleable#Toolbar_titleMargin
+     */
+    public void setTitleMargin(int start, int top, int end, int bottom) {
+        mTitleMarginStart = start;
+        mTitleMarginTop = top;
+        mTitleMarginEnd = end;
+        mTitleMarginBottom = bottom;
+
+        requestLayout();
+    }
+
+    /**
+     * @return the starting title margin in pixels
+     * @see #setTitleMarginStart(int)
+     * @attr ref android.R.styleable#Toolbar_titleMarginStart
+     */
+    public int getTitleMarginStart() {
+        return mTitleMarginStart;
+    }
+
+    /**
+     * Sets the starting title margin in pixels.
+     *
+     * @param margin the starting title margin in pixels
+     * @see #getTitleMarginStart()
+     * @attr ref android.R.styleable#Toolbar_titleMarginStart
+     */
+    public void setTitleMarginStart(int margin) {
+        mTitleMarginStart = margin;
+
+        requestLayout();
+    }
+
+    /**
+     * @return the top title margin in pixels
+     * @see #setTitleMarginTop(int)
+     * @attr ref android.R.styleable#Toolbar_titleMarginTop
+     */
+    public int getTitleMarginTop() {
+        return mTitleMarginTop;
+    }
+
+    /**
+     * Sets the top title margin in pixels.
+     *
+     * @param margin the top title margin in pixels
+     * @see #getTitleMarginTop()
+     * @attr ref android.R.styleable#Toolbar_titleMarginTop
+     */
+    public void setTitleMarginTop(int margin) {
+        mTitleMarginTop = margin;
+
+        requestLayout();
+    }
+
+    /**
+     * @return the ending title margin in pixels
+     * @see #setTitleMarginEnd(int)
+     * @attr ref android.R.styleable#Toolbar_titleMarginEnd
+     */
+    public int getTitleMarginEnd() {
+        return mTitleMarginEnd;
+    }
+
+    /**
+     * Sets the ending title margin in pixels.
+     *
+     * @param margin the ending title margin in pixels
+     * @see #getTitleMarginEnd()
+     * @attr ref android.R.styleable#Toolbar_titleMarginEnd
+     */
+    public void setTitleMarginEnd(int margin) {
+        mTitleMarginEnd = margin;
+
+        requestLayout();
+    }
+
+    /**
+     * @return the bottom title margin in pixels
+     * @see #setTitleMarginBottom(int)
+     * @attr ref android.R.styleable#Toolbar_titleMarginBottom
+     */
+    public int getTitleMarginBottom() {
+        return mTitleMarginBottom;
+    }
+
+    /**
+     * Sets the bottom title margin in pixels.
+     *
+     * @param margin the bottom title margin in pixels
+     * @see #getTitleMarginBottom()
+     * @attr ref android.R.styleable#Toolbar_titleMarginBottom
+     */
+    public void setTitleMarginBottom(int margin) {
+        mTitleMarginBottom = margin;
+
+        requestLayout();
+    }
+
     @Override
     public void onRtlPropertiesChanged(@ResolvedLayoutDir int layoutDirection) {
         super.onRtlPropertiesChanged(layoutDirection);
diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java
index 5b19edf..2172b5c 100644
--- a/core/java/com/android/internal/app/ChooserActivity.java
+++ b/core/java/com/android/internal/app/ChooserActivity.java
@@ -647,7 +647,8 @@
 
         @Override
         public CharSequence getExtendedInfo() {
-            return mSourceInfo != null ? mSourceInfo.getExtendedInfo() : null;
+            // ChooserTargets have badge icons, so we won't show the extended info to disambiguate.
+            return null;
         }
 
         @Override
@@ -740,9 +741,8 @@
 
         @Override
         public boolean showsExtendedInfo(TargetInfo info) {
-            // Reserve space to show extended info if any one of the items in the adapter has
-            // extended info. This keeps grid item sizes uniform.
-            return hasExtendedInfo();
+            // We have badges so we don't need this text shown.
+            return false;
         }
 
         @Override
diff --git a/core/java/com/android/internal/app/ResolverActivity.java b/core/java/com/android/internal/app/ResolverActivity.java
index 9272193..ef9d1ce 100644
--- a/core/java/com/android/internal/app/ResolverActivity.java
+++ b/core/java/com/android/internal/app/ResolverActivity.java
@@ -896,6 +896,7 @@
         private final ResolveInfo mResolveInfo;
         private final CharSequence mDisplayLabel;
         private Drawable mDisplayIcon;
+        private Drawable mBadge;
         private final CharSequence mExtendedInfo;
         private final Intent mResolvedIntent;
         private final List<Intent> mSourceIntents = new ArrayList<>();
@@ -940,7 +941,25 @@
         }
 
         public Drawable getBadgeIcon() {
-            return null;
+            // We only expose a badge if we have extended info.
+            // The badge is a higher-priority disambiguation signal
+            // but we don't need one if we wouldn't show extended info at all.
+            if (TextUtils.isEmpty(getExtendedInfo())) {
+                return null;
+            }
+
+            if (mBadge == null && mResolveInfo != null && mResolveInfo.activityInfo != null
+                    && mResolveInfo.activityInfo.applicationInfo != null) {
+                if (mResolveInfo.activityInfo.icon == 0 || mResolveInfo.activityInfo.icon
+                        == mResolveInfo.activityInfo.applicationInfo.icon) {
+                    // Badging an icon with exactly the same icon is silly.
+                    // If the activityInfo icon resid is 0 it will fall back
+                    // to the application's icon, making it a match.
+                    return null;
+                }
+                mBadge = mResolveInfo.activityInfo.applicationInfo.loadIcon(mPm);
+            }
+            return mBadge;
         }
 
         @Override
@@ -1378,8 +1397,8 @@
             } else {
                 mHasExtendedInfo = true;
                 boolean usePkg = false;
-                CharSequence startApp = ro.getResolveInfoAt(0).activityInfo.applicationInfo
-                        .loadLabel(mPm);
+                final ApplicationInfo ai = ro.getResolveInfoAt(0).activityInfo.applicationInfo;
+                final CharSequence startApp = ai.loadLabel(mPm);
                 if (startApp == null) {
                     usePkg = true;
                 }
diff --git a/core/java/com/android/internal/inputmethod/InputMethodUtils.java b/core/java/com/android/internal/inputmethod/InputMethodUtils.java
index a4ef00a..01ac22e 100644
--- a/core/java/com/android/internal/inputmethod/InputMethodUtils.java
+++ b/core/java/com/android/internal/inputmethod/InputMethodUtils.java
@@ -793,6 +793,24 @@
         return imeMap;
     }
 
+    @NonNull
+    public static String buildInputMethodsAndSubtypesString(
+            @NonNull final ArrayMap<String, ArraySet<String>> map) {
+        // we want to use the canonical InputMethodSettings implementation,
+        // so we convert data structures first.
+        List<Pair<String, ArrayList<String>>> imeMap = new ArrayList<>(4);
+        for (ArrayMap.Entry<String, ArraySet<String>> entry : map.entrySet()) {
+            final String imeName = entry.getKey();
+            final ArraySet<String> subtypeSet = entry.getValue();
+            final ArrayList<String> subtypes = new ArrayList<>(2);
+            if (subtypeSet != null) {
+                subtypes.addAll(subtypeSet);
+            }
+            imeMap.add(new Pair<>(imeName, subtypes));
+        }
+        return InputMethodSettings.buildInputMethodsSettingString(imeMap);
+    }
+
     /**
      * Utility class for putting and getting settings for InputMethod
      * TODO: Move all putters and getters of settings to this class.
diff --git a/core/java/com/android/internal/os/BatteryStatsHelper.java b/core/java/com/android/internal/os/BatteryStatsHelper.java
index 4f4d3e0..f178c8c 100644
--- a/core/java/com/android/internal/os/BatteryStatsHelper.java
+++ b/core/java/com/android/internal/os/BatteryStatsHelper.java
@@ -338,7 +338,7 @@
         }
 
         if (mCpuPowerCalculator == null) {
-            mCpuPowerCalculator = new CpuPowerCalculator();
+            mCpuPowerCalculator = new CpuPowerCalculator(mPowerProfile);
         }
         mCpuPowerCalculator.reset();
 
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index 4ff7869..8cf2dabdb 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -105,7 +105,7 @@
     private static final int MAGIC = 0xBA757475; // 'BATSTATS'
 
     // Current on-disk Parcel version
-    private static final int VERSION = 130 + (USE_OLD_HISTORY ? 1000 : 0);
+    private static final int VERSION = 132 + (USE_OLD_HISTORY ? 1000 : 0);
 
     // Maximum number of items we will record in the history.
     private static final int MAX_HISTORY_ITEMS = 2000;
@@ -118,8 +118,6 @@
     // in to one common name.
     private static final int MAX_WAKELOCKS_PER_UID = 100;
 
-    private static int sNumSpeedSteps;
-
     private final JournaledFile mFile;
     public final AtomicFile mCheckinFile;
     public final AtomicFile mDailyFile;
@@ -133,7 +131,7 @@
     private final KernelWakelockStats mTmpWakelockStats = new KernelWakelockStats();
 
     private final KernelUidCpuTimeReader mKernelUidCpuTimeReader = new KernelUidCpuTimeReader();
-    private final KernelCpuSpeedReader mKernelCpuSpeedReader = new KernelCpuSpeedReader();
+    private KernelCpuSpeedReader[] mKernelCpuSpeedReaders;
 
     public interface BatteryCallback {
         public void batteryNeedsCpuUpdate();
@@ -4411,7 +4409,7 @@
         LongSamplingCounter mUserCpuTime = new LongSamplingCounter(mOnBatteryTimeBase);
         LongSamplingCounter mSystemCpuTime = new LongSamplingCounter(mOnBatteryTimeBase);
         LongSamplingCounter mCpuPower = new LongSamplingCounter(mOnBatteryTimeBase);
-        LongSamplingCounter[] mSpeedBins;
+        LongSamplingCounter[][] mCpuClusterSpeed;
 
         /**
          * The statistics we have collected for this uid's wake locks.
@@ -4470,7 +4468,6 @@
             mWifiMulticastTimer = new StopwatchTimer(Uid.this, WIFI_MULTICAST_ENABLED,
                     mWifiMulticastTimers, mOnBatteryTimeBase);
             mProcessStateTimer = new StopwatchTimer[NUM_PROCESS_STATE];
-            mSpeedBins = new LongSamplingCounter[getCpuSpeedSteps()];
         }
 
         @Override
@@ -5008,10 +5005,18 @@
         }
 
         @Override
-        public long getTimeAtCpuSpeed(int step, int which) {
-            if (step >= 0 && step < mSpeedBins.length) {
-                if (mSpeedBins[step] != null) {
-                    return mSpeedBins[step].getCountLocked(which);
+        public long getTimeAtCpuSpeed(int cluster, int step, int which) {
+            if (mCpuClusterSpeed != null) {
+                if (cluster >= 0 && cluster < mCpuClusterSpeed.length) {
+                    final LongSamplingCounter[] cpuSpeeds = mCpuClusterSpeed[cluster];
+                    if (cpuSpeeds != null) {
+                        if (step >= 0 && step < cpuSpeeds.length) {
+                            final LongSamplingCounter c = cpuSpeeds[step];
+                            if (c != null) {
+                                return c.getCountLocked(which);
+                            }
+                        }
+                    }
                 }
             }
             return 0;
@@ -5128,10 +5133,16 @@
             mUserCpuTime.reset(false);
             mSystemCpuTime.reset(false);
             mCpuPower.reset(false);
-            for (int i = 0; i < mSpeedBins.length; i++) {
-                LongSamplingCounter c = mSpeedBins[i];
-                if (c != null) {
-                    c.reset(false);
+
+            if (mCpuClusterSpeed != null) {
+                for (LongSamplingCounter[] speeds : mCpuClusterSpeed) {
+                    if (speeds != null) {
+                        for (LongSamplingCounter speed : speeds) {
+                            if (speed != null) {
+                                speed.reset(false);
+                            }
+                        }
+                    }
                 }
             }
 
@@ -5280,10 +5291,16 @@
                 mUserCpuTime.detach();
                 mSystemCpuTime.detach();
                 mCpuPower.detach();
-                for (int i = 0; i < mSpeedBins.length; i++) {
-                    LongSamplingCounter c = mSpeedBins[i];
-                    if (c != null) {
-                        c.detach();
+
+                if (mCpuClusterSpeed != null) {
+                    for (LongSamplingCounter[] cpuSpeeds : mCpuClusterSpeed) {
+                        if (cpuSpeeds != null) {
+                            for (LongSamplingCounter c : cpuSpeeds) {
+                                if (c != null) {
+                                    c.detach();
+                                }
+                            }
+                        }
                     }
                 }
             }
@@ -5461,15 +5478,27 @@
             mSystemCpuTime.writeToParcel(out);
             mCpuPower.writeToParcel(out);
 
-            out.writeInt(mSpeedBins.length);
-            for (int i = 0; i < mSpeedBins.length; i++) {
-                LongSamplingCounter c = mSpeedBins[i];
-                if (c != null) {
-                    out.writeInt(1);
-                    c.writeToParcel(out);
-                } else {
-                    out.writeInt(0);
+            if (mCpuClusterSpeed != null) {
+                out.writeInt(1);
+                out.writeInt(mCpuClusterSpeed.length);
+                for (LongSamplingCounter[] cpuSpeeds : mCpuClusterSpeed) {
+                    if (cpuSpeeds != null) {
+                        out.writeInt(1);
+                        out.writeInt(cpuSpeeds.length);
+                        for (LongSamplingCounter c : cpuSpeeds) {
+                            if (c != null) {
+                                out.writeInt(1);
+                                c.writeToParcel(out);
+                            } else {
+                                out.writeInt(0);
+                            }
+                        }
+                    } else {
+                        out.writeInt(0);
+                    }
                 }
+            } else {
+                out.writeInt(0);
             }
         }
 
@@ -5653,13 +5682,32 @@
             mSystemCpuTime = new LongSamplingCounter(mOnBatteryTimeBase, in);
             mCpuPower = new LongSamplingCounter(mOnBatteryTimeBase, in);
 
-            int bins = in.readInt();
-            int steps = getCpuSpeedSteps();
-            mSpeedBins = new LongSamplingCounter[bins >= steps ? bins : steps];
-            for (int i = 0; i < bins; i++) {
-                if (in.readInt() != 0) {
-                    mSpeedBins[i] = new LongSamplingCounter(mOnBatteryTimeBase, in);
+            if (in.readInt() != 0) {
+                int numCpuClusters = in.readInt();
+                if (mPowerProfile != null && mPowerProfile.getNumCpuClusters() != numCpuClusters) {
+                    throw new ParcelFormatException("Incompatible number of cpu clusters");
                 }
+
+                mCpuClusterSpeed = new LongSamplingCounter[numCpuClusters][];
+                for (int cluster = 0; cluster < numCpuClusters; cluster++) {
+                    if (in.readInt() != 0) {
+                        int numSpeeds = in.readInt();
+                        if (mPowerProfile != null &&
+                                mPowerProfile.getNumSpeedStepsInCpuCluster(cluster) != numSpeeds) {
+                            throw new ParcelFormatException("Incompatible number of cpu speeds");
+                        }
+
+                        final LongSamplingCounter[] cpuSpeeds = new LongSamplingCounter[numSpeeds];
+                        mCpuClusterSpeed[cluster] = cpuSpeeds;
+                        for (int speed = 0; speed < numSpeeds; speed++) {
+                            if (in.readInt() != 0) {
+                                cpuSpeeds[speed] = new LongSamplingCounter(mOnBatteryTimeBase, in);
+                            }
+                        }
+                    }
+                }
+            } else {
+                mCpuClusterSpeed = null;
             }
         }
 
@@ -6874,6 +6922,19 @@
     public void setPowerProfile(PowerProfile profile) {
         synchronized (this) {
             mPowerProfile = profile;
+
+            // We need to initialize the KernelCpuSpeedReaders to read from
+            // the first cpu of each core. Once we have the PowerProfile, we have access to this
+            // information.
+            final int numClusters = mPowerProfile.getNumCpuClusters();
+            mKernelCpuSpeedReaders = new KernelCpuSpeedReader[numClusters];
+            int firstCpuOfCluster = 0;
+            for (int i = 0; i < numClusters; i++) {
+                final int numSpeedSteps = mPowerProfile.getNumSpeedStepsInCpuCluster(i);
+                mKernelCpuSpeedReaders[i] = new KernelCpuSpeedReader(firstCpuOfCluster,
+                        numSpeedSteps);
+                firstCpuOfCluster += mPowerProfile.getNumCoresInCpuCluster(i);
+            }
         }
     }
 
@@ -6881,10 +6942,6 @@
         mCallback = cb;
     }
 
-    public void setNumSpeedSteps(int steps) {
-        if (sNumSpeedSteps == 0) sNumSpeedSteps = steps;
-    }
-
     public void setRadioScanningTimeout(long timeout) {
         if (mPhoneSignalScanningTimer != null) {
             mPhoneSignalScanningTimer.setTimeout(timeout);
@@ -7987,6 +8044,10 @@
      * wakelocks. If the screen is on, we just assign the actual cpu time an app used.
      */
     public void updateCpuTimeLocked() {
+        if (mPowerProfile == null) {
+            return;
+        }
+
         if (DEBUG_ENERGY_CPU) {
             Slog.d(TAG, "!Cpu updating!");
         }
@@ -7997,9 +8058,11 @@
         // If no app is holding a wakelock, then the distribution is normal.
         final int wakelockWeight = 50;
 
-        // Read the time spent at various cpu frequencies.
-        final int cpuSpeedSteps = getCpuSpeedSteps();
-        final long[] cpuSpeeds = mKernelCpuSpeedReader.readDelta();
+        // Read the time spent for each cluster at various cpu frequencies.
+        final long[][] clusterSpeeds = new long[mKernelCpuSpeedReaders.length][];
+        for (int cluster = 0; cluster < mKernelCpuSpeedReaders.length; cluster++) {
+            clusterSpeeds[cluster] = mKernelCpuSpeedReaders[cluster].readDelta();
+        }
 
         int numWakelocks = 0;
 
@@ -8072,11 +8135,28 @@
 
                         // Add the cpu speeds to this UID. These are used as a ratio
                         // for computing the power this UID used.
-                        for (int i = 0; i < cpuSpeedSteps; i++) {
-                            if (u.mSpeedBins[i] == null) {
-                                u.mSpeedBins[i] = new LongSamplingCounter(mOnBatteryTimeBase);
+                        final int numClusters = mPowerProfile.getNumCpuClusters();
+                        if (u.mCpuClusterSpeed == null || u.mCpuClusterSpeed.length !=
+                                numClusters) {
+                            u.mCpuClusterSpeed = new LongSamplingCounter[numClusters][];
+                        }
+
+                        for (int cluster = 0; cluster < clusterSpeeds.length; cluster++) {
+                            final int speedsInCluster = mPowerProfile.getNumSpeedStepsInCpuCluster(
+                                    cluster);
+                            if (u.mCpuClusterSpeed[cluster] == null || speedsInCluster !=
+                                    u.mCpuClusterSpeed[cluster].length) {
+                                u.mCpuClusterSpeed[cluster] =
+                                        new LongSamplingCounter[speedsInCluster];
                             }
-                            u.mSpeedBins[i].addCountLocked(cpuSpeeds[i]);
+
+                            final LongSamplingCounter[] cpuSpeeds = u.mCpuClusterSpeed[cluster];
+                            for (int speed = 0; speed < clusterSpeeds[cluster].length; speed++) {
+                                if (cpuSpeeds[speed] == null) {
+                                    cpuSpeeds[speed] = new LongSamplingCounter(mOnBatteryTimeBase);
+                                }
+                                cpuSpeeds[speed].addCountLocked(clusterSpeeds[cluster][speed]);
+                            }
                         }
                     }
                 });
@@ -8776,11 +8856,6 @@
         }
     }
 
-    @Override
-    public int getCpuSpeedSteps() {
-        return sNumSpeedSteps;
-    }
-
     /**
      * Retrieve the statistics object for a particular uid, creating if needed.
      */
@@ -9216,11 +9291,6 @@
             }
         }
 
-        sNumSpeedSteps = in.readInt();
-        if (sNumSpeedSteps < 0 || sNumSpeedSteps > 100) {
-            throw new ParcelFormatException("Bad speed steps in data: " + sNumSpeedSteps);
-        }
-
         final int NU = in.readInt();
         if (NU > 10000) {
             throw new ParcelFormatException("File corrupt: too many uids " + NU);
@@ -9304,17 +9374,33 @@
             u.mSystemCpuTime.readSummaryFromParcelLocked(in);
             u.mCpuPower.readSummaryFromParcelLocked(in);
 
-            int NSB = in.readInt();
-            if (NSB > 100) {
-                throw new ParcelFormatException("File corrupt: too many speed bins " + NSB);
-            }
-
-            u.mSpeedBins = new LongSamplingCounter[NSB];
-            for (int i=0; i<NSB; i++) {
-                if (in.readInt() != 0) {
-                    u.mSpeedBins[i] = new LongSamplingCounter(mOnBatteryTimeBase);
-                    u.mSpeedBins[i].readSummaryFromParcelLocked(in);
+            if (in.readInt() != 0) {
+                final int numClusters = in.readInt();
+                if (mPowerProfile != null && mPowerProfile.getNumCpuClusters() != numClusters) {
+                    throw new ParcelFormatException("Incompatible cpu cluster arrangement");
                 }
+
+                u.mCpuClusterSpeed = new LongSamplingCounter[numClusters][];
+                for (int cluster = 0; cluster < numClusters; cluster++) {
+                    int NSB = in.readInt();
+                    if (mPowerProfile != null &&
+                            mPowerProfile.getNumSpeedStepsInCpuCluster(cluster) != NSB) {
+                        throw new ParcelFormatException("File corrupt: too many speed bins " + NSB);
+                    }
+
+                    if (in.readInt() != 0) {
+                        u.mCpuClusterSpeed[cluster] = new LongSamplingCounter[NSB];
+                        for (int speed = 0; speed < NSB; speed++) {
+                            if (in.readInt() != 0) {
+                                u.mCpuClusterSpeed[cluster][speed] = new LongSamplingCounter(
+                                        mOnBatteryTimeBase);
+                                u.mCpuClusterSpeed[cluster][speed].readSummaryFromParcelLocked(in);
+                            }
+                        }
+                    }
+                }
+            } else {
+                u.mCpuClusterSpeed = null;
             }
 
             int NW = in.readInt();
@@ -9531,7 +9617,6 @@
             }
         }
 
-        out.writeInt(sNumSpeedSteps);
         final int NU = mUidStats.size();
         out.writeInt(NU);
         for (int iu = 0; iu < NU; iu++) {
@@ -9640,15 +9725,27 @@
             u.mSystemCpuTime.writeSummaryFromParcelLocked(out);
             u.mCpuPower.writeSummaryFromParcelLocked(out);
 
-            out.writeInt(u.mSpeedBins.length);
-            for (int i = 0; i < u.mSpeedBins.length; i++) {
-                LongSamplingCounter speedBin = u.mSpeedBins[i];
-                if (speedBin != null) {
-                    out.writeInt(1);
-                    speedBin.writeSummaryFromParcelLocked(out);
-                } else {
-                    out.writeInt(0);
+            if (u.mCpuClusterSpeed != null) {
+                out.writeInt(1);
+                out.writeInt(u.mCpuClusterSpeed.length);
+                for (LongSamplingCounter[] cpuSpeeds : u.mCpuClusterSpeed) {
+                    if (cpuSpeeds != null) {
+                        out.writeInt(1);
+                        out.writeInt(cpuSpeeds.length);
+                        for (LongSamplingCounter c : cpuSpeeds) {
+                            if (c != null) {
+                                out.writeInt(1);
+                                c.writeSummaryFromParcelLocked(out);
+                            } else {
+                                out.writeInt(0);
+                            }
+                        }
+                    } else {
+                        out.writeInt(0);
+                    }
                 }
+            } else {
+                out.writeInt(0);
             }
 
             final ArrayMap<String, Uid.Wakelock> wakeStats = u.mWakelockStats.getMap();
@@ -9897,8 +9994,6 @@
         mFlashlightTurnedOnTimers.clear();
         mCameraTurnedOnTimers.clear();
 
-        sNumSpeedSteps = in.readInt();
-
         int numUids = in.readInt();
         mUidStats.clear();
         for (int i = 0; i < numUids; i++) {
@@ -10037,8 +10132,6 @@
             out.writeInt(0);
         }
 
-        out.writeInt(sNumSpeedSteps);
-
         if (inclUids) {
             int size = mUidStats.size();
             out.writeInt(size);
diff --git a/core/java/com/android/internal/os/CpuPowerCalculator.java b/core/java/com/android/internal/os/CpuPowerCalculator.java
index d62f7a6..8417856 100644
--- a/core/java/com/android/internal/os/CpuPowerCalculator.java
+++ b/core/java/com/android/internal/os/CpuPowerCalculator.java
@@ -22,12 +22,47 @@
 public class CpuPowerCalculator extends PowerCalculator {
     private static final String TAG = "CpuPowerCalculator";
     private static final boolean DEBUG = BatteryStatsHelper.DEBUG;
+    private final PowerProfile mProfile;
+
+    public CpuPowerCalculator(PowerProfile profile) {
+        mProfile = profile;
+    }
 
     @Override
     public void calculateApp(BatterySipper app, BatteryStats.Uid u, long rawRealtimeUs,
                              long rawUptimeUs, int statsType) {
+
         app.cpuTimeMs = (u.getUserCpuTimeUs(statsType) + u.getSystemCpuTimeUs(statsType)) / 1000;
-        app.cpuPowerMah = (double) u.getCpuPowerMaUs(statsType) / (60.0 * 60.0 * 1000.0 * 1000.0);
+
+        // Aggregate total time spent on each cluster.
+        long totalTime = 0;
+        final int numClusters = mProfile.getNumCpuClusters();
+        for (int cluster = 0; cluster < numClusters; cluster++) {
+            final int speedsForCluster = mProfile.getNumSpeedStepsInCpuCluster(cluster);
+            for (int speed = 0; speed < speedsForCluster; speed++) {
+                totalTime += u.getTimeAtCpuSpeed(cluster, speed, statsType);
+            }
+        }
+        totalTime = Math.max(totalTime, 1);
+
+        double cpuPowerMaMs = 0;
+        for (int cluster = 0; cluster < numClusters; cluster++) {
+            final int speedsForCluster = mProfile.getNumSpeedStepsInCpuCluster(cluster);
+            for (int speed = 0; speed < speedsForCluster; speed++) {
+                final double ratio = (double) u.getTimeAtCpuSpeed(cluster, speed, statsType) /
+                        totalTime;
+                final double cpuSpeedStepPower = ratio * app.cpuTimeMs *
+                        mProfile.getAveragePowerForCpu(cluster, speed);
+                if (DEBUG && ratio != 0) {
+                    Log.d(TAG, "UID " + u.getUid() + ": CPU cluster #" + cluster + " step #"
+                            + speed + " ratio=" + BatteryStatsHelper.makemAh(ratio) + " power="
+                            + BatteryStatsHelper.makemAh(cpuSpeedStepPower / (60 * 60 * 1000)));
+                }
+                cpuPowerMaMs += cpuSpeedStepPower;
+            }
+        }
+        app.cpuPowerMah = cpuPowerMaMs / (60 * 60 * 1000);
+
         if (DEBUG && (app.cpuTimeMs != 0 || app.cpuPowerMah != 0)) {
             Log.d(TAG, "UID " + u.getUid() + ": CPU time=" + app.cpuTimeMs + " ms power="
                     + BatteryStatsHelper.makemAh(app.cpuPowerMah));
diff --git a/core/java/com/android/internal/os/InstallerConnection.java b/core/java/com/android/internal/os/InstallerConnection.java
index dcc6a5e..13d046e 100644
--- a/core/java/com/android/internal/os/InstallerConnection.java
+++ b/core/java/com/android/internal/os/InstallerConnection.java
@@ -20,6 +20,7 @@
 import android.net.LocalSocketAddress;
 import android.os.SystemClock;
 import android.util.Slog;
+
 import libcore.io.IoUtils;
 import libcore.io.Streams;
 
@@ -91,31 +92,29 @@
         }
     }
 
-    public int dexopt(String apkPath, int uid, boolean isPublic,
-            String instructionSet, int dexoptNeeded) {
-        return dexopt(apkPath, uid, isPublic, "*", instructionSet, dexoptNeeded,
-                false, false, null);
+    public int dexopt(String apkPath, int uid, String instructionSet,
+            int dexoptNeeded, int dexFlags) {
+        return dexopt(apkPath, uid, "*", instructionSet, dexoptNeeded,
+                null /*outputPath*/, dexFlags);
     }
 
-    public int dexopt(String apkPath, int uid, boolean isPublic, String pkgName,
-            String instructionSet, int dexoptNeeded, boolean vmSafeMode,
-            boolean debuggable, String outputPath) {
+    public int dexopt(String apkPath, int uid, String pkgName, String instructionSet,
+            int dexoptNeeded, String outputPath, int dexFlags) {
         StringBuilder builder = new StringBuilder("dexopt");
         builder.append(' ');
         builder.append(apkPath);
         builder.append(' ');
         builder.append(uid);
-        builder.append(isPublic ? " 1" : " 0");
         builder.append(' ');
         builder.append(pkgName);
         builder.append(' ');
         builder.append(instructionSet);
         builder.append(' ');
         builder.append(dexoptNeeded);
-        builder.append(vmSafeMode ? " 1" : " 0");
-        builder.append(debuggable ? " 1" : " 0");
         builder.append(' ');
         builder.append(outputPath != null ? outputPath : "!");
+        builder.append(' ');
+        builder.append(dexFlags);
         return execute(builder.toString());
     }
 
diff --git a/core/java/com/android/internal/os/KernelCpuSpeedReader.java b/core/java/com/android/internal/os/KernelCpuSpeedReader.java
index c30df28..5b776ac 100644
--- a/core/java/com/android/internal/os/KernelCpuSpeedReader.java
+++ b/core/java/com/android/internal/os/KernelCpuSpeedReader.java
@@ -24,8 +24,8 @@
 import java.util.Arrays;
 
 /**
- * Reads CPU time spent at various frequencies and provides a delta from the last call to
- * {@link #readDelta}. Each line in the proc file has the format:
+ * Reads CPU time of a specific core spent at various frequencies and provides a delta from the
+ * last call to {@link #readDelta}. Each line in the proc file has the format:
  *
  * freq time
  *
@@ -33,12 +33,20 @@
  */
 public class KernelCpuSpeedReader {
     private static final String TAG = "KernelCpuSpeedReader";
-    private static final String sProcFile =
-            "/sys/devices/system/cpu/cpu0/cpufreq/stats/time_in_state";
-    private static final int MAX_SPEEDS = 60;
 
-    private long[] mLastSpeedTimes = new long[MAX_SPEEDS];
-    private long[] mDeltaSpeedTimes = new long[MAX_SPEEDS];
+    private final String mProcFile;
+    private final long[] mLastSpeedTimes;
+    private final long[] mDeltaSpeedTimes;
+
+    /**
+     * @param cpuNumber The cpu (cpu0, cpu1, etc) whose state to read.
+     */
+    public KernelCpuSpeedReader(int cpuNumber, int numSpeedSteps) {
+        mProcFile = String.format("/sys/devices/system/cpu/cpu%d/cpufreq/stats/time_in_state",
+                cpuNumber);
+        mLastSpeedTimes = new long[numSpeedSteps];
+        mDeltaSpeedTimes = new long[numSpeedSteps];
+    }
 
     /**
      * The returned array is modified in subsequent calls to {@link #readDelta}.
@@ -46,22 +54,28 @@
      * {@link #readDelta}.
      */
     public long[] readDelta() {
-        try (BufferedReader reader = new BufferedReader(new FileReader(sProcFile))) {
+        try (BufferedReader reader = new BufferedReader(new FileReader(mProcFile))) {
             TextUtils.SimpleStringSplitter splitter = new TextUtils.SimpleStringSplitter(' ');
             String line;
             int speedIndex = 0;
-            while ((line = reader.readLine()) != null) {
+            while (speedIndex < mLastSpeedTimes.length && (line = reader.readLine()) != null) {
                 splitter.setString(line);
                 Long.parseLong(splitter.next());
 
                 // The proc file reports time in 1/100 sec, so convert to milliseconds.
                 long time = Long.parseLong(splitter.next()) * 10;
-                mDeltaSpeedTimes[speedIndex] = time - mLastSpeedTimes[speedIndex];
+                if (time < mLastSpeedTimes[speedIndex]) {
+                    // The stats reset when the cpu hotplugged. That means that the time
+                    // we read is offset from 0, so the time is the delta.
+                    mDeltaSpeedTimes[speedIndex] = time;
+                } else {
+                    mDeltaSpeedTimes[speedIndex] = time - mLastSpeedTimes[speedIndex];
+                }
                 mLastSpeedTimes[speedIndex] = time;
                 speedIndex++;
             }
         } catch (IOException e) {
-            Slog.e(TAG, "Failed to read cpu-freq", e);
+            Slog.e(TAG, "Failed to read cpu-freq: " + e.getMessage());
             Arrays.fill(mDeltaSpeedTimes, 0);
         }
         return mDeltaSpeedTimes;
diff --git a/core/java/com/android/internal/os/KernelUidCpuTimeReader.java b/core/java/com/android/internal/os/KernelUidCpuTimeReader.java
index 0df78ed..5d3043c 100644
--- a/core/java/com/android/internal/os/KernelUidCpuTimeReader.java
+++ b/core/java/com/android/internal/os/KernelUidCpuTimeReader.java
@@ -137,7 +137,7 @@
                 mLastPowerMaUs.put(uid, powerMaUs);
             }
         } catch (IOException e) {
-            Slog.e(TAG, "Failed to read uid_cputime", e);
+            Slog.e(TAG, "Failed to read uid_cputime: " + e.getMessage());
         }
         mLastTimeReadUs = nowUs;
     }
diff --git a/core/java/com/android/internal/os/PowerProfile.java b/core/java/com/android/internal/os/PowerProfile.java
index 4ede8dda..aaa9f73 100644
--- a/core/java/com/android/internal/os/PowerProfile.java
+++ b/core/java/com/android/internal/os/PowerProfile.java
@@ -59,6 +59,7 @@
     /**
      * Power consumption when CPU is in power collapse mode.
      */
+    @Deprecated
     public static final String POWER_CPU_ACTIVE = "cpu.active";
 
     /**
@@ -163,6 +164,7 @@
      */
     public static final String POWER_CAMERA = "camera.avg";
 
+    @Deprecated
     public static final String POWER_CPU_SPEEDS = "cpu.speeds";
 
     /**
@@ -191,6 +193,7 @@
         if (sPowerMap.size() == 0) {
             readPowerValuesFromXml(context);
         }
+        initCpuClusters();
     }
 
     private void readPowerValuesFromXml(Context context) {
@@ -249,7 +252,7 @@
         }
 
         // Now collect other config variables.
-        int[] configResIds = new int[] {
+        int[] configResIds = new int[]{
                 com.android.internal.R.integer.config_bluetooth_idle_cur_ma,
                 com.android.internal.R.integer.config_bluetooth_rx_cur_ma,
                 com.android.internal.R.integer.config_bluetooth_tx_cur_ma,
@@ -260,7 +263,7 @@
                 com.android.internal.R.integer.config_wifi_operating_voltage_mv,
         };
 
-        String[] configResIdKeys = new String[] {
+        String[] configResIdKeys = new String[]{
                 POWER_BLUETOOTH_CONTROLLER_IDLE,
                 POWER_BLUETOOTH_CONTROLLER_RX,
                 POWER_BLUETOOTH_CONTROLLER_TX,
@@ -279,6 +282,69 @@
         }
     }
 
+    private CpuClusterKey[] mCpuClusters;
+
+    private static final String POWER_CPU_CLUSTER_CORE_COUNT = "cpu.clusters.cores";
+    private static final String POWER_CPU_CLUSTER_SPEED_PREFIX = "cpu.speeds.cluster";
+    private static final String POWER_CPU_CLUSTER_ACTIVE_PREFIX = "cpu.active.cluster";
+
+    @SuppressWarnings("deprecated")
+    private void initCpuClusters() {
+        // Figure out how many CPU clusters we're dealing with
+        final Object obj = sPowerMap.get(POWER_CPU_CLUSTER_CORE_COUNT);
+        if (obj == null || !(obj instanceof Double[])) {
+            // Default to single.
+            mCpuClusters = new CpuClusterKey[1];
+            mCpuClusters[0] = new CpuClusterKey(POWER_CPU_SPEEDS, POWER_CPU_ACTIVE, 1);
+
+        } else {
+            final Double[] array = (Double[]) obj;
+            mCpuClusters = new CpuClusterKey[array.length];
+            for (int cluster = 0; cluster < array.length; cluster++) {
+                int numCpusInCluster = (int) Math.round(array[cluster]);
+                mCpuClusters[cluster] = new CpuClusterKey(
+                        POWER_CPU_CLUSTER_SPEED_PREFIX + cluster,
+                        POWER_CPU_CLUSTER_ACTIVE_PREFIX + cluster,
+                        numCpusInCluster);
+            }
+        }
+    }
+
+    public static class CpuClusterKey {
+        private final String timeKey;
+        private final String powerKey;
+        private final int numCpus;
+
+        private CpuClusterKey(String timeKey, String powerKey, int numCpus) {
+            this.timeKey = timeKey;
+            this.powerKey = powerKey;
+            this.numCpus = numCpus;
+        }
+    }
+
+    public int getNumCpuClusters() {
+        return mCpuClusters.length;
+    }
+
+    public int getNumCoresInCpuCluster(int index) {
+        return mCpuClusters[index].numCpus;
+    }
+
+    public int getNumSpeedStepsInCpuCluster(int index) {
+        Object value = sPowerMap.get(mCpuClusters[index].timeKey);
+        if (value != null && value instanceof Double[]) {
+            return ((Double[])value).length;
+        }
+        return 1; // Only one speed
+    }
+
+    public double getAveragePowerForCpu(int cluster, int step) {
+        if (cluster >= 0 && cluster < mCpuClusters.length) {
+            return getAveragePower(mCpuClusters[cluster].powerKey, step);
+        }
+        return 0;
+    }
+
     /**
      * Returns the average current in mA consumed by the subsystem, or the given
      * default value if the subsystem has no recorded value.
@@ -344,16 +410,4 @@
     public double getBatteryCapacity() {
         return getAveragePower(POWER_BATTERY_CAPACITY);
     }
-
-    /**
-     * Returns the number of speeds that the CPU can be run at.
-     * @return
-     */
-    public int getNumSpeedSteps() {
-        Object value = sPowerMap.get(POWER_CPU_SPEEDS);
-        if (value != null && value instanceof Double[]) {
-            return ((Double[])value).length;
-        }
-        return 1; // Only one speed
-    }
 }
diff --git a/core/java/com/android/internal/os/WrapperInit.java b/core/java/com/android/internal/os/WrapperInit.java
index 34ae58a..c558cf8 100644
--- a/core/java/com/android/internal/os/WrapperInit.java
+++ b/core/java/com/android/internal/os/WrapperInit.java
@@ -121,22 +121,4 @@
         Zygote.appendQuotedShellArgs(command, args);
         Zygote.execShell(command.toString());
     }
-
-    /**
-     * Executes a standalone application with a wrapper command.
-     * This method never returns.
-     *
-     * @param invokeWith The wrapper command.
-     * @param classPath The class path.
-     * @param className The class name to invoke.
-     * @param args Arguments for the main() method of the specified class.
-     */
-    public static void execStandalone(String invokeWith, String classPath, String className,
-            String[] args) {
-        StringBuilder command = new StringBuilder(invokeWith);
-        command.append(" /system/bin/dalvikvm -classpath '").append(classPath);
-        command.append("' ").append(className);
-        Zygote.appendQuotedShellArgs(command, args);
-        Zygote.execShell(command.toString());
-    }
 }
diff --git a/core/java/com/android/internal/os/ZygoteInit.java b/core/java/com/android/internal/os/ZygoteInit.java
index af73097..aaa89df 100644
--- a/core/java/com/android/internal/os/ZygoteInit.java
+++ b/core/java/com/android/internal/os/ZygoteInit.java
@@ -486,8 +486,8 @@
                 final int dexoptNeeded = DexFile.getDexOptNeeded(
                         classPathElement, "*", instructionSet, false /* defer */);
                 if (dexoptNeeded != DexFile.NO_DEXOPT_NEEDED) {
-                    installer.dexopt(classPathElement, Process.SYSTEM_UID, false,
-                            instructionSet, dexoptNeeded);
+                    installer.dexopt(classPathElement, Process.SYSTEM_UID, instructionSet,
+                            dexoptNeeded, 0 /*dexFlags*/);
                 }
             }
         } catch (IOException ioe) {
diff --git a/core/java/com/android/internal/policy/PhoneWindow.java b/core/java/com/android/internal/policy/PhoneWindow.java
index 3353d16..2e4d9b5 100644
--- a/core/java/com/android/internal/policy/PhoneWindow.java
+++ b/core/java/com/android/internal/policy/PhoneWindow.java
@@ -71,6 +71,7 @@
 import com.android.internal.view.menu.ListMenuPresenter;
 import com.android.internal.view.menu.MenuBuilder;
 import com.android.internal.view.menu.MenuDialogHelper;
+import com.android.internal.view.menu.MenuPopupHelper;
 import com.android.internal.view.menu.MenuPresenter;
 import com.android.internal.view.menu.MenuView;
 import com.android.internal.widget.ActionBarContextView;
@@ -274,6 +275,7 @@
 
     private ContextMenuBuilder mContextMenu;
     private MenuDialogHelper mContextMenuHelper;
+    private MenuPopupHelper mContextMenuPopupHelper;
     private boolean mClosingActionMenu;
 
     private int mVolumeControlStreamType = AudioManager.USE_DEFAULT_STREAM_TYPE;
@@ -1123,6 +1125,10 @@
             mContextMenuHelper.dismiss();
             mContextMenuHelper = null;
         }
+        if (mContextMenuPopupHelper != null) {
+            mContextMenuPopupHelper.dismiss();
+            mContextMenuPopupHelper = null;
+        }
     }
 
     @Override
@@ -2846,6 +2852,29 @@
         }
 
         @Override
+        public boolean showContextMenuForChild(View originalView, float x, float y) {
+            // Reuse the context menu builder
+            if (mContextMenu == null) {
+                mContextMenu = new ContextMenuBuilder(getContext());
+                mContextMenu.setCallback(mContextMenuCallback);
+            } else {
+                mContextMenu.clearAll();
+            }
+
+            final MenuPopupHelper helper = mContextMenu.showPopup(
+                    getContext(), originalView, x, y);
+            if (helper != null) {
+                helper.setCallback(mContextMenuCallback);
+            } else if (mContextMenuPopupHelper != null) {
+                // No menu to show, but if we have a menu currently showing it just became blank.
+                // Close it.
+                mContextMenuPopupHelper.dismiss();
+            }
+            mContextMenuPopupHelper = helper;
+            return helper != null;
+        }
+
+        @Override
         public ActionMode startActionModeForChild(View originalView,
                 ActionMode.Callback callback) {
             return startActionModeForChild(originalView, callback, ActionMode.TYPE_PRIMARY);
diff --git a/core/java/com/android/internal/statusbar/IStatusBar.aidl b/core/java/com/android/internal/statusbar/IStatusBar.aidl
index ab3ec98..11ef18b 100644
--- a/core/java/com/android/internal/statusbar/IStatusBar.aidl
+++ b/core/java/com/android/internal/statusbar/IStatusBar.aidl
@@ -74,7 +74,9 @@
 
     /**
      * Notifies the status bar that a camera launch gesture has been detected.
+     *
+     * @param source the identifier for the gesture, see {@link StatusBarManager}
      */
-    void onCameraLaunchGestureDetected();
+    void onCameraLaunchGestureDetected(int source);
 }
 
diff --git a/core/java/com/android/internal/view/FloatingActionMode.java b/core/java/com/android/internal/view/FloatingActionMode.java
index 44df0ce..b44baa2 100644
--- a/core/java/com/android/internal/view/FloatingActionMode.java
+++ b/core/java/com/android/internal/view/FloatingActionMode.java
@@ -35,7 +35,7 @@
 public class FloatingActionMode extends ActionMode {
 
     private static final int MAX_HIDE_DURATION = 3000;
-    private static final int MOVING_HIDE_DELAY = 300;
+    private static final int MOVING_HIDE_DELAY = 50;
 
     private final Context mContext;
     private final ActionMode.Callback2 mCallback;
@@ -181,7 +181,6 @@
                 // Content rect is moving.
                 mOriginatingView.removeCallbacks(mMovingOff);
                 mFloatingToolbarVisibilityHelper.setMoving(true);
-                mFloatingToolbarVisibilityHelper.updateToolbarVisibility();
                 mOriginatingView.postDelayed(mMovingOff, MOVING_HIDE_DELAY);
 
                 mFloatingToolbar.setContentRect(mContentRectOnScreen);
@@ -189,9 +188,9 @@
             }
         } else {
             mFloatingToolbarVisibilityHelper.setOutOfBounds(true);
-            mFloatingToolbarVisibilityHelper.updateToolbarVisibility();
             mContentRectOnScreen.setEmpty();
         }
+        mFloatingToolbarVisibilityHelper.updateToolbarVisibility();
 
         mPreviousContentRectOnScreen.set(mContentRectOnScreen);
     }
diff --git a/core/java/com/android/internal/view/IDropPermissionHolder.aidl b/core/java/com/android/internal/view/IDropPermissionHolder.aidl
new file mode 100644
index 0000000..e60ab0e
--- /dev/null
+++ b/core/java/com/android/internal/view/IDropPermissionHolder.aidl
@@ -0,0 +1,26 @@
+/*
+** Copyright 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.internal.view;
+
+/**
+ * Interface to allow a drop receiver to request permissions for URIs passed along with ClipData
+ * contained in DragEvent.
+ */
+interface IDropPermissionHolder {
+    void grant();
+    void revoke();
+}
diff --git a/core/java/com/android/internal/view/menu/CascadingMenuPopup.java b/core/java/com/android/internal/view/menu/CascadingMenuPopup.java
index 4e3e40f2..293e2ad 100644
--- a/core/java/com/android/internal/view/menu/CascadingMenuPopup.java
+++ b/core/java/com/android/internal/view/menu/CascadingMenuPopup.java
@@ -10,19 +10,26 @@
 import android.content.res.Configuration;
 import android.content.res.Resources;
 import android.graphics.Rect;
+import android.os.Handler;
 import android.os.Parcelable;
 import android.view.Gravity;
 import android.view.KeyEvent;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewTreeObserver;
+import android.view.View.OnAttachStateChangeListener;
 import android.view.View.OnKeyListener;
-import android.widget.AdapterView;
+import android.view.ViewTreeObserver.OnGlobalLayoutListener;
 import android.widget.DropDownListView;
+import android.widget.FrameLayout;
+import android.widget.MenuItemHoverListener;
+import android.widget.ListAdapter;
 import android.widget.ListView;
 import android.widget.MenuPopupWindow;
+import android.widget.MenuPopupWindow.MenuDropDownListView;
 import android.widget.PopupWindow;
 import android.widget.PopupWindow.OnDismissListener;
+import android.widget.TextView;
 
 import com.android.internal.util.Preconditions;
 
@@ -31,9 +38,8 @@
  * side.
  * @hide
  */
-final class CascadingMenuPopup extends MenuPopup implements AdapterView.OnItemClickListener,
-        MenuPresenter, OnKeyListener, PopupWindow.OnDismissListener,
-        ViewTreeObserver.OnGlobalLayoutListener, View.OnAttachStateChangeListener{
+final class CascadingMenuPopup extends MenuPopup implements MenuPresenter, OnKeyListener,
+        PopupWindow.OnDismissListener {
     @Retention(RetentionPolicy.SOURCE)
     @IntDef({HORIZ_POSITION_LEFT, HORIZ_POSITION_RIGHT})
     public @interface HorizPosition {}
@@ -41,21 +47,149 @@
     private static final int HORIZ_POSITION_LEFT = 0;
     private static final int HORIZ_POSITION_RIGHT = 1;
 
+    private static final int SUBMENU_TIMEOUT_MS = 200;
+
     private final Context mContext;
     private final int mMenuMaxWidth;
     private final int mPopupStyleAttr;
     private final int mPopupStyleRes;
     private final boolean mOverflowOnly;
     private final int mLayoutDirection;
+    private final Handler mSubMenuHoverHandler;
+
+    private final OnGlobalLayoutListener mGlobalLayoutListener = new OnGlobalLayoutListener() {
+        @Override
+        public void onGlobalLayout() {
+            if (isShowing()) {
+                final View anchor = mShownAnchorView;
+                if (anchor == null || !anchor.isShown()) {
+                    dismiss();
+                } else if (isShowing()) {
+                    // Recompute window sizes and positions.
+                    for (MenuPopupWindow popup : mPopupWindows) {
+                        popup.show();
+                    }
+                }
+            }
+        }
+    };
+
+    private final OnAttachStateChangeListener mAttachStateChangeListener =
+            new OnAttachStateChangeListener() {
+                @Override
+                public void onViewAttachedToWindow(View v) {
+                }
+
+                @Override
+                public void onViewDetachedFromWindow(View v) {
+                    if (mTreeObserver != null) {
+                        if (!mTreeObserver.isAlive()) {
+                            mTreeObserver = v.getViewTreeObserver();
+                        }
+                        mTreeObserver.removeGlobalOnLayoutListener(mGlobalLayoutListener);
+                    }
+                    v.removeOnAttachStateChangeListener(this);
+                }
+            };
+
+    private final MenuItemHoverListener mMenuItemHoverListener = new MenuItemHoverListener() {
+        @Override
+        public void onItemHovered(MenuBuilder menu, int position) {
+            int menuIndex = -1;
+            for (int i = 0; i < mListViews.size(); i++) {
+                final MenuDropDownListView view = (MenuDropDownListView) mListViews.get(i);
+                final MenuAdapter adapter = toMenuAdapter(view.getAdapter());
+
+                if (adapter.getAdapterMenu() == menu) {
+                    menuIndex = i;
+                    break;
+                }
+            }
+
+            if (menuIndex == -1) {
+                return;
+            }
+
+            final MenuDropDownListView view = (MenuDropDownListView) mListViews.get(menuIndex);
+            final ListMenuItemView selectedItemView = (ListMenuItemView) view.getSelectedView();
+
+            if (selectedItemView != null && selectedItemView.isEnabled()
+                    && selectedItemView.getItemData().hasSubMenu()) {
+                // If the currently selected item corresponds to a submenu, schedule to open the
+                // submenu on a timeout.
+
+                mSubMenuHoverHandler.removeCallbacksAndMessages(null);
+                mSubMenuHoverHandler.postDelayed(new Runnable() {
+                    @Override
+                    public void run() {
+                        // Make sure the submenu item is still the one selected.
+                        if (view.getSelectedView() == selectedItemView
+                                && selectedItemView.isEnabled()
+                                && selectedItemView.getItemData().hasSubMenu()) {
+                            // Close any other submenus that might be open at the current or
+                            // a deeper level.
+                            int nextIndex = mListViews.indexOf(view) + 1;
+                            if (nextIndex < mListViews.size()) {
+                                MenuAdapter nextSubMenuAdapter =
+                                        toMenuAdapter(mListViews.get(nextIndex).getAdapter());
+                                // Disable exit animation, to prevent overlapping fading out
+                                // submenus.
+                                mPopupWindows.get(nextIndex).setExitTransition(null);
+                                nextSubMenuAdapter.getAdapterMenu().close();
+                            }
+
+                            // Then open the selected submenu.
+                            view.performItemClick(
+                                    selectedItemView,
+                                    view.getSelectedItemPosition(),
+                                    view.getSelectedItemId());
+                        }
+                    }
+                }, SUBMENU_TIMEOUT_MS);
+            } else if (menuIndex + 1 < mListViews.size()) {
+                // If the currently selected item does NOT corresponds to a submenu, check if there
+                // is a submenu already open that is one level deeper. If so, schedule to close it
+                // on a timeout.
+
+                final MenuDropDownListView nextView =
+                        (MenuDropDownListView) mListViews.get(menuIndex + 1);
+                final MenuAdapter nextAdapter = toMenuAdapter(nextView.getAdapter());
+
+                mSubMenuHoverHandler.removeCallbacksAndMessages(null);
+                mSubMenuHoverHandler.postDelayed(new Runnable() {
+                    @Override
+                    public void run() {
+                        // Make sure the menu wasn't already closed by something else and that
+                        // it wasn't re-hovered by the user since this was scheduled.
+                        int nextMenuIndex = mListViews.indexOf(nextView);
+
+                        if (nextMenuIndex != -1 && nextView.getSelectedView() == null) {
+                            // Disable exit animation, to prevent overlapping fading out submenus.
+                            for (int i = nextMenuIndex; i < mPopupWindows.size(); i++) {
+                                final MenuPopupWindow popupWindow = mPopupWindows.get(i);
+                                popupWindow.setExitTransition(null);
+                                popupWindow.setAnimationStyle(0);
+                            }
+                            nextAdapter.getAdapterMenu().close();
+                        }
+                    }
+                }, SUBMENU_TIMEOUT_MS);
+            }
+        }
+    };
 
     private int mDropDownGravity = Gravity.NO_GRAVITY;
     private View mAnchorView;
     private View mShownAnchorView;
     private List<DropDownListView> mListViews;
     private List<MenuPopupWindow> mPopupWindows;
+    private int mLastPosition;
+    private List<Integer> mPositions;
     private List<int[]> mOffsets;
-    private int mPreferredPosition;
+    private int mInitXOffset;
+    private int mInitYOffset;
     private boolean mForceShowIcon;
+    private boolean mShowTitle;
     private Callback mPresenterCallback;
     private ViewTreeObserver mTreeObserver;
     private PopupWindow.OnDismissListener mOnDismissListener;
@@ -78,14 +212,15 @@
         final Resources res = context.getResources();
         final Configuration config = res.getConfiguration();
         mLayoutDirection = config.getLayoutDirection();
-        mPreferredPosition = mLayoutDirection == View.LAYOUT_DIRECTION_RTL ? HORIZ_POSITION_LEFT :
-                HORIZ_POSITION_RIGHT;
+        mLastPosition = getInitialMenuPosition();
         mMenuMaxWidth = Math.max(res.getDisplayMetrics().widthPixels / 2,
                 res.getDimensionPixelSize(com.android.internal.R.dimen.config_prefDialogWidth));
 
         mPopupWindows = new ArrayList<MenuPopupWindow>();
         mListViews = new ArrayList<DropDownListView>();
         mOffsets = new ArrayList<int[]>();
+        mPositions = new ArrayList<Integer>();
+        mSubMenuHoverHandler = new Handler();
     }
 
     @Override
@@ -96,12 +231,12 @@
     private MenuPopupWindow createPopupWindow() {
         MenuPopupWindow popupWindow = new MenuPopupWindow(
                 mContext, null, mPopupStyleAttr, mPopupStyleRes);
+        popupWindow.setHoverListener(mMenuItemHoverListener);
         popupWindow.setOnItemClickListener(this);
         popupWindow.setOnDismissListener(this);
         popupWindow.setAnchorView(mAnchorView);
         popupWindow.setDropDownGravity(mDropDownGravity);
         popupWindow.setModal(true);
-        popupWindow.setTouchModal(false);
         return popupWindow;
     }
 
@@ -118,15 +253,35 @@
         for (int i = 0; i < mPopupWindows.size(); i++) {
             MenuPopupWindow popupWindow = mPopupWindows.get(i);
             popupWindow.show();
-            mListViews.add((DropDownListView) popupWindow.getListView());
+            DropDownListView listView = (DropDownListView) popupWindow.getListView();
+            mListViews.add(listView);
+
+            MenuBuilder menu = toMenuAdapter(listView.getAdapter()).getAdapterMenu();
+            if (i == 0 && mShowTitle && menu.getHeaderTitle() != null) {
+                FrameLayout titleItemView =
+                        (FrameLayout) LayoutInflater.from(mContext).inflate(
+                                com.android.internal.R.layout.popup_menu_header_item_layout,
+                                listView,
+                                false);
+                TextView titleView = (TextView) titleItemView.findViewById(
+                        com.android.internal.R.id.title);
+                titleView.setText(menu.getHeaderTitle());
+                titleItemView.setEnabled(false);
+                listView.addHeaderView(titleItemView, null, false);
+
+                // Update to show the title.
+                popupWindow.show();
+            }
         }
 
         mShownAnchorView = mAnchorView;
         if (mShownAnchorView != null) {
             final boolean addGlobalListener = mTreeObserver == null;
             mTreeObserver = mShownAnchorView.getViewTreeObserver(); // Refresh to latest
-            if (addGlobalListener) mTreeObserver.addOnGlobalLayoutListener(this);
-            mShownAnchorView.addOnAttachStateChangeListener(this);
+            if (addGlobalListener) {
+                mTreeObserver.addOnGlobalLayoutListener(mGlobalLayoutListener);
+            }
+            mShownAnchorView.addOnAttachStateChangeListener(mAttachStateChangeListener);
         }
     }
 
@@ -143,12 +298,6 @@
     }
 
     @Override
-    public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
-        MenuAdapter adapter = (MenuAdapter) parent.getAdapter();
-        adapter.mAdapterMenu.performItemAction(adapter.getItem(position), 0);
-    }
-
-    @Override
     public boolean onKey(View v, int keyCode, KeyEvent event) {
         if (event.getAction() == KeyEvent.ACTION_UP && keyCode == KeyEvent.KEYCODE_MENU) {
             dismiss();
@@ -158,6 +307,16 @@
     }
 
     /**
+     * Determines the proper initial menu position for the current LTR/RTL configuration.
+     * @return The initial position.
+     */
+    @HorizPosition
+    private int getInitialMenuPosition() {
+        return mLayoutDirection == View.LAYOUT_DIRECTION_RTL ? HORIZ_POSITION_LEFT :
+                HORIZ_POSITION_RIGHT;
+    }
+
+    /**
      * Determines whether the next submenu (of the given width) should display on the right or on
      * the left of the most recent menu.
      *
@@ -174,7 +333,7 @@
         final Rect displayFrame = new Rect();
         mShownAnchorView.getWindowVisibleDisplayFrame(displayFrame);
 
-        if (mPreferredPosition == HORIZ_POSITION_RIGHT) {
+        if (mLastPosition == HORIZ_POSITION_RIGHT) {
             final int right = screenLocation[0] + lastListView.getWidth() + nextMenuWidth;
             if (right > displayFrame.right) {
                 return HORIZ_POSITION_LEFT;
@@ -208,12 +367,13 @@
         int y = 0;
 
         if (addSubMenu) {
+            popupWindow.setTouchModal(false);
             popupWindow.setEnterTransition(null);
 
             ListView lastListView = mListViews.get(mListViews.size() - 1);
             @HorizPosition int nextMenuPosition = getNextMenuPosition(menuWidth);
             boolean showOnRight = nextMenuPosition == HORIZ_POSITION_RIGHT;
-            mPreferredPosition = nextMenuPosition;
+            mLastPosition = nextMenuPosition;
 
             int[] lastLocation = new int[2];
             lastListView.getLocationOnScreen(lastLocation);
@@ -238,6 +398,9 @@
 
             y = lastOffset[1] + lastListView.getSelectedView().getTop() -
                     lastListView.getChildAt(0).getTop();
+        } else {
+            x = mInitXOffset;
+            y = mInitYOffset;
         }
 
         popupWindow.setWidth(menuWidth);
@@ -250,11 +413,13 @@
         // we deliberately do not yet show the popupWindow, as #show() will do that later.
         if (isShowing()) {
             popupWindow.show();
-            mListViews.add((DropDownListView) popupWindow.getListView());
+            DropDownListView listView = (DropDownListView) popupWindow.getListView();
+            mListViews.add(listView);
         }
 
         int[] offsets = {x, y};
         mOffsets.add(offsets);
+        mPositions.add(mLastPosition);
     }
 
     /**
@@ -281,8 +446,9 @@
         if (dismissedIndex != -1) {
             for (int i = dismissedIndex; i < mListViews.size(); i++) {
                 ListView view = mListViews.get(i);
-                MenuAdapter adapter = (MenuAdapter) view.getAdapter();
-                adapter.mAdapterMenu.close();
+                ListAdapter adapter = view.getAdapter();
+                MenuAdapter menuAdapter = toMenuAdapter(adapter);
+                menuAdapter.mAdapterMenu.close();
             }
         }
     }
@@ -290,7 +456,7 @@
     @Override
     public void updateMenuView(boolean cleared) {
         for (ListView view : mListViews) {
-            ((MenuAdapter) view.getAdapter()).notifyDataSetChanged();
+            toMenuAdapter(view.getAdapter()).notifyDataSetChanged();
         }
     }
 
@@ -303,7 +469,7 @@
     public boolean onSubMenuSelected(SubMenuBuilder subMenu) {
         // Don't allow double-opening of the same submenu.
         for (ListView view : mListViews) {
-            if (((MenuAdapter) view.getAdapter()).mAdapterMenu.equals(subMenu)) {
+            if (toMenuAdapter(view.getAdapter()).mAdapterMenu.equals(subMenu)) {
                 // Just re-focus that one.
                 view.requestFocus();
                 return true;
@@ -327,11 +493,11 @@
 
         for (int i = 0; i < mListViews.size(); i++) {
             ListView view = mListViews.get(i);
-            MenuAdapter adapter = (MenuAdapter) view.getAdapter();
+            MenuAdapter adapter = toMenuAdapter(view.getAdapter());
 
             if (menuIndex == -1 && menu == adapter.mAdapterMenu) {
                 menuIndex = i;
-                wasSelected = view.getSelectedItem() != null;
+                wasSelected = view.getSelectedView() != null;
             }
 
             // Once the menu has been found, remove it and all submenus beneath it from the
@@ -350,9 +516,11 @@
             mListViews.subList(menuIndex, mListViews.size()).clear();
             mOffsets.subList(menuIndex, mOffsets.size()).clear();
 
-            // If there's still a menu open, refocus the new leaf [sub]menu.
-            if (mListViews.size() > 0) {
-                mListViews.get(mListViews.size() - 1).requestFocus();
+            mPositions.subList(menuIndex, mPositions.size()).clear();
+            if (mPositions.size() > 0) {
+                mLastPosition = mPositions.get(mPositions.size() - 1);
+            } else {
+                mLastPosition = getInitialMenuPosition();
             }
         }
 
@@ -365,12 +533,12 @@
 
         if (mPopupWindows.size() == 0) {
             if (mTreeObserver != null) {
-                if (!mTreeObserver.isAlive()) mTreeObserver =
-                        mShownAnchorView.getViewTreeObserver();
-                mTreeObserver.removeGlobalOnLayoutListener(this);
+                if (mTreeObserver.isAlive()) {
+                    mTreeObserver.removeGlobalOnLayoutListener(mGlobalLayoutListener);
+                }
                 mTreeObserver = null;
             }
-            mShownAnchorView.removeOnAttachStateChangeListener(this);
+            mShownAnchorView.removeOnAttachStateChangeListener(mAttachStateChangeListener);
             // If every [sub]menu was dismissed, that means the whole thing was dismissed, so notify
             // the owner.
             mOnDismissListener.onDismiss();
@@ -412,30 +580,17 @@
     }
 
     @Override
-    public void onGlobalLayout() {
-        if (isShowing()) {
-            final View anchor = mShownAnchorView;
-            if (anchor == null || !anchor.isShown()) {
-                dismiss();
-            } else if (isShowing()) {
-                // Recompute window sizes and positions.
-                for (MenuPopupWindow popup : mPopupWindows) {
-                    popup.show();
-                }
-            }
-        }
+    public void setHorizontalOffset(int x) {
+        mInitXOffset = x;
     }
 
     @Override
-    public void onViewAttachedToWindow(View v) {
+    public void setVerticalOffset(int y) {
+        mInitYOffset = y;
     }
 
     @Override
-    public void onViewDetachedFromWindow(View v) {
-        if (mTreeObserver != null) {
-            if (!mTreeObserver.isAlive()) mTreeObserver = v.getViewTreeObserver();
-            mTreeObserver.removeGlobalOnLayoutListener(this);
-        }
-        v.removeOnAttachStateChangeListener(this);
+    public void setShowTitle(boolean showTitle) {
+        mShowTitle = showTitle;
     }
 }
\ No newline at end of file
diff --git a/core/java/com/android/internal/view/menu/ContextMenuBuilder.java b/core/java/com/android/internal/view/menu/ContextMenuBuilder.java
index bf44d51..aaa1bf1 100644
--- a/core/java/com/android/internal/view/menu/ContextMenuBuilder.java
+++ b/core/java/com/android/internal/view/menu/ContextMenuBuilder.java
@@ -17,6 +17,7 @@
 package com.android.internal.view.menu;
 
 import android.content.Context;
+import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
 import android.os.IBinder;
 import android.util.EventLog;
@@ -93,4 +94,29 @@
         return null;
     }
     
+    public MenuPopupHelper showPopup(Context context, View originalView, float x, float y) {
+        if (originalView != null) {
+            // Let relevant views and their populate context listeners populate
+            // the context menu
+            originalView.createContextMenu(this);
+        }
+
+        if (getVisibleItems().size() > 0) {
+            EventLog.writeEvent(50001, 1);
+
+            int location[] = new int[2];
+            originalView.getLocationOnScreen(location);
+
+            final MenuPopupHelper helper = new MenuPopupHelper(
+                    context,
+                    this,
+                    originalView,
+                    false /* overflowOnly */,
+                    com.android.internal.R.attr.contextPopupMenuStyle);
+            helper.show(Math.round(x), Math.round(y));
+            return helper;
+        }
+
+        return null;
+    }
 }
diff --git a/core/java/com/android/internal/view/menu/MenuAdapter.java b/core/java/com/android/internal/view/menu/MenuAdapter.java
index 1e03b1f..673cfd1 100644
--- a/core/java/com/android/internal/view/menu/MenuAdapter.java
+++ b/core/java/com/android/internal/view/menu/MenuAdapter.java
@@ -40,6 +40,10 @@
         findExpandedIndex();
     }
 
+    public boolean getForceShowIcon() {
+        return mForceShowIcon;
+    }
+
     public void setForceShowIcon(boolean forceShow) {
         mForceShowIcon = forceShow;
     }
diff --git a/core/java/com/android/internal/view/menu/MenuItemImpl.java b/core/java/com/android/internal/view/menu/MenuItemImpl.java
index 08d4e86..624c9e2 100644
--- a/core/java/com/android/internal/view/menu/MenuItemImpl.java
+++ b/core/java/com/android/internal/view/menu/MenuItemImpl.java
@@ -149,7 +149,7 @@
             return true;
         }
 
-        if (mMenu.dispatchMenuItemSelected(mMenu.getRootMenu(), this)) {
+        if (mMenu.dispatchMenuItemSelected(mMenu, this)) {
             return true;
         }
 
diff --git a/core/java/com/android/internal/view/menu/MenuPopup.java b/core/java/com/android/internal/view/menu/MenuPopup.java
index b43e8ad..98f5d90 100644
--- a/core/java/com/android/internal/view/menu/MenuPopup.java
+++ b/core/java/com/android/internal/view/menu/MenuPopup.java
@@ -17,10 +17,13 @@
 package com.android.internal.view.menu;
 
 import android.content.Context;
+import android.view.MenuItem;
 import android.view.View;
 import android.view.View.MeasureSpec;
 import android.view.ViewGroup;
+import android.widget.AdapterView;
 import android.widget.FrameLayout;
+import android.widget.HeaderViewListAdapter;
 import android.widget.ListAdapter;
 import android.widget.PopupWindow;
 
@@ -30,7 +33,8 @@
  *
  * @hide
  */
-public abstract class MenuPopup implements ShowableListMenu, MenuPresenter {
+public abstract class MenuPopup implements ShowableListMenu, MenuPresenter,
+        AdapterView.OnItemClickListener {
 
     public abstract void setForceShowIcon(boolean forceShow);
 
@@ -49,6 +53,18 @@
 
     public abstract void setAnchorView(View anchor);
 
+    public abstract void setHorizontalOffset(int x);
+
+    public abstract void setVerticalOffset(int y);
+
+    /**
+     * Set whether a title entry should be shown in the popup menu (if a title exists for the
+     * menu).
+     *
+     * @param showTitle
+     */
+    public abstract void setShowTitle(boolean showTitle);
+
     /**
      * Set a listener to receive a callback when the popup is dismissed.
      *
@@ -81,6 +97,16 @@
         return 0;
     }
 
+    @Override
+    public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
+        ListAdapter outerAdapter = (ListAdapter) parent.getAdapter();
+        MenuAdapter wrappedAdapter = toMenuAdapter(outerAdapter);
+
+        // Use the position from the outer adapter so that if a header view was added, we don't get
+        // an off-by-1 error in position.
+        wrappedAdapter.mAdapterMenu.performItemAction((MenuItem) outerAdapter.getItem(position), 0);
+    }
+
     /**
      * Measures the width of the given menu view.
      *
@@ -121,4 +147,19 @@
 
         return maxWidth;
     }
-}
\ No newline at end of file
+
+    /**
+     * Converts the given ListAdapter originating from a menu, to a MenuAdapter, accounting for
+     * the possibility of the parameter adapter actually wrapping the MenuAdapter. (That could
+     * happen if a header view was added on the menu.)
+     *
+     * @param adapter
+     * @return
+     */
+    protected static MenuAdapter toMenuAdapter(ListAdapter adapter) {
+        if (adapter instanceof HeaderViewListAdapter) {
+            return (MenuAdapter) ((HeaderViewListAdapter) adapter).getWrappedAdapter();
+        }
+        return (MenuAdapter) adapter;
+    }
+}
diff --git a/core/java/com/android/internal/view/menu/MenuPopupHelper.java b/core/java/com/android/internal/view/menu/MenuPopupHelper.java
index ea79983..e674ecc 100644
--- a/core/java/com/android/internal/view/menu/MenuPopupHelper.java
+++ b/core/java/com/android/internal/view/menu/MenuPopupHelper.java
@@ -39,7 +39,11 @@
     private MenuPopup mPopup;
 
     private int mDropDownGravity = Gravity.NO_GRAVITY;
+    private boolean mForceShowIcon;
+    private boolean mShowTitle;
     private Callback mPresenterCallback;
+    private int mInitXOffset;
+    private int mInitYOffset;
 
     public MenuPopupHelper(Context context, MenuBuilder menu) {
         this(context, menu, null, false, com.android.internal.R.attr.popupMenuStyle, 0);
@@ -81,6 +85,7 @@
     }
 
     public void setForceShowIcon(boolean forceShow) {
+        mForceShowIcon = forceShow;
         mPopup.setForceShowIcon(forceShow);
     }
 
@@ -99,6 +104,12 @@
         }
     }
 
+    public void show(int x, int y) {
+        if (!tryShow(x, y)) {
+            throw new IllegalStateException("MenuPopupHelper cannot be used without an anchor");
+        }
+    }
+
     public ShowableListMenu getPopup() {
         return mPopup;
     }
@@ -118,10 +129,40 @@
             return false;
         }
 
+        mInitXOffset = 0;
+        mInitYOffset = 0;
+        mShowTitle = false;
+
+        showPopup();
+        return true;
+    }
+
+    public boolean tryShow(int x, int y) {
+        if (isShowing()) {
+            return true;
+        }
+
+        if (mAnchorView == null) {
+            return false;
+        }
+
+        mInitXOffset = x;
+        mInitYOffset = y;
+        mShowTitle = true;
+
+        showPopup();
+        return true;
+    }
+
+    private void showPopup() {
         mPopup = createMenuPopup();
         mPopup.setAnchorView(mAnchorView);
-        mPopup.setGravity(mDropDownGravity);
         mPopup.setCallback(mPresenterCallback);
+        mPopup.setForceShowIcon(mForceShowIcon);
+        mPopup.setGravity(mDropDownGravity);
+        mPopup.setHorizontalOffset(mInitXOffset);
+        mPopup.setShowTitle(mShowTitle);
+        mPopup.setVerticalOffset(mInitYOffset);
 
         // In order for subclasses of MenuPopupHelper to satisfy the OnDismissedListener interface,
         // we must set the listener to this outer Helper rather than to the inner MenuPopup.
@@ -131,7 +172,6 @@
 
         mPopup.addMenu(mMenu);
         mPopup.show();
-        return true;
     }
 
     public void dismiss() {
@@ -149,7 +189,6 @@
         return mPopup != null && mPopup.isShowing();
     }
 
-
     public void setCallback(MenuPresenter.Callback cb) {
         mPresenterCallback = cb;
         mPopup.setCallback(cb);
diff --git a/core/java/com/android/internal/view/menu/StandardMenuPopup.java b/core/java/com/android/internal/view/menu/StandardMenuPopup.java
index 530b16a..caee0d2 100644
--- a/core/java/com/android/internal/view/menu/StandardMenuPopup.java
+++ b/core/java/com/android/internal/view/menu/StandardMenuPopup.java
@@ -6,15 +6,17 @@
 import android.view.Gravity;
 import android.view.KeyEvent;
 import android.view.LayoutInflater;
-import android.view.MenuItem;
 import android.view.View;
 import android.view.ViewGroup;
+import android.view.View.OnAttachStateChangeListener;
 import android.view.View.OnKeyListener;
+import android.view.ViewTreeObserver.OnGlobalLayoutListener;
 import android.view.ViewTreeObserver;
-import android.widget.AdapterView;
+import android.widget.FrameLayout;
 import android.widget.ListView;
 import android.widget.MenuPopupWindow;
 import android.widget.PopupWindow;
+import android.widget.TextView;
 import android.widget.AdapterView.OnItemClickListener;
 import android.widget.PopupWindow.OnDismissListener;
 
@@ -25,8 +27,7 @@
  * viewport.
  */
 final class StandardMenuPopup extends MenuPopup implements OnDismissListener, OnItemClickListener,
-        MenuPresenter, OnKeyListener, ViewTreeObserver.OnGlobalLayoutListener,
-        View.OnAttachStateChangeListener {
+        MenuPresenter, OnKeyListener {
 
     private final Context mContext;
     private final LayoutInflater mInflater;
@@ -40,6 +41,37 @@
     // StandardMenuPopup.
     private final MenuPopupWindow mPopup;
 
+    private final OnGlobalLayoutListener mGlobalLayoutListener = new OnGlobalLayoutListener() {
+        @Override
+        public void onGlobalLayout() {
+            if (isShowing()) {
+                final View anchor = mShownAnchorView;
+                if (anchor == null || !anchor.isShown()) {
+                    dismiss();
+                } else if (isShowing()) {
+                    // Recompute window size and position
+                    mPopup.show();
+                }
+            }
+        }
+    };
+
+    private final OnAttachStateChangeListener mAttachStateChangeListener =
+            new OnAttachStateChangeListener() {
+        @Override
+        public void onViewAttachedToWindow(View v) {
+        }
+
+        @Override
+        public void onViewDetachedFromWindow(View v) {
+            if (mTreeObserver != null) {
+                if (!mTreeObserver.isAlive()) mTreeObserver = v.getViewTreeObserver();
+                mTreeObserver.removeGlobalOnLayoutListener(mGlobalLayoutListener);
+            }
+            v.removeOnAttachStateChangeListener(this);
+        }
+    };
+
     private PopupWindow.OnDismissListener mOnDismissListener;
 
     private View mAnchorView;
@@ -50,7 +82,7 @@
     private ViewGroup mMeasureParent;
 
     /** Whether the popup has been dismissed. Once dismissed, it cannot be opened again. */
-    private boolean wasDismissed;
+    private boolean mWasDismissed;
 
     /** Whether the cached content width value is valid. */
     private boolean mHasContentWidth;
@@ -60,6 +92,10 @@
 
     private int mDropDownGravity = Gravity.NO_GRAVITY;
 
+    private int mXOffset;
+    private int mYOffset;
+    private boolean mShowTitle;
+
     public StandardMenuPopup(Context context, MenuBuilder menu, View anchorView, int popupStyleAttr,
             int popupStyleRes, boolean overflowOnly) {
         mContext = Preconditions.checkNotNull(context);
@@ -97,7 +133,7 @@
             return true;
         }
 
-        if (mAnchorView == null) {
+        if (mWasDismissed || mAnchorView == null) {
             return false;
         }
 
@@ -112,9 +148,9 @@
         final boolean addGlobalListener = mTreeObserver == null;
         mTreeObserver = anchor.getViewTreeObserver(); // Refresh to latest
         if (addGlobalListener) {
-            mTreeObserver.addOnGlobalLayoutListener(this);
+            mTreeObserver.addOnGlobalLayoutListener(mGlobalLayoutListener);
         }
-        anchor.addOnAttachStateChangeListener(this);
+        anchor.addOnAttachStateChangeListener(mAttachStateChangeListener);
         mPopup.setAnchorView(anchor);
         mPopup.setDropDownGravity(mDropDownGravity);
 
@@ -126,8 +162,28 @@
 
         mPopup.setContentWidth(mContentWidth);
         mPopup.setInputMethodMode(PopupWindow.INPUT_METHOD_NOT_NEEDED);
+        mPopup.setHorizontalOffset(mXOffset);
+        mPopup.setVerticalOffset(mYOffset);
         mPopup.show();
-        mPopup.getListView().setOnKeyListener(this);
+
+        ListView listView = mPopup.getListView();
+        listView.setOnKeyListener(this);
+
+        if (mShowTitle && mMenu.getHeaderTitle() != null) {
+            FrameLayout titleItemView =
+                    (FrameLayout) LayoutInflater.from(mContext).inflate(
+                            com.android.internal.R.layout.popup_menu_header_item_layout,
+                            listView,
+                            false);
+            TextView titleView = (TextView) titleItemView.findViewById(
+                    com.android.internal.R.id.title);
+            titleView.setText(mMenu.getHeaderTitle());
+            titleItemView.setEnabled(false);
+            listView.addHeaderView(titleItemView, null, false);
+
+            // Update to show the title.
+            mPopup.show();
+        }
         return true;
     }
 
@@ -146,32 +202,26 @@
     }
 
     @Override
-    public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
-        MenuAdapter adapter = mAdapter;
-        adapter.mAdapterMenu.performItemAction(adapter.getItem(position), 0);
-    }
-
-    @Override
     public void addMenu(MenuBuilder menu) {
         // No-op: standard implementation has only one menu which is set in the constructor.
     }
 
     @Override
     public boolean isShowing() {
-        return !wasDismissed && mPopup.isShowing();
+        return !mWasDismissed && mPopup.isShowing();
     }
 
     @Override
     public void onDismiss() {
-        wasDismissed = true;
+        mWasDismissed = true;
         mMenu.close();
 
         if (mTreeObserver != null) {
             if (!mTreeObserver.isAlive()) mTreeObserver = mShownAnchorView.getViewTreeObserver();
-            mTreeObserver.removeGlobalOnLayoutListener(this);
+            mTreeObserver.removeGlobalOnLayoutListener(mGlobalLayoutListener);
             mTreeObserver = null;
         }
-        mShownAnchorView.removeOnAttachStateChangeListener(this);
+        mShownAnchorView.removeOnAttachStateChangeListener(mAttachStateChangeListener);
         mOnDismissListener.onDismiss();
     }
 
@@ -196,17 +246,7 @@
                     mContext, subMenu, mShownAnchorView, mOverflowOnly, mPopupStyleAttr,
                     mPopupStyleRes);
             subPopup.setCallback(mPresenterCallback);
-
-            boolean preserveIconSpacing = false;
-            final int count = subMenu.size();
-            for (int i = 0; i < count; i++) {
-                MenuItem childItem = subMenu.getItem(i);
-                if (childItem.isVisible() && childItem.getIcon() != null) {
-                    preserveIconSpacing = true;
-                    break;
-                }
-            }
-            subPopup.setForceShowIcon(preserveIconSpacing);
+            subPopup.setForceShowIcon(mAdapter.getForceShowIcon());
 
             if (subPopup.tryShow()) {
                 if (mPresenterCallback != null) {
@@ -234,7 +274,6 @@
         return false;
     }
 
-
     @Override
     public Parcelable onSaveInstanceState() {
         return null;
@@ -268,29 +307,19 @@
         return mPopup.getListView();
     }
 
+
     @Override
-    public void onGlobalLayout() {
-        if (isShowing()) {
-            final View anchor = mShownAnchorView;
-            if (anchor == null || !anchor.isShown()) {
-                dismiss();
-            } else if (isShowing()) {
-                // Recompute window size and position
-                mPopup.show();
-            }
-        }
+    public void setHorizontalOffset(int x) {
+        mXOffset = x;
     }
 
     @Override
-    public void onViewAttachedToWindow(View v) {
+    public void setVerticalOffset(int y) {
+        mYOffset = y;
     }
 
     @Override
-    public void onViewDetachedFromWindow(View v) {
-        if (mTreeObserver != null) {
-            if (!mTreeObserver.isAlive()) mTreeObserver = v.getViewTreeObserver();
-            mTreeObserver.removeGlobalOnLayoutListener(this);
-        }
-        v.removeOnAttachStateChangeListener(this);
+    public void setShowTitle(boolean showTitle) {
+        mShowTitle = showTitle;
     }
-}
\ No newline at end of file
+}
diff --git a/core/java/com/android/internal/view/menu/SubMenuBuilder.java b/core/java/com/android/internal/view/menu/SubMenuBuilder.java
index 92acf8c..cf741bf 100644
--- a/core/java/com/android/internal/view/menu/SubMenuBuilder.java
+++ b/core/java/com/android/internal/view/menu/SubMenuBuilder.java
@@ -73,7 +73,7 @@
 
     @Override
     public MenuBuilder getRootMenu() {
-        return mParentMenu;
+        return mParentMenu.getRootMenu();
     }
 
     @Override
diff --git a/core/java/com/android/internal/widget/FloatingToolbar.java b/core/java/com/android/internal/widget/FloatingToolbar.java
index ca6fe61..2a25db6 100644
--- a/core/java/com/android/internal/widget/FloatingToolbar.java
+++ b/core/java/com/android/internal/widget/FloatingToolbar.java
@@ -1488,10 +1488,9 @@
     private static AnimatorSet createEnterAnimation(View view) {
         AnimatorSet animation =  new AnimatorSet();
         animation.playTogether(
-                ObjectAnimator.ofFloat(view, View.ALPHA, 0, 1).setDuration(200),
+                ObjectAnimator.ofFloat(view, View.ALPHA, 0, 1).setDuration(150),
                 // Make sure that view.x is always fixed throughout the duration of this animation.
                 ObjectAnimator.ofFloat(view, View.X, view.getX(), view.getX()));
-        animation.setStartDelay(50);
         return animation;
     }
 
@@ -1506,7 +1505,7 @@
             View view, int startDelay, Animator.AnimatorListener listener) {
         AnimatorSet animation =  new AnimatorSet();
         animation.playTogether(
-                ObjectAnimator.ofFloat(view, View.ALPHA, 1, 0).setDuration(200));
+                ObjectAnimator.ofFloat(view, View.ALPHA, 1, 0).setDuration(100));
         animation.setStartDelay(startDelay);
         animation.addListener(listener);
         return animation;
diff --git a/core/java/com/android/internal/widget/LockPatternUtils.java b/core/java/com/android/internal/widget/LockPatternUtils.java
index 6a98ec7..f7e9add 100644
--- a/core/java/com/android/internal/widget/LockPatternUtils.java
+++ b/core/java/com/android/internal/widget/LockPatternUtils.java
@@ -1075,12 +1075,22 @@
      *   enter a pattern.
      */
     public long getLockoutAttemptDeadline(int userId) {
-        final long deadline = getLong(LOCKOUT_ATTEMPT_DEADLINE, 0L, userId);
+        long deadline = getLong(LOCKOUT_ATTEMPT_DEADLINE, 0L, userId);
         final long timeoutMs = getLong(LOCKOUT_ATTEMPT_TIMEOUT_MS, 0L, userId);
         final long now = SystemClock.elapsedRealtime();
-        if (deadline < now || deadline > (now + timeoutMs)) {
+        if (deadline < now) {
+            // timeout expired
+            setLong(LOCKOUT_ATTEMPT_DEADLINE, 0, userId);
+            setLong(LOCKOUT_ATTEMPT_TIMEOUT_MS, 0, userId);
             return 0L;
         }
+
+        if (deadline > (now + timeoutMs)) {
+            // device was rebooted, set new deadline
+            deadline = now + timeoutMs;
+            setLong(LOCKOUT_ATTEMPT_DEADLINE, deadline, userId);
+        }
+
         return deadline;
     }
 
diff --git a/core/java/com/android/server/LocalServices.java b/core/java/com/android/server/LocalServices.java
index 25dcb30..9c632ea 100644
--- a/core/java/com/android/server/LocalServices.java
+++ b/core/java/com/android/server/LocalServices.java
@@ -16,6 +16,8 @@
 
 package com.android.server;
 
+import com.android.internal.annotations.VisibleForTesting;
+
 import android.util.ArrayMap;
 
 /**
@@ -57,4 +59,14 @@
             sLocalServiceObjects.put(type, service);
         }
     }
+
+    /**
+     * Remove a service instance, must be only used in tests.
+     */
+    @VisibleForTesting
+    public static <T> void removeServiceForTest(Class<T> type) {
+        synchronized (sLocalServiceObjects) {
+            sLocalServiceObjects.remove(type);
+        }
+    }
 }
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index e4bc800..ffc69a9 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -217,7 +217,7 @@
 /*
  * JNI registration.
  */
-static JNINativeMethod gMethods[] = {
+static const JNINativeMethod gMethods[] = {
     { "nativeFinishInit", "()V",
         (void*) com_android_internal_os_RuntimeInit_nativeFinishInit },
     { "nativeZygoteInit", "()V",
diff --git a/core/jni/android/graphics/Bitmap.cpp b/core/jni/android/graphics/Bitmap.cpp
index fbe3ece..e6c7c2b 100755
--- a/core/jni/android/graphics/Bitmap.cpp
+++ b/core/jni/android/graphics/Bitmap.cpp
@@ -1348,7 +1348,7 @@
 
 ///////////////////////////////////////////////////////////////////////////////
 
-static JNINativeMethod gBitmapMethods[] = {
+static const JNINativeMethod gBitmapMethods[] = {
     {   "nativeCreate",             "([IIIIIIZ)Landroid/graphics/Bitmap;",
         (void*)Bitmap_creator },
     {   "nativeCopy",               "(JIZ)Landroid/graphics/Bitmap;",
diff --git a/core/jni/android/graphics/BitmapFactory.cpp b/core/jni/android/graphics/BitmapFactory.cpp
index 20a54e5..28bc7fe 100644
--- a/core/jni/android/graphics/BitmapFactory.cpp
+++ b/core/jni/android/graphics/BitmapFactory.cpp
@@ -542,7 +542,7 @@
 
 ///////////////////////////////////////////////////////////////////////////////
 
-static JNINativeMethod gMethods[] = {
+static const JNINativeMethod gMethods[] = {
     {   "nativeDecodeStream",
         "(Ljava/io/InputStream;[BLandroid/graphics/Rect;Landroid/graphics/BitmapFactory$Options;)Landroid/graphics/Bitmap;",
         (void*)nativeDecodeStream
@@ -569,7 +569,7 @@
     },
 };
 
-static JNINativeMethod gOptionsMethods[] = {
+static const JNINativeMethod gOptionsMethods[] = {
     {   "requestCancel", "()V", (void*)nativeRequestCancel }
 };
 
diff --git a/core/jni/android/graphics/BitmapRegionDecoder.cpp b/core/jni/android/graphics/BitmapRegionDecoder.cpp
index 8535e6a..2df3a46 100644
--- a/core/jni/android/graphics/BitmapRegionDecoder.cpp
+++ b/core/jni/android/graphics/BitmapRegionDecoder.cpp
@@ -47,7 +47,7 @@
         fHeight = height;
     }
     ~SkBitmapRegionDecoder() {
-        SkDELETE(fDecoder);
+        delete fDecoder;
     }
 
     bool decodeRegion(SkBitmap* bitmap, const SkIRect& rect,
@@ -72,7 +72,7 @@
     SkImageDecoder* decoder = SkImageDecoder::Factory(stream);
     int width, height;
     if (NULL == decoder) {
-        SkDELETE(stream);
+        delete stream;
         doThrowIOE(env, "Image format not supported");
         return nullObjectReturn("SkImageDecoder::Factory returned null");
     }
@@ -87,7 +87,7 @@
         snprintf(msg, sizeof(msg), "Image failed to decode using %s decoder",
                 decoder->getFormatName());
         doThrowIOE(env, msg);
-        SkDELETE(decoder);
+        delete decoder;
         return nullObjectReturn("decoder->buildTileIndex returned false");
     }
 
@@ -261,7 +261,7 @@
 
 ///////////////////////////////////////////////////////////////////////////////
 
-static JNINativeMethod gBitmapRegionDecoderMethods[] = {
+static const JNINativeMethod gBitmapRegionDecoderMethods[] = {
     {   "nativeDecodeRegion",
         "(JIIIILandroid/graphics/BitmapFactory$Options;)Landroid/graphics/Bitmap;",
         (void*)nativeDecodeRegion},
diff --git a/core/jni/android/graphics/Camera.cpp b/core/jni/android/graphics/Camera.cpp
index 036ece1..6fcf689 100644
--- a/core/jni/android/graphics/Camera.cpp
+++ b/core/jni/android/graphics/Camera.cpp
@@ -115,7 +115,7 @@
 /*
  * JNI registration.
  */
-static JNINativeMethod gCameraMethods[] = {
+static const JNINativeMethod gCameraMethods[] = {
     /* name, signature, funcPtr */
 
     { "nativeConstructor",   "()V",    (void*)Camera_constructor   },
diff --git a/core/jni/android/graphics/CanvasProperty.cpp b/core/jni/android/graphics/CanvasProperty.cpp
index deb4971..728bc1c 100644
--- a/core/jni/android/graphics/CanvasProperty.cpp
+++ b/core/jni/android/graphics/CanvasProperty.cpp
@@ -39,7 +39,7 @@
 // JNI Glue
 // ----------------------------------------------------------------------------
 
-static JNINativeMethod gMethods[] = {
+static const JNINativeMethod gMethods[] = {
     { "nCreateFloat", "(F)J", (void*) createFloat },
     { "nCreatePaint", "(J)J", (void*) createPaint },
 };
diff --git a/core/jni/android/graphics/ColorFilter.cpp b/core/jni/android/graphics/ColorFilter.cpp
index d03bcf0..83fd073 100644
--- a/core/jni/android/graphics/ColorFilter.cpp
+++ b/core/jni/android/graphics/ColorFilter.cpp
@@ -57,19 +57,19 @@
     }
 };
 
-static JNINativeMethod colorfilter_methods[] = {
+static const JNINativeMethod colorfilter_methods[] = {
     {"destroyFilter", "(J)V", (void*) SkColorFilterGlue::finalizer}
 };
 
-static JNINativeMethod porterduff_methods[] = {
+static const JNINativeMethod porterduff_methods[] = {
     { "native_CreatePorterDuffFilter", "(II)J", (void*) SkColorFilterGlue::CreatePorterDuffFilter   },
 };
 
-static JNINativeMethod lighting_methods[] = {
+static const JNINativeMethod lighting_methods[] = {
     { "native_CreateLightingFilter", "(II)J", (void*) SkColorFilterGlue::CreateLightingFilter   },
 };
 
-static JNINativeMethod colormatrix_methods[] = {
+static const JNINativeMethod colormatrix_methods[] = {
     { "nativeColorMatrixFilter", "([F)J", (void*) SkColorFilterGlue::CreateColorMatrixFilter   },
 };
 
diff --git a/core/jni/android/graphics/DrawFilter.cpp b/core/jni/android/graphics/DrawFilter.cpp
index 90ef6c0..c1dc0dd 100644
--- a/core/jni/android/graphics/DrawFilter.cpp
+++ b/core/jni/android/graphics/DrawFilter.cpp
@@ -97,11 +97,11 @@
     }
 };
 
-static JNINativeMethod drawfilter_methods[] = {
+static const JNINativeMethod drawfilter_methods[] = {
     {"nativeDestructor", "(J)V", (void*) SkDrawFilterGlue::finalizer}
 };
 
-static JNINativeMethod paintflags_methods[] = {
+static const JNINativeMethod paintflags_methods[] = {
     {"nativeConstructor","(II)J", (void*) SkDrawFilterGlue::CreatePaintFlagsDF}
 };
 
diff --git a/core/jni/android/graphics/FontFamily.cpp b/core/jni/android/graphics/FontFamily.cpp
index 38db76b..dac6d96 100644
--- a/core/jni/android/graphics/FontFamily.cpp
+++ b/core/jni/android/graphics/FontFamily.cpp
@@ -124,7 +124,7 @@
 
 ///////////////////////////////////////////////////////////////////////////////
 
-static JNINativeMethod gFontFamilyMethods[] = {
+static const JNINativeMethod gFontFamilyMethods[] = {
     { "nCreateFamily",         "(Ljava/lang/String;I)J", (void*)FontFamily_create },
     { "nUnrefFamily",          "(J)V", (void*)FontFamily_unref },
     { "nAddFont",              "(JLjava/lang/String;)Z", (void*)FontFamily_addFont },
diff --git a/core/jni/android/graphics/Interpolator.cpp b/core/jni/android/graphics/Interpolator.cpp
index 3593d1a..fa28359 100644
--- a/core/jni/android/graphics/Interpolator.cpp
+++ b/core/jni/android/graphics/Interpolator.cpp
@@ -71,7 +71,7 @@
 /*
  * JNI registration.
  */
-static JNINativeMethod gInterpolatorMethods[] = {
+static const JNINativeMethod gInterpolatorMethods[] = {
     { "nativeConstructor",      "(II)J",        (void*)Interpolator_constructor     },
     { "nativeDestructor",       "(J)V",         (void*)Interpolator_destructor      },
     { "nativeReset",            "(JII)V",       (void*)Interpolator_reset           },
diff --git a/core/jni/android/graphics/MaskFilter.cpp b/core/jni/android/graphics/MaskFilter.cpp
index d658643..2b4a1ab 100644
--- a/core/jni/android/graphics/MaskFilter.cpp
+++ b/core/jni/android/graphics/MaskFilter.cpp
@@ -61,19 +61,19 @@
     }
 };
 
-static JNINativeMethod gMaskFilterMethods[] = {
+static const JNINativeMethod gMaskFilterMethods[] = {
     { "nativeDestructor",   "(J)V",     (void*)SkMaskFilterGlue::destructor      }
 };
 
-static JNINativeMethod gBlurMaskFilterMethods[] = {
+static const JNINativeMethod gBlurMaskFilterMethods[] = {
     { "nativeConstructor",  "(FI)J",    (void*)SkMaskFilterGlue::createBlur      }
 };
 
-static JNINativeMethod gEmbossMaskFilterMethods[] = {
+static const JNINativeMethod gEmbossMaskFilterMethods[] = {
     { "nativeConstructor",  "([FFFF)J", (void*)SkMaskFilterGlue::createEmboss    }
 };
 
-static JNINativeMethod gTableMaskFilterMethods[] = {
+static const JNINativeMethod gTableMaskFilterMethods[] = {
     { "nativeNewTable", "([B)J", (void*)SkMaskFilterGlue::createTable    },
     { "nativeNewClip",  "(II)J", (void*)SkMaskFilterGlue::createClipTable    },
     { "nativeNewGamma", "(F)J", (void*)SkMaskFilterGlue::createGammaTable    }
diff --git a/core/jni/android/graphics/Matrix.cpp b/core/jni/android/graphics/Matrix.cpp
index 101e2ba..b0f3bb4 100644
--- a/core/jni/android/graphics/Matrix.cpp
+++ b/core/jni/android/graphics/Matrix.cpp
@@ -302,7 +302,7 @@
     }
  };
 
-static JNINativeMethod methods[] = {
+static const JNINativeMethod methods[] = {
     {"finalizer", "(J)V", (void*) SkMatrixGlue::finalizer},
     {"native_create","(J)J", (void*) SkMatrixGlue::create},
 
diff --git a/core/jni/android/graphics/Movie.cpp b/core/jni/android/graphics/Movie.cpp
index d67ed10..498c590 100644
--- a/core/jni/android/graphics/Movie.cpp
+++ b/core/jni/android/graphics/Movie.cpp
@@ -135,7 +135,7 @@
 
 //////////////////////////////////////////////////////////////////////////////////////////////
 
-static JNINativeMethod gMethods[] = {
+static const JNINativeMethod gMethods[] = {
     {   "width",    "()I",  (void*)movie_width  },
     {   "height",   "()I",  (void*)movie_height  },
     {   "isOpaque", "()Z",  (void*)movie_isOpaque  },
diff --git a/core/jni/android/graphics/NinePatch.cpp b/core/jni/android/graphics/NinePatch.cpp
index 1c28262..3ccbb35 100644
--- a/core/jni/android/graphics/NinePatch.cpp
+++ b/core/jni/android/graphics/NinePatch.cpp
@@ -108,7 +108,7 @@
 
 /////////////////////////////////////////////////////////////////////////////////////////
 
-static JNINativeMethod gNinePatchMethods[] = {
+static const JNINativeMethod gNinePatchMethods[] = {
     { "isNinePatchChunk", "([B)Z", (void*) SkNinePatchGlue::isNinePatchChunk },
     { "validateNinePatchChunk", "([B)J",
             (void*) SkNinePatchGlue::validateNinePatchChunk },
diff --git a/core/jni/android/graphics/Paint.cpp b/core/jni/android/graphics/Paint.cpp
index 988d13a..520dc4f 100644
--- a/core/jni/android/graphics/Paint.cpp
+++ b/core/jni/android/graphics/Paint.cpp
@@ -1095,7 +1095,7 @@
 
 };
 
-static JNINativeMethod methods[] = {
+static const JNINativeMethod methods[] = {
     {"finalizer", "(J)V", (void*) PaintGlue::finalizer},
     {"native_init","()J", (void*) PaintGlue::init},
     {"native_initWithPaint","(J)J", (void*) PaintGlue::initWithPaint},
diff --git a/core/jni/android/graphics/Path.cpp b/core/jni/android/graphics/Path.cpp
index dbd7c89..2998c99 100644
--- a/core/jni/android/graphics/Path.cpp
+++ b/core/jni/android/graphics/Path.cpp
@@ -475,7 +475,7 @@
     }
 };
 
-static JNINativeMethod methods[] = {
+static const JNINativeMethod methods[] = {
     {"finalizer", "(J)V", (void*) SkPathGlue::finalizer},
     {"init1","()J", (void*) SkPathGlue::init1},
     {"init2","(J)J", (void*) SkPathGlue::init2},
diff --git a/core/jni/android/graphics/PathEffect.cpp b/core/jni/android/graphics/PathEffect.cpp
index 265944e..b289b21 100644
--- a/core/jni/android/graphics/PathEffect.cpp
+++ b/core/jni/android/graphics/PathEffect.cpp
@@ -69,31 +69,31 @@
 
 ////////////////////////////////////////////////////////////////////////////////////////////////////////
 
-static JNINativeMethod gPathEffectMethods[] = {
+static const JNINativeMethod gPathEffectMethods[] = {
     { "nativeDestructor", "(J)V", (void*)SkPathEffectGlue::destructor }
 };
 
-static JNINativeMethod gComposePathEffectMethods[] = {
+static const JNINativeMethod gComposePathEffectMethods[] = {
     { "nativeCreate", "(JJ)J", (void*)SkPathEffectGlue::Compose_constructor }
 };
 
-static JNINativeMethod gSumPathEffectMethods[] = {
+static const JNINativeMethod gSumPathEffectMethods[] = {
     { "nativeCreate", "(JJ)J", (void*)SkPathEffectGlue::Sum_constructor }
 };
 
-static JNINativeMethod gDashPathEffectMethods[] = {
+static const JNINativeMethod gDashPathEffectMethods[] = {
     { "nativeCreate", "([FF)J", (void*)SkPathEffectGlue::Dash_constructor }
 };
 
-static JNINativeMethod gPathDashPathEffectMethods[] = {
+static const JNINativeMethod gPathDashPathEffectMethods[] = {
     { "nativeCreate", "(JFFI)J", (void*)SkPathEffectGlue::OneD_constructor }
 };
 
-static JNINativeMethod gCornerPathEffectMethods[] = {
+static const JNINativeMethod gCornerPathEffectMethods[] = {
     { "nativeCreate", "(F)J", (void*)SkPathEffectGlue::Corner_constructor }
 };
 
-static JNINativeMethod gDiscretePathEffectMethods[] = {
+static const JNINativeMethod gDiscretePathEffectMethods[] = {
     { "nativeCreate", "(FF)J", (void*)SkPathEffectGlue::Discrete_constructor }
 };
 
diff --git a/core/jni/android/graphics/PathMeasure.cpp b/core/jni/android/graphics/PathMeasure.cpp
index fec5d9d..70e528d 100644
--- a/core/jni/android/graphics/PathMeasure.cpp
+++ b/core/jni/android/graphics/PathMeasure.cpp
@@ -143,7 +143,7 @@
     } 
 };
 
-static JNINativeMethod methods[] = {
+static const JNINativeMethod methods[] = {
     {"native_create",       "(JZ)J",        (void*) SkPathMeasureGlue::create      },
     {"native_setPath",      "(JJZ)V",       (void*) SkPathMeasureGlue::setPath     },
     {"native_getLength",    "(J)F",         (void*) SkPathMeasureGlue::getLength   },
diff --git a/core/jni/android/graphics/PorterDuff.cpp b/core/jni/android/graphics/PorterDuff.cpp
index fed90a5..dfc3c9d 100644
--- a/core/jni/android/graphics/PorterDuff.cpp
+++ b/core/jni/android/graphics/PorterDuff.cpp
@@ -58,7 +58,7 @@
 
 };
 
-static JNINativeMethod methods[] = {
+static const JNINativeMethod methods[] = {
     {"nativeCreateXfermode","(I)J", (void*) SkPorterDuffGlue::CreateXfermode},
 };
 
diff --git a/core/jni/android/graphics/Rasterizer.cpp b/core/jni/android/graphics/Rasterizer.cpp
index cfc23ac8..a106ecf 100644
--- a/core/jni/android/graphics/Rasterizer.cpp
+++ b/core/jni/android/graphics/Rasterizer.cpp
@@ -61,7 +61,7 @@
     }
 };
 
-static JNINativeMethod gRasterizerMethods[] = {
+static const JNINativeMethod gRasterizerMethods[] = {
     {"finalizer", "(J)V", (void*) SkRasterizerGlue::finalizer}
 };
 
@@ -85,7 +85,7 @@
     }
 };
 
-static JNINativeMethod gLayerRasterizerMethods[] = {
+static const JNINativeMethod gLayerRasterizerMethods[] = {
     { "nativeConstructor",  "()J",      (void*)SkLayerRasterizerGlue::create    },
     { "nativeAddLayer",     "(JJFF)V",  (void*)SkLayerRasterizerGlue::addLayer  }
 };
diff --git a/core/jni/android/graphics/Region.cpp b/core/jni/android/graphics/Region.cpp
index e99bdfc..bcd0b60 100644
--- a/core/jni/android/graphics/Region.cpp
+++ b/core/jni/android/graphics/Region.cpp
@@ -306,13 +306,13 @@
 
 ////////////////////////////////////////////////////////////////////////////////////////////////////////////
 
-static JNINativeMethod gRegionIterMethods[] = {
+static const JNINativeMethod gRegionIterMethods[] = {
     { "nativeConstructor",  "(J)J",                         (void*)RegionIter_constructor   },
     { "nativeDestructor",   "(J)V",                         (void*)RegionIter_destructor    },
     { "nativeNext",         "(JLandroid/graphics/Rect;)Z",  (void*)RegionIter_next          }
 };
 
-static JNINativeMethod gRegionMethods[] = {
+static const JNINativeMethod gRegionMethods[] = {
     // these are static methods
     { "nativeConstructor",      "()J",                              (void*)Region_constructor       },
     { "nativeDestructor",       "(J)V",                             (void*)Region_destructor        },
diff --git a/core/jni/android/graphics/Shader.cpp b/core/jni/android/graphics/Shader.cpp
index 49c377e..799ed83 100644
--- a/core/jni/android/graphics/Shader.cpp
+++ b/core/jni/android/graphics/Shader.cpp
@@ -240,36 +240,36 @@
 
 ///////////////////////////////////////////////////////////////////////////////////////////////
 
-static JNINativeMethod gColorMethods[] = {
+static const JNINativeMethod gColorMethods[] = {
     { "nativeRGBToHSV",     "(III[F)V", (void*)Color_RGBToHSV   },
     { "nativeHSVToColor",   "(I[F)I",   (void*)Color_HSVToColor }
 };
 
-static JNINativeMethod gShaderMethods[] = {
+static const JNINativeMethod gShaderMethods[] = {
     { "nativeDestructor",        "(J)V",    (void*)Shader_destructor        },
     { "nativeSetLocalMatrix",    "(JJ)J",   (void*)Shader_setLocalMatrix    }
 };
 
-static JNINativeMethod gBitmapShaderMethods[] = {
+static const JNINativeMethod gBitmapShaderMethods[] = {
     { "nativeCreate",     "(Landroid/graphics/Bitmap;II)J",  (void*)BitmapShader_constructor },
 };
 
-static JNINativeMethod gLinearGradientMethods[] = {
+static const JNINativeMethod gLinearGradientMethods[] = {
     { "nativeCreate1",     "(FFFF[I[FI)J",  (void*)LinearGradient_create1     },
     { "nativeCreate2",     "(FFFFIII)J",    (void*)LinearGradient_create2     },
 };
 
-static JNINativeMethod gRadialGradientMethods[] = {
+static const JNINativeMethod gRadialGradientMethods[] = {
     { "nativeCreate1",     "(FFF[I[FI)J",  (void*)RadialGradient_create1     },
     { "nativeCreate2",     "(FFFIII)J",    (void*)RadialGradient_create2     },
 };
 
-static JNINativeMethod gSweepGradientMethods[] = {
+static const JNINativeMethod gSweepGradientMethods[] = {
     { "nativeCreate1",     "(FF[I[F)J",  (void*)SweepGradient_create1     },
     { "nativeCreate2",     "(FFII)J",    (void*)SweepGradient_create2     },
 };
 
-static JNINativeMethod gComposeShaderMethods[] = {
+static const JNINativeMethod gComposeShaderMethods[] = {
     { "nativeCreate1",      "(JJJ)J",   (void*)ComposeShader_create1     },
     { "nativeCreate2",      "(JJI)J",   (void*)ComposeShader_create2     },
 };
diff --git a/core/jni/android/graphics/SurfaceTexture.cpp b/core/jni/android/graphics/SurfaceTexture.cpp
index b9e48a0..61dc6e4 100644
--- a/core/jni/android/graphics/SurfaceTexture.cpp
+++ b/core/jni/android/graphics/SurfaceTexture.cpp
@@ -241,17 +241,16 @@
     BufferQueue::createBufferQueue(&producer, &consumer);
 
     if (singleBufferMode) {
-        consumer->disableAsyncBuffer();
-        consumer->setDefaultMaxBufferCount(1);
+        consumer->setMaxBufferCount(1);
     }
 
     sp<GLConsumer> surfaceTexture;
     if (isDetached) {
         surfaceTexture = new GLConsumer(consumer, GL_TEXTURE_EXTERNAL_OES,
-                true, true);
+                true, !singleBufferMode);
     } else {
         surfaceTexture = new GLConsumer(consumer, texName,
-                GL_TEXTURE_EXTERNAL_OES, true, true);
+                GL_TEXTURE_EXTERNAL_OES, true, !singleBufferMode);
     }
 
     if (surfaceTexture == 0) {
@@ -360,7 +359,7 @@
 
 // ----------------------------------------------------------------------------
 
-static JNINativeMethod gSurfaceTextureMethods[] = {
+static const JNINativeMethod gSurfaceTextureMethods[] = {
     {"nativeClassInit",            "()V",   (void*)SurfaceTexture_classInit },
     {"nativeInit",                 "(ZIZLjava/lang/ref/WeakReference;)V", (void*)SurfaceTexture_init },
     {"nativeFinalize",             "()V",   (void*)SurfaceTexture_finalize },
diff --git a/core/jni/android/graphics/Typeface.cpp b/core/jni/android/graphics/Typeface.cpp
index e0cbc9e..e97b768 100644
--- a/core/jni/android/graphics/Typeface.cpp
+++ b/core/jni/android/graphics/Typeface.cpp
@@ -68,7 +68,7 @@
 
 ///////////////////////////////////////////////////////////////////////////////
 
-static JNINativeMethod gTypefaceMethods[] = {
+static const JNINativeMethod gTypefaceMethods[] = {
     { "nativeCreateFromTypeface", "(JI)J", (void*)Typeface_createFromTypeface },
     { "nativeCreateWeightAlias",  "(JI)J", (void*)Typeface_createWeightAlias },
     { "nativeUnref",              "(J)V",  (void*)Typeface_unref },
diff --git a/core/jni/android/graphics/Xfermode.cpp b/core/jni/android/graphics/Xfermode.cpp
index 4a424ae..7441acc 100644
--- a/core/jni/android/graphics/Xfermode.cpp
+++ b/core/jni/android/graphics/Xfermode.cpp
@@ -47,15 +47,15 @@
 
 ///////////////////////////////////////////////////////////////////////////////
 
-static JNINativeMethod gXfermodeMethods[] = {
+static const JNINativeMethod gXfermodeMethods[] = {
     {"finalizer", "(J)V", (void*) SkXfermodeGlue::finalizer}
 };
 
-static JNINativeMethod gAvoidMethods[] = {
+static const JNINativeMethod gAvoidMethods[] = {
     {"nativeCreate", "(III)J", (void*) SkXfermodeGlue::avoid_create}
 };
 
-static JNINativeMethod gPixelXorMethods[] = {
+static const JNINativeMethod gPixelXorMethods[] = {
     {"nativeCreate", "(I)J", (void*) SkXfermodeGlue::pixelxor_create}
 };
 
diff --git a/core/jni/android/graphics/YuvToJpegEncoder.cpp b/core/jni/android/graphics/YuvToJpegEncoder.cpp
index 5eede2a..7d0c39c 100644
--- a/core/jni/android/graphics/YuvToJpegEncoder.cpp
+++ b/core/jni/android/graphics/YuvToJpegEncoder.cpp
@@ -243,7 +243,7 @@
 }
 ///////////////////////////////////////////////////////////////////////////////
 
-static JNINativeMethod gYuvImageMethods[] = {
+static const JNINativeMethod gYuvImageMethods[] = {
     {   "nativeCompressToJpeg",  "([BIII[I[IILjava/io/OutputStream;[B)Z",
         (void*)YuvImage_compressToJpeg }
 };
diff --git a/core/jni/android/graphics/pdf/PdfDocument.cpp b/core/jni/android/graphics/pdf/PdfDocument.cpp
index a91b15b..7a13fe4 100644
--- a/core/jni/android/graphics/pdf/PdfDocument.cpp
+++ b/core/jni/android/graphics/pdf/PdfDocument.cpp
@@ -150,7 +150,7 @@
     document->close();
 }
 
-static JNINativeMethod gPdfDocument_Methods[] = {
+static const JNINativeMethod gPdfDocument_Methods[] = {
     {"nativeCreateDocument", "()J", (void*) nativeCreateDocument},
     {"nativeStartPage", "(JIIIIII)J", (void*) nativeStartPage},
     {"nativeFinishPage", "(J)V", (void*) nativeFinishPage},
diff --git a/core/jni/android/graphics/pdf/PdfEditor.cpp b/core/jni/android/graphics/pdf/PdfEditor.cpp
index 52b69e0..0177635 100644
--- a/core/jni/android/graphics/pdf/PdfEditor.cpp
+++ b/core/jni/android/graphics/pdf/PdfEditor.cpp
@@ -343,7 +343,7 @@
     nativeSetPageBox(env, thiz, documentPtr, pageIndex, PAGE_BOX_CROP, mediaBox);
 }
 
-static JNINativeMethod gPdfEditor_Methods[] = {
+static const JNINativeMethod gPdfEditor_Methods[] = {
     {"nativeOpen", "(IJ)J", (void*) nativeOpen},
     {"nativeClose", "(J)V", (void*) nativeClose},
     {"nativeGetPageCount", "(J)I", (void*) nativeGetPageCount},
diff --git a/core/jni/android/graphics/pdf/PdfRenderer.cpp b/core/jni/android/graphics/pdf/PdfRenderer.cpp
index 006eef8..6ddfacf 100644
--- a/core/jni/android/graphics/pdf/PdfRenderer.cpp
+++ b/core/jni/android/graphics/pdf/PdfRenderer.cpp
@@ -283,7 +283,7 @@
     skBitmap.notifyPixelsChanged();
 }
 
-static JNINativeMethod gPdfRenderer_Methods[] = {
+static const JNINativeMethod gPdfRenderer_Methods[] = {
     {"nativeCreate", "(IJ)J", (void*) nativeCreate},
     {"nativeClose", "(J)V", (void*) nativeClose},
     {"nativeGetPageCount", "(J)I", (void*) nativeGetPageCount},
diff --git a/core/jni/android/opengl/util.cpp b/core/jni/android/opengl/util.cpp
index 40029bb..e045f5f 100644
--- a/core/jni/android/opengl/util.cpp
+++ b/core/jni/android/opengl/util.cpp
@@ -1087,18 +1087,18 @@
  * JNI registration
  */
 
-static JNINativeMethod gMatrixMethods[] = {
+static const JNINativeMethod gMatrixMethods[] = {
     { "multiplyMM", "([FI[FI[FI)V", (void*)util_multiplyMM },
     { "multiplyMV", "([FI[FI[FI)V", (void*)util_multiplyMV },
 };
 
-static JNINativeMethod gVisibilityMethods[] = {
+static const JNINativeMethod gVisibilityMethods[] = {
     { "computeBoundingSphere", "([FII[FI)V", (void*)util_computeBoundingSphere },
     { "frustumCullSpheres", "([FI[FII[III)I", (void*)util_frustumCullSpheres },
     { "visibilityTest", "([FI[FI[CII)I", (void*)util_visibilityTest },
 };
 
-static JNINativeMethod gUtilsMethods[] = {
+static const JNINativeMethod gUtilsMethods[] = {
     { "native_getInternalFormat", "(Landroid/graphics/Bitmap;)I", (void*) util_getInternalFormat },
     { "native_getType", "(Landroid/graphics/Bitmap;)I", (void*) util_getType },
     { "native_texImage2D", "(IIILandroid/graphics/Bitmap;II)I", (void*)util_texImage2D },
@@ -1106,7 +1106,7 @@
     { "setTracingLevel", "(I)V",                        (void*)setTracingLevel },
 };
 
-static JNINativeMethod gEtc1Methods[] = {
+static const JNINativeMethod gEtc1Methods[] = {
     { "encodeBlock", "(Ljava/nio/Buffer;ILjava/nio/Buffer;)V", (void*) etc1_encodeBlock },
     { "decodeBlock", "(Ljava/nio/Buffer;Ljava/nio/Buffer;)V", (void*) etc1_decodeBlock },
     { "getEncodedDataSize", "(II)I", (void*) etc1_getEncodedDataSize },
@@ -1120,11 +1120,11 @@
 
 typedef struct _ClassRegistrationInfo {
     const char* classPath;
-    JNINativeMethod* methods;
+    const JNINativeMethod* methods;
     size_t methodCount;
 } ClassRegistrationInfo;
 
-static ClassRegistrationInfo gClasses[] = {
+static const ClassRegistrationInfo gClasses[] = {
     {"android/opengl/Matrix", gMatrixMethods, NELEM(gMatrixMethods)},
     {"android/opengl/Visibility", gVisibilityMethods, NELEM(gVisibilityMethods)},
     {"android/opengl/GLUtils", gUtilsMethods, NELEM(gUtilsMethods)},
@@ -1136,7 +1136,7 @@
     nativeClassInitBuffer(env);
     int result = 0;
     for (int i = 0; i < NELEM(gClasses); i++) {
-        ClassRegistrationInfo* cri = &gClasses[i];
+        const ClassRegistrationInfo* cri = &gClasses[i];
         result = RegisterMethodsOrDie(env, cri->classPath, cri->methods, cri->methodCount);
     }
     return result;
diff --git a/core/jni/android_animation_PropertyValuesHolder.cpp b/core/jni/android_animation_PropertyValuesHolder.cpp
index d117741..6065c24 100644
--- a/core/jni/android_animation_PropertyValuesHolder.cpp
+++ b/core/jni/android_animation_PropertyValuesHolder.cpp
@@ -139,7 +139,7 @@
     env->ReleaseIntArrayElements(arg, intValues, JNI_ABORT);
 }
 
-static JNINativeMethod gMethods[] = {
+static const JNINativeMethod gMethods[] = {
     {   "nGetIntMethod", "(Ljava/lang/Class;Ljava/lang/String;)J",
             (void*)android_animation_PropertyValuesHolder_getIntMethod },
     {   "nGetFloatMethod", "(Ljava/lang/Class;Ljava/lang/String;)J",
diff --git a/core/jni/android_content_res_ObbScanner.cpp b/core/jni/android_content_res_ObbScanner.cpp
index ef17db6..36d78cf 100644
--- a/core/jni/android_content_res_ObbScanner.cpp
+++ b/core/jni/android_content_res_ObbScanner.cpp
@@ -76,7 +76,7 @@
 /*
  * JNI registration.
  */
-static JNINativeMethod gMethods[] = {
+static const JNINativeMethod gMethods[] = {
     /* name, signature, funcPtr */
     { "getObbInfo_native", "(Ljava/lang/String;Landroid/content/res/ObbInfo;)V",
             (void*) android_content_res_ObbScanner_getObbInfo },
diff --git a/core/jni/android_database_CursorWindow.cpp b/core/jni/android_database_CursorWindow.cpp
index 580ac02..bb09d00 100644
--- a/core/jni/android_database_CursorWindow.cpp
+++ b/core/jni/android_database_CursorWindow.cpp
@@ -477,7 +477,7 @@
     return true;
 }
 
-static JNINativeMethod sMethods[] =
+static const JNINativeMethod sMethods[] =
 {
     /* name, signature, funcPtr */
     { "nativeCreate", "(Ljava/lang/String;I)J",
diff --git a/core/jni/android_database_SQLiteConnection.cpp b/core/jni/android_database_SQLiteConnection.cpp
index 7a3cdf6..bcc3bb0 100644
--- a/core/jni/android_database_SQLiteConnection.cpp
+++ b/core/jni/android_database_SQLiteConnection.cpp
@@ -786,7 +786,7 @@
 }
 
 
-static JNINativeMethod sMethods[] =
+static const JNINativeMethod sMethods[] =
 {
     /* name, signature, funcPtr */
     { "nativeOpen", "(Ljava/lang/String;ILjava/lang/String;ZZ)J",
diff --git a/core/jni/android_database_SQLiteDebug.cpp b/core/jni/android_database_SQLiteDebug.cpp
index 26e13cf..4e4c36c 100644
--- a/core/jni/android_database_SQLiteDebug.cpp
+++ b/core/jni/android_database_SQLiteDebug.cpp
@@ -58,7 +58,7 @@
  * JNI registration.
  */
 
-static JNINativeMethod gMethods[] =
+static const JNINativeMethod gMethods[] =
 {
     { "nativeGetPagerStats", "(Landroid/database/sqlite/SQLiteDebug$PagerStats;)V",
             (void*) nativeGetPagerStats },
diff --git a/core/jni/android_database_SQLiteGlobal.cpp b/core/jni/android_database_SQLiteGlobal.cpp
index 0a1c9f7..03e2387 100644
--- a/core/jni/android_database_SQLiteGlobal.cpp
+++ b/core/jni/android_database_SQLiteGlobal.cpp
@@ -75,7 +75,7 @@
     return sqlite3_release_memory(SOFT_HEAP_LIMIT);
 }
 
-static JNINativeMethod sMethods[] =
+static const JNINativeMethod sMethods[] =
 {
     /* name, signature, funcPtr */
     { "nativeReleaseMemory", "()I", (void*)nativeReleaseMemory },
diff --git a/core/jni/android_ddm_DdmHandleNativeHeap.cpp b/core/jni/android_ddm_DdmHandleNativeHeap.cpp
index ae96936..3e7a04e 100644
--- a/core/jni/android_ddm_DdmHandleNativeHeap.cpp
+++ b/core/jni/android_ddm_DdmHandleNativeHeap.cpp
@@ -105,7 +105,7 @@
     return array;
 }
 
-static JNINativeMethod method_table[] = {
+static const JNINativeMethod method_table[] = {
     { "getLeakInfo", "()[B", (void*) DdmHandleNativeHeap_getLeakInfo },
 };
 
diff --git a/core/jni/android_graphics_Canvas.cpp b/core/jni/android_graphics_Canvas.cpp
index 5ddf235..32a877a 100644
--- a/core/jni/android_graphics_Canvas.cpp
+++ b/core/jni/android_graphics_Canvas.cpp
@@ -746,7 +746,7 @@
 
 }; // namespace CanvasJNI
 
-static JNINativeMethod gMethods[] = {
+static const JNINativeMethod gMethods[] = {
     {"finalizer", "(J)V", (void*) CanvasJNI::finalizer},
     {"initRaster", "(Landroid/graphics/Bitmap;)J", (void*) CanvasJNI::initRaster},
     {"native_setBitmap", "(JLandroid/graphics/Bitmap;)V", (void*) CanvasJNI::setBitmap},
diff --git a/core/jni/android_graphics_Picture.cpp b/core/jni/android_graphics_Picture.cpp
index fd42ddb..03fcdef 100644
--- a/core/jni/android_graphics_Picture.cpp
+++ b/core/jni/android_graphics_Picture.cpp
@@ -91,7 +91,7 @@
     pict->endRecording();
 }
 
-static JNINativeMethod gMethods[] = {
+static const JNINativeMethod gMethods[] = {
     {"nativeGetWidth", "(J)I", (void*) android_graphics_Picture_getWidth},
     {"nativeGetHeight", "(J)I", (void*) android_graphics_Picture_getHeight},
     {"nativeConstructor", "(J)J", (void*) android_graphics_Picture_newPicture},
diff --git a/core/jni/android_hardware_Camera.cpp b/core/jni/android_hardware_Camera.cpp
index 4f44c262..414eba7 100644
--- a/core/jni/android_hardware_Camera.cpp
+++ b/core/jni/android_hardware_Camera.cpp
@@ -949,7 +949,7 @@
 
 //-------------------------------------------------
 
-static JNINativeMethod camMethods[] = {
+static const JNINativeMethod camMethods[] = {
   { "getNumberOfCameras",
     "()I",
     (void *)android_hardware_Camera_getNumberOfCameras },
diff --git a/core/jni/android_hardware_SensorManager.cpp b/core/jni/android_hardware_SensorManager.cpp
index 7d0afdc..2e5cda0 100644
--- a/core/jni/android_hardware_SensorManager.cpp
+++ b/core/jni/android_hardware_SensorManager.cpp
@@ -343,7 +343,7 @@
 }
 //----------------------------------------------------------------------------
 
-static JNINativeMethod gSystemSensorManagerMethods[] = {
+static const JNINativeMethod gSystemSensorManagerMethods[] = {
     {"nativeClassInit",
             "()V",
             (void*)nativeClassInit },
@@ -360,7 +360,7 @@
             (void*)nativeIsDataInjectionEnabled},
 };
 
-static JNINativeMethod gBaseEventQueueMethods[] = {
+static const JNINativeMethod gBaseEventQueueMethods[] = {
     {"nativeInitBaseEventQueue",
              "(JLjava/lang/ref/WeakReference;Landroid/os/MessageQueue;[FLjava/lang/String;ILjava/lang/String;)J",
              (void*)nativeInitSensorEventQueue },
diff --git a/core/jni/android_hardware_SerialPort.cpp b/core/jni/android_hardware_SerialPort.cpp
index 2d2ff4d..393dc7b 100644
--- a/core/jni/android_hardware_SerialPort.cpp
+++ b/core/jni/android_hardware_SerialPort.cpp
@@ -243,7 +243,7 @@
     tcsendbreak(fd, 0);
 }
 
-static JNINativeMethod method_table[] = {
+static const JNINativeMethod method_table[] = {
     {"native_open",             "(Ljava/io/FileDescriptor;I)V",
                                         (void *)android_hardware_SerialPort_open},
     {"native_close",            "()V",  (void *)android_hardware_SerialPort_close},
diff --git a/core/jni/android_hardware_SoundTrigger.cpp b/core/jni/android_hardware_SoundTrigger.cpp
index 1c4c9ec..048b3c7 100644
--- a/core/jni/android_hardware_SoundTrigger.cpp
+++ b/core/jni/android_hardware_SoundTrigger.cpp
@@ -768,14 +768,14 @@
     return status;
 }
 
-static JNINativeMethod gMethods[] = {
+static const JNINativeMethod gMethods[] = {
     {"listModules",
         "(Ljava/util/ArrayList;)I",
         (void *)android_hardware_SoundTrigger_listModules},
 };
 
 
-static JNINativeMethod gModuleMethods[] = {
+static const JNINativeMethod gModuleMethods[] = {
     {"native_setup",
         "(Ljava/lang/Object;)V",
         (void *)android_hardware_SoundTrigger_setup},
diff --git a/core/jni/android_hardware_UsbDevice.cpp b/core/jni/android_hardware_UsbDevice.cpp
index ef3b646..89d33e2 100644
--- a/core/jni/android_hardware_UsbDevice.cpp
+++ b/core/jni/android_hardware_UsbDevice.cpp
@@ -44,7 +44,7 @@
     return result;
 }
 
-static JNINativeMethod method_table[] = {
+static const JNINativeMethod method_table[] = {
     // static methods
     { "native_get_device_id", "(Ljava/lang/String;)I",
                                         (void*)android_hardware_UsbDevice_get_device_id },
diff --git a/core/jni/android_hardware_UsbDeviceConnection.cpp b/core/jni/android_hardware_UsbDeviceConnection.cpp
index e0cae6f..1ba9fc5 100644
--- a/core/jni/android_hardware_UsbDeviceConnection.cpp
+++ b/core/jni/android_hardware_UsbDeviceConnection.cpp
@@ -246,7 +246,7 @@
     return result;
 }
 
-static JNINativeMethod method_table[] = {
+static const JNINativeMethod method_table[] = {
     {"native_open",             "(Ljava/lang/String;Ljava/io/FileDescriptor;)Z",
                                         (void *)android_hardware_UsbDeviceConnection_open},
     {"native_close",            "()V",  (void *)android_hardware_UsbDeviceConnection_close},
diff --git a/core/jni/android_hardware_UsbRequest.cpp b/core/jni/android_hardware_UsbRequest.cpp
index ce99e15..399e7b1 100644
--- a/core/jni/android_hardware_UsbRequest.cpp
+++ b/core/jni/android_hardware_UsbRequest.cpp
@@ -190,7 +190,7 @@
     return (usb_request_cancel(request) == 0);
 }
 
-static JNINativeMethod method_table[] = {
+static const JNINativeMethod method_table[] = {
     {"native_init",             "(Landroid/hardware/usb/UsbDeviceConnection;IIII)Z",
                                             (void *)android_hardware_UsbRequest_init},
     {"native_close",            "()V",      (void *)android_hardware_UsbRequest_close},
diff --git a/core/jni/android_hardware_camera2_CameraMetadata.cpp b/core/jni/android_hardware_camera2_CameraMetadata.cpp
index fb22689..7930027 100644
--- a/core/jni/android_hardware_camera2_CameraMetadata.cpp
+++ b/core/jni/android_hardware_camera2_CameraMetadata.cpp
@@ -529,7 +529,7 @@
 
 //-------------------------------------------------
 
-static JNINativeMethod gCameraMetadataMethods[] = {
+static const JNINativeMethod gCameraMetadataMethods[] = {
 // static methods
   { "nativeClassInit",
     "()V",
diff --git a/core/jni/android_hardware_camera2_DngCreator.cpp b/core/jni/android_hardware_camera2_DngCreator.cpp
index 8b69bbd..738a62a 100644
--- a/core/jni/android_hardware_camera2_DngCreator.cpp
+++ b/core/jni/android_hardware_camera2_DngCreator.cpp
@@ -2280,7 +2280,7 @@
 
 } /*extern "C" */
 
-static JNINativeMethod gDngCreatorMethods[] = {
+static const JNINativeMethod gDngCreatorMethods[] = {
     {"nativeClassInit",        "()V", (void*) DngCreator_nativeClassInit},
     {"nativeInit", "(Landroid/hardware/camera2/impl/CameraMetadataNative;"
             "Landroid/hardware/camera2/impl/CameraMetadataNative;Ljava/lang/String;)V",
diff --git a/core/jni/android_hardware_camera2_legacy_LegacyCameraDevice.cpp b/core/jni/android_hardware_camera2_legacy_LegacyCameraDevice.cpp
index 63915ed..f1ea7ec 100644
--- a/core/jni/android_hardware_camera2_legacy_LegacyCameraDevice.cpp
+++ b/core/jni/android_hardware_camera2_legacy_LegacyCameraDevice.cpp
@@ -730,7 +730,7 @@
 
 } // extern "C"
 
-static JNINativeMethod gCameraDeviceMethods[] = {
+static const JNINativeMethod gCameraDeviceMethods[] = {
     { "nativeDetectSurfaceType",
     "(Landroid/view/Surface;)I",
     (void *)LegacyCameraDevice_nativeDetectSurfaceType },
diff --git a/core/jni/android_hardware_camera2_legacy_PerfMeasurement.cpp b/core/jni/android_hardware_camera2_legacy_PerfMeasurement.cpp
index 7257597..f042058 100644
--- a/core/jni/android_hardware_camera2_legacy_PerfMeasurement.cpp
+++ b/core/jni/android_hardware_camera2_legacy_PerfMeasurement.cpp
@@ -302,7 +302,7 @@
 
 } // extern "C"
 
-static JNINativeMethod gPerfMeasurementMethods[] = {
+static const JNINativeMethod gPerfMeasurementMethods[] = {
     { "nativeCreateContext",
       "(I)J",
       (jlong *)PerfMeasurement_nativeCreateContext },
diff --git a/core/jni/android_hardware_location_ActivityRecognitionHardware.cpp b/core/jni/android_hardware_location_ActivityRecognitionHardware.cpp
index 470c5ba..4b279f63 100644
--- a/core/jni/android_hardware_location_ActivityRecognitionHardware.cpp
+++ b/core/jni/android_hardware_location_ActivityRecognitionHardware.cpp
@@ -275,7 +275,7 @@
 }
 
 
-static JNINativeMethod sMethods[] = {
+static const JNINativeMethod sMethods[] = {
     // {"name", "signature", (void*) functionPointer },
     { "nativeClassInit", "()V", (void*) class_init },
     { "nativeInitialize", "()V", (void*) initialize },
diff --git a/core/jni/android_media_AudioRecord.cpp b/core/jni/android_media_AudioRecord.cpp
index 6c2bbd4..b977e37 100644
--- a/core/jni/android_media_AudioRecord.cpp
+++ b/core/jni/android_media_AudioRecord.cpp
@@ -682,7 +682,7 @@
 
 // ----------------------------------------------------------------------------
 // ----------------------------------------------------------------------------
-static JNINativeMethod gMethods[] = {
+static const JNINativeMethod gMethods[] = {
     // name,               signature,  funcPtr
     {"native_start",         "(II)I",    (void *)android_media_AudioRecord_start},
     {"native_stop",          "()V",    (void *)android_media_AudioRecord_stop},
diff --git a/core/jni/android_media_AudioSystem.cpp b/core/jni/android_media_AudioSystem.cpp
index 91b3278..6d3c7d7 100644
--- a/core/jni/android_media_AudioSystem.cpp
+++ b/core/jni/android_media_AudioSystem.cpp
@@ -1616,7 +1616,7 @@
 
 // ----------------------------------------------------------------------------
 
-static JNINativeMethod gMethods[] = {
+static const JNINativeMethod gMethods[] = {
     {"setParameters",        "(Ljava/lang/String;)I", (void *)android_media_AudioSystem_setParameters},
     {"getParameters",        "(Ljava/lang/String;)Ljava/lang/String;", (void *)android_media_AudioSystem_getParameters},
     {"muteMicrophone",      "(Z)I",     (void *)android_media_AudioSystem_muteMicrophone},
@@ -1663,7 +1663,7 @@
 };
 
 
-static JNINativeMethod gEventHandlerMethods[] = {
+static const JNINativeMethod gEventHandlerMethods[] = {
     {"native_setup",
         "(Ljava/lang/Object;)V",
         (void *)android_media_AudioSystem_eventHandlerSetup},
diff --git a/core/jni/android_media_AudioTrack.cpp b/core/jni/android_media_AudioTrack.cpp
index 5faa150..7860b74 100644
--- a/core/jni/android_media_AudioTrack.cpp
+++ b/core/jni/android_media_AudioTrack.cpp
@@ -1052,7 +1052,7 @@
 
 // ----------------------------------------------------------------------------
 // ----------------------------------------------------------------------------
-static JNINativeMethod gMethods[] = {
+static const JNINativeMethod gMethods[] = {
     // name,              signature,     funcPtr
     {"native_start",         "()V",      (void *)android_media_AudioTrack_start},
     {"native_stop",          "()V",      (void *)android_media_AudioTrack_stop},
diff --git a/core/jni/android_media_JetPlayer.cpp b/core/jni/android_media_JetPlayer.cpp
index d441f10..873c3f2 100644
--- a/core/jni/android_media_JetPlayer.cpp
+++ b/core/jni/android_media_JetPlayer.cpp
@@ -488,7 +488,7 @@
 
 // ----------------------------------------------------------------------------
 // ----------------------------------------------------------------------------
-static JNINativeMethod gMethods[] = {
+static const JNINativeMethod gMethods[] = {
     // name,               signature,               funcPtr
     {"native_setup",       "(Ljava/lang/Object;II)Z", (void *)android_media_JetPlayer_setup},
     {"native_finalize",    "()V",                   (void *)android_media_JetPlayer_finalize},
diff --git a/core/jni/android_media_RemoteDisplay.cpp b/core/jni/android_media_RemoteDisplay.cpp
index 9bc223b..bd1a6ec 100644
--- a/core/jni/android_media_RemoteDisplay.cpp
+++ b/core/jni/android_media_RemoteDisplay.cpp
@@ -177,7 +177,7 @@
 
 // ----------------------------------------------------------------------------
 
-static JNINativeMethod gMethods[] = {
+static const JNINativeMethod gMethods[] = {
     {"nativeListen", "(Ljava/lang/String;Ljava/lang/String;)J",
             (void*)nativeListen },
     {"nativeDispose", "(J)V",
diff --git a/core/jni/android_media_ToneGenerator.cpp b/core/jni/android_media_ToneGenerator.cpp
index 243f040..aec6263 100644
--- a/core/jni/android_media_ToneGenerator.cpp
+++ b/core/jni/android_media_ToneGenerator.cpp
@@ -123,7 +123,7 @@
 
 // ----------------------------------------------------------------------------
 
-static JNINativeMethod gMethods[] = {
+static const JNINativeMethod gMethods[] = {
     { "startTone", "(II)Z", (void *)android_media_ToneGenerator_startTone },
     { "stopTone", "()V", (void *)android_media_ToneGenerator_stopTone },
     { "getAudioSessionId", "()I", (void *)android_media_ToneGenerator_getAudioSessionId},
diff --git a/core/jni/android_net_LocalSocketImpl.cpp b/core/jni/android_net_LocalSocketImpl.cpp
index c137b17..d6d4310 100644
--- a/core/jni/android_net_LocalSocketImpl.cpp
+++ b/core/jni/android_net_LocalSocketImpl.cpp
@@ -495,7 +495,7 @@
 /*
  * JNI registration.
  */
-static JNINativeMethod gMethods[] = {
+static const JNINativeMethod gMethods[] = {
      /* name, signature, funcPtr */
     {"connectLocal", "(Ljava/io/FileDescriptor;Ljava/lang/String;I)V",
                                                 (void*)socket_connect_local},
diff --git a/core/jni/android_net_NetUtils.cpp b/core/jni/android_net_NetUtils.cpp
index fada7ac2..ba0876d 100644
--- a/core/jni/android_net_NetUtils.cpp
+++ b/core/jni/android_net_NetUtils.cpp
@@ -302,7 +302,7 @@
 /*
  * JNI registration.
  */
-static JNINativeMethod gNetworkUtilMethods[] = {
+static const JNINativeMethod gNetworkUtilMethods[] = {
     /* name, signature, funcPtr */
     { "resetConnections", "(Ljava/lang/String;I)I",  (void *)android_net_utils_resetConnections },
     { "startDhcp", "(Ljava/lang/String;)Z",  (void *)android_net_utils_startDhcp },
diff --git a/core/jni/android_net_TrafficStats.cpp b/core/jni/android_net_TrafficStats.cpp
index 7354417..7b7d0cf 100644
--- a/core/jni/android_net_TrafficStats.cpp
+++ b/core/jni/android_net_TrafficStats.cpp
@@ -185,7 +185,7 @@
     }
 }
 
-static JNINativeMethod gMethods[] = {
+static const JNINativeMethod gMethods[] = {
     {"nativeGetTotalStat", "(I)J", (void*) getTotalStat},
     {"nativeGetIfaceStat", "(Ljava/lang/String;I)J", (void*) getIfaceStat},
     {"nativeGetUidStat", "(II)J", (void*) getUidStat},
diff --git a/core/jni/android_opengl_EGL14.cpp b/core/jni/android_opengl_EGL14.cpp
index 9f5b3bc..568473c 100644
--- a/core/jni/android_opengl_EGL14.cpp
+++ b/core/jni/android_opengl_EGL14.cpp
@@ -1231,7 +1231,7 @@
 
 static const char *classPathName = "android/opengl/EGL14";
 
-static JNINativeMethod methods[] = {
+static const JNINativeMethod methods[] = {
 {"_nativeClassInit", "()V", (void*)nativeClassInit },
 {"eglGetError", "()I", (void *) android_eglGetError },
 {"eglGetDisplay", "(I)Landroid/opengl/EGLDisplay;", (void *) android_eglGetDisplayInt },
diff --git a/core/jni/android_opengl_EGLExt.cpp b/core/jni/android_opengl_EGLExt.cpp
index 60a3bf6..62ccad4 100644
--- a/core/jni/android_opengl_EGLExt.cpp
+++ b/core/jni/android_opengl_EGLExt.cpp
@@ -149,7 +149,7 @@
 
 static const char *classPathName = "android/opengl/EGLExt";
 
-static JNINativeMethod methods[] = {
+static const JNINativeMethod methods[] = {
 {"_nativeClassInit", "()V", (void*)nativeClassInit },
 {"eglPresentationTimeANDROID", "(Landroid/opengl/EGLDisplay;Landroid/opengl/EGLSurface;J)Z", (void *) android_eglPresentationTimeANDROID },
 };
diff --git a/core/jni/android_opengl_GLES10.cpp b/core/jni/android_opengl_GLES10.cpp
index dac98de..f4135c2 100644
--- a/core/jni/android_opengl_GLES10.cpp
+++ b/core/jni/android_opengl_GLES10.cpp
@@ -3184,7 +3184,7 @@
 
 static const char *classPathName = "android/opengl/GLES10";
 
-static JNINativeMethod methods[] = {
+static const JNINativeMethod methods[] = {
 {"_nativeClassInit", "()V", (void*)nativeClassInit },
 {"glActiveTexture", "(I)V", (void *) android_glActiveTexture__I },
 {"glAlphaFunc", "(IF)V", (void *) android_glAlphaFunc__IF },
diff --git a/core/jni/android_opengl_GLES10Ext.cpp b/core/jni/android_opengl_GLES10Ext.cpp
index 95be11b..4dc4233 100644
--- a/core/jni/android_opengl_GLES10Ext.cpp
+++ b/core/jni/android_opengl_GLES10Ext.cpp
@@ -582,7 +582,7 @@
 
 static const char *classPathName = "android/opengl/GLES10Ext";
 
-static JNINativeMethod methods[] = {
+static const JNINativeMethod methods[] = {
 {"_nativeClassInit", "()V", (void*)nativeClassInit },
 {"glQueryMatrixxOES", "([II[II)I", (void *) android_glQueryMatrixxOES___3II_3II },
 {"glQueryMatrixxOES", "(Ljava/nio/IntBuffer;Ljava/nio/IntBuffer;)I", (void *) android_glQueryMatrixxOES__Ljava_nio_IntBuffer_2Ljava_nio_IntBuffer_2 },
diff --git a/core/jni/android_opengl_GLES11.cpp b/core/jni/android_opengl_GLES11.cpp
index 6970a3c..2625e03 100644
--- a/core/jni/android_opengl_GLES11.cpp
+++ b/core/jni/android_opengl_GLES11.cpp
@@ -3065,7 +3065,7 @@
 
 static const char *classPathName = "android/opengl/GLES11";
 
-static JNINativeMethod methods[] = {
+static const JNINativeMethod methods[] = {
 {"_nativeClassInit", "()V", (void*)nativeClassInit },
 {"glBindBuffer", "(II)V", (void *) android_glBindBuffer__II },
 {"glBufferData", "(IILjava/nio/Buffer;I)V", (void *) android_glBufferData__IILjava_nio_Buffer_2I },
diff --git a/core/jni/android_opengl_GLES11Ext.cpp b/core/jni/android_opengl_GLES11Ext.cpp
index 6422ff2..fb85cb0 100644
--- a/core/jni/android_opengl_GLES11Ext.cpp
+++ b/core/jni/android_opengl_GLES11Ext.cpp
@@ -3573,7 +3573,7 @@
 
 static const char *classPathName = "android/opengl/GLES11Ext";
 
-static JNINativeMethod methods[] = {
+static const JNINativeMethod methods[] = {
 {"_nativeClassInit", "()V", (void*)nativeClassInit },
 {"glBlendEquationSeparateOES", "(II)V", (void *) android_glBlendEquationSeparateOES__II },
 {"glBlendFuncSeparateOES", "(IIII)V", (void *) android_glBlendFuncSeparateOES__IIII },
diff --git a/core/jni/android_opengl_GLES20.cpp b/core/jni/android_opengl_GLES20.cpp
index f9a0dfe..b9f61a9 100644
--- a/core/jni/android_opengl_GLES20.cpp
+++ b/core/jni/android_opengl_GLES20.cpp
@@ -6152,7 +6152,7 @@
 
 static const char *classPathName = "android/opengl/GLES20";
 
-static JNINativeMethod methods[] = {
+static const JNINativeMethod methods[] = {
 {"_nativeClassInit", "()V", (void*)nativeClassInit },
 {"glActiveTexture", "(I)V", (void *) android_glActiveTexture__I },
 {"glAttachShader", "(II)V", (void *) android_glAttachShader__II },
diff --git a/core/jni/android_opengl_GLES30.cpp b/core/jni/android_opengl_GLES30.cpp
index 1d92cd4..8eb5044 100644
--- a/core/jni/android_opengl_GLES30.cpp
+++ b/core/jni/android_opengl_GLES30.cpp
@@ -5167,7 +5167,7 @@
 
 static const char *classPathName = "android/opengl/GLES30";
 
-static JNINativeMethod methods[] = {
+static const JNINativeMethod methods[] = {
 {"_nativeClassInit", "()V", (void*)nativeClassInit },
 {"glReadBuffer", "(I)V", (void *) android_glReadBuffer__I },
 {"glDrawRangeElements", "(IIIIILjava/nio/Buffer;)V", (void *) android_glDrawRangeElements__IIIIILjava_nio_Buffer_2 },
diff --git a/core/jni/android_opengl_GLES31.cpp b/core/jni/android_opengl_GLES31.cpp
index 92ecbe0..e427388 100644
--- a/core/jni/android_opengl_GLES31.cpp
+++ b/core/jni/android_opengl_GLES31.cpp
@@ -3175,7 +3175,7 @@
 
 static const char *classPathName = "android/opengl/GLES31";
 
-static JNINativeMethod methods[] = {
+static const JNINativeMethod methods[] = {
 {"_nativeClassInit", "()V", (void*)nativeClassInit },
 {"glDispatchCompute", "(III)V", (void *) android_glDispatchCompute__III },
 {"glDispatchComputeIndirect", "(J)V", (void *) android_glDispatchComputeIndirect },
diff --git a/core/jni/android_opengl_GLES31Ext.cpp b/core/jni/android_opengl_GLES31Ext.cpp
index 2856308..180c693 100644
--- a/core/jni/android_opengl_GLES31Ext.cpp
+++ b/core/jni/android_opengl_GLES31Ext.cpp
@@ -1443,7 +1443,7 @@
 
 static const char *classPathName = "android/opengl/GLES31Ext";
 
-static JNINativeMethod methods[] = {
+static const JNINativeMethod methods[] = {
 {"_nativeClassInit", "()V", (void*)nativeClassInit },
 {"glBlendBarrierKHR", "()V", (void *) android_glBlendBarrierKHR__ },
 {"glDebugMessageControlKHR", "(IIII[IIZ)V", (void *) android_glDebugMessageControlKHR__IIII_3IIZ },
diff --git a/core/jni/android_os_Debug.cpp b/core/jni/android_os_Debug.cpp
index b969477..097bbac 100644
--- a/core/jni/android_os_Debug.cpp
+++ b/core/jni/android_os_Debug.cpp
@@ -973,7 +973,7 @@
  * JNI registration.
  */
 
-static JNINativeMethod gMethods[] = {
+static const JNINativeMethod gMethods[] = {
     { "getNativeHeapSize",      "()J",
             (void*) android_os_Debug_getNativeHeapSize },
     { "getNativeHeapAllocatedSize", "()J",
diff --git a/core/jni/android_os_MessageQueue.cpp b/core/jni/android_os_MessageQueue.cpp
index d2db3c9..e57a719 100644
--- a/core/jni/android_os_MessageQueue.cpp
+++ b/core/jni/android_os_MessageQueue.cpp
@@ -209,7 +209,7 @@
 
 // ----------------------------------------------------------------------------
 
-static JNINativeMethod gMessageQueueMethods[] = {
+static const JNINativeMethod gMessageQueueMethods[] = {
     /* name, signature, funcPtr */
     { "nativeInit", "()J", (void*)android_os_MessageQueue_nativeInit },
     { "nativeDestroy", "(J)V", (void*)android_os_MessageQueue_nativeDestroy },
diff --git a/core/jni/android_os_SELinux.cpp b/core/jni/android_os_SELinux.cpp
index 762b88f..8ba77ae 100644
--- a/core/jni/android_os_SELinux.cpp
+++ b/core/jni/android_os_SELinux.cpp
@@ -320,7 +320,7 @@
 /*
  * JNI registration.
  */
-static JNINativeMethod method_table[] = {
+static const JNINativeMethod method_table[] = {
     /* name,                     signature,                    funcPtr */
     { "checkSELinuxAccess"       , "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Z" , (void*)checkSELinuxAccess },
     { "getContext"               , "()Ljava/lang/String;"                         , (void*)getCon           },
diff --git a/core/jni/android_os_SystemClock.cpp b/core/jni/android_os_SystemClock.cpp
index dfe024e..d98407d 100644
--- a/core/jni/android_os_SystemClock.cpp
+++ b/core/jni/android_os_SystemClock.cpp
@@ -104,7 +104,7 @@
 /*
  * JNI registration.
  */
-static JNINativeMethod gMethods[] = {
+static const JNINativeMethod gMethods[] = {
     /* name, signature, funcPtr */
     { "uptimeMillis",      "()J",
             (void*) android_os_SystemClock_uptimeMillis },
diff --git a/core/jni/android_os_SystemProperties.cpp b/core/jni/android_os_SystemProperties.cpp
index 554d304..5dace6b 100644
--- a/core/jni/android_os_SystemProperties.cpp
+++ b/core/jni/android_os_SystemProperties.cpp
@@ -220,7 +220,7 @@
     }
 }
 
-static JNINativeMethod method_table[] = {
+static const JNINativeMethod method_table[] = {
     { "native_get", "(Ljava/lang/String;)Ljava/lang/String;",
       (void*) SystemProperties_getS },
     { "native_get", "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;",
diff --git a/core/jni/android_os_Trace.cpp b/core/jni/android_os_Trace.cpp
index 3fd3b3c..30fc47b 100644
--- a/core/jni/android_os_Trace.cpp
+++ b/core/jni/android_os_Trace.cpp
@@ -105,7 +105,7 @@
     atrace_set_tracing_enabled(enabled);
 }
 
-static JNINativeMethod gTraceMethods[] = {
+static const JNINativeMethod gTraceMethods[] = {
     /* name, signature, funcPtr */
     { "nativeGetEnabledTags",
             "()J",
diff --git a/core/jni/android_os_UEventObserver.cpp b/core/jni/android_os_UEventObserver.cpp
index eb36f85..30d40a2 100644
--- a/core/jni/android_os_UEventObserver.cpp
+++ b/core/jni/android_os_UEventObserver.cpp
@@ -103,7 +103,7 @@
     }
 }
 
-static JNINativeMethod gMethods[] = {
+static const JNINativeMethod gMethods[] = {
     { "nativeSetup", "()V",
             (void *)nativeSetup },
     { "nativeWaitForNextEvent", "()Ljava/lang/String;",
diff --git a/core/jni/android_server_NetworkManagementSocketTagger.cpp b/core/jni/android_server_NetworkManagementSocketTagger.cpp
index ca21fd7..818bf53 100644
--- a/core/jni/android_server_NetworkManagementSocketTagger.cpp
+++ b/core/jni/android_server_NetworkManagementSocketTagger.cpp
@@ -83,7 +83,7 @@
   return (jint)res;
 }
 
-static JNINativeMethod gQTagUidMethods[] = {
+static const JNINativeMethod gQTagUidMethods[] = {
   { "native_tagSocketFd", "(Ljava/io/FileDescriptor;II)I", (void*)QTagUid_tagSocketFd},
   { "native_untagSocketFd", "(Ljava/io/FileDescriptor;)I", (void*)QTagUid_untagSocketFd},
   { "native_setCounterSet", "(II)I", (void*)QTagUid_setCounterSet},
diff --git a/core/jni/android_text_AndroidBidi.cpp b/core/jni/android_text_AndroidBidi.cpp
index 3285429..2a3f036 100644
--- a/core/jni/android_text_AndroidBidi.cpp
+++ b/core/jni/android_text_AndroidBidi.cpp
@@ -56,7 +56,7 @@
     return result;
 }
 
-static JNINativeMethod gMethods[] = {
+static const JNINativeMethod gMethods[] = {
         { "runBidi", "(I[C[BIZ)I", (void*) runBidi }
 };
 
diff --git a/core/jni/android_text_AndroidCharacter.cpp b/core/jni/android_text_AndroidCharacter.cpp
index 9258248..474a74e 100644
--- a/core/jni/android_text_AndroidCharacter.cpp
+++ b/core/jni/android_text_AndroidCharacter.cpp
@@ -178,7 +178,7 @@
     return u_charMirror(c);
 }
 
-static JNINativeMethod gMethods[] = {
+static const JNINativeMethod gMethods[] = {
 	{ "getDirectionalities", "([C[BI)V",
         (void*) getDirectionalities },
 	{ "getEastAsianWidth", "(C)I",
diff --git a/core/jni/android_text_StaticLayout.cpp b/core/jni/android_text_StaticLayout.cpp
index 90e4bb6..a151e00 100644
--- a/core/jni/android_text_StaticLayout.cpp
+++ b/core/jni/android_text_StaticLayout.cpp
@@ -172,7 +172,7 @@
     env->SetFloatArrayRegion(widths, 0, b->size(), b->charWidths());
 }
 
-static JNINativeMethod gMethods[] = {
+static const JNINativeMethod gMethods[] = {
     // TODO performance: many of these are candidates for fast jni, awaiting guidance
     {"nNewBuilder", "()J", (void*) nNewBuilder},
     {"nFreeBuilder", "(J)V", (void*) nFreeBuilder},
diff --git a/core/jni/android_util_AssetManager.cpp b/core/jni/android_util_AssetManager.cpp
index 7ca0654..614e82f 100644
--- a/core/jni/android_util_AssetManager.cpp
+++ b/core/jni/android_util_AssetManager.cpp
@@ -2112,7 +2112,7 @@
 /*
  * JNI registration.
  */
-static JNINativeMethod gAssetManagerMethods[] = {
+static const JNINativeMethod gAssetManagerMethods[] = {
     /* name, signature, funcPtr */
 
     // Basic asset stuff.
diff --git a/core/jni/android_util_EventLog.cpp b/core/jni/android_util_EventLog.cpp
index 05bc125..4f8a2cb 100644
--- a/core/jni/android_util_EventLog.cpp
+++ b/core/jni/android_util_EventLog.cpp
@@ -249,7 +249,7 @@
 /*
  * JNI registration.
  */
-static JNINativeMethod gRegisterMethods[] = {
+static const JNINativeMethod gRegisterMethods[] = {
     /* name, signature, funcPtr */
     { "writeEvent", "(II)I", (void*) android_util_EventLog_writeEvent_Integer },
     { "writeEvent", "(IJ)I", (void*) android_util_EventLog_writeEvent_Long },
diff --git a/core/jni/android_util_FileObserver.cpp b/core/jni/android_util_FileObserver.cpp
index 067d298..2b93b6d 100644
--- a/core/jni/android_util_FileObserver.cpp
+++ b/core/jni/android_util_FileObserver.cpp
@@ -127,7 +127,7 @@
 #endif
 }
 
-static JNINativeMethod sMethods[] = {
+static const JNINativeMethod sMethods[] = {
      /* name, signature, funcPtr */
     { "init", "()I", (void*)android_os_fileobserver_init },
     { "observe", "(I)V", (void*)android_os_fileobserver_observe },
diff --git a/core/jni/android_util_Log.cpp b/core/jni/android_util_Log.cpp
index 2b1067b..2d23cda 100644
--- a/core/jni/android_util_Log.cpp
+++ b/core/jni/android_util_Log.cpp
@@ -111,7 +111,7 @@
 /*
  * JNI registration.
  */
-static JNINativeMethod gMethods[] = {
+static const JNINativeMethod gMethods[] = {
     /* name, signature, funcPtr */
     { "isLoggable",      "(Ljava/lang/String;I)Z", (void*) android_util_Log_isLoggable },
     { "println_native",  "(IILjava/lang/String;Ljava/lang/String;)I", (void*) android_util_Log_println_native },
diff --git a/core/jni/android_util_StringBlock.cpp b/core/jni/android_util_StringBlock.cpp
index f83eaec..b396afe 100644
--- a/core/jni/android_util_StringBlock.cpp
+++ b/core/jni/android_util_StringBlock.cpp
@@ -155,7 +155,7 @@
 /*
  * JNI registration.
  */
-static JNINativeMethod gStringBlockMethods[] = {
+static const JNINativeMethod gStringBlockMethods[] = {
     /* name, signature, funcPtr */
     { "nativeCreate",      "([BII)J",
             (void*) android_content_StringBlock_nativeCreate },
diff --git a/core/jni/android_util_XmlBlock.cpp b/core/jni/android_util_XmlBlock.cpp
index 375710e..7ae51c8 100644
--- a/core/jni/android_util_XmlBlock.cpp
+++ b/core/jni/android_util_XmlBlock.cpp
@@ -364,7 +364,7 @@
 /*
  * JNI registration.
  */
-static JNINativeMethod gXmlBlockMethods[] = {
+static const JNINativeMethod gXmlBlockMethods[] = {
     /* name, signature, funcPtr */
     { "nativeCreate",               "([BII)J",
             (void*) android_content_XmlBlock_nativeCreate },
diff --git a/core/jni/android_view_DisplayEventReceiver.cpp b/core/jni/android_view_DisplayEventReceiver.cpp
index 0e2ec6b..437bd19 100644
--- a/core/jni/android_view_DisplayEventReceiver.cpp
+++ b/core/jni/android_view_DisplayEventReceiver.cpp
@@ -259,7 +259,7 @@
 }
 
 
-static JNINativeMethod gMethods[] = {
+static const JNINativeMethod gMethods[] = {
     /* name, signature, funcPtr */
     { "nativeInit",
             "(Ljava/lang/ref/WeakReference;Landroid/os/MessageQueue;)J",
diff --git a/core/jni/android_view_GraphicBuffer.cpp b/core/jni/android_view_GraphicBuffer.cpp
index 17d2a5e..7682dd6 100644
--- a/core/jni/android_view_GraphicBuffer.cpp
+++ b/core/jni/android_view_GraphicBuffer.cpp
@@ -268,7 +268,7 @@
 
 const char* const kClassPathName = "android/view/GraphicBuffer";
 
-static JNINativeMethod gMethods[] = {
+static const JNINativeMethod gMethods[] = {
     { "nCreateGraphicBuffer",  "(IIII)J", (void*) android_view_GraphiceBuffer_create },
     { "nDestroyGraphicBuffer", "(J)V",    (void*) android_view_GraphiceBuffer_destroy },
 
diff --git a/core/jni/android_view_HardwareLayer.cpp b/core/jni/android_view_HardwareLayer.cpp
index 36ba892..3a0ddc9 100644
--- a/core/jni/android_view_HardwareLayer.cpp
+++ b/core/jni/android_view_HardwareLayer.cpp
@@ -85,7 +85,7 @@
 
 const char* const kClassPathName = "android/view/HardwareLayer";
 
-static JNINativeMethod gMethods[] = {
+static const JNINativeMethod gMethods[] = {
     { "nPrepare",                "(JIIZ)Z",    (void*) android_view_HardwareLayer_prepare },
     { "nSetLayerPaint",          "(JJ)V",      (void*) android_view_HardwareLayer_setLayerPaint },
     { "nSetTransform",           "(JJ)V",      (void*) android_view_HardwareLayer_setTransform },
diff --git a/core/jni/android_view_InputChannel.cpp b/core/jni/android_view_InputChannel.cpp
index 4b42ab5..092ac27 100644
--- a/core/jni/android_view_InputChannel.cpp
+++ b/core/jni/android_view_InputChannel.cpp
@@ -259,7 +259,7 @@
 
 // ----------------------------------------------------------------------------
 
-static JNINativeMethod gInputChannelMethods[] = {
+static const JNINativeMethod gInputChannelMethods[] = {
     /* name, signature, funcPtr */
     { "nativeOpenInputChannelPair", "(Ljava/lang/String;)[Landroid/view/InputChannel;",
             (void*)android_view_InputChannel_nativeOpenInputChannelPair },
diff --git a/core/jni/android_view_InputEventReceiver.cpp b/core/jni/android_view_InputEventReceiver.cpp
index 43b8471..8293cd8 100644
--- a/core/jni/android_view_InputEventReceiver.cpp
+++ b/core/jni/android_view_InputEventReceiver.cpp
@@ -395,7 +395,7 @@
 }
 
 
-static JNINativeMethod gMethods[] = {
+static const JNINativeMethod gMethods[] = {
     /* name, signature, funcPtr */
     { "nativeInit",
             "(Ljava/lang/ref/WeakReference;Landroid/view/InputChannel;Landroid/os/MessageQueue;)J",
diff --git a/core/jni/android_view_InputEventSender.cpp b/core/jni/android_view_InputEventSender.cpp
index d61dee7..3bd6917 100644
--- a/core/jni/android_view_InputEventSender.cpp
+++ b/core/jni/android_view_InputEventSender.cpp
@@ -289,7 +289,7 @@
 }
 
 
-static JNINativeMethod gMethods[] = {
+static const JNINativeMethod gMethods[] = {
     /* name, signature, funcPtr */
     { "nativeInit",
             "(Ljava/lang/ref/WeakReference;Landroid/view/InputChannel;Landroid/os/MessageQueue;)J",
diff --git a/core/jni/android_view_KeyCharacterMap.cpp b/core/jni/android_view_KeyCharacterMap.cpp
index 7653f58..e5519a7 100644
--- a/core/jni/android_view_KeyCharacterMap.cpp
+++ b/core/jni/android_view_KeyCharacterMap.cpp
@@ -203,7 +203,7 @@
  * JNI registration.
  */
 
-static JNINativeMethod g_methods[] = {
+static const JNINativeMethod g_methods[] = {
     /* name, signature, funcPtr */
     { "nativeReadFromParcel", "(Landroid/os/Parcel;)J",
             (void*)nativeReadFromParcel },
diff --git a/core/jni/android_view_MotionEvent.cpp b/core/jni/android_view_MotionEvent.cpp
index 98c17c0..81d46c3 100644
--- a/core/jni/android_view_MotionEvent.cpp
+++ b/core/jni/android_view_MotionEvent.cpp
@@ -752,7 +752,7 @@
 
 // ----------------------------------------------------------------------------
 
-static JNINativeMethod gMotionEventMethods[] = {
+static const JNINativeMethod gMotionEventMethods[] = {
     /* name, signature, funcPtr */
     { "nativeInitialize",
             "(JIIIIIIIFFFFJJI[Landroid/view/MotionEvent$PointerProperties;"
diff --git a/core/jni/android_view_RenderNode.cpp b/core/jni/android_view_RenderNode.cpp
index a3b3700..388d365 100644
--- a/core/jni/android_view_RenderNode.cpp
+++ b/core/jni/android_view_RenderNode.cpp
@@ -467,7 +467,7 @@
 
 const char* const kClassPathName = "android/view/RenderNode";
 
-static JNINativeMethod gMethods[] = {
+static const JNINativeMethod gMethods[] = {
     { "nCreate",               "(Ljava/lang/String;)J",    (void*) android_view_RenderNode_create },
     { "nDestroyRenderNode",   "(J)V",   (void*) android_view_RenderNode_destroyRenderNode },
     { "nSetDisplayListData",   "(JJ)V", (void*) android_view_RenderNode_setDisplayListData },
diff --git a/core/jni/android_view_RenderNodeAnimator.cpp b/core/jni/android_view_RenderNodeAnimator.cpp
index 4177ee2..0926e9b 100644
--- a/core/jni/android_view_RenderNodeAnimator.cpp
+++ b/core/jni/android_view_RenderNodeAnimator.cpp
@@ -193,7 +193,7 @@
 
 const char* const kClassPathName = "android/view/RenderNodeAnimator";
 
-static JNINativeMethod gMethods[] = {
+static const JNINativeMethod gMethods[] = {
     { "nCreateAnimator", "(IF)J", (void*) createAnimator },
     { "nCreateCanvasPropertyFloatAnimator", "(JF)J", (void*) createCanvasPropertyFloatAnimator },
     { "nCreateCanvasPropertyPaintAnimator", "(JIF)J", (void*) createCanvasPropertyPaintAnimator },
diff --git a/core/jni/android_view_Surface.cpp b/core/jni/android_view_Surface.cpp
index 4a311d31..24055e7 100644
--- a/core/jni/android_view_Surface.cpp
+++ b/core/jni/android_view_Surface.cpp
@@ -514,7 +514,7 @@
 
 namespace hwui = android::uirenderer;
 
-static JNINativeMethod gSurfaceMethods[] = {
+static const JNINativeMethod gSurfaceMethods[] = {
     {"nativeCreateFromSurfaceTexture", "(Landroid/graphics/SurfaceTexture;)J",
             (void*)nativeCreateFromSurfaceTexture },
     {"nativeRelease", "(J)V",
diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp
index d1acb59..65ebb663 100644
--- a/core/jni/android_view_SurfaceControl.cpp
+++ b/core/jni/android_view_SurfaceControl.cpp
@@ -572,7 +572,7 @@
 
 // ----------------------------------------------------------------------------
 
-static JNINativeMethod sSurfaceControlMethods[] = {
+static const JNINativeMethod sSurfaceControlMethods[] = {
     {"nativeCreate", "(Landroid/view/SurfaceSession;Ljava/lang/String;IIII)J",
             (void*)nativeCreate },
     {"nativeRelease", "(J)V",
diff --git a/core/jni/android_view_SurfaceSession.cpp b/core/jni/android_view_SurfaceSession.cpp
index 609c565..dad6958 100644
--- a/core/jni/android_view_SurfaceSession.cpp
+++ b/core/jni/android_view_SurfaceSession.cpp
@@ -56,7 +56,7 @@
 }
 
 
-static JNINativeMethod gMethods[] = {
+static const JNINativeMethod gMethods[] = {
     /* name, signature, funcPtr */
     { "nativeCreate", "()J",
             (void*)nativeCreate },
diff --git a/core/jni/android_view_TextureView.cpp b/core/jni/android_view_TextureView.cpp
index beb83b1..b736a17 100644
--- a/core/jni/android_view_TextureView.cpp
+++ b/core/jni/android_view_TextureView.cpp
@@ -197,7 +197,7 @@
 
 const char* const kClassPathName = "android/view/TextureView";
 
-static JNINativeMethod gMethods[] = {
+static const JNINativeMethod gMethods[] = {
     {   "nCreateNativeWindow", "(Landroid/graphics/SurfaceTexture;)V",
             (void*) android_view_TextureView_createNativeWindow },
     {   "nDestroyNativeWindow", "()V",
diff --git a/core/jni/android_view_ThreadedRenderer.cpp b/core/jni/android_view_ThreadedRenderer.cpp
index 6c3676b..c79f833 100644
--- a/core/jni/android_view_ThreadedRenderer.cpp
+++ b/core/jni/android_view_ThreadedRenderer.cpp
@@ -440,6 +440,32 @@
     }
 }
 
+static void android_view_ThreadedRenderer_addRenderNode(JNIEnv* env, jobject clazz,
+        jlong proxyPtr, jlong renderNodePtr, jboolean placeFront) {
+    RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr);
+    RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr);
+    proxy->addRenderNode(renderNode, placeFront);
+}
+
+static void android_view_ThreadedRenderer_removeRenderNode(JNIEnv* env, jobject clazz,
+        jlong proxyPtr, jlong renderNodePtr) {
+    RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr);
+    RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr);
+    proxy->removeRenderNode(renderNode);
+}
+
+static void android_view_ThreadedRendererd_drawRenderNode(JNIEnv* env, jobject clazz,
+        jlong proxyPtr, jlong renderNodePtr) {
+    RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr);
+    RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr);
+    proxy->drawRenderNode(renderNode);
+}
+
+static void android_view_ThreadedRenderer_setContentOverdrawProtectionBounds(JNIEnv* env,
+        jobject clazz, jlong proxyPtr, jint left, jint top, jint right, jint bottom) {
+    RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr);
+    proxy->setContentOverdrawProtectionBounds(left, top, right, bottom);
+}
 
 // ----------------------------------------------------------------------------
 // Shaders
@@ -447,7 +473,6 @@
 
 static void android_view_ThreadedRenderer_setupShadersDiskCache(JNIEnv* env, jobject clazz,
         jstring diskCachePath) {
-
     const char* cacheArray = env->GetStringUTFChars(diskCachePath, NULL);
     egl_cache_t::get()->setCacheFilename(cacheArray);
     env->ReleaseStringUTFChars(diskCachePath, cacheArray);
@@ -459,7 +484,7 @@
 
 const char* const kClassPathName = "android/view/ThreadedRenderer";
 
-static JNINativeMethod gMethods[] = {
+static const JNINativeMethod gMethods[] = {
     { "nSetAtlas", "(JLandroid/view/GraphicBuffer;[J)V",   (void*) android_view_ThreadedRenderer_setAtlas },
     { "nSetProcessStatsBuffer", "(JI)V", (void*) android_view_ThreadedRenderer_setProcessStatsBuffer },
     { "nCreateRootRenderNode", "()J", (void*) android_view_ThreadedRenderer_createRootRenderNode },
@@ -494,6 +519,11 @@
     { "nDumpProfileData", "([BLjava/io/FileDescriptor;)V", (void*) android_view_ThreadedRenderer_dumpProfileData },
     { "setupShadersDiskCache", "(Ljava/lang/String;)V",
                 (void*) android_view_ThreadedRenderer_setupShadersDiskCache },
+    { "nAddRenderNode", "(JJZ)V", (void*) android_view_ThreadedRenderer_addRenderNode},
+    { "nRemoveRenderNode", "(JJ)V", (void*) android_view_ThreadedRenderer_removeRenderNode},
+    { "nDrawRenderNode", "(JJ)V", (void*) android_view_ThreadedRendererd_drawRenderNode},
+    { "nSetContentOverdrawProtectionBounds", "(JIIII)V",
+                (void*)android_view_ThreadedRenderer_setContentOverdrawProtectionBounds},
 };
 
 int register_android_view_ThreadedRenderer(JNIEnv* env) {
diff --git a/core/jni/android_view_VelocityTracker.cpp b/core/jni/android_view_VelocityTracker.cpp
index ddd5fc8..04ec705 100644
--- a/core/jni/android_view_VelocityTracker.cpp
+++ b/core/jni/android_view_VelocityTracker.cpp
@@ -215,7 +215,7 @@
 
 // --- JNI Registration ---
 
-static JNINativeMethod gVelocityTrackerMethods[] = {
+static const JNINativeMethod gVelocityTrackerMethods[] = {
     /* name, signature, funcPtr */
     { "nativeInitialize",
             "(Ljava/lang/String;)J",
diff --git a/core/jni/com_android_internal_content_NativeLibraryHelper.cpp b/core/jni/com_android_internal_content_NativeLibraryHelper.cpp
index daa6f82..364ac44 100644
--- a/core/jni/com_android_internal_content_NativeLibraryHelper.cpp
+++ b/core/jni/com_android_internal_content_NativeLibraryHelper.cpp
@@ -561,7 +561,7 @@
     delete reinterpret_cast<ZipFileRO*>(apkHandle);
 }
 
-static JNINativeMethod gMethods[] = {
+static const JNINativeMethod gMethods[] = {
     {"nativeOpenApk",
             "(Ljava/lang/String;)J",
             (void *)com_android_internal_content_NativeLibraryHelper_openApk},
diff --git a/core/jni/com_android_internal_net_NetworkStatsFactory.cpp b/core/jni/com_android_internal_net_NetworkStatsFactory.cpp
index 6c0b756..70134ab 100644
--- a/core/jni/com_android_internal_net_NetworkStatsFactory.cpp
+++ b/core/jni/com_android_internal_net_NetworkStatsFactory.cpp
@@ -284,7 +284,7 @@
     return 0;
 }
 
-static JNINativeMethod gMethods[] = {
+static const JNINativeMethod gMethods[] = {
         { "nativeReadNetworkStatsDetail",
                 "(Landroid/net/NetworkStats;Ljava/lang/String;I[Ljava/lang/String;I)I",
                 (void*) readNetworkStatsDetail }
diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp
index aef70be..3f1be45 100644
--- a/core/jni/com_android_internal_os_Zygote.cpp
+++ b/core/jni/com_android_internal_os_Zygote.cpp
@@ -647,7 +647,7 @@
   return pid;
 }
 
-static JNINativeMethod gMethods[] = {
+static const JNINativeMethod gMethods[] = {
     { "nativeForkAndSpecialize",
       "(II[II[[IILjava/lang/String;Ljava/lang/String;[ILjava/lang/String;Ljava/lang/String;)I",
       (void *) com_android_internal_os_Zygote_nativeForkAndSpecialize },
diff --git a/core/jni/com_android_internal_util_VirtualRefBasePtr.cpp b/core/jni/com_android_internal_util_VirtualRefBasePtr.cpp
index 7a18c2d..d20bae2 100644
--- a/core/jni/com_android_internal_util_VirtualRefBasePtr.cpp
+++ b/core/jni/com_android_internal_util_VirtualRefBasePtr.cpp
@@ -36,7 +36,7 @@
 
 const char* const kClassPathName = "com/android/internal/util/VirtualRefBasePtr";
 
-static JNINativeMethod gMethods[] = {
+static const JNINativeMethod gMethods[] = {
     { "nIncStrong", "(J)V", (void*) incStrong },
     { "nDecStrong", "(J)V", (void*) decStrong },
 };
diff --git a/core/jni/com_android_internal_view_animation_NativeInterpolatorFactoryHelper.cpp b/core/jni/com_android_internal_view_animation_NativeInterpolatorFactoryHelper.cpp
index 2c65d62..6781e13 100644
--- a/core/jni/com_android_internal_view_animation_NativeInterpolatorFactoryHelper.cpp
+++ b/core/jni/com_android_internal_view_animation_NativeInterpolatorFactoryHelper.cpp
@@ -78,7 +78,7 @@
 
 const char* const kClassPathName = "com/android/internal/view/animation/NativeInterpolatorFactoryHelper";
 
-static JNINativeMethod gMethods[] = {
+static const JNINativeMethod gMethods[] = {
     { "createAccelerateDecelerateInterpolator", "()J", (void*) createAccelerateDecelerateInterpolator },
     { "createAccelerateInterpolator", "(F)J", (void*) createAccelerateInterpolator },
     { "createAnticipateInterpolator", "(F)J", (void*) createAnticipateInterpolator },
diff --git a/core/jni/com_google_android_gles_jni_EGLImpl.cpp b/core/jni/com_google_android_gles_jni_EGLImpl.cpp
index baeb7dd..3d63b01 100644
--- a/core/jni/com_google_android_gles_jni_EGLImpl.cpp
+++ b/core/jni/com_google_android_gles_jni_EGLImpl.cpp
@@ -532,7 +532,7 @@
 #define OBJECT  "Ljava/lang/Object;"
 #define STRING  "Ljava/lang/String;"
 
-static JNINativeMethod methods[] = {
+static const JNINativeMethod methods[] = {
 {"_nativeClassInit","()V", (void*)nativeClassInit },
 {"eglWaitGL",       "()Z", (void*)jni_eglWaitGL },
 {"eglInitialize",   "(" DISPLAY "[I)Z", (void*)jni_eglInitialize },
diff --git a/core/jni/com_google_android_gles_jni_GLImpl.cpp b/core/jni/com_google_android_gles_jni_GLImpl.cpp
index f15f957..ad7d744 100644
--- a/core/jni/com_google_android_gles_jni_GLImpl.cpp
+++ b/core/jni/com_google_android_gles_jni_GLImpl.cpp
@@ -8490,7 +8490,7 @@
 
 static const char *classPathName = "com/google/android/gles_jni/GLImpl";
 
-static JNINativeMethod methods[] = {
+static const JNINativeMethod methods[] = {
 {"_nativeClassInit", "()V", (void*)nativeClassInit },
 {"glActiveTexture", "(I)V", (void *) android_glActiveTexture__I },
 {"glAlphaFunc", "(IF)V", (void *) android_glAlphaFunc__IF },
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 921385d..c713454 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -65,6 +65,7 @@
     <protected-broadcast android:name="android.intent.action.NEW_OUTGOING_CALL" />
     <protected-broadcast android:name="android.intent.action.REBOOT" />
     <protected-broadcast android:name="android.intent.action.DOCK_EVENT" />
+    <protected-broadcast android:name="android.intent.action.THERMAL_EVENT" />
     <protected-broadcast android:name="android.intent.action.MASTER_CLEAR_NOTIFICATION" />
     <protected-broadcast android:name="android.intent.action.USER_ADDED" />
     <protected-broadcast android:name="android.intent.action.USER_REMOVED" />
@@ -188,7 +189,7 @@
     <protected-broadcast android:name="android.hardware.usb.action.USB_STATE" />
     <protected-broadcast android:name="android.hardware.usb.action.USB_PORT_CHANGED" />
     <protected-broadcast android:name="android.hardware.usb.action.USB_ACCESSORY_ATTACHED" />
-    <protected-broadcast android:name="android.hardware.usb.action.USB_ACCESSORY_ATTACHED" />
+    <protected-broadcast android:name="android.hardware.usb.action.USB_ACCESSORY_DETACHED" />
     <protected-broadcast android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED" />
     <protected-broadcast android:name="android.hardware.usb.action.USB_DEVICE_DETACHED" />
 
@@ -2175,6 +2176,13 @@
     <permission android:name="android.permission.CONTROL_WIFI_DISPLAY"
         android:protectionLevel="signature" />
 
+    <!-- Allows an application to control the color transforms applied to
+         displays system-wide.
+         <p>Not for use by third-party applications.</p>
+         @hide -->
+    <permission android:name="android.permission.CONFIGURE_DISPLAY_COLOR_TRANSFORM"
+        android:protectionLevel="signature" />
+
     <!-- @SystemApi Allows an application to control VPN.
          <p>Not for use by third-party applications.</p>
          @hide -->
diff --git a/core/res/res/color/btn_colored_material.xml b/core/res/res/color/btn_colored_background_material.xml
similarity index 87%
rename from core/res/res/color/btn_colored_material.xml
rename to core/res/res/color/btn_colored_background_material.xml
index b45f824..b7f0ef5 100644
--- a/core/res/res/color/btn_colored_material.xml
+++ b/core/res/res/color/btn_colored_background_material.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2014 The Android Open Source Project
+<!-- 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.
@@ -14,6 +14,7 @@
      limitations under the License.
 -->
 
+<!-- Used for tha background of a bordered colored button. -->
 <selector xmlns:android="http://schemas.android.com/apk/res/android">
     <item android:state_enabled="false"
           android:alpha="?attr/disabledAlpha"
diff --git a/core/res/res/color/btn_colored_material.xml b/core/res/res/color/btn_colored_borderless_text_material.xml
similarity index 78%
copy from core/res/res/color/btn_colored_material.xml
copy to core/res/res/color/btn_colored_borderless_text_material.xml
index b45f824..ee5a90c 100644
--- a/core/res/res/color/btn_colored_material.xml
+++ b/core/res/res/color/btn_colored_borderless_text_material.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2014 The Android Open Source Project
+<!-- 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.
@@ -14,9 +14,10 @@
      limitations under the License.
 -->
 
+<!-- Used for the text of a borderless colored button. -->
 <selector xmlns:android="http://schemas.android.com/apk/res/android">
     <item android:state_enabled="false"
           android:alpha="?attr/disabledAlpha"
-          android:color="?attr/colorButtonNormal" />
-    <item android:color="?attr/colorAccent" />
+          android:color="?attr/textColorSecondary" />
+    <item android:color="?attr/colorAccent"/>
 </selector>
diff --git a/core/res/res/color/btn_colored_text_material.xml b/core/res/res/color/btn_colored_text_material.xml
index 950d04a..23d05a9 100644
--- a/core/res/res/color/btn_colored_text_material.xml
+++ b/core/res/res/color/btn_colored_text_material.xml
@@ -14,9 +14,10 @@
      limitations under the License.
 -->
 
+<!-- Used for the text of a bordered colored button. -->
 <selector xmlns:android="http://schemas.android.com/apk/res/android">
     <item android:state_enabled="false"
           android:alpha="?attr/disabledAlpha"
           android:color="?attr/textColorSecondary" />
-    <item android:color="?attr/colorAccent"/>
+    <item android:color="?attr/textColorSecondaryInverse" />
 </selector>
diff --git a/core/res/res/drawable/btn_colored_material.xml b/core/res/res/drawable/btn_colored_material.xml
index 81cbe39..c3c5760 100644
--- a/core/res/res/drawable/btn_colored_material.xml
+++ b/core/res/res/drawable/btn_colored_material.xml
@@ -22,7 +22,7 @@
     <ripple android:color="?attr/colorControlHighlight">
         <item>
             <shape android:shape="rectangle"
-                   android:tint="@color/btn_colored_material">
+                   android:tint="@color/btn_colored_background_material">
                 <corners android:radius="@dimen/control_corner_material" />
                 <solid android:color="@color/white" />
                 <padding android:left="@dimen/button_padding_horizontal_material"
diff --git a/core/res/res/layout-land/time_picker_material.xml b/core/res/res/layout-land/time_picker_material.xml
index 2473e87..bb347cb 100644
--- a/core/res/res/layout-land/time_picker_material.xml
+++ b/core/res/res/layout-land/time_picker_material.xml
@@ -46,7 +46,8 @@
             android:layout_height="wrap_content"
             android:orientation="horizontal"
             android:layout_centerInParent="true"
-            android:paddingTop="@dimen/timepicker_radial_picker_top_margin">
+            android:paddingTop="@dimen/timepicker_radial_picker_top_margin"
+            android:layout_marginBottom="-12dp">
 
             <!-- The hour should always be to the left of the separator,
                  regardless of the current locale's layout direction. -->
@@ -57,14 +58,16 @@
                 android:textAppearance="@style/TextAppearance.Material.TimePicker.TimeLabel"
                 android:singleLine="true"
                 android:ellipsize="none"
-                android:gravity="right" />
+                android:gravity="right"
+                android:includeFontPadding="false" />
 
             <TextView
                 android:id="@+id/separator"
                 android:layout_width="wrap_content"
                 android:layout_height="wrap_content"
                 android:textAppearance="@style/TextAppearance.Material.TimePicker.TimeLabel"
-                android:importantForAccessibility="no" />
+                android:importantForAccessibility="no"
+                android:includeFontPadding="false" />
 
             <!-- The minutes should always be to the right of the separator,
                  regardless of the current locale's layout direction. -->
@@ -75,7 +78,8 @@
                 android:textAppearance="@style/TextAppearance.Material.TimePicker.TimeLabel"
                 android:singleLine="true"
                 android:ellipsize="none"
-                android:gravity="left" />
+                android:gravity="left"
+                android:includeFontPadding="false" />
         </LinearLayout>
 
         <!-- The layout alignment of this view will switch between toRightOf
@@ -93,22 +97,27 @@
                 android:id="@+id/am_label"
                 android:layout_width="wrap_content"
                 android:layout_height="wrap_content"
+                android:minHeight="48dp"
+                android:minWidth="48dp"
+                android:gravity="bottom"
                 android:textAppearance="@style/TextAppearance.Material.TimePicker.AmPmLabel"
                 android:paddingStart="@dimen/timepicker_ampm_horizontal_padding"
                 android:paddingEnd="@dimen/timepicker_ampm_horizontal_padding"
-                android:paddingTop="@dimen/timepicker_am_top_padding"
+                android:paddingTop="4dp"
+                android:paddingBottom="6dp"
                 android:lines="1"
-                android:ellipsize="none"
-                android:includeFontPadding="false" />
+                android:ellipsize="none" />
 
             <CheckedTextView
                 android:id="@+id/pm_label"
                 android:layout_width="wrap_content"
                 android:layout_height="wrap_content"
+                android:minHeight="48dp"
+                android:minWidth="48dp"
+                android:gravity="top"
                 android:textAppearance="@style/TextAppearance.Material.TimePicker.AmPmLabel"
                 android:paddingStart="@dimen/timepicker_ampm_horizontal_padding"
                 android:paddingEnd="@dimen/timepicker_ampm_horizontal_padding"
-                android:paddingTop="@dimen/timepicker_pm_top_padding"
                 android:lines="1"
                 android:ellipsize="none"
                 android:includeFontPadding="false" />
diff --git a/core/res/res/color/btn_colored_material.xml b/core/res/res/layout/docked_stack_divider.xml
similarity index 65%
copy from core/res/res/color/btn_colored_material.xml
copy to core/res/res/layout/docked_stack_divider.xml
index b45f824..aa6e68d 100644
--- a/core/res/res/color/btn_colored_material.xml
+++ b/core/res/res/layout/docked_stack_divider.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2014 The Android Open Source Project
+<!-- 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.
@@ -14,9 +14,8 @@
      limitations under the License.
 -->
 
-<selector xmlns:android="http://schemas.android.com/apk/res/android">
-    <item android:state_enabled="false"
-          android:alpha="?attr/disabledAlpha"
-          android:color="?attr/colorButtonNormal" />
-    <item android:color="?attr/colorAccent" />
-</selector>
+<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/layout/number_picker_material.xml b/core/res/res/layout/number_picker_material.xml
index 47edec4..b045585 100644
--- a/core/res/res/layout/number_picker_material.xml
+++ b/core/res/res/layout/number_picker_material.xml
@@ -22,4 +22,4 @@
       android:gravity="center"
       android:singleLine="true"
       android:background="@null"
-      android:textAppearance="@style/TextAppearance.Material.Caption" />
+      android:textAppearance="@style/TextAppearance.Material.Body1" />
diff --git a/core/res/res/layout/popup_menu_header_item_layout.xml b/core/res/res/layout/popup_menu_header_item_layout.xml
new file mode 100644
index 0000000..5879f85
--- /dev/null
+++ b/core/res/res/layout/popup_menu_header_item_layout.xml
@@ -0,0 +1,35 @@
+<?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="match_parent"
+    android:layout_height="?attr/dropdownListPreferredItemHeight"
+    android:minWidth="196dip"
+    android:paddingStart="16dip"
+    android:paddingEnd="16dip">
+
+    <TextView
+        android:id="@+id/title"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:textAppearance="?attr/textAppearancePopupMenuHeader"
+        android:layout_gravity="center_vertical"
+        android:singleLine="true"
+        android:ellipsize="marquee"
+        android:fadingEdge="horizontal"
+        android:textAlignment="viewStart" />
+
+</FrameLayout>
diff --git a/core/res/res/layout/time_picker_header_material.xml b/core/res/res/layout/time_picker_header_material.xml
index 3c3a8a8..acdc509 100644
--- a/core/res/res/layout/time_picker_header_material.xml
+++ b/core/res/res/layout/time_picker_header_material.xml
@@ -78,6 +78,9 @@
             android:id="@+id/am_label"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
+            android:minWidth="48dp"
+            android:minHeight="48dp"
+            android:gravity="bottom"
             android:paddingTop="@dimen/timepicker_am_top_padding"
             android:textAppearance="@style/TextAppearance.Material.TimePicker.AmPmLabel"
             android:lines="1"
@@ -86,6 +89,9 @@
             android:id="@+id/pm_label"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
+            android:minWidth="48dp"
+            android:minHeight="48dp"
+            android:gravity="top"
             android:paddingTop="@dimen/timepicker_pm_top_padding"
             android:textAppearance="@style/TextAppearance.Material.TimePicker.AmPmLabel"
             android:lines="1"
diff --git a/core/res/res/values-cs/strings.xml b/core/res/res/values-cs/strings.xml
index cd6bb4b..7b05612 100644
--- a/core/res/res/values-cs/strings.xml
+++ b/core/res/res/values-cs/strings.xml
@@ -231,7 +231,7 @@
     <string name="permgrouplab_contacts" msgid="3657758145679177612">"Kontakty"</string>
     <string name="permgroupdesc_contacts" msgid="6951499528303668046">"přístup ke kontaktům"</string>
     <string name="permgrouplab_location" msgid="7275582855722310164">"Poloha"</string>
-    <string name="permgroupdesc_location" msgid="1346617465127855033">"přístup k poloze tohoto zařízení"</string>
+    <string name="permgroupdesc_location" msgid="1346617465127855033">"přístup k poloze tohoto zařízení"</string>
     <string name="permgrouplab_calendar" msgid="5863508437783683902">"Kalendář"</string>
     <string name="permgroupdesc_calendar" msgid="3889615280211184106">"přístup ke kalendáři"</string>
     <string name="permgrouplab_sms" msgid="228308803364967808">"SMS"</string>
diff --git a/core/res/res/values-da/strings.xml b/core/res/res/values-da/strings.xml
index e718935..60f0502 100644
--- a/core/res/res/values-da/strings.xml
+++ b/core/res/res/values-da/strings.xml
@@ -493,8 +493,8 @@
     <string name="permdesc_bindCarrierMessagingService" msgid="2762882888502113944">"Dette giver indehaveren mulighed for at knytte sig til det øverste grænsefladeniveau for et mobilselskabs beskedtjeneste. Dette bør ikke være nødvendigt i normale apps."</string>
     <string name="permlab_bindCarrierServices" msgid="3233108656245526783">"knytte til tjenester fra mobilselskabet"</string>
     <string name="permdesc_bindCarrierServices" msgid="1391552602551084192">"Tillader, at brugeren knytter sig til tjenester fra mobilselskabet. Dette bør aldrig være nødvendigt for almindelige apps."</string>
-    <string name="permlab_access_notification_policy" msgid="4247510821662059671">"Adgang til Vil ikke forstyrres"</string>
-    <string name="permdesc_access_notification_policy" msgid="3296832375218749580">"Giver appen tilladelse til at læse og skrive konfigurationen af Vil ikke forstyrres."</string>
+    <string name="permlab_access_notification_policy" msgid="4247510821662059671">"have adgang til Forstyr ikke"</string>
+    <string name="permdesc_access_notification_policy" msgid="3296832375218749580">"Giver appen tilladelse til at læse og skrive konfigurationen af Forstyr ikke."</string>
     <string name="policylab_limitPassword" msgid="4497420728857585791">"Angiv regler for adgangskoder"</string>
     <string name="policydesc_limitPassword" msgid="2502021457917874968">"Kontrollér længden samt tilladte tegn i adgangskoder og pinkoder til skærmlåsen."</string>
     <string name="policylab_watchLogin" msgid="914130646942199503">"Overvåg forsøg på oplåsning af skærm"</string>
@@ -1471,10 +1471,10 @@
     </plurals>
     <string name="zen_mode_until" msgid="7336308492289875088">"Indtil <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
     <string name="zen_mode_forever" msgid="7420011936770086993">"Indtil du slår denne indstilling fra"</string>
-    <string name="zen_mode_forever_dnd" msgid="3792132696572189081">"Indtil du slår \"Vil ikke forstyrres\" fra"</string>
+    <string name="zen_mode_forever_dnd" msgid="3792132696572189081">"Indtil du slår \"Forstyr ikke\" fra"</string>
     <string name="zen_mode_rule_name_combination" msgid="191109939968076477">"<xliff:g id="FIRST">%1$s</xliff:g>/<xliff:g id="REST">%2$s</xliff:g>"</string>
     <string name="toolbar_collapse_description" msgid="2821479483960330739">"Skjul"</string>
-    <string name="zen_mode_feature_name" msgid="5254089399895895004">"Vil ikke forstyrres"</string>
+    <string name="zen_mode_feature_name" msgid="5254089399895895004">"Forstyr ikke"</string>
     <string name="zen_mode_downtime_feature_name" msgid="2626974636779860146">"Nedetid"</string>
     <string name="zen_mode_default_weeknights_name" msgid="3081318299464998143">"Hverdagsaften"</string>
     <string name="zen_mode_default_weekends_name" msgid="2786495801019345244">"Weekend"</string>
diff --git a/core/res/res/values-eu-rES/strings.xml b/core/res/res/values-eu-rES/strings.xml
index 2c7a64d..8e7193a 100644
--- a/core/res/res/values-eu-rES/strings.xml
+++ b/core/res/res/values-eu-rES/strings.xml
@@ -227,23 +227,23 @@
     <string name="user_owner_label" msgid="2804351898001038951">"Pertsonalak"</string>
     <string name="managed_profile_label" msgid="6260850669674791528">"Lana"</string>
     <string name="permgrouplab_contacts" msgid="3657758145679177612">"Kontaktuak"</string>
-    <string name="permgroupdesc_contacts" msgid="6951499528303668046">"Atzitu kontaktuak"</string>
+    <string name="permgroupdesc_contacts" msgid="6951499528303668046">"kontaktuak atzitzeko"</string>
     <string name="permgrouplab_location" msgid="7275582855722310164">"Kokapena"</string>
-    <string name="permgroupdesc_location" msgid="1346617465127855033">"Atzitu gailuaren kokapena"</string>
+    <string name="permgroupdesc_location" msgid="1346617465127855033">"gailuaren kokapena atzitzeko"</string>
     <string name="permgrouplab_calendar" msgid="5863508437783683902">"Egutegia"</string>
-    <string name="permgroupdesc_calendar" msgid="3889615280211184106">"Atzitu egutegia"</string>
+    <string name="permgroupdesc_calendar" msgid="3889615280211184106">"egutegia atzitzeko"</string>
     <string name="permgrouplab_sms" msgid="228308803364967808">"SMS mezuak"</string>
-    <string name="permgroupdesc_sms" msgid="4656988620100940350">"Bidali eta ikusi SMS mezuak"</string>
+    <string name="permgroupdesc_sms" msgid="4656988620100940350">"SMS mezuak bidaltzeko eta ikusteko"</string>
     <string name="permgrouplab_storage" msgid="1971118770546336966">"Memoria"</string>
-    <string name="permgroupdesc_storage" msgid="637758554581589203">"Atzitu gailuko argazkiak, multimedia-elementuak eta fitxategiak"</string>
+    <string name="permgroupdesc_storage" msgid="637758554581589203">"gailuko argazkiak, multimedia-elementuak eta fitxategiak atzitzeko"</string>
     <string name="permgrouplab_microphone" msgid="171539900250043464">"Mikrofonoa"</string>
-    <string name="permgroupdesc_microphone" msgid="4988812113943554584">"Grabatu audioa"</string>
+    <string name="permgroupdesc_microphone" msgid="4988812113943554584">"audioa grabatzeko"</string>
     <string name="permgrouplab_camera" msgid="4820372495894586615">"Kamera"</string>
-    <string name="permgroupdesc_camera" msgid="3250611594678347720">"Atera argazkiak eta grabatu bideoak"</string>
+    <string name="permgroupdesc_camera" msgid="3250611594678347720">"argazkiak ateratzeko eta bideoak grabatzeko"</string>
     <string name="permgrouplab_phone" msgid="5229115638567440675">"Telefonoa"</string>
-    <string name="permgroupdesc_phone" msgid="6234224354060641055">"Egin eta kudeatu telefono-deiak"</string>
+    <string name="permgroupdesc_phone" msgid="6234224354060641055">"telefono-deiak egiteko eta kudeatzeko"</string>
     <string name="permgrouplab_sensors" msgid="416037179223226722">"Gorputz-sentsoreak"</string>
-    <string name="permgroupdesc_sensors" msgid="7147968539346634043">"Atzitu bizi-konstanteei buruzko sentsore-datuak"</string>
+    <string name="permgroupdesc_sensors" msgid="7147968539346634043">"bizi-konstanteei buruzko sentsore-datuak atzitzeko"</string>
     <string name="capability_title_canRetrieveWindowContent" msgid="3901717936930170320">"Eskuratu leihoko edukia"</string>
     <string name="capability_desc_canRetrieveWindowContent" msgid="3772225008605310672">"Arakatu irekita daukazun leihoko edukia."</string>
     <string name="capability_title_canRequestTouchExploration" msgid="3108723364676667320">"Aktibatu ukipen bidez arakatzeko eginbidea"</string>
diff --git a/core/res/res/values-fa/strings.xml b/core/res/res/values-fa/strings.xml
index 6c03159..45d9dc3 100644
--- a/core/res/res/values-fa/strings.xml
+++ b/core/res/res/values-fa/strings.xml
@@ -144,14 +144,14 @@
     <string name="httpErrorOk" msgid="1191919378083472204">"تأیید"</string>
     <string name="httpError" msgid="7956392511146698522">"خطایی در شبکه وجود داشت."</string>
     <string name="httpErrorLookup" msgid="4711687456111963163">"‏URL پیدا نشد."</string>
-    <string name="httpErrorUnsupportedAuthScheme" msgid="6299980280442076799">"‏طرح کلی تأیید اعتبار سایت پشتیبانی نمی‎شود."</string>
+    <string name="httpErrorUnsupportedAuthScheme" msgid="6299980280442076799">"‏طرح کلی تأیید اعتبار سایت پشتیبانی نمی‌‎شود."</string>
     <string name="httpErrorAuth" msgid="1435065629438044534">"تأیید اعتبار انجام نشد."</string>
     <string name="httpErrorProxyAuth" msgid="1788207010559081331">"تأیید اعتبار از طریق سرور پروکسی انجام نشد."</string>
     <string name="httpErrorConnect" msgid="8714273236364640549">"اتصال به سرور انجام نشد."</string>
     <string name="httpErrorIO" msgid="2340558197489302188">"برقراری ارتباط با سرور ممکن نبود. بعداً دوباره امتحان کنید."</string>
     <string name="httpErrorTimeout" msgid="4743403703762883954">"زمان اتصال به سرور تمام شده است."</string>
     <string name="httpErrorRedirectLoop" msgid="8679596090392779516">"این صفحه دارای تعداد بسیار زیادی تغییر مسیر سرور است."</string>
-    <string name="httpErrorUnsupportedScheme" msgid="5015730812906192208">"‏پروتکل پشتیبانی نمی‎شود."</string>
+    <string name="httpErrorUnsupportedScheme" msgid="5015730812906192208">"‏پروتکل پشتیبانی نمی‌‎شود."</string>
     <string name="httpErrorFailedSslHandshake" msgid="96549606000658641">"اتصال امن ایجاد نشد."</string>
     <string name="httpErrorBadUrl" msgid="3636929722728881972">"‏بدلیل نامعتبر بودن URL، باز کردن صفحه ممکن نیست."</string>
     <string name="httpErrorFile" msgid="2170788515052558676">"دسترسی به فایل انجام نشد."</string>
@@ -781,13 +781,13 @@
     <string name="permdesc_writeHistoryBookmarks" product="tv" msgid="7007393823197766548">"به برنامه اجازه می‌دهد تا سابقه یا نشانک‌های ذخیره شده مرورگر در تلویزیون شما را تغییر دهد. شاید به برنامه اجازه دهد تا داده‌های «مرورگر» را پاک کند یا تغییر دهد. توجه: این مجوز شاید توسط مرورگرهای شخص ثالث یا سایر برنامه‌ها با قابلیت‌های مرور وب اجرا شود."</string>
     <string name="permdesc_writeHistoryBookmarks" product="default" msgid="8497389531014185509">"به برنامه اجازه می‌دهد سابقه مرورگر یا نشانک‌های ذخیره شده در تلفن شما را اصلاح کند. این ویژگی ممکن است به برنامه اجازه دهد داده‌های مرورگر را حذف یا اصلاح کند. توجه: این مجوز ممکن است توسط مرورگرهای شخص ثالث یا سایر برنامه‌های دارای قابلیت مرور وب قابل اجرا نباشد."</string>
     <string name="permlab_setAlarm" msgid="1379294556362091814">"تنظیم یک هشدار"</string>
-    <string name="permdesc_setAlarm" msgid="316392039157473848">"‏به برنامه اجازه می‎دهد تا هشداری را در برنامه ساعت زنگدار نصب شده تنظیم کند. برخی از برنامه‎های ساعت زنگدار نمی‎توانند این ویژگی را اعمال کنند."</string>
+    <string name="permdesc_setAlarm" msgid="316392039157473848">"‏به برنامه اجازه می‎دهد تا هشداری را در برنامه ساعت زنگدار نصب شده تنظیم کند. برخی از برنامه‎های ساعت زنگدار نمی‌‎توانند این ویژگی را اعمال کنند."</string>
     <string name="permlab_addVoicemail" msgid="5525660026090959044">"افزودن پست صوتی"</string>
     <string name="permdesc_addVoicemail" msgid="6604508651428252437">"به برنامه اجازه می‌دهد تا پیام‌ها را به صندوق دریافت پست صوتی شما اضافه کند."</string>
     <string name="permlab_writeGeolocationPermissions" msgid="5962224158955273932">"تغییر مجوزهای مکان جغرافیایی مرورگر"</string>
     <string name="permdesc_writeGeolocationPermissions" msgid="1083743234522638747">"‏به برنامه اجازه می‎دهد تا مجوزهای جغرافیایی مرورگر را تغییر دهد. برنامه‎های مخرب می‎توانند از آن استفاده کنند تا اطلاعات موقعیت مکانی را به سایت‌های وب کتابخانه بفرستند."</string>
     <string name="save_password_message" msgid="767344687139195790">"می‌خواهید مرورگر این رمز ورود را به خاطر داشته باشد؟"</string>
-    <string name="save_password_notnow" msgid="6389675316706699758">"الآن نه"</string>
+    <string name="save_password_notnow" msgid="6389675316706699758">"اکنون نه"</string>
     <string name="save_password_remember" msgid="6491879678996749466">"به خاطر سپردن"</string>
     <string name="save_password_never" msgid="8274330296785855105">"هیچ‌وقت"</string>
     <string name="open_permission_deny" msgid="7374036708316629800">"شما اجازه بازکردن این صفحه را ندارید."</string>
@@ -894,16 +894,16 @@
     <string name="clearDefaultHintMsg" msgid="3252584689512077257">"‏پیش‌فرض را در تنظیمات سیستم&gt; برنامه‎ها&gt; مورد دانلود شده پاک کنید."</string>
     <string name="chooseActivity" msgid="7486876147751803333">"انتخاب عملکرد"</string>
     <string name="chooseUsbActivity" msgid="6894748416073583509">"‏انتخاب برنامه برای دستگاه USB"</string>
-    <string name="noApplications" msgid="2991814273936504689">"‏هیچ برنامه‌ای نمی‎تواند این کار را انجام دهد."</string>
+    <string name="noApplications" msgid="2991814273936504689">"‏هیچ برنامه‌ای نمی‌‎تواند این کار را انجام دهد."</string>
     <string name="aerr_title" msgid="1905800560317137752"></string>
     <string name="aerr_application" msgid="932628488013092776">"متأسفانه، <xliff:g id="APPLICATION">%1$s</xliff:g> متوقف شده است."</string>
     <string name="aerr_process" msgid="4507058997035697579">"متأسفانه، پردازش <xliff:g id="PROCESS">%1$s</xliff:g> متوقف شده است."</string>
     <string name="aerr_process_silence" msgid="4226685530196000222">"تا را‌ه‌اندازی مجدد، خرابی‌ها از <xliff:g id="PROCESS">%1$s</xliff:g> نادیده گرفته شوند."</string>
     <string name="anr_title" msgid="4351948481459135709"></string>
-    <string name="anr_activity_application" msgid="1904477189057199066">"‏<xliff:g id="APPLICATION">%2$s</xliff:g> پاسخ نمی‎دهد.\n\nآیا می‎خواهید آنرا ببندید؟"</string>
-    <string name="anr_activity_process" msgid="5776209883299089767">"‏فعالیت <xliff:g id="ACTIVITY">%1$s</xliff:g> پاسخ نمی‎دهد.\n\nآیا می‎خواهید آن را ببندید؟"</string>
-    <string name="anr_application_process" msgid="8941757607340481057">"‏<xliff:g id="APPLICATION">%1$s</xliff:g> پاسخ نمی‎دهد. آیا می‎خواهید آن را ببندید؟"</string>
-    <string name="anr_process" msgid="6513209874880517125">"‏روند <xliff:g id="PROCESS">%1$s</xliff:g> پاسخ نمی‎دهد. \n\nآیا می‎خواهید آن را ببندید؟"</string>
+    <string name="anr_activity_application" msgid="1904477189057199066">"‏<xliff:g id="APPLICATION">%2$s</xliff:g> پاسخ نمی‌‎دهد.\n\nآیا می‎خواهید آنرا ببندید؟"</string>
+    <string name="anr_activity_process" msgid="5776209883299089767">"‏فعالیت <xliff:g id="ACTIVITY">%1$s</xliff:g> پاسخ نمی‌‎دهد.\n\nآیا می‎خواهید آن را ببندید؟"</string>
+    <string name="anr_application_process" msgid="8941757607340481057">"‏<xliff:g id="APPLICATION">%1$s</xliff:g> پاسخ نمی‌‎دهد. آیا می‎خواهید آن را ببندید؟"</string>
+    <string name="anr_process" msgid="6513209874880517125">"‏روند <xliff:g id="PROCESS">%1$s</xliff:g> پاسخ نمی‌‎دهد. \n\nآیا می‎خواهید آن را ببندید؟"</string>
     <string name="force_close" msgid="8346072094521265605">"تأیید"</string>
     <string name="report" msgid="4060218260984795706">"گزارش"</string>
     <string name="wait" msgid="7147118217226317732">"منتظر بمانید"</string>
@@ -1006,14 +1006,14 @@
     <string name="sms_short_code_confirm_never_allow" msgid="446992765774269673">"همیشه غیرمجاز"</string>
     <string name="sim_removed_title" msgid="6227712319223226185">"سیم کارت برداشته شد"</string>
     <string name="sim_removed_message" msgid="5450336489923274918">"تا زمانی که با یک سیم‌کارت معتبر دوباره راه‌اندازی نکنید شبکه تلفن همراه غیر قابل‌ دسترس خواهد بود."</string>
-    <string name="sim_done_button" msgid="827949989369963775">"انجام شد"</string>
+    <string name="sim_done_button" msgid="827949989369963775">"تمام"</string>
     <string name="sim_added_title" msgid="3719670512889674693">"سیم کارت اضافه شد"</string>
     <string name="sim_added_message" msgid="7797975656153714319">"برای دسترسی به شبکه تلفن همراه، دستگاهتان را مجدداً راه‌اندازی کنید."</string>
     <string name="sim_restart_button" msgid="4722407842815232347">"راه‌اندازی مجدد"</string>
     <string name="time_picker_dialog_title" msgid="8349362623068819295">"تنظیم زمان"</string>
     <string name="date_picker_dialog_title" msgid="5879450659453782278">"تاریخ تنظیم"</string>
     <string name="date_time_set" msgid="5777075614321087758">"تنظیم"</string>
-    <string name="date_time_done" msgid="2507683751759308828">"انجام شد"</string>
+    <string name="date_time_done" msgid="2507683751759308828">"تمام"</string>
     <string name="perms_new_perm_prefix" msgid="8257740710754301407"><font size="12" fgcolor="#ff33b5e5">"جدید: "</font></string>
     <string name="perms_description_app" msgid="5139836143293299417">"ارائه شده توسط <xliff:g id="APP_NAME">%1$s</xliff:g> ."</string>
     <string name="no_permissions" msgid="7283357728219338112">"مجوزی لازم نیست"</string>
@@ -1085,7 +1085,7 @@
     <string name="ime_action_search" msgid="658110271822807811">"جستجو"</string>
     <string name="ime_action_send" msgid="2316166556349314424">"ارسال"</string>
     <string name="ime_action_next" msgid="3138843904009813834">"بعدی"</string>
-    <string name="ime_action_done" msgid="8971516117910934605">"انجام شد"</string>
+    <string name="ime_action_done" msgid="8971516117910934605">"تمام"</string>
     <string name="ime_action_previous" msgid="1443550039250105948">"قبلی"</string>
     <string name="ime_action_default" msgid="2840921885558045721">"اجرا کردن"</string>
     <string name="dial_number_using" msgid="5789176425167573586">"شماره گیری \nبا استفاده از <xliff:g id="NUMBER">%s</xliff:g>"</string>
@@ -1131,7 +1131,7 @@
       <item quantity="one"><xliff:g id="INDEX">%d</xliff:g> از <xliff:g id="TOTAL">%d</xliff:g></item>
       <item quantity="other"><xliff:g id="INDEX">%d</xliff:g> از <xliff:g id="TOTAL">%d</xliff:g></item>
     </plurals>
-    <string name="action_mode_done" msgid="7217581640461922289">"انجام شد"</string>
+    <string name="action_mode_done" msgid="7217581640461922289">"تمام"</string>
     <string name="progress_erasing" product="nosdcard" msgid="4521573321524340058">"‏در حال پاک کردن حافظهٔ USB..."</string>
     <string name="progress_erasing" product="default" msgid="6596988875507043042">"‏در حال پاک کردن کارت SD..."</string>
     <string name="share" msgid="1778686618230011964">"اشتراک‌گذاری"</string>
@@ -1173,7 +1173,7 @@
     <string name="keyboardview_keycode_alt" msgid="4856868820040051939">"Alt"</string>
     <string name="keyboardview_keycode_cancel" msgid="1203984017245783244">"لغو"</string>
     <string name="keyboardview_keycode_delete" msgid="3337914833206635744">"Delete"</string>
-    <string name="keyboardview_keycode_done" msgid="1992571118466679775">"انجام شد"</string>
+    <string name="keyboardview_keycode_done" msgid="1992571118466679775">"تمام"</string>
     <string name="keyboardview_keycode_mode_change" msgid="4547387741906537519">"تغییر حالت"</string>
     <string name="keyboardview_keycode_shift" msgid="2270748814315147690">"Shift"</string>
     <string name="keyboardview_keycode_enter" msgid="2985864015076059467">"Enter"</string>
@@ -1416,7 +1416,7 @@
     <string name="immersive_cling_title" msgid="8394201622932303336">"مشاهده در حالت تمام صفحه"</string>
     <string name="immersive_cling_description" msgid="3482371193207536040">"برای خروج، انگشتتان را از بالای صفحه به پایین بکشید."</string>
     <string name="immersive_cling_positive" msgid="5016839404568297683">"متوجه شدم"</string>
-    <string name="done_label" msgid="2093726099505892398">"انجام شد"</string>
+    <string name="done_label" msgid="2093726099505892398">"تمام"</string>
     <string name="hour_picker_description" msgid="6698199186859736512">"لغزنده دایره‌ای ساعت"</string>
     <string name="minute_picker_description" msgid="8606010966873791190">"لغزنده دایره‌ای دقیقه"</string>
     <string name="select_hours" msgid="6043079511766008245">"انتخاب ساعت"</string>
diff --git a/core/res/res/values-gl-rES/strings.xml b/core/res/res/values-gl-rES/strings.xml
index 7b5ca29..c32add0 100644
--- a/core/res/res/values-gl-rES/strings.xml
+++ b/core/res/res/values-gl-rES/strings.xml
@@ -229,7 +229,7 @@
     <string name="permgrouplab_contacts" msgid="3657758145679177612">"Contactos"</string>
     <string name="permgroupdesc_contacts" msgid="6951499528303668046">"acceder aos teus contactos"</string>
     <string name="permgrouplab_location" msgid="7275582855722310164">"Localización"</string>
-    <string name="permgroupdesc_location" msgid="1346617465127855033">"acceso á localización deste dispositivo"</string>
+    <string name="permgroupdesc_location" msgid="1346617465127855033">"acceder á localización deste dispositivo"</string>
     <string name="permgrouplab_calendar" msgid="5863508437783683902">"Calendario"</string>
     <string name="permgroupdesc_calendar" msgid="3889615280211184106">"acceder ao teu calendario"</string>
     <string name="permgrouplab_sms" msgid="228308803364967808">"SMS"</string>
diff --git a/core/res/res/values-gu-rIN/strings.xml b/core/res/res/values-gu-rIN/strings.xml
index 9392997..6281abf 100644
--- a/core/res/res/values-gu-rIN/strings.xml
+++ b/core/res/res/values-gu-rIN/strings.xml
@@ -229,8 +229,8 @@
     <string name="permgrouplab_contacts" msgid="3657758145679177612">"સંપર્કો"</string>
     <string name="permgroupdesc_contacts" msgid="6951499528303668046">"તમારા સંપર્કોને ઍક્સેસ કરો"</string>
     <string name="permgrouplab_location" msgid="7275582855722310164">"સ્થાન"</string>
-    <string name="permgroupdesc_location" msgid="1346617465127855033">"આ ઉપકરણના સ્થાનને ઍક્સેસ કરો"</string>
-    <string name="permgrouplab_calendar" msgid="5863508437783683902">"કૅલેન્ડર"</string>
+    <string name="permgroupdesc_location" msgid="1346617465127855033">"આ ઉપકરણના સ્થાનને ઍક્સેસ કરવાની"</string>
+    <string name="permgrouplab_calendar" msgid="5863508437783683902">"કેલેન્ડર"</string>
     <string name="permgroupdesc_calendar" msgid="3889615280211184106">"તમારા કેલેન્ડરને ઍક્સેસ કરવાની"</string>
     <string name="permgrouplab_sms" msgid="228308803364967808">"SMS"</string>
     <string name="permgroupdesc_sms" msgid="4656988620100940350">"SMS સંદેશા મોકલો અને જોવાની"</string>
@@ -326,14 +326,14 @@
     <string name="permdesc_writeCallLog" product="default" msgid="683941736352787842">"એપ્લિકેશનને ઇનકમિંગ અને આઉટગોઇંગ કૉલ્સ વિશેનાં ડેટા સહિત, તમારા ફોનના કૉલ લૉગને સંશોધિત કરવાની મંજૂરી આપે છે. દુર્ભાવનાપૂર્ણ એપ્લિકેશનો આનો ઉપયોગ તમારા કૉલ લૉગને કાઢી નાખવા અથવા સંશોધિત માટે કરી શકે છે."</string>
     <string name="permlab_bodySensors" msgid="4871091374767171066">"બૉડી સેન્સર્સ (જેમ કે હાર્ટ રેટ મૉનિટર્સ)"</string>
     <string name="permdesc_bodySensors" product="default" msgid="4380015021754180431">"એપ્લિકેશનને તમારી હૃદય ગતિ જેવી તમારી શારીરિક સ્થિતિને મૉનિટર કરતાં સેન્સર્સથી ડેટા ઍક્સેસ કરવાની મંજૂરી આપે છે."</string>
-    <string name="permlab_readCalendar" msgid="5972727560257612398">"કૅલેન્ડર ઇવેન્ટ્સ વત્તા ગોપનીયતા માહિતી વાંચો"</string>
-    <string name="permdesc_readCalendar" product="tablet" msgid="4216462049057658723">"એપ્લિકેશનને મિત્રોના અથવા સહકાર્યકરો સહિત તમારા ટેબ્લેટ પર સંગ્રહિત તમામ કૅલેન્ડર ઇવેન્ટ્સ વાંચવાની મંજૂરી આપે છે. આ એપ્લિકેશનને તમારા કૅલેન્ડર ડેટાને શેર કરવા કે સાચવવાની મંજૂરી આપી શકે છે, પછી ભલે ગોપનીયતા અથવા સંવેદિતા કોઈપણ હોય."</string>
-    <string name="permdesc_readCalendar" product="tv" msgid="3191352452242394196">"એપ્લિકેશનને મિત્રોના અથવા સહકાર્યકરો સહિત તમારા ટીવી પર સંગ્રહિત તમામ કૅલેન્ડર ઇવેન્ટ્સ વાંચવાની મંજૂરી આપે છે. આ એપ્લિકેશનને તમારા કૅલેન્ડર ડેટાને શેર કરવા કે સાચવવાની મંજૂરી આપી શકે છે, પછી ભલે ગોપનીયતા અથવા સંવેદિતા કોઈપણ હોય."</string>
-    <string name="permdesc_readCalendar" product="default" msgid="7434548682470851583">"એપ્લિકેશનને મિત્રોના અથવા સહકાર્યકરો સહિત તમારા ફોન પર સંગ્રહિત તમામ કૅલેન્ડર ઇવેન્ટ્સ વાંચવાની મંજૂરી આપે છે. આ એપ્લિકેશનને તમારા કૅલેન્ડર ડેટાને શેર કરવા કે સાચવવાની મંજૂરી આપી શકે છે, પછી ભલે ગોપનીયતા અથવા સંવેદિતા કોઈપણ હોય."</string>
-    <string name="permlab_writeCalendar" msgid="8438874755193825647">"કૅલેન્ડર ઇવેન્ટ્સ ઉમેરો અથવા સંશોધિત કરો અને માલિકની જાણ બહાર અતિથિઓને ઇમેઇલ મોકલો"</string>
-    <string name="permdesc_writeCalendar" product="tablet" msgid="6679035520113668528">"એપ્લિકેશનને મિત્રોના અથવા સહકાર્યકરો સહિત તમારા ટેબ્લેટ પર તમે સંશોધિત કરી શકો તે ઇવેન્ટ્સ ઉમેરવા, દૂર કરવા, બદલવાની મંજૂરી આપે છે. આ એપ્લિકેશનને કૅલેન્ડર માલિક તરફથી આવતાં હોય તેવા સંદેશા મોકલવાની અથવા માલિકની જાણ વિના ઇવેન્ટ્સ સંશોધિત કરવાની મંજૂરી આપી શકે છે."</string>
-    <string name="permdesc_writeCalendar" product="tv" msgid="1273290605500902507">"એપ્લિકેશનને મિત્રોના અથવા સહકાર્યકરો સહિત તમારા ટીવી પર તમે સંશોધિત કરી શકો તે ઇવેન્ટ્સ ઉમેરવા, દૂર કરવા, બદલવાની મંજૂરી આપે છે. આ એપ્લિકેશનને કૅલેન્ડર માલિક તરફથી આવતાં હોય તેવા સંદેશા મોકલવાની અથવા માલિકની જાણ વિના ઇવેન્ટ્સ સંશોધિત કરવાની મંજૂરી આપી શકે છે."</string>
-    <string name="permdesc_writeCalendar" product="default" msgid="2324469496327249376">"એપ્લિકેશનને મિત્રોના અથવા સહકાર્યકરો સહિત તમારા ફોન પર તમે સંશોધિત કરી શકો તે ઇવેન્ટ્સ ઉમેરવા, દૂર કરવા, બદલવાની મંજૂરી આપે છે. આ એપ્લિકેશનને કૅલેન્ડર માલિક તરફથી આવતાં હોય તેવા સંદેશા મોકલવાની અથવા માલિકની જાણ વિના ઇવેન્ટ્સ સંશોધિત કરવાની મંજૂરી આપી શકે છે."</string>
+    <string name="permlab_readCalendar" msgid="5972727560257612398">"કેલેન્ડર  ઇવેન્ટ્સ વત્તા ગોપનીયતા માહિતી વાંચો"</string>
+    <string name="permdesc_readCalendar" product="tablet" msgid="4216462049057658723">"એપ્લિકેશનને મિત્રોના અથવા સહકાર્યકરો સહિત તમારા ટેબ્લેટ પર સંગ્રહિત તમામ કેલેન્ડર  ઇવેન્ટ્સ વાંચવાની મંજૂરી આપે છે. આ એપ્લિકેશનને તમારા કેલેન્ડર  ડેટાને શેર કરવા કે સાચવવાની મંજૂરી આપી શકે છે, પછી ભલે ગોપનીયતા અથવા સંવેદિતા કોઈપણ હોય."</string>
+    <string name="permdesc_readCalendar" product="tv" msgid="3191352452242394196">"એપ્લિકેશનને મિત્રોના અથવા સહકાર્યકરો સહિત તમારા ટીવી પર સંગ્રહિત તમામ કેલેન્ડર  ઇવેન્ટ્સ વાંચવાની મંજૂરી આપે છે. આ એપ્લિકેશનને તમારા કેલેન્ડર  ડેટાને શેર કરવા કે સાચવવાની મંજૂરી આપી શકે છે, પછી ભલે ગોપનીયતા અથવા સંવેદિતા કોઈપણ હોય."</string>
+    <string name="permdesc_readCalendar" product="default" msgid="7434548682470851583">"એપ્લિકેશનને મિત્રોના અથવા સહકાર્યકરો સહિત તમારા ફોન પર સંગ્રહિત તમામ કેલેન્ડર  ઇવેન્ટ્સ વાંચવાની મંજૂરી આપે છે. આ એપ્લિકેશનને તમારા કેલેન્ડર  ડેટાને શેર કરવા કે સાચવવાની મંજૂરી આપી શકે છે, પછી ભલે ગોપનીયતા અથવા સંવેદિતા કોઈપણ હોય."</string>
+    <string name="permlab_writeCalendar" msgid="8438874755193825647">"કેલેન્ડર  ઇવેન્ટ્સ ઉમેરો અથવા સંશોધિત કરો અને માલિકની જાણ બહાર અતિથિઓને ઇમેઇલ મોકલો"</string>
+    <string name="permdesc_writeCalendar" product="tablet" msgid="6679035520113668528">"એપ્લિકેશનને મિત્રોના અથવા સહકાર્યકરો સહિત તમારા ટેબ્લેટ પર તમે સંશોધિત કરી શકો તે ઇવેન્ટ્સ ઉમેરવા, દૂર કરવા, બદલવાની મંજૂરી આપે છે. આ એપ્લિકેશનને કેલેન્ડર  માલિક તરફથી આવતાં હોય તેવા સંદેશા મોકલવાની અથવા માલિકની જાણ વિના ઇવેન્ટ્સ સંશોધિત કરવાની મંજૂરી આપી શકે છે."</string>
+    <string name="permdesc_writeCalendar" product="tv" msgid="1273290605500902507">"એપ્લિકેશનને મિત્રોના અથવા સહકાર્યકરો સહિત તમારા ટીવી પર તમે સંશોધિત કરી શકો તે ઇવેન્ટ્સ ઉમેરવા, દૂર કરવા, બદલવાની મંજૂરી આપે છે. આ એપ્લિકેશનને કેલેન્ડર  માલિક તરફથી આવતાં હોય તેવા સંદેશા મોકલવાની અથવા માલિકની જાણ વિના ઇવેન્ટ્સ સંશોધિત કરવાની મંજૂરી આપી શકે છે."</string>
+    <string name="permdesc_writeCalendar" product="default" msgid="2324469496327249376">"એપ્લિકેશનને મિત્રોના અથવા સહકાર્યકરો સહિત તમારા ફોન પર તમે સંશોધિત કરી શકો તે ઇવેન્ટ્સ ઉમેરવા, દૂર કરવા, બદલવાની મંજૂરી આપે છે. આ એપ્લિકેશનને કેલેન્ડર  માલિક તરફથી આવતાં હોય તેવા સંદેશા મોકલવાની અથવા માલિકની જાણ વિના ઇવેન્ટ્સ સંશોધિત કરવાની મંજૂરી આપી શકે છે."</string>
     <string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"વધારાના સ્થાન પ્રદાતા આદેશોને ઍક્સેસ કરો"</string>
     <string name="permdesc_accessLocationExtraCommands" msgid="6078307221056649927">"એપ્લિકેશનને વધારાના સ્થાન પ્રદાતા આદેશોને ઍક્સેસ કરવાની મંજૂરી આપે છે. આ એપ્લિકેશનને GPS અથવા અન્ય સ્થાન સ્રોતોના ઓપરેશનમાં દખલ કરવાની મંજૂરી આપી શકે છે."</string>
     <string name="permlab_accessFineLocation" msgid="1191898061965273372">"નિશ્ચિત સ્થાન (GPS અને નેટવર્ક-આધારિત)"</string>
diff --git a/core/res/res/values-it-watch/strings.xml b/core/res/res/values-it-watch/strings.xml
index c348e48..aa6a4be 100644
--- a/core/res/res/values-it-watch/strings.xml
+++ b/core/res/res/values-it-watch/strings.xml
@@ -29,7 +29,7 @@
     <string name="permgrouplab_storagewear" msgid="1003807594193602313">"accedere a foto, contenuti multimediali e file sull\'orologio"</string>
     <string name="permgrouplab_microphonewear" msgid="1047561180980891136">"registrare audio"</string>
     <string name="permgrouplab_camerawear" msgid="4543951283103407017">"scattare foto e registrare video"</string>
-    <string name="permgrouplab_phonewear" msgid="134365036753766126">"fare e gestire telefonate"</string>
+    <string name="permgrouplab_phonewear" msgid="134365036753766126">"eseguire e gestire le telefonate"</string>
     <string name="permgrouplab_sensorswear" msgid="1429324744329327663">"accedere ai dati dei sensori relativi ai tuoi parametri vitali"</string>
     <string name="permlab_statusBarServicewear" msgid="2469402818964691034">"avere la funzione di barra di stato"</string>
     <string name="permlab_bodySensorswear" msgid="7857941041202791873">"accedere ai sensori (come il cardiofrequenzimetro)"</string>
diff --git a/core/res/res/values-it/strings.xml b/core/res/res/values-it/strings.xml
index 4271287..72b1c54 100644
--- a/core/res/res/values-it/strings.xml
+++ b/core/res/res/values-it/strings.xml
@@ -239,11 +239,11 @@
     <string name="permgrouplab_microphone" msgid="171539900250043464">"Microfono"</string>
     <string name="permgroupdesc_microphone" msgid="4988812113943554584">"registrare audio"</string>
     <string name="permgrouplab_camera" msgid="4820372495894586615">"Fotocamera"</string>
-    <string name="permgroupdesc_camera" msgid="3250611594678347720">"acquisire foto e registrare video"</string>
+    <string name="permgroupdesc_camera" msgid="3250611594678347720">"scattare foto e registrare video"</string>
     <string name="permgrouplab_phone" msgid="5229115638567440675">"Telefono"</string>
     <string name="permgroupdesc_phone" msgid="6234224354060641055">"eseguire e gestire le telefonate"</string>
     <string name="permgrouplab_sensors" msgid="416037179223226722">"Sensori per il corpo"</string>
-    <string name="permgroupdesc_sensors" msgid="7147968539346634043">"accedere ai dati dei sensori sui tuoi parametri vitali"</string>
+    <string name="permgroupdesc_sensors" msgid="7147968539346634043">"accedere ai dati dei sensori relativi ai tuoi parametri vitali"</string>
     <string name="capability_title_canRetrieveWindowContent" msgid="3901717936930170320">"Recuperare contenuti della finestra"</string>
     <string name="capability_desc_canRetrieveWindowContent" msgid="3772225008605310672">"Esaminare i contenuti di una finestra con cui interagisci."</string>
     <string name="capability_title_canRequestTouchExploration" msgid="3108723364676667320">"Attivare Esplora al tocco"</string>
@@ -272,7 +272,7 @@
     <string name="permdesc_readCellBroadcasts" msgid="6361972776080458979">"Consente all\'applicazione di leggere i messaggi cell broadcast ricevuti dal dispositivo. Gli avvisi cell broadcast vengono trasmessi in alcune località per avvertire di eventuali situazioni di emergenza. Le applicazioni dannose potrebbero interferire con il rendimento o con il funzionamento del dispositivo quando si riceve un messaggio cell broadcast di emergenza."</string>
     <string name="permlab_subscribedFeedsRead" msgid="4756609637053353318">"lettura feed sottoscritti"</string>
     <string name="permdesc_subscribedFeedsRead" msgid="5557058907906144505">"Consente all\'applicazione di ottenere dettagli sui feed attualmente sincronizzati."</string>
-    <string name="permlab_sendSms" msgid="7544599214260982981">"invio e lettura di SMS"</string>
+    <string name="permlab_sendSms" msgid="7544599214260982981">"inviare e visualizzare SMS"</string>
     <string name="permdesc_sendSms" msgid="7094729298204937667">"Consente all\'applicazione di inviare messaggi SMS. Ciò potrebbe comportare costi imprevisti. Applicazioni dannose potrebbero generare dei costi inviando messaggi senza la tua conferma."</string>
     <string name="permlab_readSms" msgid="8745086572213270480">"lettura messaggi di testo personali (SMS o MMS)"</string>
     <string name="permdesc_readSms" product="tablet" msgid="2467981548684735522">"Consente all\'applicazione di leggere i messaggi SMS memorizzati sul tablet o sulla scheda SIM. Ciò consente all\'applicazione di leggere tutti i messaggi SMS, indipendentemente dai contenuti o dal livello di riservatezza."</string>
@@ -342,7 +342,7 @@
     <string name="permdesc_accessCoarseLocation" msgid="2538200184373302295">"Consente all\'applicazione di ottenere la tua posizione approssimativa. Questa posizione viene ottenuta da servizi di localizzazione utilizzando fonti di geolocalizzazione delle reti come ripetitori di telefonia mobile e Wi-Fi. Questi servizi di localizzazione devono essere attivi e disponibili sul dispositivo per poter essere utilizzati dall\'applicazione. Le applicazioni potrebbero utilizzare questa autorizzazione per stabilire la tua posizione approssimativa."</string>
     <string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"modifica impostazioni audio"</string>
     <string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"Consente all\'applicazione di modificare le impostazioni audio globali, come il volume e quale altoparlante viene utilizzato per l\'uscita."</string>
-    <string name="permlab_recordAudio" msgid="3876049771427466323">"registrazione audio"</string>
+    <string name="permlab_recordAudio" msgid="3876049771427466323">"registrare audio"</string>
     <string name="permdesc_recordAudio" msgid="4906839301087980680">"Consente all\'applicazione di registrare audio con il microfono. Questa autorizzazione consente all\'applicazione di registrare audio in qualsiasi momento senza la tua conferma."</string>
     <string name="permlab_sim_communication" msgid="1180265879464893029">"comunicazione SIM"</string>
     <string name="permdesc_sim_communication" msgid="5725159654279639498">"Consente all\'app di inviare comandi alla SIM. Questo è molto pericoloso."</string>
diff --git a/core/res/res/values-ja-watch/strings.xml b/core/res/res/values-ja-watch/strings.xml
index 3248041..82c3ffd 100644
--- a/core/res/res/values-ja-watch/strings.xml
+++ b/core/res/res/values-ja-watch/strings.xml
@@ -37,7 +37,7 @@
     <string name="permlab_accessCoarseLocationwear" msgid="5880746016230166090">"おおよその位置情報(ネットワーク基地局)へのアクセス"</string>
     <string name="permlab_sim_communicationwear" msgid="1899198085342781874">"SIMへのコマンド送信"</string>
     <string name="permlab_createNetworkSocketswear" msgid="6467042386273822913">"ネットワークへのフルアクセス"</string>
-    <string name="permlab_manageProfileAndDeviceOwnerswear" msgid="7313340516937821847">"プロフィールの所有者と端末の所有者の管理"</string>
+    <string name="permlab_manageProfileAndDeviceOwnerswear" msgid="7313340516937821847">"プロファイルの所有者と端末の所有者の管理"</string>
     <string name="permlab_changeWimaxStatewear" msgid="3828470843939853744">"WiMAX状態の変更"</string>
     <string name="permlab_handoverStatuswear" msgid="4835786819716499249">"Androidビーム転送のステータスの受信"</string>
     <string name="permlab_route_media_outputwear" msgid="8737024341474587192">"メディア出力のルーティング"</string>
diff --git a/core/res/res/values-ja/strings.xml b/core/res/res/values-ja/strings.xml
index 545b963..135c7ea 100644
--- a/core/res/res/values-ja/strings.xml
+++ b/core/res/res/values-ja/strings.xml
@@ -282,8 +282,8 @@
     <string name="permdesc_receiveWapPush" msgid="748232190220583385">"WAPメッセージの受信と処理をアプリに許可します。これにより、アプリが端末に届いたメッセージを表示することなく監視または削除できるようになります。"</string>
     <string name="permlab_getTasks" msgid="6466095396623933906">"実行中のアプリの取得"</string>
     <string name="permdesc_getTasks" msgid="7454215995847658102">"現在実行中または最近実行したタスクに関する情報の取得をアプリに許可します。これにより、その端末でどのアプリを使用しているかをアプリから識別できるようになる可能性があります。"</string>
-    <string name="permlab_manageProfileAndDeviceOwners" msgid="5979288447973722097">"プロフィールの所有者と端末の所有者の管理"</string>
-    <string name="permdesc_manageProfileAndDeviceOwners" msgid="106894851498657169">"プロフィールの所有者と端末の所有者の設定をアプリに許可します。"</string>
+    <string name="permlab_manageProfileAndDeviceOwners" msgid="5979288447973722097">"プロファイルの所有者と端末の所有者の管理"</string>
+    <string name="permdesc_manageProfileAndDeviceOwners" msgid="106894851498657169">"プロファイルの所有者と端末の所有者の設定をアプリに許可します。"</string>
     <string name="permlab_reorderTasks" msgid="2018575526934422779">"実行中のアプリの順序変更"</string>
     <string name="permdesc_reorderTasks" msgid="7734217754877439351">"タスクをフォアグラウンドやバックグラウンドに移動することをアプリに許可します。これにより、アプリがユーザーからの入力なしでこの処理を実行する可能性があります。"</string>
     <string name="permlab_enableCarMode" msgid="5684504058192921098">"運転モードの有効化"</string>
diff --git a/core/res/res/values-kk-rKZ/strings.xml b/core/res/res/values-kk-rKZ/strings.xml
index d39ee05..7af4df0 100644
--- a/core/res/res/values-kk-rKZ/strings.xml
+++ b/core/res/res/values-kk-rKZ/strings.xml
@@ -229,7 +229,7 @@
     <string name="permgrouplab_contacts" msgid="3657758145679177612">"Контактілер"</string>
     <string name="permgroupdesc_contacts" msgid="6951499528303668046">"контактілерге кіру"</string>
     <string name="permgrouplab_location" msgid="7275582855722310164">"Орын"</string>
-    <string name="permgroupdesc_location" msgid="1346617465127855033">"бұл құрылғының орнына кіру"</string>
+    <string name="permgroupdesc_location" msgid="1346617465127855033">"бұл құрылғының орналасқан жерін көру"</string>
     <string name="permgrouplab_calendar" msgid="5863508437783683902">"Күнтізбе"</string>
     <string name="permgroupdesc_calendar" msgid="3889615280211184106">"күнтізбеге кіру"</string>
     <string name="permgrouplab_sms" msgid="228308803364967808">"SMS"</string>
diff --git a/core/res/res/values-ky-rKG/strings.xml b/core/res/res/values-ky-rKG/strings.xml
index e7e43f0..f20551d 100644
--- a/core/res/res/values-ky-rKG/strings.xml
+++ b/core/res/res/values-ky-rKG/strings.xml
@@ -229,7 +229,7 @@
     <string name="permgrouplab_contacts" msgid="3657758145679177612">"Байланыштар"</string>
     <string name="permgroupdesc_contacts" msgid="6951499528303668046">"байланыштарыңызга уруксат"</string>
     <string name="permgrouplab_location" msgid="7275582855722310164">"Жайгашкан жер"</string>
-    <string name="permgroupdesc_location" msgid="1346617465127855033">"бул түзмөктүн жайгашкан жерине кирүү"</string>
+    <string name="permgroupdesc_location" msgid="1346617465127855033">"бул түзмөктүн жайгашкан жери тууралуу дайындарды көрүү"</string>
     <string name="permgrouplab_calendar" msgid="5863508437783683902">"Күнбарак"</string>
     <string name="permgroupdesc_calendar" msgid="3889615280211184106">"жылнаамаңызды пайдалануу"</string>
     <string name="permgrouplab_sms" msgid="228308803364967808">"SMS"</string>
diff --git a/core/res/res/values-mcc240-mnc01/config.xml b/core/res/res/values-mcc240-mnc01/config.xml
new file mode 100644
index 0000000..7fb5c46
--- /dev/null
+++ b/core/res/res/values-mcc240-mnc01/config.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 2013, 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 my 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.
+*/
+-->
+
+<!-- These resources are around just to allow their values to be customized
+     for different hardware and product builds. -->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+
+    <!-- Do not set the system language as value of EF LI/EF PL -->
+    <bool name="config_use_sim_language_file">false</bool>
+
+</resources>
diff --git a/core/res/res/values-mcc240-mnc05/config.xml b/core/res/res/values-mcc240-mnc05/config.xml
new file mode 100644
index 0000000..7fb5c46
--- /dev/null
+++ b/core/res/res/values-mcc240-mnc05/config.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 2013, 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 my 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.
+*/
+-->
+
+<!-- These resources are around just to allow their values to be customized
+     for different hardware and product builds. -->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+
+    <!-- Do not set the system language as value of EF LI/EF PL -->
+    <bool name="config_use_sim_language_file">false</bool>
+
+</resources>
diff --git a/core/res/res/values-mk-rMK/strings.xml b/core/res/res/values-mk-rMK/strings.xml
index f10fb19..a41be56 100644
--- a/core/res/res/values-mk-rMK/strings.xml
+++ b/core/res/res/values-mk-rMK/strings.xml
@@ -227,23 +227,23 @@
     <string name="user_owner_label" msgid="2804351898001038951">"Лични"</string>
     <string name="managed_profile_label" msgid="6260850669674791528">"Работа"</string>
     <string name="permgrouplab_contacts" msgid="3657758145679177612">"Контакти"</string>
-    <string name="permgroupdesc_contacts" msgid="6951499528303668046">"пристапи до контактите"</string>
+    <string name="permgroupdesc_contacts" msgid="6951499528303668046">"пристапува до контактите"</string>
     <string name="permgrouplab_location" msgid="7275582855722310164">"Локација"</string>
-    <string name="permgroupdesc_location" msgid="1346617465127855033">"да пристапува до локацијата на овој уред"</string>
+    <string name="permgroupdesc_location" msgid="1346617465127855033">"пристапува до локацијата на овој уред"</string>
     <string name="permgrouplab_calendar" msgid="5863508437783683902">"Календар"</string>
-    <string name="permgroupdesc_calendar" msgid="3889615280211184106">"пристапи до календарот"</string>
+    <string name="permgroupdesc_calendar" msgid="3889615280211184106">"пристапува до календарот"</string>
     <string name="permgrouplab_sms" msgid="228308803364967808">"СМС"</string>
     <string name="permgroupdesc_sms" msgid="4656988620100940350">"испраќа и прикажува СМС-пораки"</string>
     <string name="permgrouplab_storage" msgid="1971118770546336966">"Меморија"</string>
-    <string name="permgroupdesc_storage" msgid="637758554581589203">"пристапува до фотографии, медиуми и датотеки на уредот"</string>
+    <string name="permgroupdesc_storage" msgid="637758554581589203">"пристапува до фотографии, аудио-видео и датотеки на уредот"</string>
     <string name="permgrouplab_microphone" msgid="171539900250043464">"Микрофон"</string>
-    <string name="permgroupdesc_microphone" msgid="4988812113943554584">"снимај аудио"</string>
+    <string name="permgroupdesc_microphone" msgid="4988812113943554584">"снима аудио"</string>
     <string name="permgrouplab_camera" msgid="4820372495894586615">"Фотоапарат"</string>
-    <string name="permgroupdesc_camera" msgid="3250611594678347720">"фотографирај и снимај видео"</string>
+    <string name="permgroupdesc_camera" msgid="3250611594678347720">"фотографира и снима видео"</string>
     <string name="permgrouplab_phone" msgid="5229115638567440675">"Телефон"</string>
-    <string name="permgroupdesc_phone" msgid="6234224354060641055">"повикувај и управувај со телефонски повици"</string>
+    <string name="permgroupdesc_phone" msgid="6234224354060641055">"упатува и управува со телефонски повици"</string>
     <string name="permgrouplab_sensors" msgid="416037179223226722">"Телесни сензори"</string>
-    <string name="permgroupdesc_sensors" msgid="7147968539346634043">"пристапи до податоците од сензорите за виталните знаци"</string>
+    <string name="permgroupdesc_sensors" msgid="7147968539346634043">"пристапува до податоците од сензорите за виталните знаци"</string>
     <string name="capability_title_canRetrieveWindowContent" msgid="3901717936930170320">"Врати содржина на прозорец"</string>
     <string name="capability_desc_canRetrieveWindowContent" msgid="3772225008605310672">"Провери ја содржината на прозорецот со кој се комуницира."</string>
     <string name="capability_title_canRequestTouchExploration" msgid="3108723364676667320">"Вклучи „Истражувај со допир“"</string>
diff --git a/core/res/res/values-my-rMM/strings.xml b/core/res/res/values-my-rMM/strings.xml
index a547ca7..41528d8 100644
--- a/core/res/res/values-my-rMM/strings.xml
+++ b/core/res/res/values-my-rMM/strings.xml
@@ -229,7 +229,7 @@
     <string name="permgrouplab_contacts" msgid="3657758145679177612">"အဆက်အသွယ်များ"</string>
     <string name="permgroupdesc_contacts" msgid="6951499528303668046">"သင့် အဆက်အသွယ်များအား ဝင်ရောက်သုံးရန်"</string>
     <string name="permgrouplab_location" msgid="7275582855722310164">"တည်နေရာ"</string>
-    <string name="permgroupdesc_location" msgid="1346617465127855033">"စက်ပစ္စည်း၏ တည်နေရာကို အသုံးပြုမည်"</string>
+    <string name="permgroupdesc_location" msgid="1346617465127855033">"ဤစက်ပစ္စည်း၏ တည်နေရာကို ရယူ"</string>
     <string name="permgrouplab_calendar" msgid="5863508437783683902">"ပြက္ခဒိန်"</string>
     <string name="permgroupdesc_calendar" msgid="3889615280211184106">"သင့်ပြက္ခဒိန်အား ဝင်ရောက်သုံးရန်"</string>
     <string name="permgrouplab_sms" msgid="228308803364967808">"စာတိုစနစ်"</string>
diff --git a/core/res/res/values-pt-rBR/strings.xml b/core/res/res/values-pt-rBR/strings.xml
index d203a6a..2f6c8bf 100644
--- a/core/res/res/values-pt-rBR/strings.xml
+++ b/core/res/res/values-pt-rBR/strings.xml
@@ -227,23 +227,23 @@
     <string name="user_owner_label" msgid="2804351898001038951">"Pessoal"</string>
     <string name="managed_profile_label" msgid="6260850669674791528">"Trabalho"</string>
     <string name="permgrouplab_contacts" msgid="3657758145679177612">"Contatos"</string>
-    <string name="permgroupdesc_contacts" msgid="6951499528303668046">"acessar seus contatos"</string>
+    <string name="permgroupdesc_contacts" msgid="6951499528303668046">"acesse seus contatos"</string>
     <string name="permgrouplab_location" msgid="7275582855722310164">"Local"</string>
     <string name="permgroupdesc_location" msgid="1346617465127855033">"acesse o local do dispositivo"</string>
     <string name="permgrouplab_calendar" msgid="5863508437783683902">"Agenda"</string>
-    <string name="permgroupdesc_calendar" msgid="3889615280211184106">"acessar sua agenda"</string>
+    <string name="permgroupdesc_calendar" msgid="3889615280211184106">"acesse sua agenda"</string>
     <string name="permgrouplab_sms" msgid="228308803364967808">"SMS"</string>
     <string name="permgroupdesc_sms" msgid="4656988620100940350">"enviar e ver mensagens SMS"</string>
     <string name="permgrouplab_storage" msgid="1971118770546336966">"Armazenamento"</string>
-    <string name="permgroupdesc_storage" msgid="637758554581589203">"acesse fotos, mídia e arquivos do seu dispositivo"</string>
+    <string name="permgroupdesc_storage" msgid="637758554581589203">"acesse fotos, mídia e arquivos do dispositivo"</string>
     <string name="permgrouplab_microphone" msgid="171539900250043464">"Microfone"</string>
     <string name="permgroupdesc_microphone" msgid="4988812113943554584">"grave áudio"</string>
     <string name="permgrouplab_camera" msgid="4820372495894586615">"Câmera"</string>
     <string name="permgroupdesc_camera" msgid="3250611594678347720">"tire fotos e grave vídeos"</string>
     <string name="permgrouplab_phone" msgid="5229115638567440675">"Telefone"</string>
-    <string name="permgroupdesc_phone" msgid="6234224354060641055">"fazer e gerenciar chamadas telefônicas"</string>
+    <string name="permgroupdesc_phone" msgid="6234224354060641055">"faça e gerencie chamadas telefônicas"</string>
     <string name="permgrouplab_sensors" msgid="416037179223226722">"Sensores corporais"</string>
-    <string name="permgroupdesc_sensors" msgid="7147968539346634043">"acessar dados do sensor sobre seus sinais vitais"</string>
+    <string name="permgroupdesc_sensors" msgid="7147968539346634043">"acesse dados do sensor sobre seus sinais vitais"</string>
     <string name="capability_title_canRetrieveWindowContent" msgid="3901717936930170320">"Recuperar cont. da janela"</string>
     <string name="capability_desc_canRetrieveWindowContent" msgid="3772225008605310672">"Inspecionar o conteúdo da janela com que você está interagindo."</string>
     <string name="capability_title_canRequestTouchExploration" msgid="3108723364676667320">"Ativar Explorar por toque"</string>
@@ -272,7 +272,7 @@
     <string name="permdesc_readCellBroadcasts" msgid="6361972776080458979">"Permite que o app leia mensagens de difusão celular recebidas por seu dispositivo. Alertas de difusão celular são recebidos em alguns locais para avisar você de situações de emergência. Apps maliciosos podem interferir no desempenho ou funcionamento de seu dispositivo quando uma difusão celular de emergência é recebida."</string>
     <string name="permlab_subscribedFeedsRead" msgid="4756609637053353318">"ler feeds inscritos"</string>
     <string name="permdesc_subscribedFeedsRead" msgid="5557058907906144505">"Permite que o app obtenha detalhes sobre os feeds sincronizados no momento."</string>
-    <string name="permlab_sendSms" msgid="7544599214260982981">"enviar e ver mensagens SMS"</string>
+    <string name="permlab_sendSms" msgid="7544599214260982981">"envie e veja mensagens SMS"</string>
     <string name="permdesc_sendSms" msgid="7094729298204937667">"Permite que o app envie mensagens SMS. Isso pode resultar em cobranças inesperadas. Apps maliciosos podem gerar custos através do envio de mensagens sem sua confirmação."</string>
     <string name="permlab_readSms" msgid="8745086572213270480">"ler suas mensagens de texto (SMS ou MMS)"</string>
     <string name="permdesc_readSms" product="tablet" msgid="2467981548684735522">"Permite que o app leia mensagens SMS armazenadas no tablet ou cartão SIM. Isso permite que o app leia todas as mensagens SMS, independentemente de seu conteúdo ou confidencialidade."</string>
diff --git a/core/res/res/values-pt/strings.xml b/core/res/res/values-pt/strings.xml
index d203a6a..2f6c8bf 100644
--- a/core/res/res/values-pt/strings.xml
+++ b/core/res/res/values-pt/strings.xml
@@ -227,23 +227,23 @@
     <string name="user_owner_label" msgid="2804351898001038951">"Pessoal"</string>
     <string name="managed_profile_label" msgid="6260850669674791528">"Trabalho"</string>
     <string name="permgrouplab_contacts" msgid="3657758145679177612">"Contatos"</string>
-    <string name="permgroupdesc_contacts" msgid="6951499528303668046">"acessar seus contatos"</string>
+    <string name="permgroupdesc_contacts" msgid="6951499528303668046">"acesse seus contatos"</string>
     <string name="permgrouplab_location" msgid="7275582855722310164">"Local"</string>
     <string name="permgroupdesc_location" msgid="1346617465127855033">"acesse o local do dispositivo"</string>
     <string name="permgrouplab_calendar" msgid="5863508437783683902">"Agenda"</string>
-    <string name="permgroupdesc_calendar" msgid="3889615280211184106">"acessar sua agenda"</string>
+    <string name="permgroupdesc_calendar" msgid="3889615280211184106">"acesse sua agenda"</string>
     <string name="permgrouplab_sms" msgid="228308803364967808">"SMS"</string>
     <string name="permgroupdesc_sms" msgid="4656988620100940350">"enviar e ver mensagens SMS"</string>
     <string name="permgrouplab_storage" msgid="1971118770546336966">"Armazenamento"</string>
-    <string name="permgroupdesc_storage" msgid="637758554581589203">"acesse fotos, mídia e arquivos do seu dispositivo"</string>
+    <string name="permgroupdesc_storage" msgid="637758554581589203">"acesse fotos, mídia e arquivos do dispositivo"</string>
     <string name="permgrouplab_microphone" msgid="171539900250043464">"Microfone"</string>
     <string name="permgroupdesc_microphone" msgid="4988812113943554584">"grave áudio"</string>
     <string name="permgrouplab_camera" msgid="4820372495894586615">"Câmera"</string>
     <string name="permgroupdesc_camera" msgid="3250611594678347720">"tire fotos e grave vídeos"</string>
     <string name="permgrouplab_phone" msgid="5229115638567440675">"Telefone"</string>
-    <string name="permgroupdesc_phone" msgid="6234224354060641055">"fazer e gerenciar chamadas telefônicas"</string>
+    <string name="permgroupdesc_phone" msgid="6234224354060641055">"faça e gerencie chamadas telefônicas"</string>
     <string name="permgrouplab_sensors" msgid="416037179223226722">"Sensores corporais"</string>
-    <string name="permgroupdesc_sensors" msgid="7147968539346634043">"acessar dados do sensor sobre seus sinais vitais"</string>
+    <string name="permgroupdesc_sensors" msgid="7147968539346634043">"acesse dados do sensor sobre seus sinais vitais"</string>
     <string name="capability_title_canRetrieveWindowContent" msgid="3901717936930170320">"Recuperar cont. da janela"</string>
     <string name="capability_desc_canRetrieveWindowContent" msgid="3772225008605310672">"Inspecionar o conteúdo da janela com que você está interagindo."</string>
     <string name="capability_title_canRequestTouchExploration" msgid="3108723364676667320">"Ativar Explorar por toque"</string>
@@ -272,7 +272,7 @@
     <string name="permdesc_readCellBroadcasts" msgid="6361972776080458979">"Permite que o app leia mensagens de difusão celular recebidas por seu dispositivo. Alertas de difusão celular são recebidos em alguns locais para avisar você de situações de emergência. Apps maliciosos podem interferir no desempenho ou funcionamento de seu dispositivo quando uma difusão celular de emergência é recebida."</string>
     <string name="permlab_subscribedFeedsRead" msgid="4756609637053353318">"ler feeds inscritos"</string>
     <string name="permdesc_subscribedFeedsRead" msgid="5557058907906144505">"Permite que o app obtenha detalhes sobre os feeds sincronizados no momento."</string>
-    <string name="permlab_sendSms" msgid="7544599214260982981">"enviar e ver mensagens SMS"</string>
+    <string name="permlab_sendSms" msgid="7544599214260982981">"envie e veja mensagens SMS"</string>
     <string name="permdesc_sendSms" msgid="7094729298204937667">"Permite que o app envie mensagens SMS. Isso pode resultar em cobranças inesperadas. Apps maliciosos podem gerar custos através do envio de mensagens sem sua confirmação."</string>
     <string name="permlab_readSms" msgid="8745086572213270480">"ler suas mensagens de texto (SMS ou MMS)"</string>
     <string name="permdesc_readSms" product="tablet" msgid="2467981548684735522">"Permite que o app leia mensagens SMS armazenadas no tablet ou cartão SIM. Isso permite que o app leia todas as mensagens SMS, independentemente de seu conteúdo ou confidencialidade."</string>
diff --git a/core/res/res/values-ro-watch/strings.xml b/core/res/res/values-ro-watch/strings.xml
index e412cad..9a8e4e0 100644
--- a/core/res/res/values-ro-watch/strings.xml
+++ b/core/res/res/values-ro-watch/strings.xml
@@ -22,15 +22,15 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="android_upgrading_apk" msgid="1090732262010398759">"Aplic. <xliff:g id="NUMBER_0">%1$d</xliff:g> din <xliff:g id="NUMBER_1">%2$d</xliff:g>."</string>
     <string name="permgrouplab_sensors" msgid="202675452368612754">"Senzori"</string>
-    <string name="permgrouplab_contactswear" msgid="2340286500790908344">"să acceseze persoanele de contact"</string>
+    <string name="permgrouplab_contactswear" msgid="2340286500790908344">"acceseze persoanele de contact"</string>
     <string name="permgrouplab_locationwear" msgid="6275317222482780209">"să acceseze locația acestui ceas"</string>
-    <string name="permgrouplab_calendarwear" msgid="441900844045065081">"să acceseze calendarul"</string>
-    <string name="permgrouplab_smswear" msgid="6849506550342974220">"să trimită și să vadă mesajele SMS"</string>
+    <string name="permgrouplab_calendarwear" msgid="441900844045065081">"acceseze calendarul"</string>
+    <string name="permgrouplab_smswear" msgid="6849506550342974220">"trimită și să vadă mesajele SMS"</string>
     <string name="permgrouplab_storagewear" msgid="1003807594193602313">"să acceseze fotografiile, conținutul media și fișierele de pe ceas"</string>
-    <string name="permgrouplab_microphonewear" msgid="1047561180980891136">"să înregistreze conținut audio"</string>
-    <string name="permgrouplab_camerawear" msgid="4543951283103407017">"să fotografieze și să înregistreze videoclipuri"</string>
-    <string name="permgrouplab_phonewear" msgid="134365036753766126">"să inițieze să și gestioneze apeluri telefonice"</string>
-    <string name="permgrouplab_sensorswear" msgid="1429324744329327663">"să acceseze datele de la senzori despre semnele vitale"</string>
+    <string name="permgrouplab_microphonewear" msgid="1047561180980891136">"înregistreze sunet"</string>
+    <string name="permgrouplab_camerawear" msgid="4543951283103407017">"fotografieze și să înregistreze imagini"</string>
+    <string name="permgrouplab_phonewear" msgid="134365036753766126">"inițieze să și gestioneze apeluri telefonice"</string>
+    <string name="permgrouplab_sensorswear" msgid="1429324744329327663">"acceseze datele de la senzori despre semnele vitale"</string>
     <string name="permlab_statusBarServicewear" msgid="2469402818964691034">"să fie bara de stare"</string>
     <string name="permlab_bodySensorswear" msgid="7857941041202791873">"să acceseze senzorii corporali (cum ar fi monitoarele cardiace)"</string>
     <string name="permlab_accessFineLocationwear" msgid="5584423486924377563">"să acceseze locația exactă (bazată pe GPS și pe rețea)"</string>
diff --git a/core/res/res/values-ro/strings.xml b/core/res/res/values-ro/strings.xml
index d215d4c..5062e76 100644
--- a/core/res/res/values-ro/strings.xml
+++ b/core/res/res/values-ro/strings.xml
@@ -135,7 +135,7 @@
     <string name="wfc_mode_wifi_preferred_summary" msgid="1994113411286935263">"Se preferă conexiunea Wi-Fi"</string>
     <string name="wfc_mode_cellular_preferred_summary" msgid="5920549484600758786">"Se preferă conexiunea mobilă"</string>
     <string name="wfc_mode_wifi_only_summary" msgid="2379919155237869320">"Numai Wi-Fi"</string>
-    <string name="cfTemplateNotForwarded" msgid="1683685883841272560">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: neredirecţionată"</string>
+    <string name="cfTemplateNotForwarded" msgid="1683685883841272560">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: neredirecționată"</string>
     <string name="cfTemplateForwarded" msgid="1302922117498590521">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
     <string name="cfTemplateForwardedTime" msgid="9206251736527085256">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> după <xliff:g id="TIME_DELAY">{2}</xliff:g> (de) secunde"</string>
     <string name="cfTemplateRegistered" msgid="5073237827620166285">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: neredirecţionat"</string>
@@ -232,19 +232,19 @@
     <string name="permgrouplab_location" msgid="7275582855722310164">"Locație"</string>
     <string name="permgroupdesc_location" msgid="1346617465127855033">"acceseze locația acestui dispozitiv"</string>
     <string name="permgrouplab_calendar" msgid="5863508437783683902">"Calendar"</string>
-    <string name="permgroupdesc_calendar" msgid="3889615280211184106">"accesează calendarul"</string>
+    <string name="permgroupdesc_calendar" msgid="3889615280211184106">"acceseze calendarul"</string>
     <string name="permgrouplab_sms" msgid="228308803364967808">"SMS"</string>
-    <string name="permgroupdesc_sms" msgid="4656988620100940350">"trimite și vede mesajele SMS"</string>
+    <string name="permgroupdesc_sms" msgid="4656988620100940350">"trimită și să vadă mesajele SMS"</string>
     <string name="permgrouplab_storage" msgid="1971118770546336966">"Stocare"</string>
     <string name="permgroupdesc_storage" msgid="637758554581589203">"acceseze fotografiile, conținutul media și fișierele de pe dispozitiv"</string>
     <string name="permgrouplab_microphone" msgid="171539900250043464">"Microfonul"</string>
-    <string name="permgroupdesc_microphone" msgid="4988812113943554584">"înregistreze conținut audio"</string>
+    <string name="permgroupdesc_microphone" msgid="4988812113943554584">"înregistreze sunet"</string>
     <string name="permgrouplab_camera" msgid="4820372495894586615">"Camera foto"</string>
-    <string name="permgroupdesc_camera" msgid="3250611594678347720">"fotografieze și să înregistreze videoclipuri"</string>
+    <string name="permgroupdesc_camera" msgid="3250611594678347720">"fotografieze și să înregistreze imagini"</string>
     <string name="permgrouplab_phone" msgid="5229115638567440675">"Telefon"</string>
     <string name="permgroupdesc_phone" msgid="6234224354060641055">"inițieze și să gestioneze apeluri telefonice"</string>
     <string name="permgrouplab_sensors" msgid="416037179223226722">"Senzori corporali"</string>
-    <string name="permgroupdesc_sensors" msgid="7147968539346634043">"accesează datele înregistrate de senzori despre semnele vitale"</string>
+    <string name="permgroupdesc_sensors" msgid="7147968539346634043">"acceseze datele de la senzori despre semnele vitale"</string>
     <string name="capability_title_canRetrieveWindowContent" msgid="3901717936930170320">"Recuperează conținutul ferestrei"</string>
     <string name="capability_desc_canRetrieveWindowContent" msgid="3772225008605310672">"Inspectează conținutul unei ferestre cu care interacționați."</string>
     <string name="capability_title_canRequestTouchExploration" msgid="3108723364676667320">"Activează funcția Explorați prin atingere"</string>
@@ -270,11 +270,11 @@
     <string name="permlab_receiveMms" msgid="1821317344668257098">"primeşte mesaje text (MMS)"</string>
     <string name="permdesc_receiveMms" msgid="533019437263212260">"Permite aplicației să primească și să proceseze mesaje MMS. Acest lucru înseamnă că aplicația ar putea monitoriza sau șterge mesajele trimise pe dispozitivul dvs. fără a vi le arăta."</string>
     <string name="permlab_readCellBroadcasts" msgid="1598328843619646166">"citeşte mesajele cu transmisie celulară"</string>
-    <string name="permdesc_readCellBroadcasts" msgid="6361972776080458979">"Permite aplicației să citească mesajele primite prin transmisie celulară de dispozitivul dvs. Alertele cu transmisie celulară sunt difuzate în unele locații pentru a vă avertiza cu privire la situaţiile de urgenţă. Aplicaţiile rău intenţionate pot afecta performanţa sau funcţionarea dispozitivului dvs. când este primită o transmisie celulară de urgenţă."</string>
+    <string name="permdesc_readCellBroadcasts" msgid="6361972776080458979">"Permite aplicației să citească mesajele primite prin transmisie celulară de dispozitivul dvs. Alertele cu transmisie celulară sunt difuzate în unele locații pentru a vă avertiza cu privire la situațiile de urgenţă. Aplicaţiile rău intenţionate pot afecta performanţa sau funcționarea dispozitivului dvs. când este primită o transmisie celulară de urgenţă."</string>
     <string name="permlab_subscribedFeedsRead" msgid="4756609637053353318">"citire feeduri abonat"</string>
     <string name="permdesc_subscribedFeedsRead" msgid="5557058907906144505">"Permite aplicației să obţină detalii despre feedurile sincronizate în prezent."</string>
-    <string name="permlab_sendSms" msgid="7544599214260982981">"trimite și vede mesajele SMS"</string>
-    <string name="permdesc_sendSms" msgid="7094729298204937667">"Permite aplicației să trimită mesaje SMS, ceea ce ar putea determina apariţia unor taxe neaşteptate. Aplicaţiile rău intenţionate pot acumula costuri prin trimiterea mesajelor fără confirmarea dvs."</string>
+    <string name="permlab_sendSms" msgid="7544599214260982981">"trimită și să vadă mesajele SMS"</string>
+    <string name="permdesc_sendSms" msgid="7094729298204937667">"Permite aplicației să trimită mesaje SMS, ceea ce ar putea determina apariția unor taxe neaşteptate. Aplicaţiile rău intenţionate pot acumula costuri prin trimiterea mesajelor fără confirmarea dvs."</string>
     <string name="permlab_readSms" msgid="8745086572213270480">"citeşte mesajele text (SMS sau MMS)"</string>
     <string name="permdesc_readSms" product="tablet" msgid="2467981548684735522">"Permite aplicației să citească mesajele SMS stocate pe tabletă sau pe cardul SIM. În acest fel, aplicația poate citi toate mesajele SMS, indiferent de conţinutul sau de gradul de confidenţialitate al acestora."</string>
     <string name="permdesc_readSms" product="tv" msgid="5102425513647038535">"Permite aplicației să citească mesajele SMS stocate pe televizor sau pe cardul SIM. Cu această permisiune, aplicația poate citi toate mesajele SMS, indiferent de conținutul sau de gradul de confidențialitate al acestora."</string>
@@ -292,11 +292,11 @@
     <string name="permlab_killBackgroundProcesses" msgid="3914026687420177202">"închide alte aplicații"</string>
     <string name="permdesc_killBackgroundProcesses" msgid="4593353235959733119">"Permite aplicației să oprească procesele derulate în fundal de alte aplicații. Acest lucru poate face ca respectivele aplicații să nu mai ruleze."</string>
     <string name="permlab_systemAlertWindow" msgid="3543347980839518613">"suprapune elemente vizuale peste alte aplicații"</string>
-    <string name="permdesc_systemAlertWindow" msgid="8584678381972820118">"Permite aplicației să suprapună elemente vizuale peste alte aplicații sau părţi ale interfeţei cu utilizatorul. Acestea pot interfera cu utilizarea de către dvs. a interfeţei în orice aplicație sau pot schimba ceea ce credeţi că vedeţi în alte aplicații."</string>
+    <string name="permdesc_systemAlertWindow" msgid="8584678381972820118">"Permite aplicației să suprapună elemente vizuale peste alte aplicații sau părți ale interfeței cu utilizatorul. Acestea pot interfera cu utilizarea de către dvs. a interfeței în orice aplicație sau pot schimba ceea ce credeți că vedeţi în alte aplicații."</string>
     <string name="permlab_persistentActivity" msgid="8841113627955563938">"rulare continuă a aplicației"</string>
-    <string name="permdesc_persistentActivity" product="tablet" msgid="8525189272329086137">"Permite aplicației să declare persistente în memorie anumite părţi ale sale. Acest lucru poate limita memoria disponibilă pentru alte aplicații și poate încetini funcţionarea tabletei."</string>
+    <string name="permdesc_persistentActivity" product="tablet" msgid="8525189272329086137">"Permite aplicației să declare persistente în memorie anumite părți ale sale. Acest lucru poate limita memoria disponibilă pentru alte aplicații și poate încetini funcționarea tabletei."</string>
     <string name="permdesc_persistentActivity" product="tv" msgid="5086862529499103587">"Permite aplicației să declare persistente în memorie anumite părți ale sale. Acest lucru poate limita memoria disponibilă pentru alte aplicații și poate încetini funcționarea televizorului."</string>
-    <string name="permdesc_persistentActivity" product="default" msgid="4384760047508278272">"Permite aplicației să declare persistente în memorie anumite părţi ale sale. Acest lucru poate limita memoria disponibilă pentru alte aplicații și poate încetini funcţionarea telefonului."</string>
+    <string name="permdesc_persistentActivity" product="default" msgid="4384760047508278272">"Permite aplicației să declare persistente în memorie anumite părți ale sale. Acest lucru poate limita memoria disponibilă pentru alte aplicații și poate încetini funcționarea telefonului."</string>
     <string name="permlab_getPackageSize" msgid="7472921768357981986">"măsurare spaţiu de stocare al aplicației"</string>
     <string name="permdesc_getPackageSize" msgid="3921068154420738296">"Permite aplicației să preia dimensiunile codului, ale datelor și ale memoriei cache"</string>
     <string name="permlab_writeSettings" msgid="2226195290955224730">"modifică setări de sistem"</string>
@@ -310,17 +310,17 @@
     <string name="permdesc_broadcastSticky" product="tv" msgid="6839285697565389467">"Permite aplicației să trimită mesaje difuzate persistente, care se păstrează după terminarea difuzării mesajului. Utilizarea excesivă a acestei funcții poate să încetinească sau să destabilizeze televizorul, determinându-l să utilizeze prea multă memorie."</string>
     <string name="permdesc_broadcastSticky" product="default" msgid="2825803764232445091">"Permite aplicației să trimită mesaje difuzate persistente, care se păstrează după terminarea difuzării mesajului. Utilizarea excesivă a acestei funcții poate să încetinească sau să destabilizeze telefonul, determinându-l să utilizeze prea multă memorie."</string>
     <string name="permlab_readContacts" msgid="8348481131899886131">"citeşte agenda"</string>
-    <string name="permdesc_readContacts" product="tablet" msgid="5294866856941149639">"Permite aplicației să citească datele despre persoanele din agenda stocată pe tabletă, inclusiv frecvenţa cu care aţi apelat, aţi trimis e-mailuri sau aţi comunicat în alte moduri cu anumite persoane. Cu această permisiune aplicația salvează datele dvs. de contact, iar aplicațiile rău intenţionate pot distribui datele de contact fără ştirea dvs."</string>
+    <string name="permdesc_readContacts" product="tablet" msgid="5294866856941149639">"Permite aplicației să citească datele despre persoanele din agenda stocată pe tabletă, inclusiv frecvența cu care aţi apelat, aţi trimis e-mailuri sau aţi comunicat în alte moduri cu anumite persoane. Cu această permisiune aplicația salvează datele dvs. de contact, iar aplicațiile rău intenţionate pot distribui datele de contact fără știrea dvs."</string>
     <string name="permdesc_readContacts" product="tv" msgid="1839238344654834087">"Permite aplicației să citească datele despre persoanele de contact salvate pe televizor, inclusiv frecvența cu care ați apelat, ați trimis e-mailuri sau ați comunicat în alte moduri cu anumite persoane. Cu această permisiune, aplicațiile pot salva datele de contact, iar aplicațiile rău-intenționate pot permite accesul la datele de contact fără cunoștința dvs."</string>
-    <string name="permdesc_readContacts" product="default" msgid="8440654152457300662">"Permite aplicației să citească datele despre persoanele din agenda stocată pe telefon, inclusiv frecvenţa cu care aţi apelat, aţi trimis e-mailuri sau aţi comunicat în alte moduri cu anumite persoane. Cu această permisiune aplicația salvează datele dvs. de contact, iar aplicațiile rău intenţionate pot distribui datele de contact fără ştirea dvs."</string>
+    <string name="permdesc_readContacts" product="default" msgid="8440654152457300662">"Permite aplicației să citească datele despre persoanele din agenda stocată pe telefon, inclusiv frecvența cu care aţi apelat, aţi trimis e-mailuri sau aţi comunicat în alte moduri cu anumite persoane. Cu această permisiune aplicația salvează datele dvs. de contact, iar aplicațiile rău intenţionate pot distribui datele de contact fără știrea dvs."</string>
     <string name="permlab_writeContacts" msgid="5107492086416793544">"modifică agenda"</string>
-    <string name="permdesc_writeContacts" product="tablet" msgid="897243932521953602">"Permite aplicației să modifice datele despre persoanele din agenda stocată pe tabletă, inclusiv frecvenţa cu care aţi apelat, aţi trimis e-mailuri sau aţi comunicat în alte moduri cu anumite persoane din agendă. Cu această permisiune aplicația poate șterge datele de contact."</string>
+    <string name="permdesc_writeContacts" product="tablet" msgid="897243932521953602">"Permite aplicației să modifice datele despre persoanele din agenda stocată pe tabletă, inclusiv frecvența cu care aţi apelat, aţi trimis e-mailuri sau aţi comunicat în alte moduri cu anumite persoane din agendă. Cu această permisiune aplicația poate șterge datele de contact."</string>
     <string name="permdesc_writeContacts" product="tv" msgid="5438230957000018959">"Permite aplicației să modifice datele despre persoanele de contact salvate pe televizor, inclusiv frecvența cu care ați apelat, ați trimis e-mailuri sau ați comunicat în alte moduri cu anumite persoane de contact. Cu această permisiune, aplicația poate șterge datele de contact."</string>
-    <string name="permdesc_writeContacts" product="default" msgid="589869224625163558">"Permite aplicației să modifice datele despre persoanele din agenda stocată pe telefon, inclusiv frecvenţa cu care aţi apelat, aţi trimis e-mailuri sau aţi comunicat în alte moduri cu anumite persoane din agendă. Cu această permisiune aplicația poate șterge datele de contact."</string>
+    <string name="permdesc_writeContacts" product="default" msgid="589869224625163558">"Permite aplicației să modifice datele despre persoanele din agenda stocată pe telefon, inclusiv frecvența cu care aţi apelat, aţi trimis e-mailuri sau aţi comunicat în alte moduri cu anumite persoane din agendă. Cu această permisiune aplicația poate șterge datele de contact."</string>
     <string name="permlab_readCallLog" msgid="3478133184624102739">"citeşte jurnalul de apeluri"</string>
-    <string name="permdesc_readCallLog" product="tablet" msgid="3700645184870760285">"Permite aplicației să citească jurnalul de apeluri al tabletei, inclusiv datele despre apelurile primite și efectuate. Cu această permisiune aplicația salvează datele dvs. din jurnalul de apeluri, iar aplicațiile rău intenţionate pot distribui aceste date fără ştirea dvs."</string>
+    <string name="permdesc_readCallLog" product="tablet" msgid="3700645184870760285">"Permite aplicației să citească jurnalul de apeluri al tabletei, inclusiv datele despre apelurile primite și efectuate. Cu această permisiune aplicația salvează datele dvs. din jurnalul de apeluri, iar aplicațiile rău intenţionate pot distribui aceste date fără știrea dvs."</string>
     <string name="permdesc_readCallLog" product="tv" msgid="5611770887047387926">"Permite aplicației să citească jurnalul de apeluri al televizorului, inclusiv datele despre apelurile primite și efectuate. Cu această permisiune, aplicațiile pot să salveze datele din jurnalul de apeluri, iar aplicațiile rău-intenționate pot permite accesul la datele din jurnalul de apeluri fără cunoștința dvs."</string>
-    <string name="permdesc_readCallLog" product="default" msgid="5777725796813217244">"Permite aplicației să citească jurnalul de apeluri al telefonului, inclusiv datele despre apelurile primite și efectuate. Cu această permisiune aplicația salvează datele dvs. din jurnalul de apeluri, iar aplicațiile rău intenţionate pot distribui aceste date fără ştirea dvs."</string>
+    <string name="permdesc_readCallLog" product="default" msgid="5777725796813217244">"Permite aplicației să citească jurnalul de apeluri al telefonului, inclusiv datele despre apelurile primite și efectuate. Cu această permisiune aplicația salvează datele dvs. din jurnalul de apeluri, iar aplicațiile rău intenţionate pot distribui aceste date fără știrea dvs."</string>
     <string name="permlab_writeCallLog" msgid="8552045664743499354">"scrie jurnalul de apeluri"</string>
     <string name="permdesc_writeCallLog" product="tablet" msgid="6661806062274119245">"Permite aplicației să modifice jurnalul de apeluri al tabletei dvs., inclusiv datele despre apelurile primite sau efectuate. Aplicaţiile rău intenţionate pot utiliza această permisiune pentru a șterge sau pentru a modifica jurnalul dvs. de apeluri."</string>
     <string name="permdesc_writeCallLog" product="tv" msgid="4225034892248398019">"Permite aplicației să modifice jurnalul de apeluri al televizorului, inclusiv datele despre apelurile primite sau efectuate. Aplicațiile rău-intenționate pot utiliza această permisiune pentru a șterge sau pentru a modifica jurnalul de apeluri."</string>
@@ -331,10 +331,10 @@
     <string name="permdesc_readCalendar" product="tablet" msgid="4216462049057658723">"Permite aplicației să citească toate evenimentele din calendar stocate pe tabletă, inclusiv cele ale prietenilor sau colegilor. Acest lucru poate permite aplicației să distribuie sau să salveze datele din calendar, indiferent dacă acestea sunt confidenţiale sau sensibile."</string>
     <string name="permdesc_readCalendar" product="tv" msgid="3191352452242394196">"Permite aplicației să citească toate evenimentele din calendar stocate pe televizor, inclusiv cele ale prietenilor sau colegilor. Cu această permisiune, aplicația poate să permită accesul la datele din calendar sau să le salveze, indiferent dacă acestea sunt confidențiale sau sensibile."</string>
     <string name="permdesc_readCalendar" product="default" msgid="7434548682470851583">"Permite aplicației să citească toate evenimentele din calendar stocate pe telefon, inclusiv cele ale prietenilor sau colegilor. Acest lucru poate permite aplicației să distribuie sau să salveze datele din calendar, indiferent dacă acestea sunt confidenţiale sau sensibile."</string>
-    <string name="permlab_writeCalendar" msgid="8438874755193825647">"adăugarea sau modificarea evenimentelor din calendar și trimiterea de e-mailuri invitaţilor fără ştirea proprietarului"</string>
-    <string name="permdesc_writeCalendar" product="tablet" msgid="6679035520113668528">"Permite aplicației să adauge, să elimine și să modifice evenimentele pe care le puteți modifica pe tabletă, inclusiv cele ale prietenilor sau colegilor dvs. În acest fel, aplicația poate trimite mesaje care par să vină de la proprietarii calendarelor sau să modifice evenimentele fără ştirea proprietarilor."</string>
+    <string name="permlab_writeCalendar" msgid="8438874755193825647">"adăugarea sau modificarea evenimentelor din calendar și trimiterea de e-mailuri invitaților fără știrea proprietarului"</string>
+    <string name="permdesc_writeCalendar" product="tablet" msgid="6679035520113668528">"Permite aplicației să adauge, să elimine și să modifice evenimentele pe care le puteți modifica pe tabletă, inclusiv cele ale prietenilor sau colegilor dvs. În acest fel, aplicația poate trimite mesaje care par să vină de la proprietarii calendarelor sau să modifice evenimentele fără știrea proprietarilor."</string>
     <string name="permdesc_writeCalendar" product="tv" msgid="1273290605500902507">"Permite aplicației să adauge, să elimine și să modifice evenimentele pe care le puteți modifica pe televizor, inclusiv pe cele ale prietenilor sau ale colegilor. Cu această permisiune, aplicația poate să trimită mesaje care par că vin din partea proprietarilor calendarului sau să modifice evenimentele fără cunoștința acestora."</string>
-    <string name="permdesc_writeCalendar" product="default" msgid="2324469496327249376">"Permite aplicației să adauge, să elimine și să modifice evenimentele pe care le puteți modifica pe telefon, inclusiv cele ale prietenilor sau colegilor dvs. În acest fel, aplicația poate trimite mesaje care par să vină de la proprietarii calendarelor sau să modifice evenimentele fără ştirea proprietarilor."</string>
+    <string name="permdesc_writeCalendar" product="default" msgid="2324469496327249376">"Permite aplicației să adauge, să elimine și să modifice evenimentele pe care le puteți modifica pe telefon, inclusiv cele ale prietenilor sau colegilor dvs. În acest fel, aplicația poate trimite mesaje care par să vină de la proprietarii calendarelor sau să modifice evenimentele fără știrea proprietarilor."</string>
     <string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"accesare comenzi suplimentare ale furnizorului locației"</string>
     <string name="permdesc_accessLocationExtraCommands" msgid="6078307221056649927">"Permite aplicației să acceseze comenzi suplimentare pentru furnizorul locației. Aplicația ar putea să utilizeze această permisiune pentru a influența operațiile GPS sau ale altor surse de locații."</string>
     <string name="permlab_accessFineLocation" msgid="1191898061965273372">"locaţia exactă (bazată pe rețea și GPS)"</string>
@@ -343,7 +343,7 @@
     <string name="permdesc_accessCoarseLocation" msgid="2538200184373302295">"Permite aplicației să obţină locaţia dvs. aproximativă. Această locație este dedusă de serviciile de localizare utilizând surse de localizare prin rețele, cum ar fi cele prin turn de celule și Wi-Fi. Pentru a fi utilizate de aplicație, aceste servicii de localizare trebuie să fie activate și disponibile pe dispozitivul dvs. Aplicaţiile pot utiliza această permisiune pentru a determina locaţia dvs. aproximativă."</string>
     <string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"modificare setări audio"</string>
     <string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"Permite aplicației să modifice setările audio globale, cum ar fi volumul și difuzorul care este utilizat pentru ieșire."</string>
-    <string name="permlab_recordAudio" msgid="3876049771427466323">"înregistrare audio"</string>
+    <string name="permlab_recordAudio" msgid="3876049771427466323">"înregistreze sunet"</string>
     <string name="permdesc_recordAudio" msgid="4906839301087980680">"Permite aplicației să efectueze înregistrări audio cu ajutorul microfonului. Cu această permisiune aplicația efectuează oricând înregistrări audio fără confirmare."</string>
     <string name="permlab_sim_communication" msgid="1180265879464893029">"comunicare cu cardul SIM"</string>
     <string name="permdesc_sim_communication" msgid="5725159654279639498">"Permite aplicației să trimită comenzi pe cardul SIM. Această permisiune este foarte periculoasă."</string>
@@ -354,11 +354,11 @@
     <string name="permlab_flashlight" msgid="2155920810121984215">"control lanternă"</string>
     <string name="permdesc_flashlight" msgid="6522284794568368310">"Permite aplicației să controleze lanterna."</string>
     <string name="permlab_callPhone" msgid="3925836347681847954">"apelare directă numere de telefon"</string>
-    <string name="permdesc_callPhone" msgid="3740797576113760827">"Permite aplicației să apeleze numere de telefon fără intervenţia dvs. Acest lucru poate determina apariţia unor taxe sau a unor apeluri neaşteptate. Cu această permisiune aplicația nu poate apela numerele de urgenţă. Aplicaţiile rău intenţionate pot acumula costuri prin efectuarea unor apeluri fără confirmare."</string>
+    <string name="permdesc_callPhone" msgid="3740797576113760827">"Permite aplicației să apeleze numere de telefon fără intervenţia dvs. Acest lucru poate determina apariția unor taxe sau a unor apeluri neaşteptate. Cu această permisiune aplicația nu poate apela numerele de urgenţă. Aplicaţiile rău intenţionate pot acumula costuri prin efectuarea unor apeluri fără confirmare."</string>
     <string name="permlab_accessImsCallService" msgid="3574943847181793918">"accesează serviciul de apelare IMS"</string>
     <string name="permdesc_accessImsCallService" msgid="8992884015198298775">"Permite aplicației să folosească serviciul IMS pentru apeluri, fără intervenția dvs."</string>
     <string name="permlab_readPhoneState" msgid="9178228524507610486">"citeşte starea și identitatea telefonului"</string>
-    <string name="permdesc_readPhoneState" msgid="1639212771826125528">"Permite aplicației să acceseze funcţiile de telefon ale dispozitivului. Cu această permisiune aplicația stabileşte numărul de telefon și ID-urile de dispozitiv, dacă un apel este activ, precum și numărul de la distanţă conectat printr-un apel."</string>
+    <string name="permdesc_readPhoneState" msgid="1639212771826125528">"Permite aplicației să acceseze funcţiile de telefon ale dispozitivului. Cu această permisiune aplicația stabilește numărul de telefon și ID-urile de dispozitiv, dacă un apel este activ, precum și numărul de la distanţă conectat printr-un apel."</string>
     <string name="permlab_wakeLock" product="tablet" msgid="1531731435011495015">"împiedicarea computerului tablet PC să intre în repaus"</string>
     <string name="permlab_wakeLock" product="tv" msgid="2601193288949154131">"împiedică intrarea televizorului în stare de inactivitate"</string>
     <string name="permlab_wakeLock" product="default" msgid="573480187941496130">"împiedicare intrare telefon în repaus"</string>
@@ -756,7 +756,7 @@
     <string name="js_dialog_before_unload_negative_button" msgid="5614861293026099715">"Rămâneți în această pagină"</string>
     <string name="js_dialog_before_unload" msgid="3468816357095378590">"<xliff:g id="MESSAGE">%s</xliff:g>\n\nSigur doriți să părăsiți această pagină?"</string>
     <string name="save_password_label" msgid="6860261758665825069">"Confirmați"</string>
-    <string name="double_tap_toast" msgid="4595046515400268881">"Sfat: măriţi și micşoraţi prin dublă atingere."</string>
+    <string name="double_tap_toast" msgid="4595046515400268881">"Sfat: măriți și micșorați prin dublă atingere."</string>
     <string name="autofill_this_form" msgid="4616758841157816676">"Automat"</string>
     <string name="setup_autofill" msgid="7103495070180590814">"Conf.Compl.auto."</string>
     <string name="autofill_address_name_separator" msgid="6350145154779706772">" "</string>
@@ -767,7 +767,7 @@
     <string name="autofill_postal_code" msgid="4696430407689377108">"Cod poştal"</string>
     <string name="autofill_state" msgid="6988894195520044613">"Stat"</string>
     <string name="autofill_zip_code" msgid="8697544592627322946">"Cod ZIP"</string>
-    <string name="autofill_county" msgid="237073771020362891">"Judeţ"</string>
+    <string name="autofill_county" msgid="237073771020362891">"Județ"</string>
     <string name="autofill_island" msgid="4020100875984667025">"Insulă"</string>
     <string name="autofill_district" msgid="8400735073392267672">"District"</string>
     <string name="autofill_department" msgid="5343279462564453309">"Departament"</string>
@@ -776,11 +776,11 @@
     <string name="autofill_area" msgid="3547409050889952423">"Zonă"</string>
     <string name="autofill_emirate" msgid="2893880978835698818">"Emirat"</string>
     <string name="permlab_readHistoryBookmarks" msgid="3775265775405106983">"citeşte marcajele și istoricul web"</string>
-    <string name="permdesc_readHistoryBookmarks" msgid="8462378226600439658">"Permite aplicației să citească istoricul tuturor adreselor URL accesate de Browser și toate marcajele din Browser. Notă: această permisiune nu poate fi aplicată de browsere terţă parte sau de alte aplicații cu capacităţi de navigare pe web."</string>
+    <string name="permdesc_readHistoryBookmarks" msgid="8462378226600439658">"Permite aplicației să citească istoricul tuturor adreselor URL accesate de Browser și toate marcajele din Browser. Notă: această permisiune nu poate fi aplicată de browsere terţă parte sau de alte aplicații cu capacități de navigare pe web."</string>
     <string name="permlab_writeHistoryBookmarks" msgid="3714785165273314490">"scrie în marcajele și în istoricul web"</string>
-    <string name="permdesc_writeHistoryBookmarks" product="tablet" msgid="6825527469145760922">"Permite aplicației să modifice istoricul Browserului sau marcajele stocate pe tabletă. În acest fel, aplicația poate șterge sau modifica datele din Browser. Notă: această permisiune nu poate fi aplicată de browsere terţă parte sau de alte aplicații cu capacităţi de navigare pe web."</string>
+    <string name="permdesc_writeHistoryBookmarks" product="tablet" msgid="6825527469145760922">"Permite aplicației să modifice istoricul Browserului sau marcajele stocate pe tabletă. În acest fel, aplicația poate șterge sau modifica datele din Browser. Notă: această permisiune nu poate fi aplicată de browsere terţă parte sau de alte aplicații cu capacități de navigare pe web."</string>
     <string name="permdesc_writeHistoryBookmarks" product="tv" msgid="7007393823197766548">"Permite aplicației să modifice istoricul sau marcajele browserului stocate pe televizor. Cu această permisiune, aplicația poate șterge sau modifica datele din browser. Notă: această permisiune nu poate fi aplicată de browsere terță parte sau de alte aplicații cu capacități de navigare pe web."</string>
-    <string name="permdesc_writeHistoryBookmarks" product="default" msgid="8497389531014185509">"Permite aplicației să modifice istoricul Browserului sau marcajele stocate pe telefon. În acest fel, aplicația poate șterge sau modifica datele din Browser. Notă: această permisiune nu poate fi aplicată de browsere terţă parte sau de alte aplicații cu capacităţi de navigare pe web."</string>
+    <string name="permdesc_writeHistoryBookmarks" product="default" msgid="8497389531014185509">"Permite aplicației să modifice istoricul Browserului sau marcajele stocate pe telefon. În acest fel, aplicația poate șterge sau modifica datele din Browser. Notă: această permisiune nu poate fi aplicată de browsere terţă parte sau de alte aplicații cu capacități de navigare pe web."</string>
     <string name="permlab_setAlarm" msgid="1379294556362091814">"setează o alarmă"</string>
     <string name="permdesc_setAlarm" msgid="316392039157473848">"Permite aplicației să seteze o alarmă într-o aplicație de ceas cu alarmă instalată. Este posibil ca unele aplicații de ceas cu alarmă să nu implementeze această funcție."</string>
     <string name="permlab_addVoicemail" msgid="5525660026090959044">"adăugare mesagerie vocală"</string>
@@ -803,11 +803,11 @@
     <string name="searchview_description_search" msgid="6749826639098512120">"Căutați"</string>
     <string name="searchview_description_query" msgid="5911778593125355124">"Interogare de căutare"</string>
     <string name="searchview_description_clear" msgid="1330281990951833033">"Ștergeți interogarea"</string>
-    <string name="searchview_description_submit" msgid="2688450133297983542">"Trimiteţi interogarea"</string>
+    <string name="searchview_description_submit" msgid="2688450133297983542">"Trimiteți interogarea"</string>
     <string name="searchview_description_voice" msgid="2453203695674994440">"Căutare vocală"</string>
-    <string name="enable_explore_by_touch_warning_title" msgid="7460694070309730149">"Activați Exploraţi prin atingere?"</string>
-    <string name="enable_explore_by_touch_warning_message" product="tablet" msgid="8655887539089910577">"<xliff:g id="ACCESSIBILITY_SERVICE_NAME">%1$s</xliff:g> doreşte să activeze funcţia Exploraţi prin atingere. Când această funcție este activată, puteți auzi sau vedea descrieri pentru ceea ce se află sub degetul dvs. sau puteți efectua gesturi pentru a interacţiona cu tableta."</string>
-    <string name="enable_explore_by_touch_warning_message" product="default" msgid="2708199672852373195">"<xliff:g id="ACCESSIBILITY_SERVICE_NAME">%1$s</xliff:g> doreşte să activeze funcţia Exploraţi prin atingere. Când această funcție este activată, puteți auzi sau vedea descrieri pentru ceea ce se află sub degetul dvs. sau puteți efectua gesturi pentru a interacţiona cu telefonul."</string>
+    <string name="enable_explore_by_touch_warning_title" msgid="7460694070309730149">"Activați Explorați prin atingere?"</string>
+    <string name="enable_explore_by_touch_warning_message" product="tablet" msgid="8655887539089910577">"<xliff:g id="ACCESSIBILITY_SERVICE_NAME">%1$s</xliff:g> doreşte să activeze funcţia Explorați prin atingere. Când această funcție este activată, puteți auzi sau vedea descrieri pentru ceea ce se află sub degetul dvs. sau puteți efectua gesturi pentru a interacţiona cu tableta."</string>
+    <string name="enable_explore_by_touch_warning_message" product="default" msgid="2708199672852373195">"<xliff:g id="ACCESSIBILITY_SERVICE_NAME">%1$s</xliff:g> doreşte să activeze funcţia Explorați prin atingere. Când această funcție este activată, puteți auzi sau vedea descrieri pentru ceea ce se află sub degetul dvs. sau puteți efectua gesturi pentru a interacţiona cu telefonul."</string>
     <string name="oneMonthDurationPast" msgid="7396384508953779925">"cu 1 lună în urmă"</string>
     <string name="beforeOneMonthDurationPast" msgid="909134546836499826">"Cu mai mult de 1 lună în urmă"</string>
     <plurals name="last_num_days" formatted="false" msgid="5104533550723932025">
@@ -918,7 +918,7 @@
     <string name="launch_warning_original" msgid="188102023021668683">"<xliff:g id="APP_NAME">%1$s</xliff:g> a fost lansată iniţial."</string>
     <string name="screen_compat_mode_scale" msgid="3202955667675944499">"Scară"</string>
     <string name="screen_compat_mode_show" msgid="4013878876486655892">"Afişaţi întotdeauna"</string>
-    <string name="screen_compat_mode_hint" msgid="1064524084543304459">"Reactivaţi acest mod din Setări de sistem &gt; Aplicații &gt; Descărcate."</string>
+    <string name="screen_compat_mode_hint" msgid="1064524084543304459">"Reactivați acest mod din Setări de sistem &gt; Aplicații &gt; Descărcate."</string>
     <string name="smv_application" msgid="3307209192155442829">"Aplicaţia <xliff:g id="APPLICATION">%1$s</xliff:g> (procesul <xliff:g id="PROCESS">%2$s</xliff:g>) a încălcat propria politică StrictMode."</string>
     <string name="smv_process" msgid="5120397012047462446">"Procesul <xliff:g id="PROCESS">%1$s</xliff:g> a încălcat propria politică StrictMode."</string>
     <string name="android_upgrading_title" msgid="1584192285441405746">"Android trece la o versiune superioară..."</string>
@@ -1002,10 +1002,10 @@
     <string name="sms_control_message" msgid="3867899169651496433">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; trimite un număr mare de mesaje SMS. Permiteți acestei aplicații să trimită în continuare mesaje?"</string>
     <string name="sms_control_yes" msgid="3663725993855816807">"Permiteți"</string>
     <string name="sms_control_no" msgid="625438561395534982">"Refuzaţi"</string>
-    <string name="sms_short_code_confirm_message" msgid="1645436466285310855">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; intenţionează să trimită un mesaj la &lt;b&gt;<xliff:g id="DEST_ADDRESS">%2$s</xliff:g>&lt;/b&gt;."</string>
+    <string name="sms_short_code_confirm_message" msgid="1645436466285310855">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; intenționează să trimită un mesaj la &lt;b&gt;<xliff:g id="DEST_ADDRESS">%2$s</xliff:g>&lt;/b&gt;."</string>
     <string name="sms_short_code_details" msgid="5873295990846059400">"Acest lucru "<b>"poate genera costuri"</b>" în contul dvs. mobil."</string>
     <string name="sms_premium_short_code_details" msgid="7869234868023975"><b>"Acest lucru va genera costuri în contul dvs. mobil."</b></string>
-    <string name="sms_short_code_confirm_allow" msgid="4458878637111023413">"Trimiteţi"</string>
+    <string name="sms_short_code_confirm_allow" msgid="4458878637111023413">"Trimiteți"</string>
     <string name="sms_short_code_confirm_deny" msgid="2927389840209170706">"Anulați"</string>
     <string name="sms_short_code_remember_choice" msgid="5289538592272218136">"Doresc să se reţină opţiunea"</string>
     <string name="sms_short_code_remember_undo_instruction" msgid="4960944133052287484">"Puteți modifica ulterior în Setări &gt; Aplicații"</string>
@@ -1080,8 +1080,8 @@
     <string name="ext_media_status_formatting" msgid="1085079556538644861">"Se formatează…"</string>
     <string name="ext_media_status_missing" msgid="5638633895221670766">"Nu este introdus"</string>
     <string name="activity_list_empty" msgid="1675388330786841066">"Nu s-a găsit nicio activitate potrivită."</string>
-    <string name="permlab_route_media_output" msgid="1642024455750414694">"Direcţionează rezultatele media"</string>
-    <string name="permdesc_route_media_output" msgid="4932818749547244346">"Permite unei aplicații să direcţioneze rezultate media către alte dispozitive externe."</string>
+    <string name="permlab_route_media_output" msgid="1642024455750414694">"Direcționează rezultatele media"</string>
+    <string name="permdesc_route_media_output" msgid="4932818749547244346">"Permite unei aplicații să direcționeze rezultate media către alte dispozitive externe."</string>
     <string name="permlab_readInstallSessions" msgid="6165432407628065939">"Citirea sesiunilor de instalare"</string>
     <string name="permdesc_readInstallSessions" msgid="2049771699626019849">"Permite unei aplicații accesul la citirea sesiunilor de instalare. Aceasta poate vedea detalii despre instalările de pachete active."</string>
     <string name="permlab_requestInstallPackages" msgid="1772330282283082214">"Solicită instalarea pachetelor"</string>
@@ -1124,7 +1124,7 @@
     <string name="upload_file" msgid="2897957172366730416">"Alegeţi un fişier"</string>
     <string name="no_file_chosen" msgid="6363648562170759465">"Nu au fost găsite fișiere"</string>
     <string name="reset" msgid="2448168080964209908">"Resetaţi"</string>
-    <string name="submit" msgid="1602335572089911941">"Trimiteţi"</string>
+    <string name="submit" msgid="1602335572089911941">"Trimiteți"</string>
     <string name="car_mode_disable_notification_title" msgid="3164768212003864316">"Mod Maşină activat"</string>
     <string name="car_mode_disable_notification_message" msgid="8035230537563503262">"Atingeți pentru a ieşi din modul Maşină."</string>
     <string name="tethered_notification_title" msgid="3146694234398202601">"Tethering sau hotspot activ"</string>
@@ -1160,21 +1160,21 @@
     <string name="choose_account_label" msgid="5655203089746423927">"Alegeţi un cont"</string>
     <string name="add_account_label" msgid="2935267344849993553">"Adăugaţi un cont"</string>
     <string name="add_account_button_label" msgid="3611982894853435874">"Adăugaţi un cont"</string>
-    <string name="number_picker_increment_button" msgid="2412072272832284313">"Creşteţi"</string>
+    <string name="number_picker_increment_button" msgid="2412072272832284313">"Creșteți"</string>
     <string name="number_picker_decrement_button" msgid="476050778386779067">"Reduceţi"</string>
     <string name="number_picker_increment_scroll_mode" msgid="3073101067441638428">"Atingeți și țineți apăsat <xliff:g id="VALUE">%s</xliff:g>."</string>
     <string name="number_picker_increment_scroll_action" msgid="9101473045891835490">"Glisaţi în sus pentru a creşte și în jos pentru a reduce."</string>
-    <string name="time_picker_increment_minute_button" msgid="8865885114028614321">"Creşteţi valoarea pentru minute"</string>
+    <string name="time_picker_increment_minute_button" msgid="8865885114028614321">"Creșteți valoarea pentru minute"</string>
     <string name="time_picker_decrement_minute_button" msgid="6246834937080684791">"Reduceţi valoarea pentru minute"</string>
-    <string name="time_picker_increment_hour_button" msgid="3652056055810223139">"Creşteţi valoarea pentru oră"</string>
+    <string name="time_picker_increment_hour_button" msgid="3652056055810223139">"Creșteți valoarea pentru oră"</string>
     <string name="time_picker_decrement_hour_button" msgid="1377479863429214792">"Reduceţi valoarea pentru oră"</string>
     <string name="time_picker_increment_set_pm_button" msgid="4147590696151230863">"Setaţi valoarea PM"</string>
     <string name="time_picker_decrement_set_am_button" msgid="8302140353539486752">"Setaţi valoarea AM"</string>
-    <string name="date_picker_increment_month_button" msgid="5369998479067934110">"Creşteţi valoarea pentru lună"</string>
+    <string name="date_picker_increment_month_button" msgid="5369998479067934110">"Creșteți valoarea pentru lună"</string>
     <string name="date_picker_decrement_month_button" msgid="1832698995541726019">"Reduceţi valoarea pentru lună"</string>
-    <string name="date_picker_increment_day_button" msgid="7130465412308173903">"Creşteţi valoarea pentru zi"</string>
+    <string name="date_picker_increment_day_button" msgid="7130465412308173903">"Creșteți valoarea pentru zi"</string>
     <string name="date_picker_decrement_day_button" msgid="4131881521818750031">"Reduceţi valoarea pentru zi"</string>
-    <string name="date_picker_increment_year_button" msgid="6318697384310808899">"Creşteţi valoarea pentru an"</string>
+    <string name="date_picker_increment_year_button" msgid="6318697384310808899">"Creșteți valoarea pentru an"</string>
     <string name="date_picker_decrement_year_button" msgid="4482021813491121717">"Reduceţi valoarea pentru an"</string>
     <string name="date_picker_prev_month_button" msgid="2858244643992056505">"Luna trecută"</string>
     <string name="date_picker_next_month_button" msgid="5559507736887605055">"Luna viitoare"</string>
@@ -1245,7 +1245,7 @@
     <string name="default_audio_route_name" product="tablet" msgid="4617053898167127471">"Tabletă"</string>
     <string name="default_audio_route_name" product="tv" msgid="9158088547603019321">"TV"</string>
     <string name="default_audio_route_name" product="default" msgid="4239291273420140123">"Telefon"</string>
-    <string name="default_audio_route_name_headphones" msgid="8119971843803439110">"Căşti"</string>
+    <string name="default_audio_route_name_headphones" msgid="8119971843803439110">"Căști"</string>
     <string name="default_audio_route_name_dock_speakers" msgid="6240602982276591864">"Difuz. dispozit. andocare"</string>
     <string name="default_media_route_name_hdmi" msgid="2450970399023478055">"HDMI"</string>
     <string name="default_audio_route_category_name" msgid="3722811174003886946">"Sistem"</string>
diff --git a/core/res/res/values-ru/strings.xml b/core/res/res/values-ru/strings.xml
index f1deb22..af80ed3 100644
--- a/core/res/res/values-ru/strings.xml
+++ b/core/res/res/values-ru/strings.xml
@@ -915,7 +915,7 @@
     <string name="anr_application_process" msgid="8941757607340481057">"Приложение \"<xliff:g id="APPLICATION">%1$s</xliff:g>\" не отвечает. Закрыть его?"</string>
     <string name="anr_process" msgid="6513209874880517125">"Приложение \"<xliff:g id="PROCESS">%1$s</xliff:g>\" не отвечает.\n\nЗакрыть его?"</string>
     <string name="force_close" msgid="8346072094521265605">"ОК"</string>
-    <string name="report" msgid="4060218260984795706">"Отзыв"</string>
+    <string name="report" msgid="4060218260984795706">"Создать отчет"</string>
     <string name="wait" msgid="7147118217226317732">"Подождать"</string>
     <string name="webpage_unresponsive" msgid="3272758351138122503">"Страница не отвечает.\n\nЗакрыть ее?"</string>
     <string name="launch_warning_title" msgid="1547997780506713581">"Приложение перенаправлено"</string>
diff --git a/core/res/res/values-si-rLK/strings.xml b/core/res/res/values-si-rLK/strings.xml
index d5bafd25..582f912 100644
--- a/core/res/res/values-si-rLK/strings.xml
+++ b/core/res/res/values-si-rLK/strings.xml
@@ -229,7 +229,7 @@
     <string name="permgrouplab_contacts" msgid="3657758145679177612">"සම්බන්ධතා"</string>
     <string name="permgroupdesc_contacts" msgid="6951499528303668046">"ඔබේ සම්බන්ධතාවලට පිවිසෙන්න"</string>
     <string name="permgrouplab_location" msgid="7275582855722310164">"ස්ථානය"</string>
-    <string name="permgroupdesc_location" msgid="1346617465127855033">"මෙම උපාංගයේ ස්ථානය ප්‍රවේශ කිරීම"</string>
+    <string name="permgroupdesc_location" msgid="1346617465127855033">"මෙම උපාංගයේ ස්ථානයට ප්‍රවේශ කරන්න"</string>
     <string name="permgrouplab_calendar" msgid="5863508437783683902">"දින දර්ශනය"</string>
     <string name="permgroupdesc_calendar" msgid="3889615280211184106">"ඔබේ දින දර්ශනයට පිවිසෙන්න"</string>
     <string name="permgrouplab_sms" msgid="228308803364967808">"කෙටි පණිවිඩ"</string>
diff --git a/core/res/res/values-sl/strings.xml b/core/res/res/values-sl/strings.xml
index cf87e2a..c5ca3c2 100644
--- a/core/res/res/values-sl/strings.xml
+++ b/core/res/res/values-sl/strings.xml
@@ -993,7 +993,7 @@
     <string name="wifi_p2p_failed_message" msgid="3763669677935623084">"Wi-Fi Direct ni bilo mogoče zagnati."</string>
     <string name="wifi_p2p_enabled_notification_title" msgid="2068321881673734886">"Wi-Fi Direct je vklopljen"</string>
     <string name="wifi_p2p_enabled_notification_message" msgid="1638949953993894335">"Dotaknite se za nastavitve"</string>
-    <string name="accept" msgid="1645267259272829559">"Sprejmi"</string>
+    <string name="accept" msgid="1645267259272829559">"Sprejmem"</string>
     <string name="decline" msgid="2112225451706137894">"Zavrni"</string>
     <string name="wifi_p2p_invitation_sent_title" msgid="1318975185112070734">"Povabilo je poslano"</string>
     <string name="wifi_p2p_invitation_to_connect_title" msgid="4958803948658533637">"Povabilo za povezavo"</string>
diff --git a/core/res/res/values-sq-rAL/strings.xml b/core/res/res/values-sq-rAL/strings.xml
index 86f9d11..7df0f86 100644
--- a/core/res/res/values-sq-rAL/strings.xml
+++ b/core/res/res/values-sq-rAL/strings.xml
@@ -229,7 +229,7 @@
     <string name="permgrouplab_contacts" msgid="3657758145679177612">"Kontaktet"</string>
     <string name="permgroupdesc_contacts" msgid="6951499528303668046">"qasu te kontaktet e tua"</string>
     <string name="permgrouplab_location" msgid="7275582855722310164">"Vendndodhja"</string>
-    <string name="permgroupdesc_location" msgid="1346617465127855033">"qasu te vendndodhja e kësaj pajisjeje"</string>
+    <string name="permgroupdesc_location" msgid="1346617465127855033">"qasjen te vendndodhja e kësaj pajisjeje"</string>
     <string name="permgrouplab_calendar" msgid="5863508437783683902">"Kalendari"</string>
     <string name="permgroupdesc_calendar" msgid="3889615280211184106">"qasu te kalendari yt"</string>
     <string name="permgrouplab_sms" msgid="228308803364967808">"SMS"</string>
diff --git a/core/res/res/values-sr/strings.xml b/core/res/res/values-sr/strings.xml
index a3a4b32..5bb2d78 100644
--- a/core/res/res/values-sr/strings.xml
+++ b/core/res/res/values-sr/strings.xml
@@ -230,21 +230,21 @@
     <string name="permgrouplab_contacts" msgid="3657758145679177612">"Контакти"</string>
     <string name="permgroupdesc_contacts" msgid="6951499528303668046">"приступи контактима"</string>
     <string name="permgrouplab_location" msgid="7275582855722310164">"Локација"</string>
-    <string name="permgroupdesc_location" msgid="1346617465127855033">"приступ локацији овог уређаја"</string>
+    <string name="permgroupdesc_location" msgid="1346617465127855033">"приступи локацији овог уређаја"</string>
     <string name="permgrouplab_calendar" msgid="5863508437783683902">"Календар"</string>
     <string name="permgroupdesc_calendar" msgid="3889615280211184106">"приступи календару"</string>
     <string name="permgrouplab_sms" msgid="228308803364967808">"SMS"</string>
     <string name="permgroupdesc_sms" msgid="4656988620100940350">"шаље и прегледа SMS поруке"</string>
     <string name="permgrouplab_storage" msgid="1971118770546336966">"Складиште"</string>
-    <string name="permgroupdesc_storage" msgid="637758554581589203">"приступи сликама, медијима и датотекама на уређају"</string>
+    <string name="permgroupdesc_storage" msgid="637758554581589203">"приступа сликама, медијима и датотекама на уређају"</string>
     <string name="permgrouplab_microphone" msgid="171539900250043464">"Микрофон"</string>
-    <string name="permgroupdesc_microphone" msgid="4988812113943554584">"снимање аудио снимака"</string>
+    <string name="permgroupdesc_microphone" msgid="4988812113943554584">"снима аудио снимке"</string>
     <string name="permgrouplab_camera" msgid="4820372495894586615">"Камера"</string>
-    <string name="permgroupdesc_camera" msgid="3250611594678347720">"снимање слика и видео снимака"</string>
+    <string name="permgroupdesc_camera" msgid="3250611594678347720">"снима слике и видео снимке"</string>
     <string name="permgrouplab_phone" msgid="5229115638567440675">"Телефон"</string>
-    <string name="permgroupdesc_phone" msgid="6234224354060641055">"упућивање телефонских позива и управљање њима"</string>
+    <string name="permgroupdesc_phone" msgid="6234224354060641055">"упућује телефонске позиве и управља њима"</string>
     <string name="permgrouplab_sensors" msgid="416037179223226722">"Сензори за тело"</string>
-    <string name="permgroupdesc_sensors" msgid="7147968539346634043">"приступ подацима сензора о виталним функцијама"</string>
+    <string name="permgroupdesc_sensors" msgid="7147968539346634043">"приступа подацима сензора о виталним функцијама"</string>
     <string name="capability_title_canRetrieveWindowContent" msgid="3901717936930170320">"Преузима садржај прозора"</string>
     <string name="capability_desc_canRetrieveWindowContent" msgid="3772225008605310672">"Проверава садржај прозора са којим остварујете интеракцију."</string>
     <string name="capability_title_canRequestTouchExploration" msgid="3108723364676667320">"Укључи Истраживања додиром"</string>
@@ -273,7 +273,7 @@
     <string name="permdesc_readCellBroadcasts" msgid="6361972776080458979">"Омогућава апликацији да чита поруке инфо сервиса које уређај прима. Упозорења инфо сервиса се на неким локацијама примају као упозорења на хитне случајеве. Злонамерне апликације могу да утичу на учинак или ометају функционисање уређаја када се прими порука инфо сервиса о хитном случају."</string>
     <string name="permlab_subscribedFeedsRead" msgid="4756609637053353318">"читање пријављених фидова"</string>
     <string name="permdesc_subscribedFeedsRead" msgid="5557058907906144505">"Дозвољава апликацији да преузима детаље о тренутно синхронизованим фидовима."</string>
-    <string name="permlab_sendSms" msgid="7544599214260982981">"шаљи и прегледај SMS поруке"</string>
+    <string name="permlab_sendSms" msgid="7544599214260982981">"шаље и прегледа SMS поруке"</string>
     <string name="permdesc_sendSms" msgid="7094729298204937667">"Дозвољава апликацији да шаље SMS поруке. Ово може да доведе до неочекиваних трошкова. Злонамерне апликације могу да шаљу поруке без ваше потврде, што може да изазове трошкове."</string>
     <string name="permlab_readSms" msgid="8745086572213270480">"читање текстуалних порука (SMS или MMS)"</string>
     <string name="permdesc_readSms" product="tablet" msgid="2467981548684735522">"Дозвољава апликацији да чита SMS поруке ускладиштене на таблету или SIM картици. Ово омогућава апликацији да чита све SMS поруке, без обзира на садржај или поверљивост."</string>
diff --git a/core/res/res/values-sv/strings.xml b/core/res/res/values-sv/strings.xml
index 237afe7..488470f 100644
--- a/core/res/res/values-sv/strings.xml
+++ b/core/res/res/values-sv/strings.xml
@@ -853,7 +853,7 @@
     <string name="Midnight" msgid="5630806906897892201">"Midnatt"</string>
     <string name="elapsed_time_short_format_mm_ss" msgid="4431555943828711473">"<xliff:g id="MINUTES">%1$02d</xliff:g>:<xliff:g id="SECONDS">%2$02d</xliff:g>"</string>
     <string name="elapsed_time_short_format_h_mm_ss" msgid="1846071997616654124">"<xliff:g id="HOURS">%1$d</xliff:g>:<xliff:g id="MINUTES">%2$02d</xliff:g>:<xliff:g id="SECONDS">%3$02d</xliff:g>"</string>
-    <string name="selectAll" msgid="6876518925844129331">"Välj alla"</string>
+    <string name="selectAll" msgid="6876518925844129331">"Markera allt"</string>
     <string name="cut" msgid="3092569408438626261">"Klipp ut"</string>
     <string name="copy" msgid="2681946229533511987">"Kopiera"</string>
     <string name="paste" msgid="5629880836805036433">"Klistra in"</string>
@@ -1190,7 +1190,7 @@
     <string name="action_menu_overflow_description" msgid="2295659037509008453">"Fler alternativ"</string>
     <string name="action_bar_home_description_format" msgid="7965984360903693903">"%1$s, %2$s"</string>
     <string name="action_bar_home_subtitle_description_format" msgid="6985546530471780727">"%1$s, %2$s, %3$s"</string>
-    <string name="storage_internal" msgid="4891916833657929263">"Internminne"</string>
+    <string name="storage_internal" msgid="4891916833657929263">"lagring"</string>
     <string name="storage_sd_card" msgid="3282948861378286745">"SD-kort"</string>
     <string name="storage_sd_card_label" msgid="6347111320774379257">"SD-kort (<xliff:g id="MANUFACTURER">%s</xliff:g>)"</string>
     <string name="storage_usb_drive" msgid="6261899683292244209">"USB-enhet"</string>
diff --git a/core/res/res/values-sw/strings.xml b/core/res/res/values-sw/strings.xml
index 6445f6a..d367887 100644
--- a/core/res/res/values-sw/strings.xml
+++ b/core/res/res/values-sw/strings.xml
@@ -231,7 +231,7 @@
     <string name="permgrouplab_contacts" msgid="3657758145679177612">"Anwani"</string>
     <string name="permgroupdesc_contacts" msgid="6951499528303668046">"ifikie anwani zako"</string>
     <string name="permgrouplab_location" msgid="7275582855722310164">"Mahali"</string>
-    <string name="permgroupdesc_location" msgid="1346617465127855033">"fikia mahali kilipo kifaa hiki"</string>
+    <string name="permgroupdesc_location" msgid="1346617465127855033">"ifikie mahali kilipo kifaa hiki"</string>
     <string name="permgrouplab_calendar" msgid="5863508437783683902">"Kalenda"</string>
     <string name="permgroupdesc_calendar" msgid="3889615280211184106">"fikia kalenda yako"</string>
     <string name="permgrouplab_sms" msgid="228308803364967808">"SMS"</string>
diff --git a/core/res/res/values-uz-rUZ-watch/strings.xml b/core/res/res/values-uz-rUZ-watch/strings.xml
index 0fe54a1..44af51d 100644
--- a/core/res/res/values-uz-rUZ-watch/strings.xml
+++ b/core/res/res/values-uz-rUZ-watch/strings.xml
@@ -22,15 +22,15 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="android_upgrading_apk" msgid="1090732262010398759">"<xliff:g id="NUMBER_1">%2$d</xliff:g>dan <xliff:g id="NUMBER_0">%1$d</xliff:g> ilova."</string>
     <string name="permgrouplab_sensors" msgid="202675452368612754">"Sensorlar"</string>
-    <string name="permgrouplab_contactswear" msgid="2340286500790908344">"kontaktlarga kirishga ruxsat berish"</string>
+    <string name="permgrouplab_contactswear" msgid="2340286500790908344">"kontaktlarga kirish"</string>
     <string name="permgrouplab_locationwear" msgid="6275317222482780209">"mazkur soatning joylashgan joyini ko‘rishga ruxsat berish"</string>
     <string name="permgrouplab_calendarwear" msgid="441900844045065081">"taqvim ma’lumotlariga kirish"</string>
     <string name="permgrouplab_smswear" msgid="6849506550342974220">"SMS xabarlarni yuborish va ko‘rish"</string>
     <string name="permgrouplab_storagewear" msgid="1003807594193602313">"Soatingizdagi rasmlar, media va fayllarga kirish"</string>
     <string name="permgrouplab_microphonewear" msgid="1047561180980891136">"ovoz yozib olish"</string>
-    <string name="permgrouplab_camerawear" msgid="4543951283103407017">"rasmga tushirish va videoga olish"</string>
+    <string name="permgrouplab_camerawear" msgid="4543951283103407017">"suratga olish va video yozib olish"</string>
     <string name="permgrouplab_phonewear" msgid="134365036753766126">"telefon qo‘ng‘iroqlarini amalga oshirish va boshqarish"</string>
-    <string name="permgrouplab_sensorswear" msgid="1429324744329327663">"asosiy belgilaringiz haqidagi sezgich ma’lumotlaridan foydalanishga ruxsat"</string>
+    <string name="permgrouplab_sensorswear" msgid="1429324744329327663">"organizm holati haqidagi sezgich ma’lumotlariga kirish"</string>
     <string name="permlab_statusBarServicewear" msgid="2469402818964691034">"holat qatorida ko‘rinishi"</string>
     <string name="permlab_bodySensorswear" msgid="7857941041202791873">"tana sezgichlari (m-n, yurak urishi sensori) ma’lumotlaridan foydalanishga ruxsat"</string>
     <string name="permlab_accessFineLocationwear" msgid="5584423486924377563">"aniq joylashuv (GPS va tarmoqqa asoslanib) ma’lumotlaridan foydalanishga ruxsat"</string>
diff --git a/core/res/res/values-uz-rUZ/strings.xml b/core/res/res/values-uz-rUZ/strings.xml
index 325ad91..b7ae992 100644
--- a/core/res/res/values-uz-rUZ/strings.xml
+++ b/core/res/res/values-uz-rUZ/strings.xml
@@ -231,19 +231,19 @@
     <string name="permgrouplab_location" msgid="7275582855722310164">"Joylashuv"</string>
     <string name="permgroupdesc_location" msgid="1346617465127855033">"qurilmaning joylashuvi haqidagi ma’lumotlarga kirish"</string>
     <string name="permgrouplab_calendar" msgid="5863508437783683902">"Taqvim"</string>
-    <string name="permgroupdesc_calendar" msgid="3889615280211184106">"taqvimga kirish"</string>
+    <string name="permgroupdesc_calendar" msgid="3889615280211184106">"taqvim ma’lumotlariga kirish"</string>
     <string name="permgrouplab_sms" msgid="228308803364967808">"SMS"</string>
     <string name="permgroupdesc_sms" msgid="4656988620100940350">"SMS xabarlarni yuborish va ko‘rish"</string>
     <string name="permgrouplab_storage" msgid="1971118770546336966">"Xotira"</string>
-    <string name="permgroupdesc_storage" msgid="637758554581589203">"qurilmangizdagi rasm, media va fayllarga kirish"</string>
+    <string name="permgroupdesc_storage" msgid="637758554581589203">"qurilmangizdagi rasm, multimedia va fayllarga kirish"</string>
     <string name="permgrouplab_microphone" msgid="171539900250043464">"Mikrofon"</string>
     <string name="permgroupdesc_microphone" msgid="4988812113943554584">"ovoz yozib olish"</string>
     <string name="permgrouplab_camera" msgid="4820372495894586615">"Kamera"</string>
-    <string name="permgroupdesc_camera" msgid="3250611594678347720">"rasm va videoga olish"</string>
+    <string name="permgroupdesc_camera" msgid="3250611594678347720">"suratga olish va video yozib olish"</string>
     <string name="permgrouplab_phone" msgid="5229115638567440675">"Telefon"</string>
     <string name="permgroupdesc_phone" msgid="6234224354060641055">"telefon qo‘ng‘iroqlarini amalga oshirish va boshqarish"</string>
     <string name="permgrouplab_sensors" msgid="416037179223226722">"Tana sezgichlari"</string>
-    <string name="permgroupdesc_sensors" msgid="7147968539346634043">"asosiy belgilaringiz haqidagi sezgich ma’lumotlariga kirish"</string>
+    <string name="permgroupdesc_sensors" msgid="7147968539346634043">"organizm holati haqidagi sezgich ma’lumotlariga kirish"</string>
     <string name="capability_title_canRetrieveWindowContent" msgid="3901717936930170320">"Oynadagi kontentni o‘qiydi"</string>
     <string name="capability_desc_canRetrieveWindowContent" msgid="3772225008605310672">"Joriy oynadagi kontent mazmunini aniqlaydi."</string>
     <string name="capability_title_canRequestTouchExploration" msgid="3108723364676667320">"Teginib o‘rganish xizmatini yoqadi"</string>
@@ -342,7 +342,7 @@
     <string name="permdesc_accessCoarseLocation" msgid="2538200184373302295">"Ilovaga sizning taxminiy joylashuvingizni topishga ruxsat beradi. Ushbu joylashuv Wi-Fi va uyali tarmoq antennalari kabi tarmoq joylashuv manbalaridan foydlanuvchi joylashuv xizmatlari orqali aniqlanadi. Ushbu joylashuv xizmatlari yoqib qo‘yilgan bo‘lishi va qurilmangizdagi ilovaga ulardan foydalanish uchun mavjud bo‘lishi kerak. Ilovalar bundan foydalanib, sizning taxminiy joylashuvingizni aniqlaydi."</string>
     <string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"audio sozlamalaringizni o‘zgartirish"</string>
     <string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"Ilovalarga tovush va ovoz chiqarish uchun foydalaniladigan karnay kabi global audio sozlamalarini o‘zgartirish uchun ruxsat beradi."</string>
-    <string name="permlab_recordAudio" msgid="3876049771427466323">"audioni yozib olish"</string>
+    <string name="permlab_recordAudio" msgid="3876049771427466323">"ovoz yozib olish"</string>
     <string name="permdesc_recordAudio" msgid="4906839301087980680">"Ilovaga mikrofon yordamida audio yozish uchun ruxsat beradi. Bu huquq ilovaga ruxsatingizsiz audio fayllarni yozib olishga ruxsat beradi."</string>
     <string name="permlab_sim_communication" msgid="1180265879464893029">"sim orqali ulanish"</string>
     <string name="permdesc_sim_communication" msgid="5725159654279639498">"Dasturga SIM kartaga buyruqlar jo‘natishga ruxsat beradi. Bu juda ham xavfli."</string>
@@ -797,9 +797,9 @@
     <string name="menu_space_shortcut_label" msgid="2410328639272162537">"space"</string>
     <string name="menu_enter_shortcut_label" msgid="2743362785111309668">"enter"</string>
     <string name="menu_delete_shortcut_label" msgid="3658178007202748164">"delete"</string>
-    <string name="search_go" msgid="8298016669822141719">"Izlash"</string>
+    <string name="search_go" msgid="8298016669822141719">"Qidirish"</string>
     <string name="search_hint" msgid="1733947260773056054">"Qidirish…"</string>
-    <string name="searchview_description_search" msgid="6749826639098512120">"Izlash"</string>
+    <string name="searchview_description_search" msgid="6749826639098512120">"Qidirish"</string>
     <string name="searchview_description_query" msgid="5911778593125355124">"Qidiruv so‘rovi"</string>
     <string name="searchview_description_clear" msgid="1330281990951833033">"So‘rovni tozalash"</string>
     <string name="searchview_description_submit" msgid="2688450133297983542">"So‘rov yaratish"</string>
@@ -1082,7 +1082,7 @@
     <string name="tutorial_double_tap_to_zoom_message_short" msgid="4070433208160063538">"Masshtabni o‘zgartirish uchun ikki marta bosing"</string>
     <string name="gadget_host_error_inflating" msgid="4882004314906466162">"Vidjet qo‘shilmadi."</string>
     <string name="ime_action_go" msgid="8320845651737369027">"O‘tish"</string>
-    <string name="ime_action_search" msgid="658110271822807811">"Izlash"</string>
+    <string name="ime_action_search" msgid="658110271822807811">"Qidirish"</string>
     <string name="ime_action_send" msgid="2316166556349314424">"Jo‘natish"</string>
     <string name="ime_action_next" msgid="3138843904009813834">"Keyingi"</string>
     <string name="ime_action_done" msgid="8971516117910934605">"Tayyor"</string>
diff --git a/core/res/res/values-watch/config.xml b/core/res/res/values-watch/config.xml
index 41b05ea..919519e 100644
--- a/core/res/res/values-watch/config.xml
+++ b/core/res/res/values-watch/config.xml
@@ -54,4 +54,7 @@
 
     <!-- Do not show the message saying USB is connected in charging mode. -->
     <bool name="config_usbChargingMessage">false</bool>
+
+    <!-- Use a custom transition for RemoteViews. -->
+    <bool name="config_overrideRemoteViewsActivityTransition">true</bool>
 </resources>
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 48bfe28..a6eb68b 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -162,6 +162,9 @@
         <!-- Text color, typeface, size, and style for small text inside of a popup menu. -->
         <attr name="textAppearanceSmallPopupMenu" format="reference" />
 
+        <!-- Text color, typeface, size, and style for header text inside of a popup menu. -->
+        <attr name="textAppearancePopupMenuHeader" format="reference" />
+
         <!-- The underline color and thickness for easy correct suggestion -->
         <attr name="textAppearanceEasyCorrectSuggestion" format="reference" />
 
@@ -729,6 +732,8 @@
         <attr name="listPopupWindowStyle" format="reference" />
         <!-- Default PopupMenu style. -->
         <attr name="popupMenuStyle" format="reference" />
+        <!-- Default context menu PopupMenu style. -->
+        <attr name="contextPopupMenuStyle" format="reference" />
         <!-- Default StackView style. -->
         <attr name="stackViewStyle" format="reference" />
 
@@ -2150,6 +2155,13 @@
               (which is exiting the screen).  The wallpaper remains
               static behind the animation. -->
         <attr name="wallpaperIntraCloseExitAnimation" format="reference" />
+
+        <!--  When opening a new activity from a RemoteViews, this is the
+              animation that is run on the next activity (which is entering the
+              screen). Requires config_overrideRemoteViewsActivityTransition to
+              be true. -->
+        <attr name="activityOpenRemoteViewsEnterAnimation" format="reference" />
+
     </declare-styleable>
 
     <!-- ============================= -->
@@ -5550,6 +5562,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.
@@ -7769,10 +7783,24 @@
         <attr name="title" />
         <attr name="subtitle" />
         <attr name="gravity" />
-        <attr name="titleMargins" format="dimension" />
+        <!--  Specifies extra space on the left, start, right and end sides
+              of the toolbar's title. Margin values should be positive. -->
+        <attr name="titleMargin" format="dimension" />
+        <!--  Specifies extra space on the start side of the toolbar's title.
+              If both this attribute and titleMargin are specified, then this
+              attribute takes precedence. Margin values should be positive. -->
         <attr name="titleMarginStart" format="dimension" />
+        <!--  Specifies extra space on the end side of the toolbar's title.
+              If both this attribute and titleMargin are specified, then this
+              attribute takes precedence. Margin values should be positive. -->
         <attr name="titleMarginEnd" format="dimension" />
+        <!--  Specifies extra space on the top side of the toolbar's title.
+              If both this attribute and titleMargin are specified, then this
+              attribute takes precedence. Margin values should be positive. -->
         <attr name="titleMarginTop" format="dimension" />
+        <!--  Specifies extra space on the bottom side of the toolbar's title.
+              If both this attribute and titleMargin are specified, then this
+              attribute takes precedence. Margin values should be positive. -->
         <attr name="titleMarginBottom" format="dimension" />
         <attr name="contentInsetStart" />
         <attr name="contentInsetEnd" />
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index a5960a9..24d760f 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>
@@ -1759,6 +1762,28 @@
     -->
     <bool name="config_enableWifiDisplay">false</bool>
 
+    <!-- The color transform values that correspond to each respective configuration mode for the
+         built-in display, or -1 if the mode is unsupported by the device. The possible
+         configuration modes are:
+            1. Wide-gamut ("Vibrant")
+            2. Adobe RGB ("Natural")
+            3. sRGB ("Standard")
+
+        For example, if a device had Wide-gamut as color transform mode 1, sRGB mode as color
+        transform mode 7, and did not support Adobe RGB at all this would look like:
+
+            <integer-array name="config_colorTransforms">
+                <item>1</item>
+                <item>-1</item>
+                <item>7</item>
+            </integer-array>
+    -->
+    <integer-array name="config_colorTransforms">
+        <item>-1</item>
+        <item>-1</item>
+        <item>-1</item>
+    </integer-array>
+
     <!-- When true use the linux /dev/input/event subsystem to detect the switch changes
          on the headphone/microphone jack. When false use the older uevent framework. -->
     <bool name="config_useDevInputEventForAudioJack">false</bool>
@@ -2189,6 +2214,10 @@
     <bool name="config_defaultWindowFeatureOptionsPanel">true</bool>
     <bool name="config_defaultWindowFeatureContextMenu">true</bool>
 
+    <!-- If true, the transition for a RemoteViews is read from a resource instead of using the
+         default scale-up transition. -->
+    <bool name="config_overrideRemoteViewsActivityTransition">false</bool>
+
     <!-- This config is used to check if the carrier requires converting destination
          number before sending out a SMS.
          Formats for this configuration as below:
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/dimens_material.xml b/core/res/res/values/dimens_material.xml
index 55bea9e..96a81d1 100644
--- a/core/res/res/values/dimens_material.xml
+++ b/core/res/res/values/dimens_material.xml
@@ -72,6 +72,7 @@
     <dimen name="text_size_title_material_toolbar">20dp</dimen>
     <dimen name="text_size_subtitle_material_toolbar">16dp</dimen>
     <dimen name="text_size_menu_material">16sp</dimen>
+    <dimen name="text_size_menu_header_material">14sp</dimen>
     <dimen name="text_size_body_2_material">14sp</dimen>
     <dimen name="text_size_body_1_material">14sp</dimen>
     <dimen name="text_size_caption_material">12sp</dimen>
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index a2b6a7b..f4d0b39 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -2662,6 +2662,20 @@
 
     <public type="attr" name="listMenuViewStyle" />
     <public type="attr" name="subMenuArrow" />
+    <public type="attr" name="activityWidth" />
+    <public type="attr" name="activityHeight" />
+    <public type="attr" name="resizeableActivity" />
+    <public type="attr" name="titleMargin" />
+    <public type="attr" name="titleMarginStart" />
+    <public type="attr" name="titleMarginEnd" />
+    <public type="attr" name="titleMarginTop" />
+    <public type="attr" name="titleMarginBottom" />
+    <public type="attr" name="maxButtonHeight" />
+    <public type="attr" name="buttonGravity" />
+    <public type="attr" name="collapseIcon" />
+    <public type="attr" name="level" />
+    <public type="attr" name="contextPopupMenuStyle" />
+    <public type="attr" name="textAppearancePopupMenuHeader" />
 
     <public type="style" name="Theme.Material.DayNight" />
     <public type="style" name="Theme.Material.DayNight.DarkActionBar" />
@@ -2682,8 +2696,5 @@
     <public type="style" name="Theme.Material.DayNight.DialogWhenLarge.DarkActionBar" />
 
     <public type="id" name="accessibilityActionSetProgress" />
-    <public type="attr" name="activityWidth" />
-    <public type="attr" name="activityHeight" />
-    <public type="attr" name="resizeableActivity" />
 
 </resources>
diff --git a/core/res/res/values/styles.xml b/core/res/res/values/styles.xml
index 4bad16d..11c4cc0 100644
--- a/core/res/res/values/styles.xml
+++ b/core/res/res/values/styles.xml
@@ -1232,7 +1232,7 @@
         <item name="titleTextAppearance">@style/TextAppearance.Widget.Toolbar.Title</item>
         <item name="subtitleTextAppearance">@style/TextAppearance.Widget.Toolbar.Subtitle</item>
         <item name="minHeight">?attr/actionBarSize</item>
-        <item name="titleMargins">4dp</item>
+        <item name="titleMargin">4dp</item>
         <item name="maxButtonHeight">56dp</item>
         <item name="buttonGravity">top</item>
         <item name="navigationButtonStyle">@style/Widget.Toolbar.Button.Navigation</item>
diff --git a/core/res/res/values/styles_material.xml b/core/res/res/values/styles_material.xml
index 5dc14e3..38a1693 100644
--- a/core/res/res/values/styles_material.xml
+++ b/core/res/res/values/styles_material.xml
@@ -307,6 +307,10 @@
     <style name="TextAppearance.Material.Widget.PopupMenu"/>
     <style name="TextAppearance.Material.Widget.PopupMenu.Large" parent="TextAppearance.Material.Menu" />
     <style name="TextAppearance.Material.Widget.PopupMenu.Small" parent="TextAppearance.Material.Menu" />
+    <style name="TextAppearance.Material.Widget.PopupMenu.Header" parent="TextAppearance.Material.Subhead">
+        <item name="fontFamily">@string/font_family_title_material</item>
+        <item name="textSize">@dimen/text_size_menu_header_material</item>
+    </style>
 
     <style name="TextAppearance.Material.Widget.DropDownHint" parent="TextAppearance.Material.Menu" />
 
@@ -471,6 +475,7 @@
     <style name="Widget.Material.Button.Colored">
         <item name="background">@drawable/btn_colored_material</item>
         <item name="textAppearance">@style/TextAppearance.Material.Widget.Button.Inverse</item>
+        <item name="textColor">@color/btn_colored_text_material</item>
     </style>
 
     <!-- Small bordered ink button -->
@@ -487,7 +492,7 @@
 
     <!-- Colored borderless ink button -->
     <style name="Widget.Material.Button.Borderless.Colored">
-        <item name="textColor">@color/btn_colored_text_material</item>
+        <item name="textColor">@color/btn_colored_borderless_text_material</item>
     </style>
 
     <!-- Alert dialog button bar button -->
@@ -862,6 +867,11 @@
         <item name="dropDownHorizontalOffset">-4dip</item>
     </style>
 
+    <style name="Widget.Material.ContextPopupMenu" parent="Widget.Material.ListPopupWindow">
+        <item name="overlapAnchor">true</item>
+        <item name="popupEnterTransition">@null</item>
+    </style>
+
     <style name="Widget.Material.ActionButton">
         <item name="background">?attr/actionBarItemBackground</item>
         <item name="paddingStart">12dp</item>
diff --git a/core/res/res/values/styles_micro.xml b/core/res/res/values/styles_micro.xml
index c6052ff..05835e7 100644
--- a/core/res/res/values/styles_micro.xml
+++ b/core/res/res/values/styles_micro.xml
@@ -18,6 +18,7 @@
 
     <style name="Animation.Micro.Activity" parent="Animation.Material.Activity">
         <item name="activityOpenEnterAnimation">@anim/slide_in_micro</item>
+        <item name="activityOpenRemoteViewsEnterAnimation">@anim/slide_in_micro</item>
         <item name="activityOpenExitAnimation">@null</item>
         <item name="activityCloseEnterAnimation">@null</item>
         <item name="activityCloseExitAnimation">@anim/slide_out_micro</item>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 16e44b8..06de81d 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" />
@@ -1133,6 +1134,7 @@
   <java-symbol type="array" name="config_telephonyHardware" />
   <java-symbol type="array" name="config_keySystemUuidMapping" />
   <java-symbol type="array" name="config_gpsParameters" />
+  <java-symbol type="array" name="config_colorTransforms" />
 
   <java-symbol type="drawable" name="default_wallpaper" />
   <java-symbol type="drawable" name="indicator_input_error" />
@@ -1288,6 +1290,7 @@
   <java-symbol type="layout" name="number_picker" />
   <java-symbol type="layout" name="permissions_package_list_item" />
   <java-symbol type="layout" name="popup_menu_item_layout" />
+  <java-symbol type="layout" name="popup_menu_header_item_layout" />
   <java-symbol type="layout" name="remote_views_adapter_default_loading_view" />
   <java-symbol type="layout" name="search_bar" />
   <java-symbol type="layout" name="search_dropdown_item_icons_2line" />
@@ -1476,6 +1479,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" />
@@ -1718,6 +1722,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" />
@@ -2187,6 +2192,7 @@
   <java-symbol type="bool" name="config_sms_force_7bit_encoding" />
   <java-symbol type="bool" name="config_defaultWindowFeatureOptionsPanel" />
   <java-symbol type="bool" name="config_defaultWindowFeatureContextMenu" />
+  <java-symbol type="bool" name="config_overrideRemoteViewsActivityTransition" />
 
   <java-symbol type="layout" name="simple_account_item" />
   <java-symbol type="array" name="config_sms_convert_destination_number_support" />
diff --git a/core/res/res/values/themes_material.xml b/core/res/res/values/themes_material.xml
index 3010190..59dfc92 100644
--- a/core/res/res/values/themes_material.xml
+++ b/core/res/res/values/themes_material.xml
@@ -93,6 +93,7 @@
 
         <item name="textAppearanceLargePopupMenu">@style/TextAppearance.Material.Widget.PopupMenu.Large</item>
         <item name="textAppearanceSmallPopupMenu">@style/TextAppearance.Material.Widget.PopupMenu.Small</item>
+        <item name="textAppearancePopupMenuHeader">@style/TextAppearance.Material.Widget.PopupMenu.Header</item>
 
         <!-- Button styles -->
         <item name="buttonStyle">@style/Widget.Material.Button</item>
@@ -283,6 +284,7 @@
         <item name="stackViewStyle">@style/Widget.Material.StackView</item>
         <item name="activityChooserViewStyle">@style/Widget.Material.ActivityChooserView</item>
         <item name="fragmentBreadCrumbsStyle">@style/Widget.Material.FragmentBreadCrumbs</item>
+        <item name="contextPopupMenuStyle">@style/Widget.Material.ContextPopupMenu</item>
 
         <!-- Preference styles -->
         <item name="preferenceScreenStyle">@style/Preference.Material.PreferenceScreen</item>
@@ -449,6 +451,7 @@
 
         <item name="textAppearanceLargePopupMenu">@style/TextAppearance.Material.Widget.PopupMenu.Large</item>
         <item name="textAppearanceSmallPopupMenu">@style/TextAppearance.Material.Widget.PopupMenu.Small</item>
+        <item name="textAppearancePopupMenuHeader">@style/TextAppearance.Material.Widget.PopupMenu.Header</item>
 
         <!-- Button styles -->
         <item name="buttonStyle">@style/Widget.Material.Light.Button</item>
@@ -640,6 +643,7 @@
         <item name="stackViewStyle">@style/Widget.Material.Light.StackView</item>
         <item name="activityChooserViewStyle">@style/Widget.Material.Light.ActivityChooserView</item>
         <item name="fragmentBreadCrumbsStyle">@style/Widget.Material.FragmentBreadCrumbs</item>
+        <item name="contextPopupMenuStyle">@style/Widget.Material.ContextPopupMenu</item>
 
         <!-- Preference styles -->
         <item name="preferenceScreenStyle">@style/Preference.Material.PreferenceScreen</item>
diff --git a/core/res/res/xml/power_profile.xml b/core/res/res/xml/power_profile.xml
index 28d99d8..ddd0ca2 100644
--- a/core/res/res/xml/power_profile.xml
+++ b/core/res/res/xml/power_profile.xml
@@ -47,17 +47,38 @@
       <value>0.2</value> <!-- ~2mA -->
       <value>0.1</value> <!-- ~1mA -->
   </array>
-  <!-- Different CPU speeds as reported in
-       /sys/devices/system/cpu/cpu0/cpufreq/stats/time_in_state -->
-  <array name="cpu.speeds">
+
+  <!-- A list of heterogeneous CPU clusters, where the value for each cluster represents the
+       number of CPU cores for that cluster.
+
+       Ex:
+       <array name="cpu.clusters.cores">
+         <value>4</value> // cluster 0 has cpu0, cpu1, cpu2, cpu3
+         <value>2</value> // cluster 1 has cpu4, cpu5
+       </array> -->
+  <array name="cpu.clusters.cores">
+      <value>1</value> <!-- cluster 0 has cpu0 -->
+  </array>
+
+    <!-- Different CPU speeds for cluster 0 as reported in
+       /sys/devices/system/cpu/cpu0/cpufreq/stats/time_in_state.
+
+       There must be one of these for each cluster, labeled:
+       cpu.speeds.cluster0, cpu.speeds.cluster1, etc... -->
+  <array name="cpu.speeds.cluster0">
       <value>400000</value> <!-- 400 MHz CPU speed -->
   </array>
-  <!-- Current when CPU is idle -->
-  <item name="cpu.idle">0.1</item>
-  <!-- Current at each CPU speed, as per 'cpu.speeds' -->
-  <array name="cpu.active">
+
+  <!-- Current at each CPU speed for cluster 0, as per 'cpu.speeds.cluster0'.
+       Like cpu.speeds.cluster0, there must be one of these present for
+       each heterogeneous CPU cluster. -->
+  <array name="cpu.active.cluster0">
       <value>0.1</value>  <!-- ~100mA -->
   </array>
+
+  <!-- Current when CPU is idle -->
+  <item name="cpu.idle">0.1</item>
+
   <!-- This is the battery capacity in mAh (measured at nominal voltage) -->
   <item name="battery.capacity">1000</item>
 
diff --git a/core/tests/BTtraffic/Android.mk b/core/tests/BTtraffic/Android.mk
new file mode 100644
index 0000000..7d83527
--- /dev/null
+++ b/core/tests/BTtraffic/Android.mk
@@ -0,0 +1,16 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_RESOURCE_DIR := \
+    $(LOCAL_PATH)/res
+
+LOCAL_PACKAGE_NAME := bttraffic
+LOCAL_CERTIFICATE := platform
+
+include $(BUILD_PACKAGE)
+
+include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/core/tests/BTtraffic/AndroidManifest.xml b/core/tests/BTtraffic/AndroidManifest.xml
new file mode 100644
index 0000000..00d9707
--- /dev/null
+++ b/core/tests/BTtraffic/AndroidManifest.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.google.android.experimental.bttraffic" >
+
+    <uses-permission android:name="android.permission.BLUETOOTH"/>
+    <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS_FULL"/>
+
+    <uses-sdk
+        android:minSdkVersion="18"
+        android:targetSdkVersion="18"
+        />
+    <application
+        android:allowBackup="false"
+        android:label="@string/app_name" >
+        <service
+            android:name=".BTtraffic"
+            android:enabled="true"
+            android:exported="true" >
+        </service>
+    </application>
+
+</manifest>
diff --git a/core/tests/BTtraffic/README b/core/tests/BTtraffic/README
new file mode 100644
index 0000000..430488f
--- /dev/null
+++ b/core/tests/BTtraffic/README
@@ -0,0 +1,45 @@
+This is a tool to generate classic Bluetooth traffic with specified period and package size.
+Together with the SvcMonitor, which will be called automatically in this android service, can be
+used to measure the CPU usage from the Java layer Bluetooth code and the underlying system service
+com.android.bluetooth.
+
+1. Server (Listener) - Client (Sender) model. Both run as an Android service.
+2. No pairing needed. Communicate via unsecured RFcomm. Client establishes the connection by
+providing the MAC addr of the server.
+3. Bluetooth has to be turned on on both side.
+4. Client can configure the traffic by specifying the transfer period and package size.
+5. A separate monitor process will be automatically forked and will be reading from /proc file
+system to calculate the cpu usage. The measurement is updated once per second.
+6. The monitor process (com.google.android.experimental.svcmonitor/.ScvMonitor) can be run as an
+independent service to measure cpu usage on any similarly configured tests (e.g. wifi, BLE). Refer
+to SvcMonitor's README for usage and details.
+
+Usage:
+To instal the test:
+On both the server and client device, install the 2 apk:
+$ adb install $OUT/system/app/bttraffic/bttraffic.apk
+$ adb install $OUT/system/app/svcmonitor/svcmonitor.apk
+
+To start the service on the SERVER side:
+$ adb shell am startservice -a start --ez ack true \
+com.google.android.experimental.bttraffic/.BTtraffic
+
+To start the service on the CLIENT side:
+$ adb shell am startservice -a start \
+-e addr "F8:A9:D0:A8:74:8E" --ei size 1000 --ei period 15 \
+com.google.android.experimental.bttraffic/.BTtraffic
+
+To stop the test:
+On either the server or client:
+$ adb shell am startservice -a stop \
+com.google.android.experimental.bttraffic/.BTtraffic
+
+To look at the data:
+$ adb logcat | grep bttraffic
+
+Options:
+-e addr: MAC addr of the server, in uppercase letter.
+--ei size: package size, unit: byte; default: 1024, MAX: 20MB
+--ei period: system sleep time between sending each package, unit: ms, default: 5000
+                  ** if -1 is provided, client will only send the package once.
+--ez ack: whether acknowledge is required (true/false)
diff --git a/core/tests/BTtraffic/res/values/strings.xml b/core/tests/BTtraffic/res/values/strings.xml
new file mode 100644
index 0000000..e70276e
--- /dev/null
+++ b/core/tests/BTtraffic/res/values/strings.xml
@@ -0,0 +1,3 @@
+<resources>
+    <string name="app_name">Bluetooth Test</string>
+</resources>
diff --git a/core/tests/BTtraffic/src/com/android/google/experimental/bttraffic/BTtraffic.java b/core/tests/BTtraffic/src/com/android/google/experimental/bttraffic/BTtraffic.java
new file mode 100644
index 0000000..286c0aa
--- /dev/null
+++ b/core/tests/BTtraffic/src/com/android/google/experimental/bttraffic/BTtraffic.java
@@ -0,0 +1,328 @@
+package com.google.android.experimental.bttraffic;
+
+import android.app.Service;
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothServerSocket;
+import android.bluetooth.BluetoothSocket;
+import android.content.Intent;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.os.SystemClock;
+import android.util.Log;
+
+import java.io.Closeable;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.lang.Exception;
+import java.lang.Runtime;
+import java.lang.RuntimeException;
+import java.lang.Process;
+import java.nio.ByteBuffer;
+import java.util.Random;
+import java.util.Set;
+import java.util.UUID;
+
+public class BTtraffic extends Service {
+    public static final String TAG = "bttraffic";
+    static final String SERVICE_NAME = "bttraffic";
+    static final String SYS_SERVICE_NAME = "com.android.bluetooth";
+    static final UUID SERVICE_UUID = UUID.fromString("5e8945b0-1234-5432-a5e2-0800200c9a67");
+    volatile Thread mWorkerThread;
+    volatile boolean isShuttingDown = false;
+    volatile boolean isServer = false;
+
+    public BTtraffic() {}
+
+    static void safeClose(Closeable closeable) {
+        try {
+            closeable.close();
+        } catch (IOException e) {
+            Log.d(TAG, "Unable to close resource.\n");
+        }
+    }
+
+    @Override
+    public int onStartCommand(Intent intent, int flags, int startId) {
+        if (intent == null) {
+            stopSelf();
+            return 0;
+        }
+        if ("stop".equals(intent.getAction())) {
+            stopService();
+        } else if ("start".equals(intent.getAction())) {
+            startWorker(intent);
+        } else {
+            Log.d(TAG, "unknown action: + " + intent.getAction());
+        }
+        return 0;
+    }
+
+    private void startWorker(Intent intent) {
+        if (mWorkerThread != null) {
+            Log.d(TAG, "worker thread already active");
+            return;
+        }
+        isShuttingDown = false;
+        String remoteAddr = intent.getStringExtra("addr");
+        Log.d(TAG, "startWorker: addr=" + remoteAddr);
+        Runnable worker =
+                remoteAddr == null
+                        ? new ListenerRunnable(this, intent)
+                        : new SenderRunnable(this, remoteAddr, intent);
+        isServer = remoteAddr == null ? true: false;
+        mWorkerThread = new Thread(worker, "BTtrafficWorker");
+        try {
+            startMonitor();
+            Log.d(TAG, "Monitor service started");
+            mWorkerThread.start();
+            Log.d(TAG, "Worker thread started");
+        } catch (Exception e) {
+            Log.d(TAG, "Failed to start service", e);
+        }
+    }
+
+    private void startMonitor()
+            throws Exception {
+        if (isServer) {
+            Log.d(TAG, "Start monitor on server");
+            String[] startmonitorCmd = {
+                    "/system/bin/am",
+                    "startservice",
+                    "-a", "start",
+                    "-e", "java", SERVICE_NAME,
+                    "-e", "hal", SYS_SERVICE_NAME,
+                    "com.google.android.experimental.svcmonitor/.SvcMonitor"
+            };
+            Process ps = new ProcessBuilder()
+                    .command(startmonitorCmd)
+                    .redirectErrorStream(true)
+                    .start();
+        } else {
+            Log.d(TAG, "No need to start SvcMonitor on client");
+        }
+    }
+
+    private void stopMonitor()
+            throws Exception {
+        if (isServer) {
+            Log.d(TAG, "StopMonitor on server");
+            String[] stopmonitorCmd = {
+                    "/system/bin/am",
+                    "startservice",
+                    "-a", "stop",
+                    "com.google.android.experimental.svcmonitor/.SvcMonitor"
+            };
+            Process ps = new ProcessBuilder()
+                    .command(stopmonitorCmd)
+                    .redirectErrorStream(true)
+                    .start();
+        } else {
+            Log.d(TAG, "No need to stop Svcmonitor on client");
+        }
+    }
+
+    public void stopService() {
+        if (mWorkerThread == null) {
+            Log.d(TAG, "no active thread");
+            return;
+        }
+
+        isShuttingDown = true;
+
+        try {
+            stopMonitor();
+        } catch (Exception e) {
+            Log.d(TAG, "Unable to stop SvcMonitor!", e);
+        }
+
+        if (Thread.currentThread() != mWorkerThread) {
+            mWorkerThread.interrupt();
+            Log.d(TAG, "Interrupting thread");
+            try {
+                mWorkerThread.join();
+            } catch (InterruptedException e) {
+                Log.d(TAG, "Unable to join thread!");
+            }
+        }
+
+        mWorkerThread = null;
+        stopSelf();
+        Log.d(TAG, "Service stopped");
+    }
+
+    @Override
+    public void onDestroy() {
+        super.onDestroy();
+    }
+
+    @Override
+    public IBinder onBind(Intent intent) {
+        throw new UnsupportedOperationException("Not yet implemented");
+    }
+
+    public static class ListenerRunnable implements Runnable {
+        private final BTtraffic bttraffic;
+        private final boolean sendAck;
+        private Intent intent;
+        private final int maxbuffersize = 20 * 1024 * 1024;
+
+        public ListenerRunnable(BTtraffic bttraffic, Intent intent) {
+            this.bttraffic = bttraffic;
+            this.sendAck = intent.getBooleanExtra("ack", true);
+            this.intent = intent;
+        }
+
+        @Override
+        public void run() {
+            BluetoothServerSocket serverSocket;
+
+            try {
+                Log.d(TAG, "getting server socket");
+                serverSocket = BluetoothAdapter.getDefaultAdapter()
+                        .listenUsingInsecureRfcommWithServiceRecord(
+                                SERVICE_NAME, SERVICE_UUID);
+            } catch (IOException e) {
+                Log.d(TAG, "error creating server socket, stopping thread");
+                bttraffic.stopService();
+                return;
+            }
+
+            Log.d(TAG, "got server socket, starting accept loop");
+            BluetoothSocket socket = null;
+            try {
+                Log.d(TAG, "accepting");
+                socket = serverSocket.accept();
+
+                if (!Thread.interrupted()) {
+                    Log.d(TAG, "accepted, listening");
+                    doListening(socket.getInputStream(), socket.getOutputStream());
+                    Log.d(TAG, "listen finished");
+                }
+            } catch (IOException e) {
+                Log.d(TAG, "error while accepting or listening", e);
+            } finally {
+                Log.d(TAG, "Linster interruped");
+                Log.d(TAG, "closing socket and stopping service");
+                safeClose(serverSocket);
+                safeClose(socket);
+                if (!bttraffic.isShuttingDown)
+                    bttraffic.stopService();
+            }
+
+        }
+
+        private void doListening(InputStream inputStream, OutputStream outputStream)
+                throws IOException {
+            ByteBuffer byteBuffer = ByteBuffer.allocate(maxbuffersize);
+
+            while (!Thread.interrupted()) {
+                readBytesIntoBuffer(inputStream, byteBuffer, 4);
+                byteBuffer.flip();
+                int length = byteBuffer.getInt();
+                if (Thread.interrupted())
+                    break;
+                readBytesIntoBuffer(inputStream, byteBuffer, length);
+
+                if (sendAck)
+                    outputStream.write(0x55);
+            }
+        }
+
+        void readBytesIntoBuffer(InputStream inputStream, ByteBuffer byteBuffer, int numToRead)
+                throws IOException {
+            byteBuffer.clear();
+            while (true) {
+                int position = byteBuffer.position();
+                int remaining = numToRead - position;
+                if (remaining == 0) {
+                    break;
+                }
+                int count = inputStream.read(byteBuffer.array(), position, remaining);
+                if (count < 0) {
+                    throw new IOException("read the EOF");
+                }
+                byteBuffer.position(position + count);
+            }
+        }
+    }
+
+    public static class SenderRunnable implements Runnable {
+        private final BTtraffic bttraffic;
+        private final String remoteAddr;
+        private final int pkgsize, period;
+        private final int defaultpkgsize = 1024;
+        private final int defaultperiod = 5000;
+        private static ByteBuffer lengthBuffer = ByteBuffer.allocate(4);
+
+        public SenderRunnable(BTtraffic bttraffic, String remoteAddr, Intent intent) {
+            this.bttraffic = bttraffic;
+            this.remoteAddr = remoteAddr;
+            this.pkgsize = intent.getIntExtra("size", defaultpkgsize);
+            this.period = intent.getIntExtra("period", defaultperiod);
+        }
+
+        @Override
+        public void run() {
+            BluetoothDevice device = null;
+            try {
+                device = BluetoothAdapter.getDefaultAdapter().getRemoteDevice(remoteAddr);
+            } catch (IllegalArgumentException e) {
+                Log.d(TAG, "Invalid BT MAC address!\n");
+            }
+            if (device == null) {
+                Log.d(TAG, "can't find matching device, stopping thread and service");
+                bttraffic.stopService();
+                return;
+            }
+
+            BluetoothSocket socket = null;
+            try {
+                Log.d(TAG, "connecting to device with MAC addr: " + remoteAddr);
+                socket = device.createInsecureRfcommSocketToServiceRecord(SERVICE_UUID);
+                socket.connect();
+                Log.d(TAG, "connected, starting to send");
+                doSending(socket.getOutputStream());
+                Log.d(TAG, "send stopped, stopping service");
+            } catch (Exception e) {
+                Log.d(TAG, "error while sending", e);
+            } finally {
+                Log.d(TAG, "finishing, closing thread and service");
+                safeClose(socket);
+                if (!bttraffic.isShuttingDown)
+                    bttraffic.stopService();
+            }
+        }
+
+        private void doSending(OutputStream outputStream) throws IOException {
+            Log.w(TAG, "doSending");
+            try {
+                Random random = new Random(System.currentTimeMillis());
+
+                byte[] bytes = new byte[pkgsize];
+                random.nextBytes(bytes);
+                while (!Thread.interrupted()) {
+                    writeBytes(outputStream, bytes.length);
+                    outputStream.write(bytes, 0, bytes.length);
+                    if (period < 0)
+                        break;
+                    if (period == 0)
+                        continue;
+
+                    SystemClock.sleep(period);
+                }
+                Log.d(TAG, "Sender interrupted");
+            } catch (IOException e) {
+                Log.d(TAG, "doSending got error", e);
+            }
+        }
+
+        private static void writeBytes(OutputStream outputStream, int value) throws IOException {
+            lengthBuffer.putInt(value);
+            lengthBuffer.flip();
+            outputStream.write(lengthBuffer.array(), lengthBuffer.position(), lengthBuffer.limit());
+        }
+    }
+
+}
diff --git a/core/tests/SvcMonitor/Android.mk b/core/tests/SvcMonitor/Android.mk
new file mode 100644
index 0000000..2b80455
--- /dev/null
+++ b/core/tests/SvcMonitor/Android.mk
@@ -0,0 +1,16 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_RESOURCE_DIR := \
+    $(LOCAL_PATH)/res
+
+LOCAL_PACKAGE_NAME := svcmonitor
+LOCAL_CERTIFICATE := platform
+
+include $(BUILD_PACKAGE)
+
+include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/core/tests/SvcMonitor/AndroidManifest.xml b/core/tests/SvcMonitor/AndroidManifest.xml
new file mode 100644
index 0000000..de5a9bd
--- /dev/null
+++ b/core/tests/SvcMonitor/AndroidManifest.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.google.android.experimental.svcmonitor" >
+
+    <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS_FULL"/>
+
+    <uses-sdk
+        android:minSdkVersion="18"
+        android:targetSdkVersion="18"
+        />
+    <application
+        android:allowBackup="false"
+        android:label="@string/app_name" >
+        <service
+            android:name=".SvcMonitor"
+            android:enabled="true"
+            android:exported="true" >
+        </service>
+    </application>
+
+</manifest>
diff --git a/core/tests/SvcMonitor/README b/core/tests/SvcMonitor/README
new file mode 100644
index 0000000..13a4380
--- /dev/null
+++ b/core/tests/SvcMonitor/README
@@ -0,0 +1,27 @@
+This Android service measures CPU usage of a program and an underlying system service it relies on.
+An example of this would be an android app XYZ communicates to some other device via Bluetooth. The
+SvcMonitor service can monitor the CPU usage of XYZ and com.android.bluetooth.
+
+Usage:
+
+To start the service:
+$ adb shell am startservice -a start \
+-e java XYZ -e hal com.android.bluetooth \
+com.google.android.experimental.svcmonitor/.SvcMonitor
+
+To stop the service:
+$ adb shell am startservice -a stop \
+com.google.android.experimental.svcmonitor/.SvcMonitor
+
+To stop the service config:
+$ adb shell am startservice -a change \
+-e java NewName -e hal NewService \
+com.google.android.experimental.svcmonitor/.SvcMonitor
+
+To monitor the data:
+$ adb logcat | grep XYZ
+
+Options:
+-e java NameOfProgram: any running process’s name.
+-e hal NameOfSysService: name of the system service the previous process relies on.
+--ei period: period between each measurement (frequency). Unit: ms, Default:1000, Min: 100
diff --git a/core/tests/SvcMonitor/res/values/strings.xml b/core/tests/SvcMonitor/res/values/strings.xml
new file mode 100644
index 0000000..e70276e
--- /dev/null
+++ b/core/tests/SvcMonitor/res/values/strings.xml
@@ -0,0 +1,3 @@
+<resources>
+    <string name="app_name">Bluetooth Test</string>
+</resources>
diff --git a/core/tests/SvcMonitor/src/com/android/google/experimental/svcmoniter/SvcMonitor.java b/core/tests/SvcMonitor/src/com/android/google/experimental/svcmoniter/SvcMonitor.java
new file mode 100644
index 0000000..a451445
--- /dev/null
+++ b/core/tests/SvcMonitor/src/com/android/google/experimental/svcmoniter/SvcMonitor.java
@@ -0,0 +1,209 @@
+package com.google.android.experimental.svcmonitor;
+
+import android.app.Service;
+import android.content.Intent;
+import android.os.IBinder;
+import android.os.SystemClock;
+import android.util.Log;
+
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.BufferedReader;
+import java.io.FileInputStream;
+import java.lang.Runnable;
+import java.lang.Thread;
+import java.util.Set;
+
+public class SvcMonitor extends Service {
+    public static final String TAG = "svcmonitor";
+    String javaProc, halProc;
+    volatile Thread tMonitor;
+    int period;
+
+    public SvcMonitor() {};
+
+    @Override
+    public int onStartCommand(Intent intent, int flags, int startId) {
+        if (intent == null) {
+            stopSelf();
+            return 0;
+        }
+        Log.d(TAG, "Starting SvcMonitor");
+        if ("stop".equals(intent.getAction())) {
+            stopService();
+        } else if ("start".equals(intent.getAction())) {
+            startMonitor(intent);
+        } else if ("change".equals(intent.getAction())) {
+            changeConfig(intent);
+        } else {
+            Log.d(TAG, "unknown action: + " + intent.getAction());
+        }
+        return 0;
+    }
+
+    private void changeConfig(Intent intent) {
+        if (tMonitor == null) {
+            Log.d(TAG, "Service not active. Start service first");
+            return;
+        }
+        stopThread();
+        startMonitor(intent);
+    }
+
+    private void startMonitor(Intent intent) {
+        if (tMonitor != null) {
+            Log.d(TAG, "thread already active");
+            return;
+        }
+        javaProc = intent.getStringExtra("java");
+        halProc = intent.getStringExtra("hal");
+        period = intent.getIntExtra("period", 1000);
+        if (javaProc == null || halProc == null || period < 100) {
+            Log.d(TAG, "Failed starting monitor, invalid arguments.");
+            stopSelf();
+            return;
+        }
+        Runnable monitor = new MonitorRunnable(this);
+        tMonitor = new Thread(monitor);
+        tMonitor.start();
+    }
+
+    private void stopService() {
+        stopThread();
+        stopSelf();
+        Log.d(TAG, "SvcMonitor stopped");
+    }
+
+    private void stopThread() {
+        if (tMonitor == null) {
+            Log.d(TAG, "no active thread");
+            return;
+        }
+        Log.d(TAG, "interrupting monitor thread");
+        tMonitor.interrupt();
+        try {
+            tMonitor.join();
+        } catch (InterruptedException e) {
+            Log.d(TAG, "Unable to finish monitor thread");
+        }
+        tMonitor = null;
+    }
+
+    @Override
+    public void onDestroy() {
+        super.onDestroy();
+    }
+
+    @Override
+    public IBinder onBind(Intent intent) {
+        throw new UnsupportedOperationException("Not yet implemented");
+    }
+
+    public static class MonitorRunnable implements Runnable {
+        long java_time_old, hal_time_old, cpu_time_old = -1;
+        String javaPID, halPID;
+        SvcMonitor svcmonitor;
+        static String javaProcTAG;
+        int period;
+
+        public MonitorRunnable(SvcMonitor svcmonitor) {
+            this.svcmonitor = svcmonitor;
+            this.period = svcmonitor.period;
+            javaPID = getPIDof(svcmonitor.javaProc);
+            halPID = getPIDof(svcmonitor.halProc);
+            java_time_old = getPsTime(javaPID);
+            hal_time_old = getPsTime(halPID);
+            cpu_time_old = getPsTime("");
+            javaProcTAG = String.valueOf(svcmonitor.javaProc.toCharArray());
+        }
+
+        @Override
+        public void run() {
+            if (halPID.isEmpty() || javaPID.isEmpty()) {
+                Log.d(javaProcTAG, "No such process: " +
+                        (halPID.isEmpty() ? svcmonitor.halProc : svcmonitor.javaProc));
+                return;
+            }
+            while (!Thread.interrupted()) {
+                calculateUsage();
+                SystemClock.sleep(period);
+            }
+            Log.d(TAG, "Stopping monitor thread");
+        }
+
+        private void calculateUsage() {
+            long java_time = getPsTime(javaPID);
+            long hal_time = getPsTime(halPID);
+            long cpu_time = getPsTime("");
+
+            if (cpu_time_old >= 0) {
+                float java_diff = (float) (java_time - java_time_old);
+                float hal_diff = (float) (hal_time - hal_time_old);
+                float cpu_diff = (float) (cpu_time - cpu_time_old);
+                Log.w(javaProcTAG, "\n----------------\n");
+                Log.w(javaProcTAG, "JAVA level CPU: "
+                        + (java_diff * 100.0 / cpu_diff) + "%\n");
+                Log.w(javaProcTAG, " HAL level CPU: "
+                        + (hal_diff * 100.0 / cpu_diff) + "%\n");
+                Log.w(javaProcTAG, " SYS level CPU: "
+                        + ((java_diff + hal_diff) * 100.0 / cpu_diff) + "%\n");
+            } else {
+                Log.w(TAG, "Waiting for status\n");
+            }
+
+            java_time_old = java_time;
+            hal_time_old = hal_time;
+            cpu_time_old = cpu_time;
+        }
+
+        private String getPIDof(String psName) {
+            String pid = "";
+
+            try {
+                String[] cmd = {"/system/bin/sh", "-c", "ps | grep " + psName};
+                Process ps = Runtime.getRuntime().exec(cmd);
+                BufferedReader in = new BufferedReader(
+                        new InputStreamReader(ps.getInputStream()));
+                String temp = in.readLine();
+                if (temp == null || temp.isEmpty())
+                    throw new IOException("No such process: " + psName);
+                pid = temp.split(" +")[1];
+                in.close();
+            } catch (IOException e) {
+                Log.d(javaProcTAG, "Error finding PID of process: " + psName + "\n", e);
+            }
+            return pid;
+        }
+
+        private long getPsTime(String pid) {
+            String psStat = getPsStat("/" + pid);
+            String[] statBreakDown = psStat.split(" +");
+            long psTime;
+
+            if (pid.isEmpty()) {
+                psTime = Long.parseLong(statBreakDown[1])
+                        + Long.parseLong(statBreakDown[2])
+                        + Long.parseLong(statBreakDown[3])
+                        + Long.parseLong(statBreakDown[4]);
+            } else {
+                psTime = Long.parseLong(statBreakDown[13])
+                        + Long.parseLong(statBreakDown[14]);
+            }
+
+            return psTime;
+        }
+
+        private String getPsStat(String psname) {
+            String stat = "";
+            try {
+                FileInputStream fs = new FileInputStream("/proc" + psname + "/stat");
+                BufferedReader br = new BufferedReader(new InputStreamReader(fs));
+                stat = br.readLine();
+                fs.close();
+            } catch (IOException e) {
+                Log.d(TAG, "Error retreiving stat. \n");
+            }
+            return stat;
+        }
+    }
+}
diff --git a/core/tests/coretests/apks/install_jni_lib/com_android_frameworks_coretests_JNITest.cpp b/core/tests/coretests/apks/install_jni_lib/com_android_frameworks_coretests_JNITest.cpp
index e0b616c..3e83010 100644
--- a/core/tests/coretests/apks/install_jni_lib/com_android_frameworks_coretests_JNITest.cpp
+++ b/core/tests/coretests/apks/install_jni_lib/com_android_frameworks_coretests_JNITest.cpp
@@ -22,7 +22,7 @@
     return 1;
 }
 
-static JNINativeMethod sMethods[] = {
+static const JNINativeMethod sMethods[] = {
     /* name, signature, funcPtr */
     { "checkFunction", "()I", (void*) checkFunction },
 };
diff --git a/core/tests/coretests/assets/fonts/HintedAdvanceWidthTest-Regular.ttf b/core/tests/coretests/assets/fonts/HintedAdvanceWidthTest-Regular.ttf
new file mode 100644
index 0000000..1924c35
--- /dev/null
+++ b/core/tests/coretests/assets/fonts/HintedAdvanceWidthTest-Regular.ttf
Binary files differ
diff --git a/core/tests/coretests/assets/fonts/HintedAdvanceWidthTest-Regular.ttx b/core/tests/coretests/assets/fonts/HintedAdvanceWidthTest-Regular.ttx
new file mode 100644
index 0000000..2b8478d
--- /dev/null
+++ b/core/tests/coretests/assets/fonts/HintedAdvanceWidthTest-Regular.ttx
@@ -0,0 +1,328 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.0">
+
+  <GlyphOrder>
+    <!-- The 'id' attribute is only for humans; it is ignored when parsed. -->
+    <GlyphID id="0" name=".notdef"/>
+    <GlyphID id="1" name="space"/>
+    <GlyphID id="9" name="H"/>
+    <GlyphID id="16" name="O"/>
+  </GlyphOrder>
+
+  <head>
+    <!-- Most of this table will be recalculated by the compiler -->
+    <tableVersion value="1.0"/>
+    <fontRevision value="1.0"/>
+    <checkSumAdjustment value="0x640cdb2f"/>
+    <magicNumber value="0x5f0f3cf5"/>
+    <flags value="00000000 00000011"/>
+    <unitsPerEm value="1000"/>
+    <created value="Wed Sep  9 08:01:17 2015"/>
+    <modified value="Wed Sep  9 08:48:07 2015"/>
+    <xMin value="30"/>
+    <yMin value="-200"/>
+    <xMax value="629"/>
+    <yMax value="800"/>
+    <macStyle value="00000000 00000000"/>
+    <lowestRecPPEM value="7"/>
+    <fontDirectionHint value="2"/>
+    <indexToLocFormat value="0"/>
+    <glyphDataFormat value="0"/>
+  </head>
+
+  <hhea>
+    <tableVersion value="1.0"/>
+    <ascent value="1000"/>
+    <descent value="-200"/>
+    <lineGap value="0"/>
+    <advanceWidthMax value="659"/>
+    <minLeftSideBearing value="0"/>
+    <minRightSideBearing value="30"/>
+    <xMaxExtent value="629"/>
+    <caretSlopeRise value="1"/>
+    <caretSlopeRun value="0"/>
+    <caretOffset value="0"/>
+    <reserved0 value="0"/>
+    <reserved1 value="0"/>
+    <reserved2 value="0"/>
+    <reserved3 value="0"/>
+    <metricDataFormat value="0"/>
+    <numberOfHMetrics value="18"/>
+  </hhea>
+
+  <maxp>
+    <!-- Most of this table will be recalculated by the compiler -->
+    <tableVersion value="0x10000"/>
+    <numGlyphs value="54"/>
+    <maxPoints value="73"/>
+    <maxContours value="10"/>
+    <maxCompositePoints value="0"/>
+    <maxCompositeContours value="0"/>
+    <maxZones value="2"/>
+    <maxTwilightPoints value="12"/>
+    <maxStorage value="28"/>
+    <maxFunctionDefs value="119"/>
+    <maxInstructionDefs value="0"/>
+    <maxStackElements value="61"/>
+    <maxSizeOfInstructions value="2967"/>
+    <maxComponentElements value="0"/>
+    <maxComponentDepth value="0"/>
+  </maxp>
+
+  <OS_2>
+    <!-- The fields 'usFirstCharIndex' and 'usLastCharIndex'
+         will be recalculated by the compiler -->
+    <version value="3"/>
+    <xAvgCharWidth value="594"/>
+    <usWeightClass value="400"/>
+    <usWidthClass value="5"/>
+    <fsType value="00000000 00001000"/>
+    <ySubscriptXSize value="650"/>
+    <ySubscriptYSize value="600"/>
+    <ySubscriptXOffset value="0"/>
+    <ySubscriptYOffset value="75"/>
+    <ySuperscriptXSize value="650"/>
+    <ySuperscriptYSize value="600"/>
+    <ySuperscriptXOffset value="0"/>
+    <ySuperscriptYOffset value="350"/>
+    <yStrikeoutSize value="50"/>
+    <yStrikeoutPosition value="300"/>
+    <sFamilyClass value="0"/>
+    <panose>
+      <bFamilyType value="0"/>
+      <bSerifStyle value="0"/>
+      <bWeight value="5"/>
+      <bProportion value="0"/>
+      <bContrast value="0"/>
+      <bStrokeVariation value="0"/>
+      <bArmStyle value="0"/>
+      <bLetterForm value="0"/>
+      <bMidline value="0"/>
+      <bXHeight value="0"/>
+    </panose>
+    <ulUnicodeRange1 value="00000000 00000000 00000000 00000001"/>
+    <ulUnicodeRange2 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange3 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange4 value="00000000 00000000 00000000 00000000"/>
+    <achVendID value="UKWN"/>
+    <fsSelection value="00000000 01000000"/>
+    <usFirstCharIndex value="32"/>
+    <usLastCharIndex value="122"/>
+    <sTypoAscender value="800"/>
+    <sTypoDescender value="-200"/>
+    <sTypoLineGap value="200"/>
+    <usWinAscent value="1000"/>
+    <usWinDescent value="200"/>
+    <ulCodePageRange1 value="00000000 00000000 00000000 00000001"/>
+    <ulCodePageRange2 value="00000000 00000000 00000000 00000000"/>
+    <sxHeight value="500"/>
+    <sCapHeight value="700"/>
+    <usDefaultChar value="0"/>
+    <usBreakChar value="32"/>
+    <usMaxContext value="0"/>
+  </OS_2>
+
+  <hmtx>
+    <mtx name=".notdef" width="500" lsb="93"/>
+    <mtx name="H" width="627" lsb="50"/>
+    <mtx name="O" width="659" lsb="30"/>
+    <mtx name="space" width="300" lsb="0"/>
+  </hmtx>
+
+  <cmap>
+    <tableVersion version="0"/>
+    <cmap_format_4 platformID="0" platEncID="3" language="0">
+      <map code="0x20" name="space"/><!-- SPACE -->
+      <map code="0x48" name="H"/><!-- LATIN CAPITAL LETTER H -->
+      <map code="0x4f" name="O"/><!-- LATIN CAPITAL LETTER O -->
+    </cmap_format_4>
+    <cmap_format_4 platformID="3" platEncID="1" language="0">
+      <map code="0x20" name="space"/><!-- SPACE -->
+      <map code="0x48" name="H"/><!-- LATIN CAPITAL LETTER H -->
+      <map code="0x4f" name="O"/><!-- LATIN CAPITAL LETTER O -->
+    </cmap_format_4>
+  </cmap>
+
+  <fpgm>
+    <assembly>
+    </assembly>
+  </fpgm>
+
+  <prep>
+    <assembly>
+    </assembly>
+  </prep>
+
+  <cvt>
+    <cv index="0" value="0"/>
+  </cvt>
+
+  <loca>
+    <!-- The 'loca' table will be calculated by the compiler -->
+  </loca>
+
+  <glyf>
+
+    <!-- The xMin, yMin, xMax and yMax values
+         will be recalculated by the compiler. -->
+
+    <TTGlyph name=".notdef" xMin="93" yMin="-200" xMax="410" yMax="800">
+      <contour>
+        <pt x="410" y="0" on="1"/>
+        <pt x="50" y="0" on="1"/>
+        <pt x="50" y="700" on="1"/>
+        <pt x="410" y="700" on="1"/>
+      </contour>
+      <instructions><assembly>
+	PUSHB[] 1 2 4 3
+	SLOOP[]
+	PUSH[] -400
+	SHPIX[]
+
+	PUSHB[] 0 3 5 3
+	SLOOP[]
+	PUSH[] 400
+	SHPIX[]
+      </assembly></instructions>
+    </TTGlyph>
+
+    <TTGlyph name="H" xMin="50" yMin="0" xMax="577" yMax="700">
+      <contour>
+        <pt x="50" y="700" on="1"/>
+        <pt x="200" y="700" on="1"/>
+        <pt x="200" y="447" on="1"/>
+        <pt x="427" y="447" on="1"/>
+        <pt x="427" y="700" on="1"/>
+        <pt x="577" y="700" on="1"/>
+        <pt x="577" y="0" on="1"/>
+        <pt x="427" y="0" on="1"/>
+        <pt x="427" y="297" on="1"/>
+        <pt x="200" y="297" on="1"/>
+        <pt x="200" y="0" on="1"/>
+        <pt x="50" y="0" on="1"/>
+      </contour>
+      <instructions><assembly>
+	PUSHB[] 0 11 12 3
+	SLOOP[]
+	PUSH[] -200
+	SHPIX[]
+	PUSHB[] 5 6 13 3
+	SLOOP[]
+	PUSH[] 200
+	SHPIX[]
+      </assembly></instructions>
+    </TTGlyph>
+
+    <TTGlyph name="O" xMin="30" yMin="0" xMax="629" yMax="700">
+      <contour>
+        <pt x="248" y="0" on="0"/>
+        <pt x="111" y="94" on="0"/>
+        <pt x="30" y="255" on="0"/>
+        <pt x="30" y="350" on="1"/>
+        <pt x="30" y="445" on="0"/>
+        <pt x="111" y="606" on="0"/>
+        <pt x="248" y="700" on="0"/>
+        <pt x="330" y="700" on="1"/>
+        <pt x="411" y="700" on="0"/>
+        <pt x="548" y="606" on="0"/>
+        <pt x="629" y="445" on="0"/>
+        <pt x="629" y="350" on="1"/>
+        <pt x="629" y="255" on="0"/>
+        <pt x="548" y="94" on="0"/>
+        <pt x="411" y="0" on="0"/>
+        <pt x="330" y="0" on="1"/>
+      </contour>
+      <contour>
+        <pt x="370" y="150" on="0"/>
+        <pt x="439" y="209" on="0"/>
+        <pt x="480" y="302" on="0"/>
+        <pt x="480" y="350" on="1"/>
+        <pt x="480" y="398" on="0"/>
+        <pt x="439" y="491" on="0"/>
+        <pt x="370" y="550" on="0"/>
+        <pt x="330" y="550" on="1"/>
+        <pt x="289" y="550" on="0"/>
+        <pt x="220" y="491" on="0"/>
+        <pt x="179" y="398" on="0"/>
+        <pt x="179" y="350" on="1"/>
+        <pt x="179" y="302" on="0"/>
+        <pt x="220" y="209" on="0"/>
+        <pt x="289" y="150" on="0"/>
+        <pt x="330" y="150" on="1"/>
+      </contour>
+      <instructions><assembly>
+	PUSH[] 32 -200
+	SHPIX[]	
+	PUSHB[]	33 200
+	SHPIX[]
+      </assembly></instructions>
+    </TTGlyph>
+
+    <TTGlyph name="space">
+      <contour></contour>
+      <instructions><assembly>
+	PUSH[] 0 -200
+	SHPIX[]
+	PUSHB[]	1 200
+	SHPIX[]
+      </assembly></instructions>
+    </TTGlyph>
+
+  </glyf>
+
+  <name>
+    <namerecord nameID="1" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Hinted Advance Width Test
+    </namerecord>
+    <namerecord nameID="2" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Regular
+    </namerecord>
+    <namerecord nameID="4" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Hinted Advance Width Test
+    </namerecord>
+    <namerecord nameID="6" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      HintedAdvanceWidthTest-Regular
+    </namerecord>
+    <namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
+      Hinted Advance Width Test
+    </namerecord>
+    <namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
+      Regular
+    </namerecord>
+    <namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
+      Hinted Advance Width Test
+    </namerecord>
+    <namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
+      HintedAdvanceWidthTest-Regular
+    </namerecord>
+  </name>
+
+  <post>
+    <formatType value="2.0"/>
+    <italicAngle value="0.0"/>
+    <underlinePosition value="-75"/>
+    <underlineThickness value="50"/>
+    <isFixedPitch value="0"/>
+    <minMemType42 value="0"/>
+    <maxMemType42 value="0"/>
+    <minMemType1 value="0"/>
+    <maxMemType1 value="0"/>
+    <psNames>
+      <!-- This file uses unique glyph names based on the information
+           found in the 'post' table. Since these names might not be unique,
+           we have to invent artificial names in case of clashes. In order to
+           be able to retain the original information, we need a name to
+           ps name mapping for those cases where they differ. That's what
+           you see below.
+            -->
+    </psNames>
+    <extraNames>
+      <!-- following are the name that are not taken from the standard Mac glyph order -->
+    </extraNames>
+  </post>
+
+  <gasp>
+    <gaspRange rangeMaxPPEM="65535" rangeGaspBehavior="15"/>
+  </gasp>
+
+</ttFont>
diff --git a/core/tests/coretests/src/android/content/pm/PackageManagerTests.java b/core/tests/coretests/src/android/content/pm/PackageManagerTests.java
index 0fee6c3..9498f4c 100644
--- a/core/tests/coretests/src/android/content/pm/PackageManagerTests.java
+++ b/core/tests/coretests/src/android/content/pm/PackageManagerTests.java
@@ -1168,16 +1168,10 @@
     }
 
     boolean checkMediaState(String desired) {
-        try {
-            String mPath = Environment.getExternalStorageDirectory().getPath();
-            String actual = getMs().getVolumeState(mPath);
-            if (desired.equals(actual)) {
-                return true;
-            } else {
-                return false;
-            }
-        } catch (RemoteException e) {
-            Log.e(TAG, "Exception while checking media state", e);
+        String actual = Environment.getExternalStorageState();
+        if (desired.equals(actual)) {
+            return true;
+        } else {
             return false;
         }
     }
diff --git a/core/tests/coretests/src/android/graphics/PaintTest.java b/core/tests/coretests/src/android/graphics/PaintTest.java
new file mode 100644
index 0000000..e97bb33
--- /dev/null
+++ b/core/tests/coretests/src/android/graphics/PaintTest.java
@@ -0,0 +1,97 @@
+/*
+ * 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.graphics;
+
+import android.graphics.Paint;
+import android.test.InstrumentationTestCase;
+import android.test.suitebuilder.annotation.SmallTest;
+
+/**
+ * PaintTest tests {@link Paint}.
+ */
+public class PaintTest extends InstrumentationTestCase {
+    private static final String FONT_PATH = "fonts/HintedAdvanceWidthTest-Regular.ttf";
+
+    static void assertEquals(String message, float[] expected, float[] actual) {
+        if (expected.length != actual.length) {
+            fail(message + " expected array length:<" + expected.length + "> but was:<"
+                    + actual.length + ">");
+        }
+        for (int i = 0; i < expected.length; ++i) {
+            if (expected[i] != actual[i]) {
+                fail(message + " expected array element[" +i + "]:<" + expected[i] + ">but was:<"
+                        + actual[i] + ">");
+            }
+        }
+    }
+
+    static class HintingTestCase {
+        public final String mText;
+        public final float mTextSize;
+        public final float[] mWidthWithoutHinting;
+        public final float[] mWidthWithHinting;
+
+        public HintingTestCase(String text, float textSize, float[] widthWithoutHinting,
+                               float[] widthWithHinting) {
+            mText = text;
+            mTextSize = textSize;
+            mWidthWithoutHinting = widthWithoutHinting;
+            mWidthWithHinting = widthWithHinting;
+        }
+    }
+
+    // Following test cases are only valid for HintedAdvanceWidthTest-Regular.ttf in assets/fonts.
+    HintingTestCase[] HINTING_TESTCASES = {
+        new HintingTestCase("H", 11f, new float[] { 7f }, new float[] { 13f }),
+        new HintingTestCase("O", 11f, new float[] { 7f }, new float[] { 13f }),
+
+        new HintingTestCase("H", 13f, new float[] { 8f }, new float[] { 14f }),
+        new HintingTestCase("O", 13f, new float[] { 9f }, new float[] { 15f }),
+
+        new HintingTestCase("HO", 11f, new float[] { 7f, 7f }, new float[] { 13f, 13f }),
+        new HintingTestCase("OH", 11f, new float[] { 7f, 7f }, new float[] { 13f, 13f }),
+
+        new HintingTestCase("HO", 13f, new float[] { 8f, 9f }, new float[] { 14f, 15f }),
+        new HintingTestCase("OH", 13f, new float[] { 9f, 8f }, new float[] { 15f, 14f }),
+    };
+
+    @SmallTest
+    public void testHintingWidth() {
+        final Typeface fontTypeface = Typeface.createFromAsset(
+                getInstrumentation().getContext().getAssets(), FONT_PATH);
+        Paint paint = new Paint();
+        paint.setTypeface(fontTypeface);
+
+        for (int i = 0; i < HINTING_TESTCASES.length; ++i) {
+            HintingTestCase testCase = HINTING_TESTCASES[i];
+
+            paint.setTextSize(testCase.mTextSize);
+
+            float[] widths = new float[testCase.mText.length()];
+
+            paint.setHinting(Paint.HINTING_OFF);
+            paint.getTextWidths(String.valueOf(testCase.mText), widths);
+            assertEquals("Text width of '" + testCase.mText + "' without hinting is not expected.",
+                    testCase.mWidthWithoutHinting, widths);
+
+            paint.setHinting(Paint.HINTING_ON);
+            paint.getTextWidths(String.valueOf(testCase.mText), widths);
+            assertEquals("Text width of '" + testCase.mText + "' with hinting is not expected.",
+                    testCase.mWidthWithHinting, widths);
+        }
+    }
+}
diff --git a/core/tests/coretests/src/android/widget/SuggestionsPopupWindowTest.java b/core/tests/coretests/src/android/widget/SuggestionsPopupWindowTest.java
index e5d5542..87b3785 100644
--- a/core/tests/coretests/src/android/widget/SuggestionsPopupWindowTest.java
+++ b/core/tests/coretests/src/android/widget/SuggestionsPopupWindowTest.java
@@ -38,15 +38,22 @@
     }
 
     @SmallTest
-    public void testTextAndAppearanceInSuggestionsPopup() {
+    public void testTextAppearanceInSuggestionsPopup() {
         final Activity activity = getActivity();
 
         final String sampleText = "abc def ghi";
-        final String[] candidate = {"DEF", "Def"};
-        final SuggestionSpan suggestionSpan = new SuggestionSpan(activity, candidate,
-                SuggestionSpan.FLAG_AUTO_CORRECTION);
-        final int spanStart = 4;
-        final int spanEnd = 7;
+        final String[] singleWordCandidates = {"DEF", "Def"};
+        final SuggestionSpan singleWordSuggestionSpan = new SuggestionSpan(activity,
+                singleWordCandidates, SuggestionSpan.FLAG_AUTO_CORRECTION);
+        final int singleWordSpanStart = 4;
+        final int singleWordSpanEnd = 7;
+
+        final String[] multiWordCandidates = {"ABC DEF GHI", "Abc Def Ghi"};
+        final SuggestionSpan multiWordSuggestionSpan = new SuggestionSpan(activity,
+                multiWordCandidates, SuggestionSpan.FLAG_AUTO_CORRECTION);
+        final int multiWordSpanStart = 0;
+        final int multiWordSpanEnd = 11;
+
         TextAppearanceSpan expectedSpan = new TextAppearanceSpan(activity,
                 android.R.style.TextAppearance_SuggestionHighlight);
         TextPaint tmpTp = new TextPaint();
@@ -62,22 +69,27 @@
             public void run() {
                 SpannableStringBuilder ssb = new SpannableStringBuilder();
                 ssb.append(sampleText);
-                ssb.setSpan(suggestionSpan, spanStart, spanEnd, Spanned.SPAN_INCLUSIVE_INCLUSIVE);
+                ssb.setSpan(singleWordSuggestionSpan, singleWordSpanStart, singleWordSpanEnd,
+                        Spanned.SPAN_INCLUSIVE_INCLUSIVE);
+                ssb.setSpan(multiWordSuggestionSpan, multiWordSpanStart, multiWordSpanEnd,
+                        Spanned.SPAN_INCLUSIVE_INCLUSIVE);
                 editText.setText(ssb);
 
-                Selection.setSelection(editText.getText(), spanStart, spanEnd);
+                Selection.setSelection(editText.getText(), singleWordSpanStart, singleWordSpanEnd);
                 editText.onTextContextMenuItem(TextView.ID_REPLACE);
             }
         });
         getInstrumentation().waitForIdleSync();
 
         // In this test, the SuggestionsPopupWindow looks like
-        // abc def ghi
-        //   ----------
-        //   | *DEF*  |
-        //   | *Def*  |
-        //   | DELETE |
-        //   ----------
+        //   abc def ghi
+        // -----------------
+        // | abc *DEF* ghi |
+        // | abc *Def* ghi |
+        // | *ABC DEF GHI* |
+        // | *Abc Def Ghi* |
+        // | DELETE        |
+        // -----------------
         // *XX* means that XX is highlighted.
         activity.runOnUiThread(new Runnable() {
             public void run() {
@@ -90,9 +102,10 @@
 
                 int childNum = listView.getChildCount();
                 // +1 for "DELETE" command.
-                assertEquals(candidate.length + 1, childNum);
+                assertEquals(singleWordCandidates.length + multiWordCandidates.length + 1,
+                        childNum);
 
-                for (int i = 0; i < candidate.length; ++i) {
+                for (int i = 0; i < singleWordCandidates.length; ++i) {
                     TextView textView = (TextView) listView.getChildAt(i);
                     assertNotNull(textView);
 
@@ -100,16 +113,46 @@
                     assertNotNull(spanned);
 
                     // Check that the suggestion item order is kept.
-                    assertEquals(candidate[i], spanned.toString());
+                    String expectedText = "abc " + singleWordCandidates[i] + " ghi";
+                    assertEquals(expectedText, spanned.toString());
 
                     // Check that the text is highlighted with correct color and text size.
-                    TextAppearanceSpan[] taSpan = spanned.getSpans(0, candidate[i].length(),
-                            TextAppearanceSpan.class);
+                    TextAppearanceSpan[] taSpan = spanned.getSpans(singleWordSpanStart,
+                            singleWordSpanEnd, TextAppearanceSpan.class);
                     assertEquals(1, taSpan.length);
                     TextPaint tp = new TextPaint();
                     taSpan[0].updateDrawState(tp);
                     assertEquals(expectedHighlightTextColor, tp.getColor());
                     assertEquals(expectedHighlightTextSize, tp.getTextSize());
+
+                    // Check only center word is highlighted.
+                    assertEquals(singleWordSpanStart, spanned.getSpanStart(taSpan[0]));
+                    assertEquals(singleWordSpanEnd, spanned.getSpanEnd(taSpan[0]));
+                }
+
+                for (int i = 0; i < multiWordCandidates.length; ++i) {
+                    int indexInListView = singleWordCandidates.length + i;
+                    TextView textView = (TextView) listView.getChildAt(indexInListView);
+                    assertNotNull(textView);
+
+                    Spanned spanned = (Spanned) textView.getText();
+                    assertNotNull(spanned);
+
+                    // Check that the suggestion item order is kept.
+                    assertEquals(multiWordCandidates[i], spanned.toString());
+
+                    // Check that the text is highlighted with correct color and text size.
+                    TextAppearanceSpan[] taSpan = spanned.getSpans(
+                            0, multiWordCandidates[i].length(), TextAppearanceSpan.class);
+                    assertEquals(1, taSpan.length);
+                    TextPaint tp = new TextPaint();
+                    taSpan[0].updateDrawState(tp);
+                    assertEquals(expectedHighlightTextColor, tp.getColor());
+                    assertEquals(expectedHighlightTextSize, tp.getTextSize());
+
+                    // Check the whole text is highlighted.
+                    assertEquals(multiWordSpanStart, spanned.getSpanStart(taSpan[0]));
+                    assertEquals(multiWordSpanEnd, spanned.getSpanEnd(taSpan[0]));
                 }
             }
         });
diff --git a/core/tests/coretests/src/android/widget/TextViewActivityTest.java b/core/tests/coretests/src/android/widget/TextViewActivityTest.java
index 54117df..ce9ae02 100644
--- a/core/tests/coretests/src/android/widget/TextViewActivityTest.java
+++ b/core/tests/coretests/src/android/widget/TextViewActivityTest.java
@@ -16,8 +16,13 @@
 
 package android.widget;
 
+import static android.widget.espresso.TextViewActions.clickOnTextAtIndex;
+import static android.widget.espresso.TextViewActions.doubleTapAndDragOnText;
+import static android.widget.espresso.TextViewActions.longPressAndDragOnText;
+import static android.widget.espresso.TextViewAssertions.hasSelection;
 import static android.support.test.espresso.Espresso.onView;
 import static android.support.test.espresso.action.ViewActions.click;
+import static android.support.test.espresso.action.ViewActions.pressKey;
 import static android.support.test.espresso.action.ViewActions.typeTextIntoFocusedView;
 import static android.support.test.espresso.assertion.ViewAssertions.matches;
 import static android.support.test.espresso.matcher.ViewMatchers.withId;
@@ -27,6 +32,7 @@
 
 import android.test.ActivityInstrumentationTestCase2;
 import android.test.suitebuilder.annotation.SmallTest;
+import android.view.KeyEvent;
 
 /**
  * Tests the TextView widget from an Activity
@@ -47,4 +53,41 @@
 
         onView(withId(R.id.textview)).check(matches(withText(helloWorld)));
     }
+
+    @SmallTest
+    public void testPositionCursorAtTextAtIndex() throws Exception {
+        getActivity();
+
+        final String helloWorld = "Hello world!";
+        onView(withId(R.id.textview)).perform(typeTextIntoFocusedView(helloWorld));
+        onView(withId(R.id.textview)).perform(clickOnTextAtIndex(helloWorld.indexOf("world")));
+
+        // Delete text at specified index and see if we got the right one.
+        onView(withId(R.id.textview)).perform(pressKey(KeyEvent.KEYCODE_FORWARD_DEL));
+        onView(withId(R.id.textview)).check(matches(withText("Hello orld!")));
+    }
+
+    @SmallTest
+    public void testLongPressAndDragToSelect() throws Exception {
+        getActivity();
+
+        final String helloWorld = "Hello little handsome boy!";
+        onView(withId(R.id.textview)).perform(typeTextIntoFocusedView(helloWorld));
+        onView(withId(R.id.textview)).perform(
+                longPressAndDragOnText(helloWorld.indexOf("little"), helloWorld.indexOf(" boy!")));
+
+        onView(withId(R.id.textview)).check(hasSelection("little handsome"));
+    }
+
+    @SmallTest
+    public void testDoubleTapAndDragToSelect() throws Exception {
+        getActivity();
+
+        final String helloWorld = "Hello young beautiful girl!";
+        onView(withId(R.id.textview)).perform(typeTextIntoFocusedView(helloWorld));
+        onView(withId(R.id.textview)).perform(
+                doubleTapAndDragOnText(helloWorld.indexOf("young"), helloWorld.indexOf(" girl!")));
+
+        onView(withId(R.id.textview)).check(hasSelection("young beautiful"));
+    }
 }
diff --git a/core/tests/coretests/src/android/widget/espresso/DragOnTextViewActions.java b/core/tests/coretests/src/android/widget/espresso/DragOnTextViewActions.java
new file mode 100644
index 0000000..a0cd848
--- /dev/null
+++ b/core/tests/coretests/src/android/widget/espresso/DragOnTextViewActions.java
@@ -0,0 +1,281 @@
+/*
+ * 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.widget.espresso;
+
+import static android.support.test.espresso.matcher.ViewMatchers.isAssignableFrom;
+import static android.support.test.espresso.matcher.ViewMatchers.isCompletelyDisplayed;
+import static com.android.internal.util.Preconditions.checkNotNull;
+import static org.hamcrest.Matchers.allOf;
+
+import android.annotation.Nullable;
+import android.os.SystemClock;
+import android.support.test.espresso.UiController;
+import android.support.test.espresso.PerformException;
+import android.support.test.espresso.ViewAction;
+import android.support.test.espresso.action.CoordinatesProvider;
+import android.support.test.espresso.action.GeneralClickAction;
+import android.support.test.espresso.action.MotionEvents;
+import android.support.test.espresso.action.PrecisionDescriber;
+import android.support.test.espresso.action.Press;
+import android.support.test.espresso.action.Swiper;
+import android.support.test.espresso.action.Tap;
+import android.support.test.espresso.util.HumanReadables;
+import android.util.Log;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewConfiguration;
+import android.widget.TextView;
+import org.hamcrest.Matcher;
+
+
+/**
+ * Drags on text in a TextView using touch events.<br>
+ * <br>
+ * View constraints:
+ * <ul>
+ * <li>must be a TextView displayed on screen
+ * <ul>
+ */
+public final class DragOnTextViewActions implements ViewAction {
+
+    /**
+     * Executes different "drag on text" types to given positions.
+     */
+    public enum Drag implements Swiper {
+
+        /**
+         * Starts a drag with a long-press.
+         */
+        LONG_PRESS {
+            private DownMotionPerformer downMotion = new DownMotionPerformer() {
+                @Override
+                public MotionEvent perform(
+                        UiController uiController, float[] coordinates, float[] precision) {
+                    MotionEvent downEvent = MotionEvents.sendDown(
+                            uiController, coordinates, precision)
+                            .down;
+                    // Duration before a press turns into a long press.
+                    // Factor 1.5 is needed, otherwise a long press is not safely detected.
+                    // See android.test.TouchUtils longClickView
+                    long longPressTimeout = (long) (ViewConfiguration.getLongPressTimeout() * 1.5f);
+                    uiController.loopMainThreadForAtLeast(longPressTimeout);
+                    return downEvent;
+                }
+            };
+
+            @Override
+            public Status sendSwipe(
+                    UiController uiController,
+                    float[] startCoordinates, float[] endCoordinates, float[] precision) {
+                return sendLinearDrag(
+                        uiController, downMotion, startCoordinates, endCoordinates, precision);
+            }
+
+            @Override
+            public String toString() {
+                return "long press and drag to select";
+            }
+        },
+
+        /**
+         * Starts a drag with a double-tap.
+         */
+        DOUBLE_TAP {
+            private DownMotionPerformer downMotion = new DownMotionPerformer() {
+                @Override
+                @Nullable
+                public MotionEvent perform(
+                        UiController uiController,  float[] coordinates, float[] precision) {
+                    MotionEvent downEvent = MotionEvents.sendDown(
+                            uiController, coordinates, precision)
+                            .down;
+                    try {
+                        if (!MotionEvents.sendUp(uiController, downEvent)) {
+                            String logMessage = "Injection of up event as part of the double tap " +
+                                    "failed. Sending cancel event.";
+                            Log.d(TAG, logMessage);
+                            MotionEvents.sendCancel(uiController, downEvent);
+                            return null;
+                        }
+
+                        long doubleTapMinimumTimeout = ViewConfiguration.getDoubleTapMinTime();
+                        uiController.loopMainThreadForAtLeast(doubleTapMinimumTimeout);
+
+                        return MotionEvents.sendDown(uiController, coordinates, precision).down;
+                    } finally {
+                        downEvent.recycle();
+                    }
+                }
+            };
+
+            @Override
+            public Status sendSwipe(
+                    UiController uiController,
+                    float[] startCoordinates, float[] endCoordinates, float[] precision) {
+                return sendLinearDrag(
+                        uiController, downMotion, startCoordinates, endCoordinates, precision);
+            }
+
+            @Override
+            public String toString() {
+                return "double-tap and drag to select";
+            }
+        };
+
+        private static final String TAG = Drag.class.getSimpleName();
+
+        /** The number of move events to send for each drag. */
+        private static final int DRAG_STEP_COUNT = 10;
+
+        /** Length of time a drag should last for, in milliseconds. */
+        private static final int DRAG_DURATION = 1500;
+
+        private static Status sendLinearDrag(
+                UiController uiController, DownMotionPerformer downMotion,
+                float[] startCoordinates, float[] endCoordinates, float[] precision) {
+            float[][] steps = interpolate(startCoordinates, endCoordinates);
+            final int delayBetweenMovements = DRAG_DURATION / steps.length;
+
+            MotionEvent downEvent = downMotion.perform(uiController, startCoordinates, precision);
+            if (downEvent == null) {
+                return Status.FAILURE;
+            }
+
+            try {
+                for (int i = 0; i < steps.length; i++) {
+                    if (!MotionEvents.sendMovement(uiController, downEvent, steps[i])) {
+                        String logMessage = "Injection of move event as part of the drag failed. " +
+                                "Sending cancel event.";
+                        Log.e(TAG, logMessage);
+                        MotionEvents.sendCancel(uiController, downEvent);
+                        return Status.FAILURE;
+                    }
+
+                    long desiredTime = downEvent.getDownTime() + delayBetweenMovements * i;
+                    long timeUntilDesired = desiredTime - SystemClock.uptimeMillis();
+                    if (timeUntilDesired > 10) {
+                        // If the wait time until the next event isn't long enough, skip the wait
+                        // and execute the next event.
+                        uiController.loopMainThreadForAtLeast(timeUntilDesired);
+                    }
+                }
+
+                if (!MotionEvents.sendUp(uiController, downEvent, endCoordinates)) {
+                    String logMessage = "Injection of up event as part of the drag failed. " +
+                            "Sending cancel event.";
+                    Log.e(TAG, logMessage);
+                    MotionEvents.sendCancel(uiController, downEvent);
+                    return Status.FAILURE;
+                }
+            } finally {
+                downEvent.recycle();
+            }
+            return Status.SUCCESS;
+        }
+
+        private static float[][] interpolate(float[] start, float[] end) {
+            float[][] res = new float[DRAG_STEP_COUNT][2];
+
+            for (int i = 1; i < DRAG_STEP_COUNT + 1; i++) {
+                res[i - 1][0] = start[0] + (end[0] - start[0]) * i / (DRAG_STEP_COUNT + 2f);
+                res[i - 1][1] = start[1] + (end[1] - start[1]) * i / (DRAG_STEP_COUNT + 2f);
+            }
+
+            return res;
+        }
+    }
+
+    /**
+     * Interface to implement different "down motion" types.
+     */
+    private interface DownMotionPerformer {
+        /**
+         * Performs and returns a down motion.
+         *
+         * @param uiController a UiController to use to send MotionEvents to the screen.
+         * @param coordinates a float[] with x and y values of center of the tap.
+         * @param precision  a float[] with x and y values of precision of the tap.
+         * @return the down motion event or null if the down motion event failed.
+         */
+        @Nullable
+        MotionEvent perform(UiController uiController, float[] coordinates, float[] precision);
+    }
+
+    private final Swiper mDragger;
+    private final CoordinatesProvider mStartCoordinatesProvider;
+    private final CoordinatesProvider mEndCoordinatesProvider;
+    private final PrecisionDescriber mPrecisionDescriber;
+
+    public DragOnTextViewActions(
+            Swiper dragger,
+            CoordinatesProvider startCoordinatesProvider,
+            CoordinatesProvider endCoordinatesProvider,
+            PrecisionDescriber precisionDescriber) {
+        mDragger = checkNotNull(dragger);
+        mStartCoordinatesProvider = checkNotNull(startCoordinatesProvider);
+        mEndCoordinatesProvider = checkNotNull(endCoordinatesProvider);
+        mPrecisionDescriber = checkNotNull(precisionDescriber);
+    }
+
+    @Override
+    @SuppressWarnings("unchecked")
+    public Matcher<View> getConstraints() {
+        return allOf(isCompletelyDisplayed(), isAssignableFrom(TextView.class));
+    }
+
+    @Override
+    public void perform(UiController uiController, View view) {
+        checkNotNull(uiController);
+        checkNotNull(view);
+
+        float[] startCoordinates = mStartCoordinatesProvider.calculateCoordinates(view);
+        float[] endCoordinates = mEndCoordinatesProvider.calculateCoordinates(view);
+        float[] precision = mPrecisionDescriber.describePrecision();
+
+        Swiper.Status status;
+
+        try {
+            status = mDragger.sendSwipe(
+                    uiController, startCoordinates, endCoordinates, precision);
+        } catch (RuntimeException re) {
+            throw new PerformException.Builder()
+                    .withActionDescription(this.getDescription())
+                    .withViewDescription(HumanReadables.describe(view))
+                    .withCause(re)
+                    .build();
+        }
+
+        int duration = ViewConfiguration.getPressedStateDuration();
+        // ensures that all work enqueued to process the swipe has been run.
+        if (duration > 0) {
+            uiController.loopMainThreadForAtLeast(duration);
+        }
+
+        if (status == Swiper.Status.FAILURE) {
+            throw new PerformException.Builder()
+                    .withActionDescription(getDescription())
+                    .withViewDescription(HumanReadables.describe(view))
+                    .withCause(new RuntimeException(getDescription() + " failed"))
+                    .build();
+        }
+    }
+
+    @Override
+    public String getDescription() {
+        return mDragger.toString();
+    }
+}
diff --git a/core/tests/coretests/src/android/widget/espresso/TextViewActions.java b/core/tests/coretests/src/android/widget/espresso/TextViewActions.java
new file mode 100644
index 0000000..7e4735b
--- /dev/null
+++ b/core/tests/coretests/src/android/widget/espresso/TextViewActions.java
@@ -0,0 +1,146 @@
+/*
+ * 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.widget.espresso;
+
+import static android.support.test.espresso.action.ViewActions.actionWithAssertions;
+
+import android.support.test.espresso.PerformException;
+import android.support.test.espresso.ViewAction;
+import android.support.test.espresso.action.CoordinatesProvider;
+import android.support.test.espresso.action.GeneralClickAction;
+import android.support.test.espresso.action.Press;
+import android.support.test.espresso.action.Tap;
+import android.support.test.espresso.util.HumanReadables;
+import android.text.Layout;
+import android.view.View;
+import android.widget.TextView;
+
+/**
+ * A collection of actions on a {@link android.widget.TextView}.
+ */
+public final class TextViewActions {
+
+    private TextViewActions() {}
+
+    /**
+     * Returns an action that clicks on text at an index on the TextView.<br>
+     * <br>
+     * View constraints:
+     * <ul>
+     * <li>must be a TextView displayed on screen
+     * <ul>
+     *
+     * @param index The index of the TextView's text to click on.
+     */
+    public static ViewAction clickOnTextAtIndex(int index) {
+        return actionWithAssertions(
+                new GeneralClickAction(Tap.SINGLE, new TextCoordinates(index), Press.FINGER));
+    }
+
+    /**
+     * Returns an action that long presses then drags on text from startIndex to endIndex on the
+     * TextView.<br>
+     * <br>
+     * View constraints:
+     * <ul>
+     * <li>must be a TextView displayed on screen
+     * <ul>
+     *
+     * @param startIndex The index of the TextView's text to start a drag from
+     * @param endIndex The index of the TextView's text to end the drag at
+     */
+    public static ViewAction longPressAndDragOnText(int startIndex, int endIndex) {
+        return actionWithAssertions(
+                new DragOnTextViewActions(
+                        DragOnTextViewActions.Drag.LONG_PRESS,
+                        new TextCoordinates(startIndex),
+                        new TextCoordinates(endIndex),
+                        Press.FINGER));
+    }
+
+    /**
+     * Returns an action that double taps then drags on text from startIndex to endIndex on the
+     * TextView.<br>
+     * <br>
+     * View constraints:
+     * <ul>
+     * <li>must be a TextView displayed on screen
+     * <ul>
+     *
+     * @param startIndex The index of the TextView's text to start a drag from
+     * @param endIndex The index of the TextView's text to end the drag at
+     */
+    public static ViewAction doubleTapAndDragOnText(int startIndex, int endIndex) {
+        return actionWithAssertions(
+                new DragOnTextViewActions(
+                        DragOnTextViewActions.Drag.DOUBLE_TAP,
+                        new TextCoordinates(startIndex),
+                        new TextCoordinates(endIndex),
+                        Press.FINGER));
+    }
+
+    /**
+     * A provider of the x, y coordinates of the text at the specified index in a text view.
+     */
+    private static final class TextCoordinates implements CoordinatesProvider {
+
+        private final int mIndex;
+        private final String mActionDescription;
+
+        public TextCoordinates(int index) {
+            mIndex = index;
+            mActionDescription = "Could not locate text at index: " + mIndex;
+        }
+
+        @Override
+        public float[] calculateCoordinates(View view) {
+            try {
+                return locateTextAtIndex((TextView) view, mIndex);
+            } catch (ClassCastException e) {
+                throw new PerformException.Builder()
+                        .withActionDescription(mActionDescription)
+                        .withViewDescription(HumanReadables.describe(view))
+                        .withCause(e)
+                        .build();
+            } catch (StringIndexOutOfBoundsException e) {
+                throw new PerformException.Builder()
+                        .withActionDescription(mActionDescription)
+                        .withViewDescription(HumanReadables.describe(view))
+                        .withCause(e)
+                        .build();
+            }
+        }
+
+        /**
+         * @throws StringIndexOutOfBoundsException
+         */
+        private float[] locateTextAtIndex(TextView textView, int index) {
+            if (index < 0 || index > textView.getText().length()) {
+                throw new StringIndexOutOfBoundsException(index);
+            }
+            final int[] xy = new int[2];
+            textView.getLocationOnScreen(xy);
+            final Layout layout = textView.getLayout();
+            final int line = layout.getLineForOffset(index);
+            final float x = textView.getTotalPaddingLeft() - textView.getScrollX()
+                    + layout.getPrimaryHorizontal(index);
+            final float y = textView.getTotalPaddingTop() - textView.getScrollY()
+                    + layout.getLineTop(line);
+            return new float[]{x + xy[0], y + xy[1]};
+        }
+    }
+}
diff --git a/core/tests/coretests/src/android/widget/espresso/TextViewAssertions.java b/core/tests/coretests/src/android/widget/espresso/TextViewAssertions.java
new file mode 100644
index 0000000..dce3182
--- /dev/null
+++ b/core/tests/coretests/src/android/widget/espresso/TextViewAssertions.java
@@ -0,0 +1,98 @@
+/*
+ * 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.widget.espresso;
+
+import static android.support.test.espresso.matcher.ViewMatchers.assertThat;
+import static com.android.internal.util.Preconditions.checkNotNull;
+import static org.hamcrest.Matchers.is;
+
+import android.support.test.espresso.NoMatchingViewException;
+import android.support.test.espresso.ViewAssertion;
+import android.view.View;
+import android.widget.TextView;
+
+import junit.framework.AssertionFailedError;
+import org.hamcrest.Matcher;
+
+/**
+ * A collection of assertions on a {@link android.widget.TextView}.
+ */
+public final class TextViewAssertions {
+
+    private TextViewAssertions() {}
+
+    /**
+     * Returns a {@link ViewAssertion} that asserts that the text view has a specified
+     * selection.<br>
+     * <br>
+     * View constraints:
+     * <ul>
+     * <li>must be a text view displayed on screen
+     * <ul>
+     *
+     * @param selection  The expected selection.
+     */
+    public static ViewAssertion hasSelection(String selection) {
+        return new TextSelectionAssertion(is(selection));
+    }
+
+    /**
+     * Returns a {@link ViewAssertion} that asserts that the text view has a specified
+     * selection.<br>
+     * <br>
+     * View constraints:
+     * <ul>
+     * <li>must be a text view displayed on screen
+     * <ul>
+     *
+     * @param selection  A matcher representing the expected selection.
+     */
+    public static ViewAssertion hasSelection(Matcher<String> selection) {
+        return new TextSelectionAssertion(selection);
+    }
+
+    /**
+     * A {@link ViewAssertion} to check the selected text in a {@link TextView}.
+     */
+    private static final class TextSelectionAssertion implements ViewAssertion {
+
+        private final Matcher<String> mSelection;
+
+        public TextSelectionAssertion(Matcher<String> selection) {
+            mSelection = checkNotNull(selection);
+        }
+
+        @Override
+        public void check(View view, NoMatchingViewException exception) {
+            if (view instanceof TextView) {
+                TextView textView = (TextView) view;
+                int selectionStart = textView.getSelectionStart();
+                int selectionEnd = textView.getSelectionEnd();
+                try {
+                    String selectedText = textView.getText()
+                            .subSequence(selectionStart, selectionEnd)
+                            .toString();
+                    assertThat(selectedText, mSelection);
+                } catch (IndexOutOfBoundsException e) {
+                    throw new AssertionFailedError(e.getMessage());
+                }
+            } else {
+                throw new AssertionFailedError("TextView not found");
+            }
+        }
+    }
+}
diff --git a/core/tests/coretests/src/com/android/internal/inputmethod/InputMethodUtilsTest.java b/core/tests/coretests/src/com/android/internal/inputmethod/InputMethodUtilsTest.java
index 5e7f127..c279c8f 100644
--- a/core/tests/coretests/src/com/android/internal/inputmethod/InputMethodUtilsTest.java
+++ b/core/tests/coretests/src/com/android/internal/inputmethod/InputMethodUtilsTest.java
@@ -1075,4 +1075,120 @@
             assertTrue(subtypes2.isEmpty());
         }
     }
+
+    @SmallTest
+    public void testbuildInputMethodsAndSubtypesString() {
+        {
+            ArrayMap<String, ArraySet<String>> map = new ArrayMap<>();
+            assertEquals("", InputMethodUtils.buildInputMethodsAndSubtypesString(map));
+        }
+        {
+            ArrayMap<String, ArraySet<String>> map = new ArrayMap<>();
+            map.put("ime0", new ArraySet<String>());
+            assertEquals("ime0", InputMethodUtils.buildInputMethodsAndSubtypesString(map));
+        }
+        {
+            ArrayMap<String, ArraySet<String>> map = new ArrayMap<>();
+            ArraySet<String> subtypes1 = new ArraySet<>();
+            subtypes1.add("subtype0");
+            map.put("ime0", subtypes1);
+            assertEquals("ime0;subtype0", InputMethodUtils.buildInputMethodsAndSubtypesString(map));
+        }
+        {
+            ArrayMap<String, ArraySet<String>> map = new ArrayMap<>();
+            ArraySet<String> subtypes1 = new ArraySet<>();
+            subtypes1.add("subtype0");
+            subtypes1.add("subtype1");
+            map.put("ime0", subtypes1);
+
+            // We do not expect what order will be used to concatenate items in
+            // InputMethodUtils.buildInputMethodsAndSubtypesString() hence enumerate all possible
+            // permutations here.
+            ArraySet<String> validSequences = new ArraySet<>();
+            validSequences.add("ime0;subtype0;subtype1");
+            validSequences.add("ime0;subtype1;subtype0");
+            assertTrue(validSequences.contains(
+                    InputMethodUtils.buildInputMethodsAndSubtypesString(map)));
+        }
+        {
+            ArrayMap<String, ArraySet<String>> map = new ArrayMap<>();
+            map.put("ime0", new ArraySet<String>());
+            map.put("ime1", new ArraySet<String>());
+
+            ArraySet<String> validSequences = new ArraySet<>();
+            validSequences.add("ime0:ime1");
+            validSequences.add("ime1:ime0");
+            assertTrue(validSequences.contains(
+                    InputMethodUtils.buildInputMethodsAndSubtypesString(map)));
+        }
+        {
+            ArrayMap<String, ArraySet<String>> map = new ArrayMap<>();
+            ArraySet<String> subtypes1 = new ArraySet<>();
+            subtypes1.add("subtype0");
+            map.put("ime0", subtypes1);
+            map.put("ime1", new ArraySet<String>());
+
+            ArraySet<String> validSequences = new ArraySet<>();
+            validSequences.add("ime0;subtype0:ime1");
+            validSequences.add("ime1;ime0;subtype0");
+            assertTrue(validSequences.contains(
+                    InputMethodUtils.buildInputMethodsAndSubtypesString(map)));
+        }
+        {
+            ArrayMap<String, ArraySet<String>> map = new ArrayMap<>();
+            ArraySet<String> subtypes1 = new ArraySet<>();
+            subtypes1.add("subtype0");
+            subtypes1.add("subtype1");
+            map.put("ime0", subtypes1);
+            map.put("ime1", new ArraySet<String>());
+
+            ArraySet<String> validSequences = new ArraySet<>();
+            validSequences.add("ime0;subtype0;subtype1:ime1");
+            validSequences.add("ime0;subtype1;subtype0:ime1");
+            validSequences.add("ime1:ime0;subtype0;subtype1");
+            validSequences.add("ime1:ime0;subtype1;subtype0");
+            assertTrue(validSequences.contains(
+                    InputMethodUtils.buildInputMethodsAndSubtypesString(map)));
+        }
+        {
+            ArrayMap<String, ArraySet<String>> map = new ArrayMap<>();
+            ArraySet<String> subtypes1 = new ArraySet<>();
+            subtypes1.add("subtype0");
+            map.put("ime0", subtypes1);
+
+            ArraySet<String> subtypes2 = new ArraySet<>();
+            subtypes2.add("subtype1");
+            map.put("ime1", subtypes2);
+
+            ArraySet<String> validSequences = new ArraySet<>();
+            validSequences.add("ime0;subtype0:ime1;subtype1");
+            validSequences.add("ime1;subtype1:ime0;subtype0");
+            assertTrue(validSequences.contains(
+                    InputMethodUtils.buildInputMethodsAndSubtypesString(map)));
+        }
+        {
+            ArrayMap<String, ArraySet<String>> map = new ArrayMap<>();
+            ArraySet<String> subtypes1 = new ArraySet<>();
+            subtypes1.add("subtype0");
+            subtypes1.add("subtype1");
+            map.put("ime0", subtypes1);
+
+            ArraySet<String> subtypes2 = new ArraySet<>();
+            subtypes2.add("subtype2");
+            subtypes2.add("subtype3");
+            map.put("ime1", subtypes2);
+
+            ArraySet<String> validSequences = new ArraySet<>();
+            validSequences.add("ime0;subtype0;subtype1:ime1;subtype2;subtype3");
+            validSequences.add("ime0;subtype1;subtype0:ime1;subtype2;subtype3");
+            validSequences.add("ime0;subtype0;subtype1:ime1;subtype3;subtype2");
+            validSequences.add("ime0;subtype1;subtype0:ime1;subtype3;subtype2");
+            validSequences.add("ime1;subtype2;subtype3:ime0;subtype0;subtype1");
+            validSequences.add("ime2;subtype3;subtype2:ime0;subtype0;subtype1");
+            validSequences.add("ime3;subtype2;subtype3:ime0;subtype1;subtype0");
+            validSequences.add("ime4;subtype3;subtype2:ime0;subtype1;subtype0");
+            assertTrue(validSequences.contains(
+                    InputMethodUtils.buildInputMethodsAndSubtypesString(map)));
+        }
+    }
 }
diff --git a/core/tests/hosttests/test-apps/SharedUid/32/jni/native.cpp b/core/tests/hosttests/test-apps/SharedUid/32/jni/native.cpp
index 4c16154..67b12d7 100644
--- a/core/tests/hosttests/test-apps/SharedUid/32/jni/native.cpp
+++ b/core/tests/hosttests/test-apps/SharedUid/32/jni/native.cpp
@@ -30,7 +30,7 @@
 
 static const char *classPathName = "com/framework/shareduid/bit32/Native";
 
-static JNINativeMethod methods[] = {
+static const JNINativeMethod methods[] = {
   {"add", "(II)I", (void*)add },
 };
 
@@ -38,7 +38,7 @@
  * Register several native methods for one class.
  */
 static int registerNativeMethods(JNIEnv* env, const char* className,
-    JNINativeMethod* gMethods, int numMethods)
+    const JNINativeMethod* gMethods, int numMethods)
 {
     jclass clazz;
 
diff --git a/core/tests/hosttests/test-apps/SharedUid/64/jni/native.cpp b/core/tests/hosttests/test-apps/SharedUid/64/jni/native.cpp
index c2f9f52..342b3bc 100644
--- a/core/tests/hosttests/test-apps/SharedUid/64/jni/native.cpp
+++ b/core/tests/hosttests/test-apps/SharedUid/64/jni/native.cpp
@@ -30,7 +30,7 @@
 
 static const char *classPathName = "com/framework/shareduid/bit64/Native";
 
-static JNINativeMethod methods[] = {
+static const JNINativeMethod methods[] = {
   {"add", "(II)I", (void*)add },
 };
 
@@ -38,7 +38,7 @@
  * Register several native methods for one class.
  */
 static int registerNativeMethods(JNIEnv* env, const char* className,
-    JNINativeMethod* gMethods, int numMethods)
+    const JNINativeMethod* gMethods, int numMethods)
 {
     jclass clazz;
 
diff --git a/core/tests/hosttests/test-apps/SharedUid/dual/jni/native.cpp b/core/tests/hosttests/test-apps/SharedUid/dual/jni/native.cpp
index 5d3ca06..9b38e3e 100644
--- a/core/tests/hosttests/test-apps/SharedUid/dual/jni/native.cpp
+++ b/core/tests/hosttests/test-apps/SharedUid/dual/jni/native.cpp
@@ -30,7 +30,7 @@
 
 static const char *classPathName = "com/framework/shareduid/dual/Native";
 
-static JNINativeMethod methods[] = {
+static const JNINativeMethod methods[] = {
   {"add", "(II)I", (void*)add },
 };
 
@@ -38,7 +38,7 @@
  * Register several native methods for one class.
  */
 static int registerNativeMethods(JNIEnv* env, const char* className,
-    JNINativeMethod* gMethods, int numMethods)
+    const JNINativeMethod* gMethods, int numMethods)
 {
     jclass clazz;
 
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/drm/jni/android_drm_DrmManagerClient.cpp b/drm/jni/android_drm_DrmManagerClient.cpp
index 52597e1..63fe8ac 100644
--- a/drm/jni/android_drm_DrmManagerClient.cpp
+++ b/drm/jni/android_drm_DrmManagerClient.cpp
@@ -702,7 +702,7 @@
     return status;
 }
 
-static JNINativeMethod nativeMethods[] = {
+static const JNINativeMethod nativeMethods[] = {
 
     {"_initialize", "()I",
                                     (void*)android_drm_DrmManagerClient_initialize},
diff --git a/graphics/java/android/graphics/drawable/DrawableContainer.java b/graphics/java/android/graphics/drawable/DrawableContainer.java
index 1915dd7..1f7d996 100644
--- a/graphics/java/android/graphics/drawable/DrawableContainer.java
+++ b/graphics/java/android/graphics/drawable/DrawableContainer.java
@@ -420,14 +420,23 @@
         return mCurIndex;
     }
 
-    public boolean selectDrawable(int idx) {
-        if (idx == mCurIndex) {
+    /**
+     * Sets the currently displayed drawable by index.
+     * <p>
+     * If an invalid index is specified, the current drawable will be set to
+     * {@code null} and the index will be set to {@code -1}.
+     *
+     * @param index the index of the drawable to display
+     * @return {@code true} if the drawable changed, {@code false} otherwise
+     */
+    public boolean selectDrawable(int index) {
+        if (index == mCurIndex) {
             return false;
         }
 
         final long now = SystemClock.uptimeMillis();
 
-        if (DEBUG) android.util.Log.i(TAG, toString() + " from " + mCurIndex + " to " + idx
+        if (DEBUG) android.util.Log.i(TAG, toString() + " from " + mCurIndex + " to " + index
                 + ": exit=" + mDrawableContainerState.mExitFadeDuration
                 + " enter=" + mDrawableContainerState.mEnterFadeDuration);
 
@@ -448,10 +457,10 @@
             mCurrDrawable.setVisible(false, false);
         }
 
-        if (idx >= 0 && idx < mDrawableContainerState.mNumChildren) {
-            final Drawable d = mDrawableContainerState.getChild(idx);
+        if (index >= 0 && index < mDrawableContainerState.mNumChildren) {
+            final Drawable d = mDrawableContainerState.getChild(index);
             mCurrDrawable = d;
-            mCurIndex = idx;
+            mCurIndex = index;
             if (d != null) {
                 if (mDrawableContainerState.mEnterFadeDuration > 0) {
                     mEnterAnimationEnd = now + mDrawableContainerState.mEnterFadeDuration;
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/graphics/java/android/graphics/drawable/VectorDrawable.java b/graphics/java/android/graphics/drawable/VectorDrawable.java
index 1747225..1105ca4 100644
--- a/graphics/java/android/graphics/drawable/VectorDrawable.java
+++ b/graphics/java/android/graphics/drawable/VectorDrawable.java
@@ -126,9 +126,13 @@
  * <dd>Defines path data using exactly same format as "d" attribute
  * in the SVG's path data. This is defined in the viewport space.</dd>
  * <dt><code>android:fillColor</code></dt>
- * <dd>Defines the color to fill the path (none if not present).</dd>
+ * <dd>Specifies the color used to fill the path. May be a color or (SDK 24+ only) a color state
+ * list. If this property is animated, any value set by the animation will override the original
+ * value. No path fill is drawn if this property is not specified.</dd>
  * <dt><code>android:strokeColor</code></dt>
- * <dd>Defines the color to draw the path outline (none if not present).</dd>
+ * <dd>Specifies the color used to draw the path outline. May be a color or (SDK 24+ only) a color
+ * state list. If this property is animated, any value set by the animation will override the
+ * original value. No path outline is drawn if this property is not specified.</dd>
  * <dt><code>android:strokeWidth</code></dt>
  * <dd>The width a path stroke.</dd>
  * <dt><code>android:strokeAlpha</code></dt>
@@ -374,19 +378,24 @@
 
     @Override
     public boolean isStateful() {
-        return super.isStateful() || (mVectorState != null && mVectorState.mTint != null
-                && mVectorState.mTint.isStateful());
+        return super.isStateful() || (mVectorState != null && mVectorState.isStateful());
     }
 
     @Override
     protected boolean onStateChange(int[] stateSet) {
+        boolean changed = false;
+
         final VectorDrawableState state = mVectorState;
+        if (state.mVPathRenderer != null && state.mVPathRenderer.onStateChange(stateSet)) {
+            changed = true;
+            state.mCacheDirty = true;
+        }
         if (state.mTint != null && state.mTintMode != null) {
             mTintFilter = updateTintFilter(mTintFilter, state.mTint, state.mTintMode);
-            invalidateSelf();
-            return true;
+            changed = true;
         }
-        return false;
+
+        return changed;
     }
 
     @Override
@@ -664,7 +673,7 @@
                 if (SHAPE_PATH.equals(tagName)) {
                     final VFullPath path = new VFullPath();
                     path.inflate(res, attrs, theme);
-                    currentGroup.mChildren.add(path);
+                    currentGroup.addChild(path);
                     if (path.getPathName() != null) {
                         pathRenderer.mVGTargetsMap.put(path.getPathName(), path);
                     }
@@ -673,7 +682,7 @@
                 } else if (SHAPE_CLIP_PATH.equals(tagName)) {
                     final VClipPath path = new VClipPath();
                     path.inflate(res, attrs, theme);
-                    currentGroup.mChildren.add(path);
+                    currentGroup.addChild(path);
                     if (path.getPathName() != null) {
                         pathRenderer.mVGTargetsMap.put(path.getPathName(), path);
                     }
@@ -681,7 +690,7 @@
                 } else if (SHAPE_GROUP.equals(tagName)) {
                     VGroup newChildGroup = new VGroup();
                     newChildGroup.inflate(res, attrs, theme);
-                    currentGroup.mChildren.add(newChildGroup);
+                    currentGroup.addChild(newChildGroup);
                     groupStack.push(newChildGroup);
                     if (newChildGroup.getGroupName() != null) {
                         pathRenderer.mVGTargetsMap.put(newChildGroup.getGroupName(),
@@ -700,7 +709,7 @@
 
         // Print the tree out for debug.
         if (DBG_VECTOR_DRAWABLE) {
-            printGroupTree(pathRenderer.mRootGroup, 0);
+            pathRenderer.printGroupTree();
         }
 
         if (noPathTag) {
@@ -715,24 +724,6 @@
         }
     }
 
-    private void printGroupTree(VGroup currentGroup, int level) {
-        String indent = "";
-        for (int i = 0; i < level; i++) {
-            indent += "    ";
-        }
-        // Print the current node
-        Log.v(LOGTAG, indent + "current group is :" + currentGroup.getGroupName()
-                + " rotation is " + currentGroup.mRotate);
-        Log.v(LOGTAG, indent + "matrix is :" + currentGroup.getLocalMatrix().toString());
-        // Then print all the children groups
-        for (int i = 0; i < currentGroup.mChildren.size(); i++) {
-            final VObject child = currentGroup.mChildren.get(i);
-            if (child instanceof VGroup) {
-                printGroupTree((VGroup) child, level + 1);
-            }
-        }
-    }
-
     @Override
     public int getChangingConfigurations() {
         return super.getChangingConfigurations() | mVectorState.getChangingConfigurations();
@@ -890,6 +881,11 @@
             return mChangingConfigurations
                     | (mTint != null ? mTint.getChangingConfigurations() : 0);
         }
+
+        public boolean isStateful() {
+            return (mTint != null && mTint.isStateful())
+                    || (mVPathRenderer != null && mVPathRenderer.isStateful());
+        }
     }
 
     private static class VPathRenderer {
@@ -972,21 +968,35 @@
             mRootGroup.applyTheme(t);
         }
 
+        public boolean onStateChange(int[] stateSet) {
+            return mRootGroup.onStateChange(stateSet);
+        }
+
+        public boolean isStateful() {
+            return mRootGroup.isStateful();
+        }
+
         public void draw(Canvas canvas, int w, int h, ColorFilter filter) {
             final float scaleX = w / mViewportWidth;
             final float scaleY = h / mViewportHeight;
             mRootGroup.draw(canvas, mTempState, Matrix.IDENTITY_MATRIX, filter, scaleX, scaleY);
         }
+
+        public void printGroupTree() {
+            mRootGroup.printGroupTree("");
+        }
     }
 
     private static class VGroup implements VObject {
+        private static final String GROUP_INDENT = "    ";
+
         // mStackedMatrix is only used temporarily when drawing, it combines all
         // the parents' local matrices with the current one.
         private final Matrix mStackedMatrix = new Matrix();
 
         /////////////////////////////////////////////////////
         // Variables below need to be copied (deep copy if applicable) for mutation.
-        final ArrayList<VObject> mChildren = new ArrayList<>();
+        private final ArrayList<VObject> mChildren = new ArrayList<>();
 
         private float mRotate = 0;
         private float mPivotX = 0;
@@ -995,6 +1005,7 @@
         private float mScaleY = 1;
         private float mTranslateX = 0;
         private float mTranslateY = 0;
+        private boolean mIsStateful;
 
         // mLocalMatrix is updated based on the update of transformation information,
         // either parsed from the XML or by animation.
@@ -1011,6 +1022,7 @@
             mScaleY = copy.mScaleY;
             mTranslateX = copy.mTranslateX;
             mTranslateY = copy.mTranslateY;
+            mIsStateful = copy.mIsStateful;
             mThemeAttrs = copy.mThemeAttrs;
             mGroupName = copy.mGroupName;
             mChangingConfigurations = copy.mChangingConfigurations;
@@ -1054,6 +1066,12 @@
             return mLocalMatrix;
         }
 
+        public void addChild(VObject child) {
+            mChildren.add(child);
+
+            mIsStateful |= child.isStateful();
+        }
+
         @Override
         public void draw(Canvas canvas, TempState temp, Matrix currentMatrix,
                 ColorFilter filter, float scaleX, float scaleY) {
@@ -1109,6 +1127,26 @@
         }
 
         @Override
+        public boolean onStateChange(int[] stateSet) {
+            boolean changed = false;
+
+            final ArrayList<VObject> children = mChildren;
+            for (int i = 0, count = children.size(); i < count; i++) {
+                final VObject child = children.get(i);
+                if (child.isStateful()) {
+                    changed |= child.onStateChange(stateSet);
+                }
+            }
+
+            return changed;
+        }
+
+        @Override
+        public boolean isStateful() {
+            return mIsStateful;
+        }
+
+        @Override
         public boolean canApplyTheme() {
             if (mThemeAttrs != null) {
                 return true;
@@ -1139,6 +1177,9 @@
                 final VObject child = children.get(i);
                 if (child.canApplyTheme()) {
                     child.applyTheme(t);
+
+                    // Applying a theme may have made the child stateful.
+                    mIsStateful |= child.isStateful();
                 }
             }
         }
@@ -1153,6 +1194,24 @@
             mLocalMatrix.postTranslate(mTranslateX + mPivotX, mTranslateY + mPivotY);
         }
 
+        public void printGroupTree(String indent) {
+            Log.v(LOGTAG, indent + "group:" + getGroupName() + " rotation is " + mRotate);
+            Log.v(LOGTAG, indent + "matrix:" + getLocalMatrix().toString());
+
+            final int count = mChildren.size();
+            if (count > 0) {
+                indent += GROUP_INDENT;
+            }
+
+            // Then print all the children groups.
+            for (int i = 0; i < count; i++) {
+                final VObject child = mChildren.get(i);
+                if (child instanceof VGroup) {
+                    ((VGroup) child).printGroupTree(indent);
+                }
+            }
+        }
+
         /* Setters and Getters, used by animator from AnimatedVectorDrawable. */
         @SuppressWarnings("unused")
         public float getRotation() {
@@ -1398,6 +1457,16 @@
             // No-op.
         }
 
+        @Override
+        public boolean onStateChange(int[] stateSet) {
+            return false;
+        }
+
+        @Override
+        public boolean isStateful() {
+            return false;
+        }
+
         private void updateStateFromTypedArray(TypedArray a) {
             // Account for any configuration changes.
             mChangingConfigurations |= a.getChangingConfigurations();
@@ -1427,9 +1496,11 @@
         // Variables below need to be copied (deep copy if applicable) for mutation.
         private int[] mThemeAttrs;
 
+        ColorStateList mStrokeColors = null;
         int mStrokeColor = Color.TRANSPARENT;
         float mStrokeWidth = 0;
 
+        ColorStateList mFillColors = null;
         int mFillColor = Color.TRANSPARENT;
         float mStrokeAlpha = 1.0f;
         int mFillRule;
@@ -1448,11 +1519,14 @@
 
         public VFullPath(VFullPath copy) {
             super(copy);
+
             mThemeAttrs = copy.mThemeAttrs;
 
+            mStrokeColors = copy.mStrokeColors;
             mStrokeColor = copy.mStrokeColor;
             mStrokeWidth = copy.mStrokeWidth;
             mStrokeAlpha = copy.mStrokeAlpha;
+            mFillColors = copy.mFillColors;
             mFillColor = copy.mFillColor;
             mFillRule = copy.mFillRule;
             mFillAlpha = copy.mFillAlpha;
@@ -1492,6 +1566,31 @@
         }
 
         @Override
+        public boolean onStateChange(int[] stateSet) {
+            boolean changed = false;
+
+            if (mStrokeColors != null && mStrokeColors.isStateful()) {
+                final int strokeColor = mStrokeColor;
+                mStrokeColor = mStrokeColors.getColorForState(stateSet, strokeColor);
+                changed |= strokeColor != mStrokeColor;
+            }
+
+            if (mFillColors != null && mFillColors.isStateful()) {
+                final int fillColor = mFillColor;
+                mFillColor = mFillColors.getColorForState(stateSet, fillColor);
+                changed |= fillColor != mFillColor;
+            }
+
+            return changed;
+        }
+
+        @Override
+        public boolean isStateful() {
+            return mStrokeColors != null && mStrokeColors.isStateful()
+                    || mFillColors != null && mFillColors.isStateful();
+        }
+
+        @Override
         public void toPath(TempState temp, Path path) {
             super.toPath(temp, path);
 
@@ -1614,8 +1713,20 @@
                 mNodes = PathParser.createNodesFromPathData(pathData);
             }
 
-            mFillColor = a.getColor(R.styleable.VectorDrawablePath_fillColor,
-                    mFillColor);
+            final ColorStateList fillColors = a.getColorStateList(
+                    R.styleable.VectorDrawablePath_fillColor);
+            if (fillColors != null) {
+                mFillColors = fillColors;
+                mFillColor = fillColors.getDefaultColor();
+            }
+
+            final ColorStateList strokeColors = a.getColorStateList(
+                    R.styleable.VectorDrawablePath_strokeColor);
+            if (strokeColors != null) {
+                mStrokeColors = strokeColors;
+                mStrokeColor = strokeColors.getDefaultColor();
+            }
+
             mFillAlpha = a.getFloat(R.styleable.VectorDrawablePath_fillAlpha,
                     mFillAlpha);
             mStrokeLineCap = getStrokeLineCap(a.getInt(
@@ -1624,8 +1735,6 @@
                     R.styleable.VectorDrawablePath_strokeLineJoin, -1), mStrokeLineJoin);
             mStrokeMiterlimit = a.getFloat(
                     R.styleable.VectorDrawablePath_strokeMiterLimit, mStrokeMiterlimit);
-            mStrokeColor = a.getColor(R.styleable.VectorDrawablePath_strokeColor,
-                    mStrokeColor);
             mStrokeAlpha = a.getFloat(R.styleable.VectorDrawablePath_strokeAlpha,
                     mStrokeAlpha);
             mStrokeWidth = a.getFloat(R.styleable.VectorDrawablePath_strokeWidth,
@@ -1662,6 +1771,7 @@
 
         @SuppressWarnings("unused")
         void setStrokeColor(int strokeColor) {
+            mStrokeColors = null;
             mStrokeColor = strokeColor;
         }
 
@@ -1692,6 +1802,7 @@
 
         @SuppressWarnings("unused")
         void setFillColor(int fillColor) {
+            mFillColors = null;
             mFillColor = fillColor;
         }
 
@@ -1752,5 +1863,7 @@
         void inflate(Resources r, AttributeSet attrs, Theme theme);
         boolean canApplyTheme();
         void applyTheme(Theme t);
+        boolean onStateChange(int[] state);
+        boolean isStateful();
     }
 }
diff --git a/libs/hwui/Android.mk b/libs/hwui/Android.mk
index 3eb13ab..cc0943f 100644
--- a/libs/hwui/Android.mk
+++ b/libs/hwui/Android.mk
@@ -24,6 +24,7 @@
     utils/GLUtils.cpp \
     utils/LinearAllocator.cpp \
     utils/NinePatchImpl.cpp \
+    utils/StringUtils.cpp \
     AmbientShadow.cpp \
     AnimationContext.cpp \
     Animator.cpp \
@@ -168,7 +169,8 @@
     unit_tests/CanvasStateTests.cpp \
     unit_tests/ClipAreaTests.cpp \
     unit_tests/DamageAccumulatorTests.cpp \
-    unit_tests/LinearAllocatorTests.cpp
+    unit_tests/LinearAllocatorTests.cpp \
+    unit_tests/StringUtilsTests.cpp
 
 include $(BUILD_NATIVE_TEST)
 
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/Debug.h b/libs/hwui/Debug.h
index 5808aac..e98fa04 100644
--- a/libs/hwui/Debug.h
+++ b/libs/hwui/Debug.h
@@ -20,9 +20,6 @@
 // Turn on to check for OpenGL errors on each frame
 #define DEBUG_OPENGL 1
 
-// Turn on to display informations about the GPU
-#define DEBUG_EXTENSIONS 0
-
 // Turn on to enable initialization information
 #define DEBUG_INIT 0
 
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/Extensions.cpp b/libs/hwui/Extensions.cpp
index 3d350c9..06c8a21 100644
--- a/libs/hwui/Extensions.cpp
+++ b/libs/hwui/Extensions.cpp
@@ -18,23 +18,14 @@
 
 #include "Debug.h"
 #include "Properties.h"
+#include "utils/StringUtils.h"
 
-#include <EGL/egl.h>
-#include <EGL/eglext.h>
 #include <GLES2/gl2ext.h>
 #include <utils/Log.h>
 
 namespace android {
-
-using namespace uirenderer;
-ANDROID_SINGLETON_STATIC_INSTANCE(Extensions);
-
 namespace uirenderer {
 
-///////////////////////////////////////////////////////////////////////////////
-// Defines
-///////////////////////////////////////////////////////////////////////////////
-
 // Debug
 #if DEBUG_EXTENSIONS
     #define EXT_LOGD(...) ALOGD(__VA_ARGS__)
@@ -42,20 +33,16 @@
     #define EXT_LOGD(...)
 #endif
 
-///////////////////////////////////////////////////////////////////////////////
-// Constructors
-///////////////////////////////////////////////////////////////////////////////
 
 Extensions::Extensions() {
-    // Query GL extensions
-    findExtensions((const char*) glGetString(GL_EXTENSIONS), mGlExtensionList);
-    mHasNPot = hasGlExtension("GL_OES_texture_npot");
-    mHasFramebufferFetch = hasGlExtension("GL_NV_shader_framebuffer_fetch");
-    mHasDiscardFramebuffer = hasGlExtension("GL_EXT_discard_framebuffer");
-    mHasDebugMarker = hasGlExtension("GL_EXT_debug_marker");
-    mHas1BitStencil = hasGlExtension("GL_OES_stencil1");
-    mHas4BitStencil = hasGlExtension("GL_OES_stencil4");
-    mHasUnpackSubImage = hasGlExtension("GL_EXT_unpack_subimage");
+    StringCollection extensions((const char*) glGetString(GL_EXTENSIONS));
+    mHasNPot = extensions.has("GL_OES_texture_npot");
+    mHasFramebufferFetch = extensions.has("GL_NV_shader_framebuffer_fetch");
+    mHasDiscardFramebuffer = extensions.has("GL_EXT_discard_framebuffer");
+    mHasDebugMarker = extensions.has("GL_EXT_debug_marker");
+    mHas1BitStencil = extensions.has("GL_OES_stencil1");
+    mHas4BitStencil = extensions.has("GL_OES_stencil4");
+    mHasUnpackSubImage = extensions.has("GL_EXT_unpack_subimage");
 
     const char* version = (const char*) glGetString(GL_VERSION);
 
@@ -78,40 +65,5 @@
     }
 }
 
-///////////////////////////////////////////////////////////////////////////////
-// Methods
-///////////////////////////////////////////////////////////////////////////////
-
-bool Extensions::hasGlExtension(const char* extension) const {
-   const String8 s(extension);
-   return mGlExtensionList.indexOf(s) >= 0;
-}
-
-bool Extensions::hasEglExtension(const char* extension) const {
-   const String8 s(extension);
-   return mEglExtensionList.indexOf(s) >= 0;
-}
-
-void Extensions::findExtensions(const char* extensions, SortedVector<String8>& list) const {
-    const char* current = extensions;
-    const char* head = current;
-    EXT_LOGD("Available extensions:");
-    do {
-        head = strchr(current, ' ');
-        String8 s(current, head ? head - current : strlen(current));
-        if (s.length()) {
-            list.add(s);
-            EXT_LOGD("  %s", s.string());
-        }
-        current = head + 1;
-    } while (head);
-}
-
-void Extensions::dump() const {
-   ALOGD("%s", (const char*) glGetString(GL_VERSION));
-   ALOGD("Supported GL extensions:\n%s", (const char*) glGetString(GL_EXTENSIONS));
-   ALOGD("Supported EGL extensions:\n%s", eglQueryString(eglGetCurrentDisplay(), EGL_EXTENSIONS));
-}
-
 }; // namespace uirenderer
 }; // namespace android
diff --git a/libs/hwui/Extensions.h b/libs/hwui/Extensions.h
index 636b631..0a30d16 100644
--- a/libs/hwui/Extensions.h
+++ b/libs/hwui/Extensions.h
@@ -50,17 +50,7 @@
     inline int getMajorGlVersion() const { return mVersionMajor; }
     inline int getMinorGlVersion() const { return mVersionMinor; }
 
-    bool hasGlExtension(const char* extension) const;
-    bool hasEglExtension(const char* extension) const;
-
-    void dump() const;
-
 private:
-    void findExtensions(const char* extensions, SortedVector<String8>& list) const;
-
-    SortedVector<String8> mGlExtensionList;
-    SortedVector<String8> mEglExtensionList;
-
     bool mHasNPot;
     bool mHasFramebufferFetch;
     bool mHasDiscardFramebuffer;
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 0bcd83a..96cac86 100644
--- a/libs/hwui/GammaFontRenderer.cpp
+++ b/libs/hwui/GammaFontRenderer.cpp
@@ -21,231 +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();
-        } else if (!strcasecmp(property, "shader")) {
-            return new ShaderGammaFontRenderer(false);
-        } else if (!strcasecmp(property, "shader3")) {
-            return new ShaderGammaFontRenderer(true);
-        }
-    }
-
-    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() {
-}
-
-///////////////////////////////////////////////////////////////////////////////
-// Shader-based renderer
-///////////////////////////////////////////////////////////////////////////////
-
-ShaderGammaFontRenderer::ShaderGammaFontRenderer(bool multiGamma)
-        : GammaFontRenderer() {
-    INIT_LOGD("Creating shader gamma font renderer");
-    mRenderer = nullptr;
-    mMultiGamma = multiGamma;
-}
-
-void ShaderGammaFontRenderer::describe(ProgramDescription& description,
-        const SkPaint* paint) const {
-    if (paint->getShader() == nullptr) {
-        if (mMultiGamma) {
-            const int l = luminance(paint);
-
-            if (l <= mBlackThreshold) {
-                description.hasGammaCorrection = true;
-                description.gamma = mGamma;
-            } else if (l >= mWhiteThreshold) {
-                description.hasGammaCorrection = true;
-                description.gamma = 1.0f / mGamma;
-            }
-        } else {
-            description.hasGammaCorrection = true;
-            description.gamma = 1.0f / mGamma;
-        }
-    }
-}
-
-void ShaderGammaFontRenderer::setupProgram(ProgramDescription& description,
-        Program& program) const {
-    if (description.hasGammaCorrection) {
-        glUniform1f(program.getUniform("gamma"), description.gamma);
-    }
-}
-
-void ShaderGammaFontRenderer::endPrecaching() {
-    if (mRenderer) {
-        mRenderer->endPrecaching();
-    }
-}
-
-///////////////////////////////////////////////////////////////////////////////
-// 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 ca55bf1..146d385 100644
--- a/libs/hwui/GammaFontRenderer.h
+++ b/libs/hwui/GammaFontRenderer.h
@@ -17,183 +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 describe(ProgramDescription& description, const SkPaint* paint) const = 0;
-    virtual void setupProgram(ProgramDescription& description, Program& program) const = 0;
-
-    virtual void endPrecaching() = 0;
-
-    static GammaFontRenderer* createRenderer();
-
-protected:
     GammaFontRenderer();
 
-    int mBlackThreshold;
-    int mWhiteThreshold;
-
-    float mGamma;
-};
-
-class ShaderGammaFontRenderer: public GammaFontRenderer {
-public:
-    ~ShaderGammaFontRenderer() {
-        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.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 describe(ProgramDescription& description, const SkPaint* paint) const override;
-    void setupProgram(ProgramDescription& description, Program& program) const override;
-
-    void endPrecaching() override;
+    void endPrecaching();
 
 private:
-    ShaderGammaFontRenderer(bool multiGamma);
-
-    FontRenderer* mRenderer;
-    bool mMultiGamma;
-
-    friend class GammaFontRenderer;
-};
-
-class LookupGammaFontRenderer: public GammaFontRenderer {
-public:
-    ~LookupGammaFontRenderer() {
-        delete mRenderer;
-    }
-
-    void clear() override {
-        delete mRenderer;
-        mRenderer = nullptr;
-    }
-
-    void flush() override {
-        if (mRenderer) {
-            mRenderer->flushLargeCaches();
-        }
-    }
-
-    FontRenderer& getFontRenderer(const SkPaint* paint) override {
-        if (!mRenderer) {
-            mRenderer = new FontRenderer;
-            mRenderer->setGammaTable(&mGammaTable[0]);
-        }
-        return *mRenderer;
-    }
-
-    uint32_t getFontRendererCount() const override {
-        return 1;
-    }
-
-    uint32_t getFontRendererSize(uint32_t fontRenderer, GLenum format) const override {
-        return mRenderer ? mRenderer->getCacheSize(format) : 0;
-    }
-
-    void describe(ProgramDescription& description, const SkPaint* paint) const override {
-    }
-
-    void setupProgram(ProgramDescription& description, Program& program) const override {
-    }
-
-    void endPrecaching() override;
-
-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 describe(ProgramDescription& description, const SkPaint* paint) const override {
-    }
-
-    void setupProgram(ProgramDescription& description, Program& program) const override {
-    }
-
-    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/GlopBuilder.cpp b/libs/hwui/GlopBuilder.cpp
index e27b26b..69559a7 100644
--- a/libs/hwui/GlopBuilder.cpp
+++ b/libs/hwui/GlopBuilder.cpp
@@ -435,7 +435,6 @@
 
     mOutGlop->fill.texture = { &texture,
             GL_TEXTURE_2D, GL_LINEAR, GL_CLAMP_TO_EDGE, nullptr };
-    mOutGlop->fill.color = { alpha, alpha, alpha, alpha };
 
     setFill(SK_ColorWHITE, alpha, mode, modeUsage, nullptr, colorFilter);
 
@@ -449,7 +448,6 @@
 
     mOutGlop->fill.texture = { &(layer.getTexture()),
             layer.getRenderTarget(), GL_LINEAR, GL_CLAMP_TO_EDGE, &layer.getTexTransform() };
-    mOutGlop->fill.color = { alpha, alpha, alpha, alpha };
 
     setFill(SK_ColorWHITE, alpha, layer.getMode(), Blend::ModeOrderSwap::NoSwap,
             nullptr, layer.getColorFilter());
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/PathCache.cpp b/libs/hwui/PathCache.cpp
index f503e5d..4031f2e1 100644
--- a/libs/hwui/PathCache.cpp
+++ b/libs/hwui/PathCache.cpp
@@ -138,10 +138,10 @@
         mSize(0), mMaxSize(MB(DEFAULT_PATH_CACHE_SIZE)) {
     char property[PROPERTY_VALUE_MAX];
     if (property_get(PROPERTY_PATH_CACHE_SIZE, property, nullptr) > 0) {
-        INIT_LOGD("  Setting %s cache size to %sMB", name, property);
+        INIT_LOGD("  Setting path cache size to %sMB", property);
         mMaxSize = MB(atof(property));
     } else {
-        INIT_LOGD("  Using default %s cache size of %.2fMB", name, DEFAULT_PATH_CACHE_SIZE);
+        INIT_LOGD("  Using default path cache size of %.2fMB", DEFAULT_PATH_CACHE_SIZE);
     }
 
     mCache.setOnEntryRemovedListener(this);
diff --git a/libs/hwui/Program.h b/libs/hwui/Program.h
index b09c207..e5200a5 100644
--- a/libs/hwui/Program.h
+++ b/libs/hwui/Program.h
@@ -78,14 +78,12 @@
 #define PROGRAM_HAS_EXTERNAL_TEXTURE_SHIFT 38
 #define PROGRAM_HAS_TEXTURE_TRANSFORM_SHIFT 39
 
-#define PROGRAM_HAS_GAMMA_CORRECTION 40
+#define PROGRAM_IS_SIMPLE_GRADIENT 40
 
-#define PROGRAM_IS_SIMPLE_GRADIENT 41
+#define PROGRAM_HAS_COLORS 41
 
-#define PROGRAM_HAS_COLORS 42
-
-#define PROGRAM_HAS_DEBUG_HIGHLIGHT 43
-#define PROGRAM_HAS_ROUND_RECT_CLIP 44
+#define PROGRAM_HAS_DEBUG_HIGHLIGHT 42
+#define PROGRAM_HAS_ROUND_RECT_CLIP 43
 
 ///////////////////////////////////////////////////////////////////////////////
 // Types
@@ -157,9 +155,6 @@
     SkXfermode::Mode framebufferMode;
     bool swapSrcDst;
 
-    bool hasGammaCorrection;
-    float gamma;
-
     bool hasDebugHighlight;
     bool hasRoundRectClip;
 
@@ -199,9 +194,6 @@
         framebufferMode = SkXfermode::kClear_Mode;
         swapSrcDst = false;
 
-        hasGammaCorrection = false;
-        gamma = 2.2f;
-
         hasDebugHighlight = false;
         hasRoundRectClip = false;
     }
@@ -266,7 +258,6 @@
         if (useShadowAlphaInterp) key |= programid(0x1) << PROGRAM_USE_SHADOW_ALPHA_INTERP_SHIFT;
         if (hasExternalTexture) key |= programid(0x1) << PROGRAM_HAS_EXTERNAL_TEXTURE_SHIFT;
         if (hasTextureTransform) key |= programid(0x1) << PROGRAM_HAS_TEXTURE_TRANSFORM_SHIFT;
-        if (hasGammaCorrection) key |= programid(0x1) << PROGRAM_HAS_GAMMA_CORRECTION;
         if (isSimpleGradient) key |= programid(0x1) << PROGRAM_IS_SIMPLE_GRADIENT;
         if (hasColors) key |= programid(0x1) << PROGRAM_HAS_COLORS;
         if (hasDebugHighlight) key |= programid(0x1) << PROGRAM_HAS_DEBUG_HIGHLIGHT;
diff --git a/libs/hwui/ProgramCache.cpp b/libs/hwui/ProgramCache.cpp
index b25a4ac..05be488 100644
--- a/libs/hwui/ProgramCache.cpp
+++ b/libs/hwui/ProgramCache.cpp
@@ -167,8 +167,6 @@
         // PorterDuff
         "uniform vec4 colorBlend;\n"
 };
-const char* gFS_Uniforms_Gamma =
-        "uniform float gamma;\n";
 
 const char* gFS_Uniforms_HasRoundRectClip =
         "uniform vec4 roundRectInnerRectLTRB;\n"
@@ -204,18 +202,10 @@
         "\nvoid main(void) {\n"
         "    gl_FragColor = texture2D(baseSampler, outTexCoords);\n"
         "}\n\n";
-const char* gFS_Fast_SingleA8Texture_ApplyGamma =
-        "\nvoid main(void) {\n"
-        "    gl_FragColor = vec4(0.0, 0.0, 0.0, pow(texture2D(baseSampler, outTexCoords).a, gamma));\n"
-        "}\n\n";
 const char* gFS_Fast_SingleModulateA8Texture =
         "\nvoid main(void) {\n"
         "    gl_FragColor = color * texture2D(baseSampler, outTexCoords).a;\n"
         "}\n\n";
-const char* gFS_Fast_SingleModulateA8Texture_ApplyGamma =
-        "\nvoid main(void) {\n"
-        "    gl_FragColor = color * pow(texture2D(baseSampler, outTexCoords).a, gamma);\n"
-        "}\n\n";
 const char* gFS_Fast_SingleGradient[2] = {
         "\nvoid main(void) {\n"
         "    gl_FragColor = %s + texture2D(gradientSampler, linear);\n"
@@ -250,13 +240,11 @@
         // Modulate
         "    fragColor = color * texture2D(baseSampler, outTexCoords);\n"
 };
-const char* gFS_Main_FetchA8Texture[4] = {
+const char* gFS_Main_FetchA8Texture[2] = {
         // Don't modulate
         "    fragColor = texture2D(baseSampler, outTexCoords);\n",
-        "    fragColor = texture2D(baseSampler, outTexCoords);\n",
         // Modulate
         "    fragColor = color * texture2D(baseSampler, outTexCoords).a;\n",
-        "    fragColor = color * pow(texture2D(baseSampler, outTexCoords).a, gamma);\n"
 };
 const char* gFS_Main_FetchGradient[6] = {
         // Linear
@@ -284,38 +272,29 @@
         "    fragColor = blendShaders(gradientColor, bitmapColor)";
 const char* gFS_Main_BlendShadersGB =
         "    fragColor = blendShaders(bitmapColor, gradientColor)";
-const char* gFS_Main_BlendShaders_Modulate[6] = {
+const char* gFS_Main_BlendShaders_Modulate[3] = {
         // Don't modulate
         ";\n",
-        ";\n",
         // Modulate
         " * color.a;\n",
-        " * color.a;\n",
         // Modulate with alpha 8 texture
         " * texture2D(baseSampler, outTexCoords).a;\n",
-        " * pow(texture2D(baseSampler, outTexCoords).a, gamma);\n"
 };
-const char* gFS_Main_GradientShader_Modulate[6] = {
+const char* gFS_Main_GradientShader_Modulate[3] = {
         // Don't modulate
         "    fragColor = gradientColor;\n",
-        "    fragColor = gradientColor;\n",
         // Modulate
         "    fragColor = gradientColor * color.a;\n",
-        "    fragColor = gradientColor * color.a;\n",
         // Modulate with alpha 8 texture
         "    fragColor = gradientColor * texture2D(baseSampler, outTexCoords).a;\n",
-        "    fragColor = gradientColor * pow(texture2D(baseSampler, outTexCoords).a, gamma);\n"
     };
-const char* gFS_Main_BitmapShader_Modulate[6] = {
+const char* gFS_Main_BitmapShader_Modulate[3] = {
         // Don't modulate
         "    fragColor = bitmapColor;\n",
-        "    fragColor = bitmapColor;\n",
         // Modulate
         "    fragColor = bitmapColor * color.a;\n",
-        "    fragColor = bitmapColor * color.a;\n",
         // Modulate with alpha 8 texture
         "    fragColor = bitmapColor * texture2D(baseSampler, outTexCoords).a;\n",
-        "    fragColor = bitmapColor * pow(texture2D(baseSampler, outTexCoords).a, gamma);\n"
     };
 const char* gFS_Main_FragColor =
         "    gl_FragColor = fragColor;\n";
@@ -540,7 +519,6 @@
 static bool shaderOp(const ProgramDescription& description, String8& shader,
         const int modulateOp, const char** snippets) {
     int op = description.hasAlpha8Texture ? MODULATE_OP_MODULATE_A8 : modulateOp;
-    op = op * 2 + description.hasGammaCorrection;
     shader.append(snippets[op]);
     return description.hasAlpha8Texture;
 }
@@ -596,9 +574,6 @@
         shader.appendFormat(gFS_Uniforms_GradientSampler[description.isSimpleGradient],
                 gFS_Uniforms_Dither);
     }
-    if (description.hasGammaCorrection) {
-        shader.append(gFS_Uniforms_Gamma);
-    }
     if (description.hasRoundRectClip) {
         shader.append(gFS_Uniforms_HasRoundRectClip);
     }
@@ -633,17 +608,9 @@
             fast = true;
         } else if (singleA8Texture) {
             if (!description.modulate) {
-                if (description.hasGammaCorrection) {
-                    shader.append(gFS_Fast_SingleA8Texture_ApplyGamma);
-                } else {
-                    shader.append(gFS_Fast_SingleA8Texture);
-                }
+                shader.append(gFS_Fast_SingleA8Texture);
             } else {
-                if (description.hasGammaCorrection) {
-                    shader.append(gFS_Fast_SingleModulateA8Texture_ApplyGamma);
-                } else {
-                    shader.append(gFS_Fast_SingleModulateA8Texture);
-                }
+                shader.append(gFS_Fast_SingleModulateA8Texture);
             }
             fast = true;
         } else if (singleGradient) {
@@ -693,8 +660,7 @@
         if (description.hasTexture || description.hasExternalTexture) {
             if (description.hasAlpha8Texture) {
                 if (!description.hasGradient && !description.hasBitmap) {
-                    shader.append(gFS_Main_FetchA8Texture[modulateOp * 2 +
-                                                          description.hasGammaCorrection]);
+                    shader.append(gFS_Main_FetchA8Texture[modulateOp]);
                 }
             } else {
                 shader.append(gFS_Main_FetchTexture[modulateOp]);
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 7602848..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
@@ -242,7 +220,7 @@
 #define DEFAULT_RENDER_BUFFER_CACHE_SIZE 2.0f
 #define DEFAULT_PATH_CACHE_SIZE 4.0f
 #define DEFAULT_VERTEX_CACHE_SIZE 1.0f
-#define DEFAULT_PATCH_CACHE_SIZE 128 // in kB
+#define DEFAULT_PATCH_CACHE_SIZE 128.0f // in kB
 #define DEFAULT_GRADIENT_CACHE_SIZE 0.5f
 #define DEFAULT_DROP_SHADOW_CACHE_SIZE 2.0f
 #define DEFAULT_FBO_CACHE_SIZE 0
@@ -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/libs/hwui/TessellationCache.cpp b/libs/hwui/TessellationCache.cpp
index 01f5e2d..12a3e76 100644
--- a/libs/hwui/TessellationCache.cpp
+++ b/libs/hwui/TessellationCache.cpp
@@ -312,10 +312,10 @@
         , mShadowCache(LruCache<ShadowDescription, Task<vertexBuffer_pair_t*>*>::kUnlimitedCapacity) {
     char property[PROPERTY_VALUE_MAX];
     if (property_get(PROPERTY_VERTEX_CACHE_SIZE, property, nullptr) > 0) {
-        INIT_LOGD("  Setting %s cache size to %sMB", name, property);
+        INIT_LOGD("  Setting tessellation cache size to %sMB", property);
         setMaxSize(MB(atof(property)));
     } else {
-        INIT_LOGD("  Using default %s cache size of %.2fMB", name, DEFAULT_VERTEX_CACHE_SIZE);
+        INIT_LOGD("  Using default tessellation cache size of %.2fMB", DEFAULT_VERTEX_CACHE_SIZE);
     }
 
     mCache.setOnEntryRemovedListener(&mBufferRemovedListener);
diff --git a/libs/hwui/TreeInfo.h b/libs/hwui/TreeInfo.h
index ed853f7..98e6146 100644
--- a/libs/hwui/TreeInfo.h
+++ b/libs/hwui/TreeInfo.h
@@ -77,7 +77,7 @@
         , canvasContext(clone.canvasContext)
     {}
 
-    const TraversalMode mode;
+    TraversalMode mode;
     // TODO: Remove this? Currently this is used to signal to stop preparing
     // textures if we run out of cache space.
     bool prepareTextures;
diff --git a/libs/hwui/renderstate/Blend.cpp b/libs/hwui/renderstate/Blend.cpp
index b21e15e..93f787d 100644
--- a/libs/hwui/renderstate/Blend.cpp
+++ b/libs/hwui/renderstate/Blend.cpp
@@ -98,20 +98,6 @@
     // gl blending off by default
 }
 
-void Blend::enable(SkXfermode::Mode mode, ModeOrderSwap modeUsage) {
-    GLenum srcMode;
-    GLenum dstMode;
-    getFactors(mode, modeUsage, &srcMode, &dstMode);
-    setFactors(srcMode, dstMode);
-}
-
-void Blend::disable() {
-    if (mEnabled) {
-        glDisable(GL_BLEND);
-        mEnabled = false;
-    }
-}
-
 void Blend::invalidate() {
     syncEnabled();
     mSrcMode = mDstMode = GL_ZERO;
@@ -132,8 +118,13 @@
 
 void Blend::setFactors(GLenum srcMode, GLenum dstMode) {
     if (srcMode == GL_ZERO && dstMode == GL_ZERO) {
-        disable();
+        // disable blending
+        if (mEnabled) {
+            glDisable(GL_BLEND);
+            mEnabled = false;
+        }
     } else {
+        // enable blending
         if (!mEnabled) {
             glEnable(GL_BLEND);
             mEnabled = true;
diff --git a/libs/hwui/renderstate/Blend.h b/libs/hwui/renderstate/Blend.h
index dcc681d..df9e5a8 100644
--- a/libs/hwui/renderstate/Blend.h
+++ b/libs/hwui/renderstate/Blend.h
@@ -34,9 +34,6 @@
         NoSwap,
         Swap,
     };
-
-    void enable(SkXfermode::Mode mode, ModeOrderSwap modeUsage);
-    void disable();
     void syncEnabled();
 
     static void getFactors(SkXfermode::Mode mode, ModeOrderSwap modeUsage,
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index b74b508..9dc5b45 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -60,9 +60,10 @@
         , mEglManager(thread.eglManager())
         , mOpaque(!translucent)
         , mAnimationContext(contextFactory->createAnimationContext(mRenderThread.timeLord()))
-        , mRootRenderNode(rootRenderNode)
         , mJankTracker(thread.timeLord().frameIntervalNanos())
-        , mProfiler(mFrames) {
+        , mProfiler(mFrames)
+        , mContentOverdrawProtectionBounds(0, 0, 0, 0) {
+    mRenderNodes.emplace_back(rootRenderNode);
     mRenderThread.renderState().registerCanvasContext(this);
     mProfiler.setDensity(mRenderThread.mainDisplayInfo().density);
 }
@@ -172,7 +173,8 @@
     return info && ((*info)[FrameInfoIndex::Flags] & FrameInfoFlags::SkippedFrame);
 }
 
-void CanvasContext::prepareTree(TreeInfo& info, int64_t* uiFrameInfo, int64_t syncQueued) {
+void CanvasContext::prepareTree(TreeInfo& info, int64_t* uiFrameInfo,
+        int64_t syncQueued, RenderNode* target) {
     mRenderThread.removeFrameCallback(this);
 
     // If the previous frame was dropped we don't need to hold onto it, so
@@ -189,7 +191,13 @@
     info.canvasContext = this;
 
     mAnimationContext->startFrame(info.mode);
-    mRootRenderNode->prepareTree(info);
+    for (const sp<RenderNode>& node : mRenderNodes) {
+        // Only the primary target node will be drawn full - all other nodes would get drawn in
+        // real time mode. In case of a window, the primary node is the window content and the other
+        // node(s) are non client / filler nodes.
+        info.mode = (node.get() == target ? TreeInfo::MODE_FULL : TreeInfo::MODE_RT_ONLY);
+        node->prepareTree(info);
+    }
     mAnimationContext->runRemainingAnimations(info);
 
     freePrefetechedLayers();
@@ -299,7 +307,95 @@
             dirty.fLeft, dirty.fTop, dirty.fRight, dirty.fBottom, mOpaque);
 
     Rect outBounds;
-    mCanvas->drawRenderNode(mRootRenderNode.get(), outBounds);
+    // It there are multiple render nodes, they are as follows:
+    // #0 - backdrop
+    // #1 - content (with - and clipped to - bounds mContentOverdrawProtectionBounds)
+    // #2 - frame
+    // Usually the backdrop cannot be seen since it will be entirely covered by the content. While
+    // resizing however it might become partially visible. The following render loop will crop the
+    // backdrop against the content and draw the remaining part of it. It will then crop the content
+    // against the backdrop (since that indicates a shrinking of the window) and then the frame
+    // around everything.
+    // The bounds of the backdrop against which the content should be clipped.
+    Rect backdropBounds = mContentOverdrawProtectionBounds;
+    // If there is no content bounds we ignore the layering as stated above and start with 2.
+    int layer = mContentOverdrawProtectionBounds.isEmpty() ? 2 : 0;
+    // Draw all render nodes. Note that
+    for (const sp<RenderNode>& node : mRenderNodes) {
+        if (layer == 0) { // Backdrop.
+            // Draw the backdrop clipped to the inverse content bounds.
+            const RenderProperties& properties = node->properties();
+            Rect targetBounds(properties.getLeft(), properties.getTop(),
+                              properties.getRight(), properties.getBottom());
+            // Remember the intersection of the target bounds and the intersection bounds against
+            // which we have to crop the content.
+            backdropBounds.intersect(targetBounds);
+            // Check if we have to draw something on the left side ...
+            if (targetBounds.left < mContentOverdrawProtectionBounds.left) {
+                mCanvas->save(SkCanvas::kClip_SaveFlag);
+                if (mCanvas->clipRect(targetBounds.left, targetBounds.top,
+                                      mContentOverdrawProtectionBounds.left, targetBounds.bottom,
+                                      SkRegion::kIntersect_Op)) {
+                    mCanvas->drawRenderNode(node.get(), outBounds);
+                }
+                // Reduce the target area by the area we have just painted.
+                targetBounds.left = std::min(mContentOverdrawProtectionBounds.left,
+                                             targetBounds.right);
+                mCanvas->restore();
+            }
+            // ... or on the right side ...
+            if (targetBounds.right > mContentOverdrawProtectionBounds.right &&
+                !targetBounds.isEmpty()) {
+                mCanvas->save(SkCanvas::kClip_SaveFlag);
+                if (mCanvas->clipRect(mContentOverdrawProtectionBounds.right, targetBounds.top,
+                                      targetBounds.right, targetBounds.bottom,
+                                      SkRegion::kIntersect_Op)) {
+                    mCanvas->drawRenderNode(node.get(), outBounds);
+                }
+                // Reduce the target area by the area we have just painted.
+                targetBounds.right = std::max(targetBounds.left,
+                                              mContentOverdrawProtectionBounds.right);
+                mCanvas->restore();
+            }
+            // ... or at the top ...
+            if (targetBounds.top < mContentOverdrawProtectionBounds.top &&
+                !targetBounds.isEmpty()) {
+                mCanvas->save(SkCanvas::kClip_SaveFlag);
+                if (mCanvas->clipRect(targetBounds.left, targetBounds.top, targetBounds.right,
+                                      mContentOverdrawProtectionBounds.top,
+                                      SkRegion::kIntersect_Op)) {
+                    mCanvas->drawRenderNode(node.get(), outBounds);
+                }
+                // Reduce the target area by the area we have just painted.
+                targetBounds.top = std::min(mContentOverdrawProtectionBounds.top,
+                                            targetBounds.bottom);
+                mCanvas->restore();
+            }
+            // ... or at the bottom.
+            if (targetBounds.bottom > mContentOverdrawProtectionBounds.bottom &&
+                !targetBounds.isEmpty()) {
+                mCanvas->save(SkCanvas::kClip_SaveFlag);
+                if (mCanvas->clipRect(targetBounds.left,
+                                      mContentOverdrawProtectionBounds.bottom, targetBounds.right,
+                                      targetBounds.bottom, SkRegion::kIntersect_Op)) {
+                    mCanvas->drawRenderNode(node.get(), outBounds);
+                }
+                mCanvas->restore();
+            }
+        } else if (layer == 1) { // Content
+            // It gets cropped against the bounds of the backdrop to stay inside.
+            mCanvas->save(SkCanvas::kClip_SaveFlag);
+            if (mCanvas->clipRect(backdropBounds.left, backdropBounds.top,
+                                  backdropBounds.right, backdropBounds.bottom,
+                                  SkRegion::kIntersect_Op)) {
+                mCanvas->drawRenderNode(node.get(), outBounds);
+            }
+            mCanvas->restore();
+        } else { // draw the rest on top at will!
+            mCanvas->drawRenderNode(node.get(), outBounds);
+        }
+        layer++;
+    }
 
     profiler().draw(mCanvas);
 
@@ -343,7 +439,10 @@
     if (CC_UNLIKELY(!mCanvas || mEglSurface == EGL_NO_SURFACE)) {
         return;
     }
+    prepareAndDraw(nullptr);
+}
 
+void CanvasContext::prepareAndDraw(RenderNode* node) {
     ATRACE_CALL();
 
     int64_t frameInfo[UI_THREAD_FRAME_INFO_SIZE];
@@ -353,7 +452,7 @@
                 mRenderThread.timeLord().latestVsync());
 
     TreeInfo info(TreeInfo::MODE_RT_ONLY, mRenderThread.renderState());
-    prepareTree(info, frameInfo, systemTime(CLOCK_MONOTONIC));
+    prepareTree(info, frameInfo, systemTime(CLOCK_MONOTONIC), node);
     if (info.out.canDrawThisFrame) {
         draw();
     }
@@ -423,7 +522,9 @@
     stopDrawing();
     if (mEglManager.hasEglContext()) {
         freePrefetechedLayers();
-        mRootRenderNode->destroyHardwareResources();
+        for (const sp<RenderNode>& node : mRenderNodes) {
+            node->destroyHardwareResources();
+        }
         Caches& caches = Caches::getInstance();
         // Make sure to release all the textures we were owning as there won't
         // be another draw
diff --git a/libs/hwui/renderthread/CanvasContext.h b/libs/hwui/renderthread/CanvasContext.h
index 6a79320..1c3845c 100644
--- a/libs/hwui/renderthread/CanvasContext.h
+++ b/libs/hwui/renderthread/CanvasContext.h
@@ -34,6 +34,7 @@
 
 #include <set>
 #include <string>
+#include <vector>
 
 namespace android {
 namespace uirenderer {
@@ -77,12 +78,14 @@
     void setOpaque(bool opaque);
     void makeCurrent();
     void processLayerUpdate(DeferredLayerUpdater* layerUpdater);
-    void prepareTree(TreeInfo& info, int64_t* uiFrameInfo, int64_t syncQueued);
+    void prepareTree(TreeInfo& info, int64_t* uiFrameInfo,
+            int64_t syncQueued, RenderNode* target);
     void draw();
     void destroy();
 
     // IFrameCallback, Chroreographer-driven frame callback entry point
     virtual void doFrame() override;
+    void prepareAndDraw(RenderNode* node);
 
     void buildLayer(RenderNode* node);
     bool copyLayerInto(DeferredLayerUpdater* layer, SkBitmap* bitmap);
@@ -113,6 +116,20 @@
 
     void serializeDisplayListTree();
 
+    void addRenderNode(RenderNode* node, bool placeFront) {
+        int pos = placeFront ? 0 : static_cast<int>(mRenderNodes.size());
+        mRenderNodes.emplace( mRenderNodes.begin() + pos, node);
+    }
+
+    void removeRenderNode(RenderNode* node) {
+        mRenderNodes.erase(std::remove(mRenderNodes.begin(), mRenderNodes.end(), node),
+                mRenderNodes.end());
+    }
+
+    void setContentOverdrawProtectionBounds(int left, int top, int right, int bottom) {
+        mContentOverdrawProtectionBounds.set(left, top, right, bottom);
+    }
+
 private:
     friend class RegisterFrameCallbackTask;
     // TODO: Replace with something better for layer & other GL object
@@ -138,7 +155,7 @@
     DamageAccumulator mDamageAccumulator;
     std::unique_ptr<AnimationContext> mAnimationContext;
 
-    const sp<RenderNode> mRootRenderNode;
+    std::vector< sp<RenderNode> > mRenderNodes;
 
     FrameInfo* mCurrentFrameInfo = nullptr;
     // Ring buffer large enough for 2 seconds worth of frames
@@ -148,6 +165,9 @@
     FrameInfoVisualizer mProfiler;
 
     std::set<RenderNode*> mPrefetechedLayers;
+
+    // Stores the bounds of the main content.
+    Rect mContentOverdrawProtectionBounds;
 };
 
 } /* namespace renderthread */
diff --git a/libs/hwui/renderthread/DrawFrameTask.cpp b/libs/hwui/renderthread/DrawFrameTask.cpp
index 198906c..a47c9ec 100644
--- a/libs/hwui/renderthread/DrawFrameTask.cpp
+++ b/libs/hwui/renderthread/DrawFrameTask.cpp
@@ -38,9 +38,11 @@
 DrawFrameTask::~DrawFrameTask() {
 }
 
-void DrawFrameTask::setContext(RenderThread* thread, CanvasContext* context) {
+void DrawFrameTask::setContext(RenderThread* thread, CanvasContext* context,
+        RenderNode* targetNode) {
     mRenderThread = thread;
     mContext = context;
+    mTargetNode = targetNode;
 }
 
 void DrawFrameTask::pushLayerUpdate(DeferredLayerUpdater* layer) {
@@ -118,7 +120,7 @@
         mContext->processLayerUpdate(mLayers[i].get());
     }
     mLayers.clear();
-    mContext->prepareTree(info, mFrameInfo, mSyncQueued);
+    mContext->prepareTree(info, mFrameInfo, mSyncQueued, mTargetNode);
 
     // This is after the prepareTree so that any pending operations
     // (RenderNode tree state, prefetched layers, etc...) will be flushed.
diff --git a/libs/hwui/renderthread/DrawFrameTask.h b/libs/hwui/renderthread/DrawFrameTask.h
index ebefcba..68ee897 100644
--- a/libs/hwui/renderthread/DrawFrameTask.h
+++ b/libs/hwui/renderthread/DrawFrameTask.h
@@ -57,7 +57,7 @@
     DrawFrameTask();
     virtual ~DrawFrameTask();
 
-    void setContext(RenderThread* thread, CanvasContext* context);
+    void setContext(RenderThread* thread, CanvasContext* context, RenderNode* targetNode);
 
     void pushLayerUpdate(DeferredLayerUpdater* layer);
     void removeLayerUpdate(DeferredLayerUpdater* layer);
@@ -78,6 +78,7 @@
 
     RenderThread* mRenderThread;
     CanvasContext* mContext;
+    RenderNode* mTargetNode = nullptr;
 
     /*********************************************
      *  Single frame data
diff --git a/libs/hwui/renderthread/EglManager.cpp b/libs/hwui/renderthread/EglManager.cpp
index d2ce49f..c9b9637 100644
--- a/libs/hwui/renderthread/EglManager.cpp
+++ b/libs/hwui/renderthread/EglManager.cpp
@@ -20,6 +20,7 @@
 #include "Properties.h"
 #include "RenderThread.h"
 #include "renderstate/RenderState.h"
+#include "utils/StringUtils.h"
 
 #include <cutils/log.h>
 #include <cutils/properties.h>
@@ -133,12 +134,9 @@
 }
 
 void EglManager::initExtensions() {
-    std::string extensions(eglQueryString(mEglDisplay, EGL_EXTENSIONS));
-    auto has = [&](const char* ext) {
-        return extensions.find(ext) != std::string::npos;
-    };
-    EglExtensions.bufferAge = has("EGL_EXT_buffer_age");
-    EglExtensions.setDamage = has("EGL_KHR_partial_update");
+    StringCollection extensions(eglQueryString(mEglDisplay, EGL_EXTENSIONS));
+    EglExtensions.bufferAge = extensions.has("EGL_EXT_buffer_age");
+    EglExtensions.setDamage = extensions.has("EGL_KHR_partial_update");
 }
 
 bool EglManager::hasEglContext() {
diff --git a/libs/hwui/renderthread/RenderProxy.cpp b/libs/hwui/renderthread/RenderProxy.cpp
index b838811..f43a769 100644
--- a/libs/hwui/renderthread/RenderProxy.cpp
+++ b/libs/hwui/renderthread/RenderProxy.cpp
@@ -74,7 +74,7 @@
     args->thread = &mRenderThread;
     args->contextFactory = contextFactory;
     mContext = (CanvasContext*) postAndWait(task);
-    mDrawFrameTask.setContext(&mRenderThread, mContext);
+    mDrawFrameTask.setContext(&mRenderThread, mContext, rootRenderNode);
 }
 
 RenderProxy::~RenderProxy() {
@@ -91,7 +91,7 @@
         SETUP_TASK(destroyContext);
         args->context = mContext;
         mContext = nullptr;
-        mDrawFrameTask.setContext(nullptr, nullptr);
+        mDrawFrameTask.setContext(nullptr, nullptr, nullptr);
         // This is also a fence as we need to be certain that there are no
         // outstanding mDrawFrame tasks posted before it is destroyed
         postAndWait(task);
@@ -461,7 +461,8 @@
     staticPostAndWait(task);
 }
 
-CREATE_BRIDGE4(setTextureAtlas, RenderThread* thread, GraphicBuffer* buffer, int64_t* map, size_t size) {
+CREATE_BRIDGE4(setTextureAtlas, RenderThread* thread, GraphicBuffer* buffer, int64_t* map,
+               size_t size) {
     CanvasContext::setTextureAtlas(*args->thread, args->buffer, args->map, args->size);
     args->buffer->decStrong(nullptr);
     return nullptr;
@@ -490,6 +491,61 @@
     post(task);
 }
 
+CREATE_BRIDGE3(addRenderNode, CanvasContext* context, RenderNode* node, bool placeFront) {
+    args->context->addRenderNode(args->node, args->placeFront);
+    return nullptr;
+}
+
+void RenderProxy::addRenderNode(RenderNode* node, bool placeFront) {
+    SETUP_TASK(addRenderNode);
+    args->context = mContext;
+    args->node = node;
+    args->placeFront = placeFront;
+    post(task);
+}
+
+CREATE_BRIDGE2(removeRenderNode, CanvasContext* context, RenderNode* node) {
+    args->context->removeRenderNode(args->node);
+    return nullptr;
+}
+
+void RenderProxy::removeRenderNode(RenderNode* node) {
+    SETUP_TASK(removeRenderNode);
+    args->context = mContext;
+    args->node = node;
+    post(task);
+}
+
+CREATE_BRIDGE2(drawRenderNode, CanvasContext* context, RenderNode* node) {
+    args->context->prepareAndDraw(args->node);
+    return nullptr;
+}
+
+void RenderProxy::drawRenderNode(RenderNode* node) {
+    SETUP_TASK(drawRenderNode);
+    args->context = mContext;
+    args->node = node;
+    // Be pseudo-thread-safe and don't use any member variables
+    staticPostAndWait(task);
+}
+
+CREATE_BRIDGE5(setContentOverdrawProtectionBounds, CanvasContext* context, int left, int top,
+        int right, int bottom) {
+    args->context->setContentOverdrawProtectionBounds(args->left, args->top, args->right,
+                                                      args->bottom);
+    return nullptr;
+}
+
+void RenderProxy::setContentOverdrawProtectionBounds(int left, int top, int right, int bottom) {
+    SETUP_TASK(setContentOverdrawProtectionBounds);
+    args->context = mContext;
+    args->left = left;
+    args->top = top;
+    args->right = right;
+    args->bottom = bottom;
+    staticPostAndWait(task);
+}
+
 CREATE_BRIDGE1(serializeDisplayListTree, CanvasContext* context) {
     args->context->serializeDisplayListTree();
     return nullptr;
diff --git a/libs/hwui/renderthread/RenderProxy.h b/libs/hwui/renderthread/RenderProxy.h
index e7356db..046f24a 100644
--- a/libs/hwui/renderthread/RenderProxy.h
+++ b/libs/hwui/renderthread/RenderProxy.h
@@ -106,6 +106,11 @@
 
     ANDROID_API void serializeDisplayListTree();
 
+    ANDROID_API void addRenderNode(RenderNode* node, bool placeFront);
+    ANDROID_API void removeRenderNode(RenderNode* node);
+    ANDROID_API void drawRenderNode(RenderNode* node);
+    ANDROID_API void setContentOverdrawProtectionBounds(int left, int top, int right, int bottom);
+
 private:
     RenderThread& mRenderThread;
     CanvasContext* mContext;
diff --git a/libs/hwui/unit_tests/StringUtilsTests.cpp b/libs/hwui/unit_tests/StringUtilsTests.cpp
new file mode 100644
index 0000000..5174ae9
--- /dev/null
+++ b/libs/hwui/unit_tests/StringUtilsTests.cpp
@@ -0,0 +1,41 @@
+/*
+ * 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.
+ */
+
+#include <gtest/gtest.h>
+
+#include "utils/StringUtils.h"
+
+namespace android {
+namespace uirenderer {
+
+TEST(StringUtils, simpleBuildSet) {
+    StringCollection collection("a b c");
+
+    EXPECT_TRUE(collection.has("a"));
+    EXPECT_TRUE(collection.has("b"));
+    EXPECT_TRUE(collection.has("c"));
+    EXPECT_FALSE(collection.has("d"));
+}
+
+TEST(StringUtils, advancedBuildSet) {
+    StringCollection collection("GL_ext1 GL_ext2 GL_ext3");
+
+    EXPECT_TRUE(collection.has("GL_ext1"));
+    EXPECT_FALSE(collection.has("GL_ext")); // string present, but not in list
+}
+
+};
+};
diff --git a/libs/hwui/utils/StringUtils.cpp b/libs/hwui/utils/StringUtils.cpp
new file mode 100644
index 0000000..a1df0e7
--- /dev/null
+++ b/libs/hwui/utils/StringUtils.cpp
@@ -0,0 +1,40 @@
+/*
+ * 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.
+ */
+
+#include "StringUtils.h"
+
+namespace android {
+namespace uirenderer {
+
+StringCollection::StringCollection(const char* spacedList) {
+    const char* current = spacedList;
+    const char* head = current;
+    do {
+        head = strchr(current, ' ');
+        std::string s(current, head ? head - current : strlen(current));
+        if (s.length()) {
+            mSet.insert(s);
+        }
+        current = head + 1;
+    } while (head);
+}
+
+bool StringCollection::has(const char* s) {
+    return mSet.find(std::string(s)) != mSet.end();
+}
+
+}; // namespace uirenderer
+}; // namespace android
diff --git a/libs/hwui/utils/StringUtils.h b/libs/hwui/utils/StringUtils.h
new file mode 100644
index 0000000..ef2a6d5
--- /dev/null
+++ b/libs/hwui/utils/StringUtils.h
@@ -0,0 +1,36 @@
+/*
+ * 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.
+ */
+#ifndef STRING_UTILS_H
+#define STRING_UTILS_H
+
+#include <string>
+#include <unordered_set>
+
+namespace android {
+namespace uirenderer {
+
+class StringCollection {
+public:
+    StringCollection(const char* spacedList);
+    bool has(const char* string);
+private:
+    std::unordered_set<std::string> mSet;
+};
+
+} /* namespace uirenderer */
+} /* namespace android */
+
+#endif /* GLUTILS_H */
diff --git a/media/java/android/media/midi/MidiManager.java b/media/java/android/media/midi/MidiManager.java
index e72bdb4..266b0d9 100644
--- a/media/java/android/media/midi/MidiManager.java
+++ b/media/java/android/media/midi/MidiManager.java
@@ -169,6 +169,13 @@
     /**
      * Registers a callback to receive notifications when MIDI devices are added and removed.
      *
+     * The {@link  DeviceCallback#onDeviceStatusChanged} method will be called immediately
+     * for any devices that have open ports. This allows applications to know which input
+     * ports are already in use and, therefore, unavailable.
+     *
+     * Applications should call {@link #getDevices} before registering the callback
+     * to get a list of devices already added.
+     *
      * @param callback a {@link DeviceCallback} for MIDI device notifications
      * @param handler The {@link android.os.Handler Handler} that will be used for delivering the
      *                device notifications. If handler is null, then the thread used for the
diff --git a/media/jni/android_media_AmrInputStream.cpp b/media/jni/android_media_AmrInputStream.cpp
index afb5d5c..b56a364 100644
--- a/media/jni/android_media_AmrInputStream.cpp
+++ b/media/jni/android_media_AmrInputStream.cpp
@@ -119,7 +119,7 @@
 
 // ----------------------------------------------------------------------------
 
-static JNINativeMethod gMethods[] = {
+static const JNINativeMethod gMethods[] = {
     {"GsmAmrEncoderNew",        "()J",        (void*)android_media_AmrInputStream_GsmAmrEncoderNew},
     {"GsmAmrEncoderInitialize", "(J)V",       (void*)android_media_AmrInputStream_GsmAmrEncoderInitialize},
     {"GsmAmrEncoderEncode",     "(J[BI[BI)I", (void*)android_media_AmrInputStream_GsmAmrEncoderEncode},
diff --git a/media/jni/android_media_ImageReader.cpp b/media/jni/android_media_ImageReader.cpp
index 0034b07..3ffdb17 100644
--- a/media/jni/android_media_ImageReader.cpp
+++ b/media/jni/android_media_ImageReader.cpp
@@ -1253,7 +1253,7 @@
 
 // ----------------------------------------------------------------------------
 
-static JNINativeMethod gImageReaderMethods[] = {
+static const JNINativeMethod gImageReaderMethods[] = {
     {"nativeClassInit",        "()V",                        (void*)ImageReader_classInit },
     {"nativeInit",             "(Ljava/lang/Object;IIII)V",  (void*)ImageReader_init },
     {"nativeClose",            "()V",                        (void*)ImageReader_close },
@@ -1263,7 +1263,7 @@
     {"nativeDetachImage",      "(Landroid/media/Image;)I",   (void*)ImageReader_detachImage },
 };
 
-static JNINativeMethod gImageMethods[] = {
+static const JNINativeMethod gImageMethods[] = {
     {"nativeImageGetBuffer",   "(II)Ljava/nio/ByteBuffer;",   (void*)Image_getByteBuffer },
     {"nativeCreatePlane",      "(II)Landroid/media/ImageReader$SurfaceImage$SurfacePlane;",
                                                               (void*)Image_createSurfacePlane },
diff --git a/media/jni/android_media_MediaCodec.cpp b/media/jni/android_media_MediaCodec.cpp
index 93a4426..e8f680f 100644
--- a/media/jni/android_media_MediaCodec.cpp
+++ b/media/jni/android_media_MediaCodec.cpp
@@ -1798,7 +1798,7 @@
     android_media_MediaCodec_release(env, thiz);
 }
 
-static JNINativeMethod gMethods[] = {
+static const JNINativeMethod gMethods[] = {
     { "native_release", "()V", (void *)android_media_MediaCodec_release },
 
     { "native_reset", "()V", (void *)android_media_MediaCodec_reset },
diff --git a/media/jni/android_media_MediaCodecList.cpp b/media/jni/android_media_MediaCodecList.cpp
index 82dd48d..de9bf1f 100644
--- a/media/jni/android_media_MediaCodecList.cpp
+++ b/media/jni/android_media_MediaCodecList.cpp
@@ -286,7 +286,7 @@
 static void android_media_MediaCodecList_native_init(JNIEnv* /* env */) {
 }
 
-static JNINativeMethod gMethods[] = {
+static const JNINativeMethod gMethods[] = {
     { "native_getCodecCount", "()I", (void *)android_media_MediaCodecList_getCodecCount },
     { "getCodecName", "(I)Ljava/lang/String;",
       (void *)android_media_MediaCodecList_getCodecName },
diff --git a/media/jni/android_media_MediaCrypto.cpp b/media/jni/android_media_MediaCrypto.cpp
index d7968d2..e414f48 100644
--- a/media/jni/android_media_MediaCrypto.cpp
+++ b/media/jni/android_media_MediaCrypto.cpp
@@ -316,7 +316,7 @@
     }
 }
 
-static JNINativeMethod gMethods[] = {
+static const JNINativeMethod gMethods[] = {
     { "release", "()V", (void *)android_media_MediaCrypto_release },
     { "native_init", "()V", (void *)android_media_MediaCrypto_native_init },
 
diff --git a/media/jni/android_media_MediaDrm.cpp b/media/jni/android_media_MediaDrm.cpp
index 9ec0312..275de1ad 100644
--- a/media/jni/android_media_MediaDrm.cpp
+++ b/media/jni/android_media_MediaDrm.cpp
@@ -1455,7 +1455,7 @@
 }
 
 
-static JNINativeMethod gMethods[] = {
+static const JNINativeMethod gMethods[] = {
     { "release", "()V", (void *)android_media_MediaDrm_release },
     { "native_init", "()V", (void *)android_media_MediaDrm_native_init },
 
diff --git a/media/jni/android_media_MediaExtractor.cpp b/media/jni/android_media_MediaExtractor.cpp
index 4e9b726..96c12dd 100644
--- a/media/jni/android_media_MediaExtractor.cpp
+++ b/media/jni/android_media_MediaExtractor.cpp
@@ -769,7 +769,7 @@
     android_media_MediaExtractor_release(env, thiz);
 }
 
-static JNINativeMethod gMethods[] = {
+static const JNINativeMethod gMethods[] = {
     { "release", "()V", (void *)android_media_MediaExtractor_release },
 
     { "getTrackCount", "()I", (void *)android_media_MediaExtractor_getTrackCount },
diff --git a/media/jni/android_media_MediaHTTPConnection.cpp b/media/jni/android_media_MediaHTTPConnection.cpp
index 393003d..fa0b43f 100644
--- a/media/jni/android_media_MediaHTTPConnection.cpp
+++ b/media/jni/android_media_MediaHTTPConnection.cpp
@@ -154,7 +154,7 @@
     return n;
 }
 
-static JNINativeMethod gMethods[] = {
+static const JNINativeMethod gMethods[] = {
     { "native_getIMemory", "()Landroid/os/IBinder;",
       (void *)android_media_MediaHTTPConnection_native_getIMemory },
 
diff --git a/media/jni/android_media_MediaMetadataRetriever.cpp b/media/jni/android_media_MediaMetadataRetriever.cpp
index 59fb6d6..f4e940d 100644
--- a/media/jni/android_media_MediaMetadataRetriever.cpp
+++ b/media/jni/android_media_MediaMetadataRetriever.cpp
@@ -467,7 +467,7 @@
 }
 
 // JNI mapping between Java methods and native methods
-static JNINativeMethod nativeMethods[] = {
+static const JNINativeMethod nativeMethods[] = {
         {
             "_setDataSource",
             "(Landroid/os/IBinder;Ljava/lang/String;[Ljava/lang/String;[Ljava/lang/String;)V",
diff --git a/media/jni/android_media_MediaMuxer.cpp b/media/jni/android_media_MediaMuxer.cpp
index ecb2ac8..216624e 100644
--- a/media/jni/android_media_MediaMuxer.cpp
+++ b/media/jni/android_media_MediaMuxer.cpp
@@ -219,7 +219,7 @@
     }
 }
 
-static JNINativeMethod gMethods[] = {
+static const JNINativeMethod gMethods[] = {
 
     { "nativeAddTrack", "(J[Ljava/lang/String;[Ljava/lang/Object;)I",
         (void *)android_media_MediaMuxer_addTrack },
diff --git a/media/jni/android_media_MediaPlayer.cpp b/media/jni/android_media_MediaPlayer.cpp
index d8041f4..be36729 100644
--- a/media/jni/android_media_MediaPlayer.cpp
+++ b/media/jni/android_media_MediaPlayer.cpp
@@ -1033,7 +1033,7 @@
 
 // ----------------------------------------------------------------------------
 
-static JNINativeMethod gMethods[] = {
+static const JNINativeMethod gMethods[] = {
     {
         "nativeSetDataSource",
         "(Landroid/os/IBinder;Ljava/lang/String;[Ljava/lang/String;"
diff --git a/media/jni/android_media_MediaProfiles.cpp b/media/jni/android_media_MediaProfiles.cpp
index ca9db91..5800043 100644
--- a/media/jni/android_media_MediaProfiles.cpp
+++ b/media/jni/android_media_MediaProfiles.cpp
@@ -301,7 +301,7 @@
     }
     return static_cast<jint>(levels[index]);
 }
-static JNINativeMethod gMethodsForEncoderCapabilitiesClass[] = {
+static const JNINativeMethod gMethodsForEncoderCapabilitiesClass[] = {
     {"native_init",                            "()V",                    (void *)android_media_MediaProfiles_native_init},
     {"native_get_num_file_formats",            "()I",                    (void *)android_media_MediaProfiles_native_get_num_file_formats},
     {"native_get_file_format",                 "(I)I",                   (void *)android_media_MediaProfiles_native_get_file_format},
@@ -315,7 +315,7 @@
                                                                          (void *)android_media_MediaProfiles_native_get_audio_encoder_cap},
 };
 
-static JNINativeMethod gMethodsForCamcorderProfileClass[] = {
+static const JNINativeMethod gMethodsForCamcorderProfileClass[] = {
     {"native_init",                            "()V",                    (void *)android_media_MediaProfiles_native_init},
     {"native_get_camcorder_profile",           "(II)Landroid/media/CamcorderProfile;",
                                                                          (void *)android_media_MediaProfiles_native_get_camcorder_profile},
@@ -323,7 +323,7 @@
                                                                          (void *)android_media_MediaProfiles_native_has_camcorder_profile},
 };
 
-static JNINativeMethod gMethodsForDecoderCapabilitiesClass[] = {
+static const JNINativeMethod gMethodsForDecoderCapabilitiesClass[] = {
     {"native_init",                            "()V",                    (void *)android_media_MediaProfiles_native_init},
     {"native_get_num_video_decoders",          "()I",                    (void *)android_media_MediaProfiles_native_get_num_video_decoders},
     {"native_get_num_audio_decoders",          "()I",                    (void *)android_media_MediaProfiles_native_get_num_audio_decoders},
@@ -331,7 +331,7 @@
     {"native_get_audio_decoder_type",          "(I)I",                   (void *)android_media_MediaProfiles_native_get_audio_decoder_type},
 };
 
-static JNINativeMethod gMethodsForCameraProfileClass[] = {
+static const JNINativeMethod gMethodsForCameraProfileClass[] = {
     {"native_init",                            "()V",                    (void *)android_media_MediaProfiles_native_init},
     {"native_get_num_image_encoding_quality_levels",
                                                "(I)I",                   (void *)android_media_MediaProfiles_native_get_num_image_encoding_quality_levels},
diff --git a/media/jni/android_media_MediaRecorder.cpp b/media/jni/android_media_MediaRecorder.cpp
index f60af63..e05b348 100644
--- a/media/jni/android_media_MediaRecorder.cpp
+++ b/media/jni/android_media_MediaRecorder.cpp
@@ -510,7 +510,7 @@
 
 // ----------------------------------------------------------------------------
 
-static JNINativeMethod gMethods[] = {
+static const JNINativeMethod gMethods[] = {
     {"setCamera",            "(Landroid/hardware/Camera;)V",    (void *)android_media_MediaRecorder_setCamera},
     {"setVideoSource",       "(I)V",                            (void *)android_media_MediaRecorder_setVideoSource},
     {"setAudioSource",       "(I)V",                            (void *)android_media_MediaRecorder_setAudioSource},
diff --git a/media/jni/android_media_MediaScanner.cpp b/media/jni/android_media_MediaScanner.cpp
index 1a9384e..0f3c61f 100644
--- a/media/jni/android_media_MediaScanner.cpp
+++ b/media/jni/android_media_MediaScanner.cpp
@@ -412,7 +412,7 @@
     setNativeScanner_l(env, thiz, 0);
 }
 
-static JNINativeMethod gMethods[] = {
+static const JNINativeMethod gMethods[] = {
     {
         "processDirectory",
         "(Ljava/lang/String;Landroid/media/MediaScannerClient;)V",
diff --git a/media/jni/android_media_ResampleInputStream.cpp b/media/jni/android_media_ResampleInputStream.cpp
index 1549a30..d06baa5 100644
--- a/media/jni/android_media_ResampleInputStream.cpp
+++ b/media/jni/android_media_ResampleInputStream.cpp
@@ -107,7 +107,7 @@
 
 // ----------------------------------------------------------------------------
 
-static JNINativeMethod gMethods[] = {
+static const JNINativeMethod gMethods[] = {
     {"fir21", "([BI[BII)V", (void*)android_media_ResampleInputStream_fir21},
 };
 
diff --git a/media/jni/android_mtp_MtpDatabase.cpp b/media/jni/android_mtp_MtpDatabase.cpp
index 713f28c..ec2f98a 100644
--- a/media/jni/android_mtp_MtpDatabase.cpp
+++ b/media/jni/android_mtp_MtpDatabase.cpp
@@ -1173,12 +1173,12 @@
 
 // ----------------------------------------------------------------------------
 
-static JNINativeMethod gMtpDatabaseMethods[] = {
+static const JNINativeMethod gMtpDatabaseMethods[] = {
     {"native_setup",            "()V",  (void *)android_mtp_MtpDatabase_setup},
     {"native_finalize",         "()V",  (void *)android_mtp_MtpDatabase_finalize},
 };
 
-static JNINativeMethod gMtpPropertyGroupMethods[] = {
+static const JNINativeMethod gMtpPropertyGroupMethods[] = {
     {"format_date_time",        "(J)Ljava/lang/String;",
                                         (void *)android_mtp_MtpPropertyGroup_format_date_time},
 };
diff --git a/media/jni/android_mtp_MtpDevice.cpp b/media/jni/android_mtp_MtpDevice.cpp
index 85e3891..3f4d183 100644
--- a/media/jni/android_mtp_MtpDevice.cpp
+++ b/media/jni/android_mtp_MtpDevice.cpp
@@ -533,7 +533,7 @@
 
 // ----------------------------------------------------------------------------
 
-static JNINativeMethod gMethods[] = {
+static const JNINativeMethod gMethods[] = {
     {"native_open",             "(Ljava/lang/String;I)Z",
                                         (void *)android_mtp_MtpDevice_open},
     {"native_close",            "()V",  (void *)android_mtp_MtpDevice_close},
diff --git a/media/jni/android_mtp_MtpServer.cpp b/media/jni/android_mtp_MtpServer.cpp
index 2ce2a90..d13187c 100644
--- a/media/jni/android_mtp_MtpServer.cpp
+++ b/media/jni/android_mtp_MtpServer.cpp
@@ -179,7 +179,7 @@
 
 // ----------------------------------------------------------------------------
 
-static JNINativeMethod gMethods[] = {
+static const JNINativeMethod gMethods[] = {
     {"native_setup",                "(Landroid/mtp/MtpDatabase;Z)V",
                                             (void *)android_mtp_MtpServer_setup},
     {"native_run",                  "()V",  (void *)android_mtp_MtpServer_run},
diff --git a/media/jni/audioeffect/android_media_AudioEffect.cpp b/media/jni/audioeffect/android_media_AudioEffect.cpp
index aba4bbe..fa69135 100644
--- a/media/jni/audioeffect/android_media_AudioEffect.cpp
+++ b/media/jni/audioeffect/android_media_AudioEffect.cpp
@@ -879,7 +879,7 @@
 // ----------------------------------------------------------------------------
 
 // Dalvik VM type signatures
-static JNINativeMethod gMethods[] = {
+static const JNINativeMethod gMethods[] = {
     {"native_init",          "()V",      (void *)android_media_AudioEffect_native_init},
     {"native_setup",         "(Ljava/lang/Object;Ljava/lang/String;Ljava/lang/String;II[I[Ljava/lang/Object;Ljava/lang/String;)I",
                                          (void *)android_media_AudioEffect_native_setup},
diff --git a/media/jni/audioeffect/android_media_Visualizer.cpp b/media/jni/audioeffect/android_media_Visualizer.cpp
index 0557019..3d3adba 100644
--- a/media/jni/audioeffect/android_media_Visualizer.cpp
+++ b/media/jni/audioeffect/android_media_Visualizer.cpp
@@ -677,7 +677,7 @@
 // ----------------------------------------------------------------------------
 
 // Dalvik VM type signatures
-static JNINativeMethod gMethods[] = {
+static const JNINativeMethod gMethods[] = {
     {"native_init",            "()V",     (void *)android_media_visualizer_native_init},
     {"native_setup",           "(Ljava/lang/Object;I[ILjava/lang/String;)I",
                                           (void *)android_media_visualizer_native_setup},
diff --git a/media/jni/soundpool/SoundPool.cpp b/media/jni/soundpool/SoundPool.cpp
index a705bcc..090be88 100644
--- a/media/jni/soundpool/SoundPool.cpp
+++ b/media/jni/soundpool/SoundPool.cpp
@@ -589,6 +589,9 @@
                     ALOGV("format changed to: %s", AMediaFormat_toString(format));
                 } else if (status == AMEDIACODEC_INFO_TRY_AGAIN_LATER) {
                     ALOGV("no output buffer right now");
+                } else if (status <= AMEDIA_ERROR_BASE) {
+                    ALOGE("decode error: %d", status);
+                    break;
                 } else {
                     ALOGV("unexpected info code: %d", status);
                 }
diff --git a/media/packages/BluetoothMidiService/src/com/android/bluetoothmidiservice/BluetoothMidiDevice.java b/media/packages/BluetoothMidiService/src/com/android/bluetoothmidiservice/BluetoothMidiDevice.java
index e6d59e4..444705c 100644
--- a/media/packages/BluetoothMidiService/src/com/android/bluetoothmidiservice/BluetoothMidiDevice.java
+++ b/media/packages/BluetoothMidiService/src/com/android/bluetoothmidiservice/BluetoothMidiDevice.java
@@ -147,14 +147,22 @@
             // switch to receiving notifications after initial characteristic read
             mBluetoothGatt.setCharacteristicNotification(characteristic, true);
 
+            // Use writeType that requests acknowledgement.
+            // This improves compatibility with various BLE-MIDI devices.
+            int originalWriteType = characteristic.getWriteType();
+            characteristic.setWriteType(BluetoothGattCharacteristic.WRITE_TYPE_DEFAULT);
+
             BluetoothGattDescriptor descriptor = characteristic.getDescriptor(
                     CLIENT_CHARACTERISTIC_CONFIG);
             if (descriptor != null) {
                 descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
-                mBluetoothGatt.writeDescriptor(descriptor);
+                boolean result = mBluetoothGatt.writeDescriptor(descriptor);
+                Log.d(TAG, "writeDescriptor returned " + result);
             } else {
                 Log.e(TAG, "No CLIENT_CHARACTERISTIC_CONFIG for device " + mBluetoothDevice);
             }
+
+            characteristic.setWriteType(originalWriteType);
         }
 
         @Override
diff --git a/packages/BackupRestoreConfirmation/res/values-ro/strings.xml b/packages/BackupRestoreConfirmation/res/values-ro/strings.xml
index 969ec599..6a7dbbd 100644
--- a/packages/BackupRestoreConfirmation/res/values-ro/strings.xml
+++ b/packages/BackupRestoreConfirmation/res/values-ro/strings.xml
@@ -18,10 +18,10 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="backup_confirm_title" msgid="827563724209303345">"Copiere de rezervă completă"</string>
     <string name="restore_confirm_title" msgid="5469365809567486602">"Restabilire completă"</string>
-    <string name="backup_confirm_text" msgid="1878021282758896593">"S-a solicitat crearea unei copii de rezervă complete a tuturor datelor pe un computer desktop conectat. Doriți să permiteți acest lucru?\n\nDacă nu aţi solicitat dvs. copierea de rezervă, nu permiteți ca operaţiunea să continue."</string>
+    <string name="backup_confirm_text" msgid="1878021282758896593">"S-a solicitat crearea unei copii de rezervă complete a tuturor datelor pe un computer desktop conectat. Doriți să permiteți acest lucru?\n\nDacă nu aţi solicitat dvs. copierea de rezervă, nu permiteți ca operațiunea să continue."</string>
     <string name="allow_backup_button_label" msgid="4217228747769644068">"Creaţi copii de rezervă pentru datele dvs."</string>
     <string name="deny_backup_button_label" msgid="6009119115581097708">"Nu creaţi copii de rezervă"</string>
-    <string name="restore_confirm_text" msgid="7499866728030461776">"S-a solicitat o restabilire completă a tuturor datelor de pe un computer desktop conectat. Doriți să permiteți acest lucru?\n\nDacă nu dvs. aţi solicitat această restabilire, nu permiteți continuarea operaţiunii. Acest proces va înlocui toate datele existente în prezent pe dispozitiv!"</string>
+    <string name="restore_confirm_text" msgid="7499866728030461776">"S-a solicitat o restabilire completă a tuturor datelor de pe un computer desktop conectat. Doriți să permiteți acest lucru?\n\nDacă nu dvs. aţi solicitat această restabilire, nu permiteți continuarea operațiunii. Acest proces va înlocui toate datele existente în prezent pe dispozitiv!"</string>
     <string name="allow_restore_button_label" msgid="3081286752277127827">"Restabiliţi datele dvs."</string>
     <string name="deny_restore_button_label" msgid="1724367334453104378">"Nu restabiliţi"</string>
     <string name="current_password_text" msgid="8268189555578298067">"Introduceţi mai jos parola actuală pentru copia de rezervă:"</string>
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..295cb80 100644
--- a/packages/DocumentsUI/AndroidManifest.xml
+++ b/packages/DocumentsUI/AndroidManifest.xml
@@ -3,6 +3,7 @@
 
     <uses-permission android:name="android.permission.MANAGE_DOCUMENTS" />
     <uses-permission android:name="android.permission.REMOVE_TASKS" />
+    <uses-permission android:name="android.permission.REORDER_TASKS" />
 
     <application
         android:name=".DocumentsApplication"
@@ -35,11 +36,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
@@ -54,8 +50,8 @@
         </activity>
 
         <activity
-            android:name=".FilesActivity"
-            android:theme="@style/FilesTheme"
+            android:name=".LauncherActivity"
+            android:theme="@android:style/Theme.NoDisplay"
             android:icon="@drawable/ic_files_app"
             android:label="@string/files_label"
             android:enabled="@bool/productivity_device">
@@ -65,6 +61,22 @@
             </intent-filter>
         </activity>
 
+        <activity
+            android:name=".FilesActivity"
+            android:theme="@style/FilesTheme"
+            android:icon="@drawable/ic_files_app"
+            android:label="@string/files_label"
+            android:documentLaunchMode="intoExisting">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+            </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
             android:name=".RecentsProvider"
             android:authorities="com.android.documentsui.recents"
diff --git a/packages/DocumentsUI/res/drawable/progress_indeterminate_horizontal_material_trimmed.xml b/packages/DocumentsUI/res/drawable/progress_indeterminate_horizontal_material_trimmed.xml
new file mode 100644
index 0000000..070b9a1
--- /dev/null
+++ b/packages/DocumentsUI/res/drawable/progress_indeterminate_horizontal_material_trimmed.xml
@@ -0,0 +1,28 @@
+<?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.
+-->
+
+<!-- Variant of progress_indeterminate_horizontal_material in frameworks/base/core/res, which
+     draws the whole height of the progress bar instead having blank space above and below the
+     bar. -->
+<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:drawable="@drawable/vector_drawable_progress_indeterminate_horizontal_trimmed" >
+    <target
+        android:name="rect2_grp"
+        android:animation="@*android:anim/progress_indeterminate_horizontal_rect2" />
+    <target
+        android:name="rect1_grp"
+        android:animation="@*android:anim/progress_indeterminate_horizontal_rect1" />
+</animated-vector>
diff --git a/packages/DocumentsUI/res/drawable/vector_drawable_progress_indeterminate_horizontal_trimmed.xml b/packages/DocumentsUI/res/drawable/vector_drawable_progress_indeterminate_horizontal_trimmed.xml
new file mode 100644
index 0000000..39e3a37
--- /dev/null
+++ b/packages/DocumentsUI/res/drawable/vector_drawable_progress_indeterminate_horizontal_trimmed.xml
@@ -0,0 +1,53 @@
+<?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.
+-->
+
+<!-- Variant of vector_drawable_progress_indeterminate_horizontal in frameworks/base/core/res, which
+     draws the whole height of the progress bar instead having blank space above and below the
+     bar. -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:height="10dp"
+    android:width="360dp"
+    android:viewportHeight="10"
+    android:viewportWidth="360" >
+    <group
+        android:name="progress_group"
+        android:translateX="180"
+        android:translateY="5" >
+        <path
+            android:name="background_track"
+            android:pathData="M -180.0,-5.0 l 360.0,0 l 0,10.0 l -360.0,0 Z"
+            android:fillColor="?android:attr/colorControlActivated"
+            android:fillAlpha="?android:attr/disabledAlpha"/>
+        <group
+            android:name="rect2_grp"
+            android:translateX="-197.60001"
+            android:scaleX="0.1" >
+            <path
+                android:name="rect2"
+                android:pathData="M -144.0,-5.0 l 288.0,0 l 0,10.0 l -288.0,0 Z"
+                android:fillColor="?android:attr/colorControlActivated" />
+        </group>
+        <group
+            android:name="rect1_grp"
+            android:translateX="-522.59998"
+            android:scaleX="0.1" >
+            <path
+                android:name="rect1"
+                android:pathData="M -144.0,-5.0 l 288.0,0 l 0,10.0 l -288.0,0 Z"
+                android:fillColor="?android:attr/colorControlActivated" />
+        </group>
+    </group>
+</vector>
diff --git a/packages/DocumentsUI/res/layout/directory_cluster.xml b/packages/DocumentsUI/res/layout/directory_cluster.xml
index e47e196..8245e53 100644
--- a/packages/DocumentsUI/res/layout/directory_cluster.xml
+++ b/packages/DocumentsUI/res/layout/directory_cluster.xml
@@ -18,6 +18,13 @@
     android:layout_height="match_parent"
     android:orientation="vertical">
 
+    <FrameLayout
+        android:id="@+id/container_message_bar"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:elevation="8dp"
+        android:background="@color/material_grey_50"/>
+
     <com.android.documentsui.DirectoryContainerView
         android:id="@+id/container_directory"
         android:layout_width="match_parent"
diff --git a/packages/DocumentsUI/res/layout/fragment_directory.xml b/packages/DocumentsUI/res/layout/fragment_directory.xml
index 7aff497..45cb34d 100644
--- a/packages/DocumentsUI/res/layout/fragment_directory.xml
+++ b/packages/DocumentsUI/res/layout/fragment_directory.xml
@@ -17,35 +17,70 @@
 <com.android.documentsui.DirectoryView xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
-    android:background="@color/material_grey_50">
+    android:background="@color/material_grey_50"
+    android:orientation="vertical"
+    android:animateLayoutChanges="true">
 
-    <TextView
+    <ProgressBar
+        android:id="@+id/progressbar"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:indeterminate="true"
+        style="@style/TrimmedHorizontalProgressBar"
+        android:visibility="gone"/>
+  
+    <FrameLayout
+        android:id="@+id/container_message_bar"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:elevation="8dp"
+        android:background="@color/material_grey_50"
+        android:visibility="gone"/>
+
+    <!-- The empty directory view -->
+    <LinearLayout
         android:id="@android:id/empty"
-        android:layout_width="match_parent"
-        android:layout_height="match_parent"
         android:gravity="center"
-        android:text="@string/empty"
-        android:visibility="gone"
-        style="@android:style/TextAppearance.Material.Subhead" />
-
-    <!-- The 'list' view is still used for RecentsCreateFragment -->
-    <ListView
-        android:id="@+id/list"
-        android:layout_width="match_parent"
-        android:layout_height="match_parent" />
-
-    <android.support.v7.widget.RecyclerView
-        android:id="@+id/recyclerView"
-        android:scrollbars="vertical"
         android:layout_width="match_parent"
         android:layout_height="match_parent"
-        android:paddingStart="@dimen/grid_padding_horiz"
-        android:paddingEnd="@dimen/grid_padding_horiz"
-        android:paddingTop="@dimen/grid_padding_vert"
-        android:paddingBottom="@dimen/grid_padding_vert"
-        android:clipToPadding="false"
-        android:scrollbarStyle="outsideOverlay"
-        android:drawSelectorOnTop="true"
-        android:background="@color/directory_background" />
+        android:orientation="vertical"
+        android:visibility="gone">
+        
+        <TextView
+            android:id="@+id/message"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="@string/empty"
+            style="@android:style/TextAppearance.Material.Subhead" />
+
+         <Button
+            android:id="@+id/button_retry"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="@string/button_retry"
+            style="?android:attr/buttonBarPositiveButtonStyle" />
+        
+    </LinearLayout>
+    
+    <!-- This FrameLayout works around b/24189541 -->
+    <FrameLayout
+        android:layout_width="match_parent"
+        android:layout_height="match_parent">
+
+        <android.support.v7.widget.RecyclerView
+            android:id="@+id/recyclerView"
+            android:scrollbars="vertical"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"
+            android:paddingStart="@dimen/grid_padding_horiz"
+            android:paddingEnd="@dimen/grid_padding_horiz"
+            android:paddingTop="@dimen/grid_padding_vert"
+            android:paddingBottom="@dimen/grid_padding_vert"
+            android:clipToPadding="false"
+            android:scrollbarStyle="outsideOverlay"
+            android:drawSelectorOnTop="true"
+            android:background="@color/directory_background" />
+
+    </FrameLayout>
 
 </com.android.documentsui.DirectoryView>
diff --git a/packages/DocumentsUI/res/layout/fragment_message_bar.xml b/packages/DocumentsUI/res/layout/fragment_message_bar.xml
new file mode 100644
index 0000000..47e4e77
--- /dev/null
+++ b/packages/DocumentsUI/res/layout/fragment_message_bar.xml
@@ -0,0 +1,100 @@
+<?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.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:animateLayoutChanges="true"
+    android:layout_height="wrap_content"
+    android:layout_width="match_parent"
+    android:minHeight="?android:attr/listPreferredItemHeightSmall"
+    android:orientation="vertical"
+    android:paddingLeft="@dimen/list_item_padding"
+    android:paddingRight="@dimen/list_item_padding"
+    android:paddingTop="@dimen/list_item_padding">
+
+    <LinearLayout
+        android:id="@+id/container_info"
+        android:layout_height="wrap_content"
+        android:layout_width="wrap_content"
+        android:orientation="horizontal"
+        android:visibility="gone">
+
+        <FrameLayout
+            android:layout_height="@dimen/icon_size"
+            android:layout_width="@dimen/icon_size">
+
+            <ImageView
+                android:contentDescription="@null"
+                android:id="@+id/icon_info"
+                android:layout_height="match_parent"
+                android:layout_width="wrap_content"
+                android:scaleType="centerInside"/>
+
+        </FrameLayout>
+
+        <TextView
+            android:id="@+id/textview_info"
+            android:layout_gravity="center_vertical"
+            android:layout_height="wrap_content"
+            android:layout_width="match_parent"
+            android:selectAllOnFocus="true"/>
+
+    </LinearLayout>
+
+    <LinearLayout
+        android:id="@+id/container_error"
+        android:layout_height="wrap_content"
+        android:layout_width="wrap_content"
+        android:orientation="horizontal"
+        android:visibility="gone">
+
+        <FrameLayout
+            android:layout_height="@dimen/icon_size"
+            android:layout_width="@dimen/icon_size">
+
+            <ImageView
+                android:contentDescription="@null"
+                android:id="@+id/icon_error"
+                android:layout_height="match_parent"
+                android:layout_width="wrap_content"
+                android:scaleType="centerInside"/>
+
+        </FrameLayout>
+
+        <TextView
+            android:id="@+id/textview_error"
+            android:layout_gravity="center_vertical"
+            android:layout_height="wrap_content"
+            android:layout_width="match_parent"
+            android:selectAllOnFocus="true"/>
+
+    </LinearLayout>
+
+    <LinearLayout
+        android:layout_gravity="right"
+        android:layout_height="wrap_content"
+        android:layout_width="wrap_content"
+        android:orientation="horizontal">
+
+        <Button
+            android:id="@+id/button_dismiss"
+            android:layout_height="wrap_content"
+            android:layout_width="wrap_content"
+            android:text="@string/button_dismiss"
+            style="?android:attr/buttonBarPositiveButtonStyle"/>
+
+    </LinearLayout>
+
+</LinearLayout>
diff --git a/packages/DocumentsUI/res/layout/item_loading_grid.xml b/packages/DocumentsUI/res/layout/item_loading_grid.xml
deleted file mode 100644
index 147dfd4..0000000
--- a/packages/DocumentsUI/res/layout/item_loading_grid.xml
+++ /dev/null
@@ -1,29 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2013 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.
--->
-
-<com.android.documentsui.GridItem xmlns:android="http://schemas.android.com/apk/res/android"
-    android:layout_width="match_parent"
-    android:layout_height="@dimen/grid_height"
-    android:orientation="horizontal">
-
-    <ProgressBar
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:layout_gravity="center"
-        android:indeterminate="true"
-        style="?android:attr/progressBarStyle" />
-
-</com.android.documentsui.GridItem>
diff --git a/packages/DocumentsUI/res/layout/item_loading_list.xml b/packages/DocumentsUI/res/layout/item_loading_list.xml
deleted file mode 100644
index 6f214ed..0000000
--- a/packages/DocumentsUI/res/layout/item_loading_list.xml
+++ /dev/null
@@ -1,29 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2013 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="match_parent"
-    android:layout_height="wrap_content"
-    android:minHeight="@dimen/list_item_height">
-
-    <ProgressBar
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:layout_gravity="center"
-        android:indeterminate="true"
-        style="?android:attr/progressBarStyle" />
-
-</FrameLayout>
diff --git a/packages/DocumentsUI/res/layout/item_message_grid.xml b/packages/DocumentsUI/res/layout/item_message_grid.xml
deleted file mode 100644
index 45d61a5..0000000
--- a/packages/DocumentsUI/res/layout/item_message_grid.xml
+++ /dev/null
@@ -1,51 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2013 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.
--->
-
-<com.android.documentsui.GridItem xmlns:android="http://schemas.android.com/apk/res/android"
-    android:layout_width="match_parent"
-    android:layout_height="@dimen/grid_height"
-    android:paddingStart="?android:attr/listPreferredItemPaddingStart"
-    android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
-    android:paddingTop="8dp"
-    android:paddingBottom="8dp">
-
-    <LinearLayout
-        android:layout_width="match_parent"
-        android:layout_height="match_parent"
-        android:orientation="vertical"
-        android:gravity="center">
-
-        <ImageView
-            android:id="@android:id/icon"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:contentDescription="@null" />
-
-        <TextView
-            android:id="@android:id/title"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:gravity="center"
-            android:maxLines="4"
-            android:ellipsize="end"
-            android:paddingTop="8dp"
-            android:textAlignment="viewStart"
-            android:textAppearance="@android:style/TextAppearance.Material.Body1"
-            android:textColor="?android:attr/textColorPrimary" />
-
-    </LinearLayout>
-
-</com.android.documentsui.GridItem>
diff --git a/packages/DocumentsUI/res/layout/item_message_list.xml b/packages/DocumentsUI/res/layout/item_message_list.xml
deleted file mode 100644
index 44c8baf..0000000
--- a/packages/DocumentsUI/res/layout/item_message_list.xml
+++ /dev/null
@@ -1,54 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2013 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.
--->
-
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
-    android:layout_width="match_parent"
-    android:layout_height="wrap_content"
-    android:minHeight="@dimen/list_item_height"
-    android:paddingStart="@dimen/list_item_padding"
-    android:paddingEnd="@dimen/list_item_padding"
-    android:paddingTop="8dp"
-    android:paddingBottom="8dp"
-    android:gravity="center_vertical"
-    android:orientation="horizontal"
-    android:baselineAligned="false">
-
-    <FrameLayout
-        android:layout_width="@dimen/icon_size"
-        android:layout_height="@dimen/icon_size"
-        android:layout_marginEnd="16dp">
-
-        <ImageView
-            android:id="@android:id/icon"
-            android:layout_width="wrap_content"
-            android:layout_height="match_parent"
-            android:scaleType="centerInside"
-            android:contentDescription="@null" />
-
-    </FrameLayout>
-
-    <TextView
-        android:id="@android:id/title"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:layout_gravity="center_vertical"
-        android:maxLines="2"
-        android:ellipsize="end"
-        android:textAlignment="viewStart"
-        android:textAppearance="@android:style/TextAppearance.Material.Body1"
-        android:textColor="?android:attr/textColorPrimary" />
-
-</LinearLayout>
diff --git a/packages/DocumentsUI/res/menu/activity.xml b/packages/DocumentsUI/res/menu/activity.xml
index 7df152f..673a254 100644
--- a/packages/DocumentsUI/res/menu/activity.xml
+++ b/packages/DocumentsUI/res/menu/activity.xml
@@ -26,6 +26,7 @@
         android:id="@+id/menu_create_dir"
         android:title="@string/menu_create_dir"
         android:icon="@drawable/ic_menu_new_folder"
+        android:alphabeticShortcut="e"
         android:showAsAction="always"
         android:visible="false" />
     <item
@@ -56,11 +57,18 @@
         android:icon="@drawable/ic_menu_view_list"
         android:showAsAction="never" />
     <item
+        android:id="@+id/menu_new_window"
+        android:title="@string/menu_new_window"
+        android:alphabeticShortcut="n"
+        android:showAsAction="never"
+        android:visible="false" />
+    <item
         android:id="@+id/menu_paste_from_clipboard"
         android:title="@string/menu_paste_from_clipboard"
         android:alphabeticShortcut="v"
         android:showAsAction="never"
         android:visible="false" />
+    <!-- Copy action is defined in mode_directory.xml -->
     <item
         android:id="@+id/menu_advanced"
         android:showAsAction="never"
diff --git a/packages/DocumentsUI/res/values-af/strings.xml b/packages/DocumentsUI/res/values-af/strings.xml
index 0053f22..c2b6dbc 100644
--- a/packages/DocumentsUI/res/values-af/strings.xml
+++ b/packages/DocumentsUI/res/values-af/strings.xml
@@ -44,6 +44,8 @@
     <string name="button_select" msgid="527196987259139214">"Kies"</string>
     <string name="button_copy" msgid="8706475544635021302">"Kopieer"</string>
     <string name="button_move" msgid="2202666023104202232">"Skuif"</string>
+    <string name="button_dismiss" msgid="3714065566893946085">"Maak toe"</string>
+    <string name="button_retry" msgid="4392027584153752797">"Probeer weer"</string>
     <string name="sort_name" msgid="9183560467917256779">"Volgens naam"</string>
     <string name="sort_date" msgid="586080032956151448">"Volgens datum gewysig"</string>
     <string name="sort_size" msgid="3350681319735474741">"Volgens grootte"</string>
diff --git a/packages/DocumentsUI/res/values-am/strings.xml b/packages/DocumentsUI/res/values-am/strings.xml
index eb88830..b82fa93 100644
--- a/packages/DocumentsUI/res/values-am/strings.xml
+++ b/packages/DocumentsUI/res/values-am/strings.xml
@@ -44,6 +44,8 @@
     <string name="button_select" msgid="527196987259139214">"ምረጥ"</string>
     <string name="button_copy" msgid="8706475544635021302">"ቅዳ"</string>
     <string name="button_move" msgid="2202666023104202232">"አንቀሳቀስ"</string>
+    <string name="button_dismiss" msgid="3714065566893946085">"አሰናብት"</string>
+    <string name="button_retry" msgid="4392027584153752797">"እንደገና ይሞክሩ"</string>
     <string name="sort_name" msgid="9183560467917256779">"በስም"</string>
     <string name="sort_date" msgid="586080032956151448">"በተለወጠበት ቀን"</string>
     <string name="sort_size" msgid="3350681319735474741">"በመጠን"</string>
diff --git a/packages/DocumentsUI/res/values-ar/strings.xml b/packages/DocumentsUI/res/values-ar/strings.xml
index bf464b6..c7c9ef2 100644
--- a/packages/DocumentsUI/res/values-ar/strings.xml
+++ b/packages/DocumentsUI/res/values-ar/strings.xml
@@ -44,6 +44,9 @@
     <string name="button_select" msgid="527196987259139214">"تحديد"</string>
     <string name="button_copy" msgid="8706475544635021302">"نسخ"</string>
     <string name="button_move" msgid="2202666023104202232">"نقل"</string>
+    <string name="button_dismiss" msgid="3714065566893946085">"إزالة"</string>
+    <!-- no translation found for button_retry (4392027584153752797) -->
+    <skip />
     <string name="sort_name" msgid="9183560467917256779">"بحسب الاسم"</string>
     <string name="sort_date" msgid="586080032956151448">"بحسب تاريخ التعديل"</string>
     <string name="sort_size" msgid="3350681319735474741">"بحسب الحجم"</string>
diff --git a/packages/DocumentsUI/res/values-az-rAZ/strings.xml b/packages/DocumentsUI/res/values-az-rAZ/strings.xml
index 0bd01a5..8c79ceb 100644
--- a/packages/DocumentsUI/res/values-az-rAZ/strings.xml
+++ b/packages/DocumentsUI/res/values-az-rAZ/strings.xml
@@ -44,6 +44,9 @@
     <string name="button_select" msgid="527196987259139214">"Seçin"</string>
     <string name="button_copy" msgid="8706475544635021302">"Kopyala"</string>
     <string name="button_move" msgid="2202666023104202232">"Köçürün"</string>
+    <string name="button_dismiss" msgid="3714065566893946085">"Rədd edin"</string>
+    <!-- no translation found for button_retry (4392027584153752797) -->
+    <skip />
     <string name="sort_name" msgid="9183560467917256779">"Ad üzrə"</string>
     <string name="sort_date" msgid="586080032956151448">"Tarix üzrə dəyişmiş"</string>
     <string name="sort_size" msgid="3350681319735474741">"Ölçü üzrə"</string>
diff --git a/packages/DocumentsUI/res/values-bg/strings.xml b/packages/DocumentsUI/res/values-bg/strings.xml
index 669145e..fdf57d0 100644
--- a/packages/DocumentsUI/res/values-bg/strings.xml
+++ b/packages/DocumentsUI/res/values-bg/strings.xml
@@ -44,6 +44,9 @@
     <string name="button_select" msgid="527196987259139214">"Избиране"</string>
     <string name="button_copy" msgid="8706475544635021302">"Копиране"</string>
     <string name="button_move" msgid="2202666023104202232">"Преместване"</string>
+    <string name="button_dismiss" msgid="3714065566893946085">"Отхвърляне"</string>
+    <!-- no translation found for button_retry (4392027584153752797) -->
+    <skip />
     <string name="sort_name" msgid="9183560467917256779">"По име"</string>
     <string name="sort_date" msgid="586080032956151448">"По дата на промяната"</string>
     <string name="sort_size" msgid="3350681319735474741">"По размер"</string>
diff --git a/packages/DocumentsUI/res/values-bn-rBD/strings.xml b/packages/DocumentsUI/res/values-bn-rBD/strings.xml
index 240ba6c..7caf57f 100644
--- a/packages/DocumentsUI/res/values-bn-rBD/strings.xml
+++ b/packages/DocumentsUI/res/values-bn-rBD/strings.xml
@@ -44,6 +44,8 @@
     <string name="button_select" msgid="527196987259139214">"নির্বাচন করুন"</string>
     <string name="button_copy" msgid="8706475544635021302">"অনুলিপি করুন"</string>
     <string name="button_move" msgid="2202666023104202232">"সরান"</string>
+    <string name="button_dismiss" msgid="3714065566893946085">"খারিজ করুন"</string>
+    <string name="button_retry" msgid="4392027584153752797">"আবার চেষ্টা করুন"</string>
     <string name="sort_name" msgid="9183560467917256779">"নামের দ্বারা"</string>
     <string name="sort_date" msgid="586080032956151448">"পরিবর্তনের তারিখ দ্বারা"</string>
     <string name="sort_size" msgid="3350681319735474741">"আকার অনুযায়ী"</string>
diff --git a/packages/DocumentsUI/res/values-ca/strings.xml b/packages/DocumentsUI/res/values-ca/strings.xml
index 30accda..ab365ce 100644
--- a/packages/DocumentsUI/res/values-ca/strings.xml
+++ b/packages/DocumentsUI/res/values-ca/strings.xml
@@ -44,6 +44,9 @@
     <string name="button_select" msgid="527196987259139214">"Selecciona"</string>
     <string name="button_copy" msgid="8706475544635021302">"Copia"</string>
     <string name="button_move" msgid="2202666023104202232">"Desplaça"</string>
+    <string name="button_dismiss" msgid="3714065566893946085">"Ignora"</string>
+    <!-- no translation found for button_retry (4392027584153752797) -->
+    <skip />
     <string name="sort_name" msgid="9183560467917256779">"Per nom"</string>
     <string name="sort_date" msgid="586080032956151448">"Per data de modificació"</string>
     <string name="sort_size" msgid="3350681319735474741">"Per mida"</string>
diff --git a/packages/DocumentsUI/res/values-cs/strings.xml b/packages/DocumentsUI/res/values-cs/strings.xml
index cb1d973..62b313c 100644
--- a/packages/DocumentsUI/res/values-cs/strings.xml
+++ b/packages/DocumentsUI/res/values-cs/strings.xml
@@ -44,6 +44,8 @@
     <string name="button_select" msgid="527196987259139214">"Vybrat"</string>
     <string name="button_copy" msgid="8706475544635021302">"Kopírovat"</string>
     <string name="button_move" msgid="2202666023104202232">"Přesunout"</string>
+    <string name="button_dismiss" msgid="3714065566893946085">"Zavřít"</string>
+    <string name="button_retry" msgid="4392027584153752797">"Zkusit znovu"</string>
     <string name="sort_name" msgid="9183560467917256779">"Podle názvu"</string>
     <string name="sort_date" msgid="586080032956151448">"Podle data úpravy"</string>
     <string name="sort_size" msgid="3350681319735474741">"Podle velikosti"</string>
diff --git a/packages/DocumentsUI/res/values-da/strings.xml b/packages/DocumentsUI/res/values-da/strings.xml
index f12737c..c3d7cdd 100644
--- a/packages/DocumentsUI/res/values-da/strings.xml
+++ b/packages/DocumentsUI/res/values-da/strings.xml
@@ -44,6 +44,8 @@
     <string name="button_select" msgid="527196987259139214">"Vælg"</string>
     <string name="button_copy" msgid="8706475544635021302">"Kopiér"</string>
     <string name="button_move" msgid="2202666023104202232">"Flyt"</string>
+    <string name="button_dismiss" msgid="3714065566893946085">"Luk"</string>
+    <string name="button_retry" msgid="4392027584153752797">"Prøv igen"</string>
     <string name="sort_name" msgid="9183560467917256779">"Efter navn"</string>
     <string name="sort_date" msgid="586080032956151448">"Efter ændringsdato"</string>
     <string name="sort_size" msgid="3350681319735474741">"Efter størrelse"</string>
diff --git a/packages/DocumentsUI/res/values-de/strings.xml b/packages/DocumentsUI/res/values-de/strings.xml
index a7be4db..f88b5c4 100644
--- a/packages/DocumentsUI/res/values-de/strings.xml
+++ b/packages/DocumentsUI/res/values-de/strings.xml
@@ -44,6 +44,8 @@
     <string name="button_select" msgid="527196987259139214">"Auswählen"</string>
     <string name="button_copy" msgid="8706475544635021302">"Kopieren"</string>
     <string name="button_move" msgid="2202666023104202232">"Verschieben"</string>
+    <string name="button_dismiss" msgid="3714065566893946085">"Schließen"</string>
+    <string name="button_retry" msgid="4392027584153752797">"Erneut versuchen"</string>
     <string name="sort_name" msgid="9183560467917256779">"Nach Name"</string>
     <string name="sort_date" msgid="586080032956151448">"Nach Änderungsdatum"</string>
     <string name="sort_size" msgid="3350681319735474741">"Nach Größe"</string>
diff --git a/packages/DocumentsUI/res/values-el/strings.xml b/packages/DocumentsUI/res/values-el/strings.xml
index 82155a8..dcca46c 100644
--- a/packages/DocumentsUI/res/values-el/strings.xml
+++ b/packages/DocumentsUI/res/values-el/strings.xml
@@ -44,6 +44,8 @@
     <string name="button_select" msgid="527196987259139214">"Επιλογή"</string>
     <string name="button_copy" msgid="8706475544635021302">"Αντιγραφή"</string>
     <string name="button_move" msgid="2202666023104202232">"Μετακίνηση"</string>
+    <string name="button_dismiss" msgid="3714065566893946085">"Παράβλεψη"</string>
+    <string name="button_retry" msgid="4392027584153752797">"Δοκιμάστε ξανά"</string>
     <string name="sort_name" msgid="9183560467917256779">"Κατά όνομα"</string>
     <string name="sort_date" msgid="586080032956151448">"Κατά ημερομηνία τροποποίησης"</string>
     <string name="sort_size" msgid="3350681319735474741">"Κατά μέγεθος"</string>
diff --git a/packages/DocumentsUI/res/values-en-rAU/strings.xml b/packages/DocumentsUI/res/values-en-rAU/strings.xml
index c609047..26fd30f 100644
--- a/packages/DocumentsUI/res/values-en-rAU/strings.xml
+++ b/packages/DocumentsUI/res/values-en-rAU/strings.xml
@@ -44,6 +44,8 @@
     <string name="button_select" msgid="527196987259139214">"select"</string>
     <string name="button_copy" msgid="8706475544635021302">"Copy"</string>
     <string name="button_move" msgid="2202666023104202232">"Move"</string>
+    <string name="button_dismiss" msgid="3714065566893946085">"Dismiss"</string>
+    <string name="button_retry" msgid="4392027584153752797">"Try again"</string>
     <string name="sort_name" msgid="9183560467917256779">"By name"</string>
     <string name="sort_date" msgid="586080032956151448">"By date modified"</string>
     <string name="sort_size" msgid="3350681319735474741">"By size"</string>
diff --git a/packages/DocumentsUI/res/values-en-rGB/strings.xml b/packages/DocumentsUI/res/values-en-rGB/strings.xml
index c609047..26fd30f 100644
--- a/packages/DocumentsUI/res/values-en-rGB/strings.xml
+++ b/packages/DocumentsUI/res/values-en-rGB/strings.xml
@@ -44,6 +44,8 @@
     <string name="button_select" msgid="527196987259139214">"select"</string>
     <string name="button_copy" msgid="8706475544635021302">"Copy"</string>
     <string name="button_move" msgid="2202666023104202232">"Move"</string>
+    <string name="button_dismiss" msgid="3714065566893946085">"Dismiss"</string>
+    <string name="button_retry" msgid="4392027584153752797">"Try again"</string>
     <string name="sort_name" msgid="9183560467917256779">"By name"</string>
     <string name="sort_date" msgid="586080032956151448">"By date modified"</string>
     <string name="sort_size" msgid="3350681319735474741">"By size"</string>
diff --git a/packages/DocumentsUI/res/values-en-rIN/strings.xml b/packages/DocumentsUI/res/values-en-rIN/strings.xml
index c609047..26fd30f 100644
--- a/packages/DocumentsUI/res/values-en-rIN/strings.xml
+++ b/packages/DocumentsUI/res/values-en-rIN/strings.xml
@@ -44,6 +44,8 @@
     <string name="button_select" msgid="527196987259139214">"select"</string>
     <string name="button_copy" msgid="8706475544635021302">"Copy"</string>
     <string name="button_move" msgid="2202666023104202232">"Move"</string>
+    <string name="button_dismiss" msgid="3714065566893946085">"Dismiss"</string>
+    <string name="button_retry" msgid="4392027584153752797">"Try again"</string>
     <string name="sort_name" msgid="9183560467917256779">"By name"</string>
     <string name="sort_date" msgid="586080032956151448">"By date modified"</string>
     <string name="sort_size" msgid="3350681319735474741">"By size"</string>
diff --git a/packages/DocumentsUI/res/values-es-rUS/strings.xml b/packages/DocumentsUI/res/values-es-rUS/strings.xml
index 5936d83..6e805b9 100644
--- a/packages/DocumentsUI/res/values-es-rUS/strings.xml
+++ b/packages/DocumentsUI/res/values-es-rUS/strings.xml
@@ -44,6 +44,8 @@
     <string name="button_select" msgid="527196987259139214">"Seleccionar"</string>
     <string name="button_copy" msgid="8706475544635021302">"Copiar"</string>
     <string name="button_move" msgid="2202666023104202232">"Mover"</string>
+    <string name="button_dismiss" msgid="3714065566893946085">"Descartar"</string>
+    <string name="button_retry" msgid="4392027584153752797">"Reintentar"</string>
     <string name="sort_name" msgid="9183560467917256779">"Por nombre"</string>
     <string name="sort_date" msgid="586080032956151448">"Por fecha de modificación"</string>
     <string name="sort_size" msgid="3350681319735474741">"Por tamaño"</string>
diff --git a/packages/DocumentsUI/res/values-es/strings.xml b/packages/DocumentsUI/res/values-es/strings.xml
index 2ed67dd..8fa1d2e 100644
--- a/packages/DocumentsUI/res/values-es/strings.xml
+++ b/packages/DocumentsUI/res/values-es/strings.xml
@@ -44,6 +44,8 @@
     <string name="button_select" msgid="527196987259139214">"Seleccionar"</string>
     <string name="button_copy" msgid="8706475544635021302">"Copiar"</string>
     <string name="button_move" msgid="2202666023104202232">"Mover"</string>
+    <string name="button_dismiss" msgid="3714065566893946085">"Ignorar"</string>
+    <string name="button_retry" msgid="4392027584153752797">"Reintentar"</string>
     <string name="sort_name" msgid="9183560467917256779">"Por nombre"</string>
     <string name="sort_date" msgid="586080032956151448">"Por fecha de modificación"</string>
     <string name="sort_size" msgid="3350681319735474741">"Por tamaño"</string>
diff --git a/packages/DocumentsUI/res/values-et-rEE/strings.xml b/packages/DocumentsUI/res/values-et-rEE/strings.xml
index e32936f..7008885 100644
--- a/packages/DocumentsUI/res/values-et-rEE/strings.xml
+++ b/packages/DocumentsUI/res/values-et-rEE/strings.xml
@@ -44,6 +44,9 @@
     <string name="button_select" msgid="527196987259139214">"Vali"</string>
     <string name="button_copy" msgid="8706475544635021302">"Kopeeri"</string>
     <string name="button_move" msgid="2202666023104202232">"Teisalda"</string>
+    <string name="button_dismiss" msgid="3714065566893946085">"Loobu"</string>
+    <!-- no translation found for button_retry (4392027584153752797) -->
+    <skip />
     <string name="sort_name" msgid="9183560467917256779">"Nime järgi"</string>
     <string name="sort_date" msgid="586080032956151448">"Muutmiskuupäeva järgi"</string>
     <string name="sort_size" msgid="3350681319735474741">"Suuruse järgi"</string>
diff --git a/packages/DocumentsUI/res/values-eu-rES/strings.xml b/packages/DocumentsUI/res/values-eu-rES/strings.xml
index a1b6fc2..17d7c66 100644
--- a/packages/DocumentsUI/res/values-eu-rES/strings.xml
+++ b/packages/DocumentsUI/res/values-eu-rES/strings.xml
@@ -44,6 +44,9 @@
     <string name="button_select" msgid="527196987259139214">"Hautatu"</string>
     <string name="button_copy" msgid="8706475544635021302">"Kopiatu"</string>
     <string name="button_move" msgid="2202666023104202232">"Mugitu"</string>
+    <string name="button_dismiss" msgid="3714065566893946085">"Baztertu"</string>
+    <!-- no translation found for button_retry (4392027584153752797) -->
+    <skip />
     <string name="sort_name" msgid="9183560467917256779">"Izenaren arabera"</string>
     <string name="sort_date" msgid="586080032956151448">"Aldatze-dataren arabera"</string>
     <string name="sort_size" msgid="3350681319735474741">"Tamainaren arabera"</string>
diff --git a/packages/DocumentsUI/res/values-fa/strings.xml b/packages/DocumentsUI/res/values-fa/strings.xml
index bbbfe52..c4f6d58 100644
--- a/packages/DocumentsUI/res/values-fa/strings.xml
+++ b/packages/DocumentsUI/res/values-fa/strings.xml
@@ -22,7 +22,7 @@
     <string name="title_save" msgid="2433679664882857999">"ذخیره در"</string>
     <string name="menu_create_dir" msgid="5947289605844398389">"ایجاد پوشه"</string>
     <string name="menu_grid" msgid="6878021334497835259">"نمای جدولی"</string>
-    <string name="menu_list" msgid="7279285939892417279">"نمای فهرست‌وار"</string>
+    <string name="menu_list" msgid="7279285939892417279">"نمای فهرستی"</string>
     <string name="menu_sort" msgid="7677740407158414452">"مرتب‌سازی براساس"</string>
     <string name="menu_search" msgid="3816712084502856974">"جستجو"</string>
     <string name="menu_settings" msgid="6008033148948428823">"تنظیمات"</string>
@@ -44,6 +44,9 @@
     <string name="button_select" msgid="527196987259139214">"انتخاب"</string>
     <string name="button_copy" msgid="8706475544635021302">"کپی"</string>
     <string name="button_move" msgid="2202666023104202232">"انتقال"</string>
+    <string name="button_dismiss" msgid="3714065566893946085">"نپذیرفتن"</string>
+    <!-- no translation found for button_retry (4392027584153752797) -->
+    <skip />
     <string name="sort_name" msgid="9183560467917256779">"براساس نام"</string>
     <string name="sort_date" msgid="586080032956151448">"براساس تاریخ اصلاح"</string>
     <string name="sort_size" msgid="3350681319735474741">"براساس اندازه"</string>
diff --git a/packages/DocumentsUI/res/values-fi/strings.xml b/packages/DocumentsUI/res/values-fi/strings.xml
index fd289a7..3e87300 100644
--- a/packages/DocumentsUI/res/values-fi/strings.xml
+++ b/packages/DocumentsUI/res/values-fi/strings.xml
@@ -44,6 +44,9 @@
     <string name="button_select" msgid="527196987259139214">"Valitse"</string>
     <string name="button_copy" msgid="8706475544635021302">"Kopioi"</string>
     <string name="button_move" msgid="2202666023104202232">"Siirrä"</string>
+    <string name="button_dismiss" msgid="3714065566893946085">"Hylkää"</string>
+    <!-- no translation found for button_retry (4392027584153752797) -->
+    <skip />
     <string name="sort_name" msgid="9183560467917256779">"Nimen mukaan"</string>
     <string name="sort_date" msgid="586080032956151448">"Muokkauspäivän mukaan"</string>
     <string name="sort_size" msgid="3350681319735474741">"Koon mukaan"</string>
diff --git a/packages/DocumentsUI/res/values-fr-rCA/strings.xml b/packages/DocumentsUI/res/values-fr-rCA/strings.xml
index 342fc97..3e82f34 100644
--- a/packages/DocumentsUI/res/values-fr-rCA/strings.xml
+++ b/packages/DocumentsUI/res/values-fr-rCA/strings.xml
@@ -44,6 +44,9 @@
     <string name="button_select" msgid="527196987259139214">"Sélectionner"</string>
     <string name="button_copy" msgid="8706475544635021302">"Copier"</string>
     <string name="button_move" msgid="2202666023104202232">"Déplacer"</string>
+    <string name="button_dismiss" msgid="3714065566893946085">"Faire disparaître"</string>
+    <!-- no translation found for button_retry (4392027584153752797) -->
+    <skip />
     <string name="sort_name" msgid="9183560467917256779">"Par nom"</string>
     <string name="sort_date" msgid="586080032956151448">"Par date de modification"</string>
     <string name="sort_size" msgid="3350681319735474741">"Par taille"</string>
diff --git a/packages/DocumentsUI/res/values-fr/strings.xml b/packages/DocumentsUI/res/values-fr/strings.xml
index 7434e2f..0512ab1 100644
--- a/packages/DocumentsUI/res/values-fr/strings.xml
+++ b/packages/DocumentsUI/res/values-fr/strings.xml
@@ -44,6 +44,8 @@
     <string name="button_select" msgid="527196987259139214">"Sélectionner"</string>
     <string name="button_copy" msgid="8706475544635021302">"Copier"</string>
     <string name="button_move" msgid="2202666023104202232">"Déplacer"</string>
+    <string name="button_dismiss" msgid="3714065566893946085">"Fermer"</string>
+    <string name="button_retry" msgid="4392027584153752797">"Réessayer"</string>
     <string name="sort_name" msgid="9183560467917256779">"Par nom"</string>
     <string name="sort_date" msgid="586080032956151448">"Par date de modification"</string>
     <string name="sort_size" msgid="3350681319735474741">"Par taille"</string>
diff --git a/packages/DocumentsUI/res/values-gl-rES/strings.xml b/packages/DocumentsUI/res/values-gl-rES/strings.xml
index b74018d..a1cd614 100644
--- a/packages/DocumentsUI/res/values-gl-rES/strings.xml
+++ b/packages/DocumentsUI/res/values-gl-rES/strings.xml
@@ -44,6 +44,9 @@
     <string name="button_select" msgid="527196987259139214">"Seleccionar"</string>
     <string name="button_copy" msgid="8706475544635021302">"Copiar"</string>
     <string name="button_move" msgid="2202666023104202232">"Mover"</string>
+    <string name="button_dismiss" msgid="3714065566893946085">"Ignorar"</string>
+    <!-- no translation found for button_retry (4392027584153752797) -->
+    <skip />
     <string name="sort_name" msgid="9183560467917256779">"Por nome"</string>
     <string name="sort_date" msgid="586080032956151448">"Por data de modificación"</string>
     <string name="sort_size" msgid="3350681319735474741">"Por tamaño"</string>
diff --git a/packages/DocumentsUI/res/values-gu-rIN/strings.xml b/packages/DocumentsUI/res/values-gu-rIN/strings.xml
index 58384c9..059fb2c 100644
--- a/packages/DocumentsUI/res/values-gu-rIN/strings.xml
+++ b/packages/DocumentsUI/res/values-gu-rIN/strings.xml
@@ -44,6 +44,8 @@
     <string name="button_select" msgid="527196987259139214">"પસંદ કરો"</string>
     <string name="button_copy" msgid="8706475544635021302">"કૉપિ કરો"</string>
     <string name="button_move" msgid="2202666023104202232">"ખસેડો"</string>
+    <string name="button_dismiss" msgid="3714065566893946085">"છોડી દો"</string>
+    <string name="button_retry" msgid="4392027584153752797">"ફરીથી પ્રયત્ન કરો"</string>
     <string name="sort_name" msgid="9183560467917256779">"નામ દ્વારા"</string>
     <string name="sort_date" msgid="586080032956151448">"સંશોધન તારીખ દ્વારા"</string>
     <string name="sort_size" msgid="3350681319735474741">"કદ દ્વારા"</string>
diff --git a/packages/DocumentsUI/res/values-hi/strings.xml b/packages/DocumentsUI/res/values-hi/strings.xml
index b42c69c..8e33a00 100644
--- a/packages/DocumentsUI/res/values-hi/strings.xml
+++ b/packages/DocumentsUI/res/values-hi/strings.xml
@@ -44,6 +44,8 @@
     <string name="button_select" msgid="527196987259139214">"चुनें"</string>
     <string name="button_copy" msgid="8706475544635021302">"कॉपी करें"</string>
     <string name="button_move" msgid="2202666023104202232">"ले जाएं"</string>
+    <string name="button_dismiss" msgid="3714065566893946085">"ख़ारिज करें"</string>
+    <string name="button_retry" msgid="4392027584153752797">"पुनः प्रयास करें"</string>
     <string name="sort_name" msgid="9183560467917256779">"नाम के अनुसार"</string>
     <string name="sort_date" msgid="586080032956151448">"बदलाव के दिनांक के अनुसार"</string>
     <string name="sort_size" msgid="3350681319735474741">"आकार के अनुसार"</string>
diff --git a/packages/DocumentsUI/res/values-hr/strings.xml b/packages/DocumentsUI/res/values-hr/strings.xml
index 575c7ff..4774753 100644
--- a/packages/DocumentsUI/res/values-hr/strings.xml
+++ b/packages/DocumentsUI/res/values-hr/strings.xml
@@ -44,6 +44,9 @@
     <string name="button_select" msgid="527196987259139214">"Odaberi"</string>
     <string name="button_copy" msgid="8706475544635021302">"Kopiraj"</string>
     <string name="button_move" msgid="2202666023104202232">"Premjesti"</string>
+    <string name="button_dismiss" msgid="3714065566893946085">"Odbaci"</string>
+    <!-- no translation found for button_retry (4392027584153752797) -->
+    <skip />
     <string name="sort_name" msgid="9183560467917256779">"Po nazivu"</string>
     <string name="sort_date" msgid="586080032956151448">"Po datumu izmjene"</string>
     <string name="sort_size" msgid="3350681319735474741">"Po veličini"</string>
diff --git a/packages/DocumentsUI/res/values-hu/strings.xml b/packages/DocumentsUI/res/values-hu/strings.xml
index 2d2e3c8..7a521ec 100644
--- a/packages/DocumentsUI/res/values-hu/strings.xml
+++ b/packages/DocumentsUI/res/values-hu/strings.xml
@@ -44,6 +44,9 @@
     <string name="button_select" msgid="527196987259139214">"Kiválasztás"</string>
     <string name="button_copy" msgid="8706475544635021302">"Másolás"</string>
     <string name="button_move" msgid="2202666023104202232">"Áthelyezés"</string>
+    <string name="button_dismiss" msgid="3714065566893946085">"Elvetés"</string>
+    <!-- no translation found for button_retry (4392027584153752797) -->
+    <skip />
     <string name="sort_name" msgid="9183560467917256779">"Név szerint"</string>
     <string name="sort_date" msgid="586080032956151448">"Módosítás dátuma szerint"</string>
     <string name="sort_size" msgid="3350681319735474741">"Méret szerint"</string>
diff --git a/packages/DocumentsUI/res/values-hy-rAM/strings.xml b/packages/DocumentsUI/res/values-hy-rAM/strings.xml
index 0a4e7a3..95d73f0 100644
--- a/packages/DocumentsUI/res/values-hy-rAM/strings.xml
+++ b/packages/DocumentsUI/res/values-hy-rAM/strings.xml
@@ -44,6 +44,9 @@
     <string name="button_select" msgid="527196987259139214">"Ընտրել"</string>
     <string name="button_copy" msgid="8706475544635021302">"Պատճենել"</string>
     <string name="button_move" msgid="2202666023104202232">"Տեղափոխել"</string>
+    <string name="button_dismiss" msgid="3714065566893946085">"Փակել"</string>
+    <!-- no translation found for button_retry (4392027584153752797) -->
+    <skip />
     <string name="sort_name" msgid="9183560467917256779">"Ըստ անվան"</string>
     <string name="sort_date" msgid="586080032956151448">"Ըստ փոփոխման ամսաթվի"</string>
     <string name="sort_size" msgid="3350681319735474741">"Ըստ չափի"</string>
diff --git a/packages/DocumentsUI/res/values-in/strings.xml b/packages/DocumentsUI/res/values-in/strings.xml
index 59aadf1..63d415c 100644
--- a/packages/DocumentsUI/res/values-in/strings.xml
+++ b/packages/DocumentsUI/res/values-in/strings.xml
@@ -44,6 +44,8 @@
     <string name="button_select" msgid="527196987259139214">"Pilih"</string>
     <string name="button_copy" msgid="8706475544635021302">"Salin"</string>
     <string name="button_move" msgid="2202666023104202232">"Pindahkan"</string>
+    <string name="button_dismiss" msgid="3714065566893946085">"Tutup"</string>
+    <string name="button_retry" msgid="4392027584153752797">"Coba Lagi"</string>
     <string name="sort_name" msgid="9183560467917256779">"Menurut nama"</string>
     <string name="sort_date" msgid="586080032956151448">"Menurut tanggal diubah"</string>
     <string name="sort_size" msgid="3350681319735474741">"Menurut ukuran"</string>
diff --git a/packages/DocumentsUI/res/values-is-rIS/strings.xml b/packages/DocumentsUI/res/values-is-rIS/strings.xml
index a0f4987..0c0e47f 100644
--- a/packages/DocumentsUI/res/values-is-rIS/strings.xml
+++ b/packages/DocumentsUI/res/values-is-rIS/strings.xml
@@ -44,6 +44,8 @@
     <string name="button_select" msgid="527196987259139214">"Velja"</string>
     <string name="button_copy" msgid="8706475544635021302">"Afrita"</string>
     <string name="button_move" msgid="2202666023104202232">"Færa"</string>
+    <string name="button_dismiss" msgid="3714065566893946085">"Hunsa"</string>
+    <string name="button_retry" msgid="4392027584153752797">"Reyna aftur"</string>
     <string name="sort_name" msgid="9183560467917256779">"Eftir heiti"</string>
     <string name="sort_date" msgid="586080032956151448">"Eftir breytingadags."</string>
     <string name="sort_size" msgid="3350681319735474741">"Eftir stærð"</string>
diff --git a/packages/DocumentsUI/res/values-it/strings.xml b/packages/DocumentsUI/res/values-it/strings.xml
index ce837da..de129a7 100644
--- a/packages/DocumentsUI/res/values-it/strings.xml
+++ b/packages/DocumentsUI/res/values-it/strings.xml
@@ -44,6 +44,8 @@
     <string name="button_select" msgid="527196987259139214">"Seleziona"</string>
     <string name="button_copy" msgid="8706475544635021302">"Copia"</string>
     <string name="button_move" msgid="2202666023104202232">"Sposta"</string>
+    <string name="button_dismiss" msgid="3714065566893946085">"Ignora"</string>
+    <string name="button_retry" msgid="4392027584153752797">"Riprova"</string>
     <string name="sort_name" msgid="9183560467917256779">"Per nome"</string>
     <string name="sort_date" msgid="586080032956151448">"Per data di modifica"</string>
     <string name="sort_size" msgid="3350681319735474741">"Per dimensioni"</string>
diff --git a/packages/DocumentsUI/res/values-iw/strings.xml b/packages/DocumentsUI/res/values-iw/strings.xml
index 89e6403..dbe4630 100644
--- a/packages/DocumentsUI/res/values-iw/strings.xml
+++ b/packages/DocumentsUI/res/values-iw/strings.xml
@@ -44,6 +44,9 @@
     <string name="button_select" msgid="527196987259139214">"בחר"</string>
     <string name="button_copy" msgid="8706475544635021302">"העתק"</string>
     <string name="button_move" msgid="2202666023104202232">"העבר"</string>
+    <string name="button_dismiss" msgid="3714065566893946085">"הסר"</string>
+    <!-- no translation found for button_retry (4392027584153752797) -->
+    <skip />
     <string name="sort_name" msgid="9183560467917256779">"לפי שם"</string>
     <string name="sort_date" msgid="586080032956151448">"לפי תאריך שינוי"</string>
     <string name="sort_size" msgid="3350681319735474741">"לפי גודל"</string>
diff --git a/packages/DocumentsUI/res/values-ja/strings.xml b/packages/DocumentsUI/res/values-ja/strings.xml
index 92e1023..45f0ea1 100644
--- a/packages/DocumentsUI/res/values-ja/strings.xml
+++ b/packages/DocumentsUI/res/values-ja/strings.xml
@@ -44,6 +44,9 @@
     <string name="button_select" msgid="527196987259139214">"選択"</string>
     <string name="button_copy" msgid="8706475544635021302">"コピー"</string>
     <string name="button_move" msgid="2202666023104202232">"移動"</string>
+    <string name="button_dismiss" msgid="3714065566893946085">"表示しない"</string>
+    <!-- no translation found for button_retry (4392027584153752797) -->
+    <skip />
     <string name="sort_name" msgid="9183560467917256779">"名前順"</string>
     <string name="sort_date" msgid="586080032956151448">"更新日順"</string>
     <string name="sort_size" msgid="3350681319735474741">"サイズ順"</string>
diff --git a/packages/DocumentsUI/res/values-ka-rGE/strings.xml b/packages/DocumentsUI/res/values-ka-rGE/strings.xml
index 9a324ba..114e73c 100644
--- a/packages/DocumentsUI/res/values-ka-rGE/strings.xml
+++ b/packages/DocumentsUI/res/values-ka-rGE/strings.xml
@@ -44,6 +44,9 @@
     <string name="button_select" msgid="527196987259139214">"არჩევა"</string>
     <string name="button_copy" msgid="8706475544635021302">"კოპირება"</string>
     <string name="button_move" msgid="2202666023104202232">"გადაადგილება"</string>
+    <string name="button_dismiss" msgid="3714065566893946085">"დახურვა"</string>
+    <!-- no translation found for button_retry (4392027584153752797) -->
+    <skip />
     <string name="sort_name" msgid="9183560467917256779">"სახელით"</string>
     <string name="sort_date" msgid="586080032956151448">"ცვლილების თარიღით"</string>
     <string name="sort_size" msgid="3350681319735474741">"ზომით"</string>
diff --git a/packages/DocumentsUI/res/values-kk-rKZ/strings.xml b/packages/DocumentsUI/res/values-kk-rKZ/strings.xml
index 66f69f5..af123c6 100644
--- a/packages/DocumentsUI/res/values-kk-rKZ/strings.xml
+++ b/packages/DocumentsUI/res/values-kk-rKZ/strings.xml
@@ -44,6 +44,9 @@
     <string name="button_select" msgid="527196987259139214">"Таңдау"</string>
     <string name="button_copy" msgid="8706475544635021302">"Көшіру"</string>
     <string name="button_move" msgid="2202666023104202232">"Жылжыту"</string>
+    <string name="button_dismiss" msgid="3714065566893946085">"Өшіру"</string>
+    <!-- no translation found for button_retry (4392027584153752797) -->
+    <skip />
     <string name="sort_name" msgid="9183560467917256779">"Атауы бойынша"</string>
     <string name="sort_date" msgid="586080032956151448">"Өзгертілген мерзімі бойынша"</string>
     <string name="sort_size" msgid="3350681319735474741">"Өлшемі бойынша"</string>
diff --git a/packages/DocumentsUI/res/values-km-rKH/strings.xml b/packages/DocumentsUI/res/values-km-rKH/strings.xml
index 5249d97..8f3feae 100644
--- a/packages/DocumentsUI/res/values-km-rKH/strings.xml
+++ b/packages/DocumentsUI/res/values-km-rKH/strings.xml
@@ -44,6 +44,9 @@
     <string name="button_select" msgid="527196987259139214">"ជ្រើស"</string>
     <string name="button_copy" msgid="8706475544635021302">"ចម្លង"</string>
     <string name="button_move" msgid="2202666023104202232">"ផ្លាស់ទី"</string>
+    <string name="button_dismiss" msgid="3714065566893946085">"បដិសេធ"</string>
+    <!-- no translation found for button_retry (4392027584153752797) -->
+    <skip />
     <string name="sort_name" msgid="9183560467917256779">"តាម​ឈ្មោះ"</string>
     <string name="sort_date" msgid="586080032956151448">"តាម​កាលបរិច្ឆេទ​បាន​កែប្រែ"</string>
     <string name="sort_size" msgid="3350681319735474741">"តាម​​ទំហំ"</string>
diff --git a/packages/DocumentsUI/res/values-kn-rIN/strings.xml b/packages/DocumentsUI/res/values-kn-rIN/strings.xml
index d5eda84..826cf1e 100644
--- a/packages/DocumentsUI/res/values-kn-rIN/strings.xml
+++ b/packages/DocumentsUI/res/values-kn-rIN/strings.xml
@@ -44,6 +44,8 @@
     <string name="button_select" msgid="527196987259139214">"ಆಯ್ಕೆಮಾಡು"</string>
     <string name="button_copy" msgid="8706475544635021302">"ನಕಲಿಸು"</string>
     <string name="button_move" msgid="2202666023104202232">"ಸರಿಸು"</string>
+    <string name="button_dismiss" msgid="3714065566893946085">"ವಜಾಗೊಳಿಸಿ"</string>
+    <string name="button_retry" msgid="4392027584153752797">"ಮತ್ತೆ ಪ್ರಯತ್ನಿಸಿ"</string>
     <string name="sort_name" msgid="9183560467917256779">"ಹೆಸರಿನ ಪ್ರಕಾರ"</string>
     <string name="sort_date" msgid="586080032956151448">"ಮಾರ್ಪಡಿಸಿರುವ ದಿನಾಂಕದ ಪ್ರಕಾರ"</string>
     <string name="sort_size" msgid="3350681319735474741">"ಗಾತ್ರದ ಪ್ರಕಾರ"</string>
diff --git a/packages/DocumentsUI/res/values-ko/strings.xml b/packages/DocumentsUI/res/values-ko/strings.xml
index 77a0df6..322b43b 100644
--- a/packages/DocumentsUI/res/values-ko/strings.xml
+++ b/packages/DocumentsUI/res/values-ko/strings.xml
@@ -44,6 +44,9 @@
     <string name="button_select" msgid="527196987259139214">"선택"</string>
     <string name="button_copy" msgid="8706475544635021302">"복사"</string>
     <string name="button_move" msgid="2202666023104202232">"이동"</string>
+    <string name="button_dismiss" msgid="3714065566893946085">"닫기"</string>
+    <!-- no translation found for button_retry (4392027584153752797) -->
+    <skip />
     <string name="sort_name" msgid="9183560467917256779">"이름순"</string>
     <string name="sort_date" msgid="586080032956151448">"수정된 날짜순"</string>
     <string name="sort_size" msgid="3350681319735474741">"크기순"</string>
diff --git a/packages/DocumentsUI/res/values-ky-rKG/strings.xml b/packages/DocumentsUI/res/values-ky-rKG/strings.xml
index 14a6331..c8aa69e 100644
--- a/packages/DocumentsUI/res/values-ky-rKG/strings.xml
+++ b/packages/DocumentsUI/res/values-ky-rKG/strings.xml
@@ -44,6 +44,9 @@
     <string name="button_select" msgid="527196987259139214">"Тандоо"</string>
     <string name="button_copy" msgid="8706475544635021302">"Көчүрүү"</string>
     <string name="button_move" msgid="2202666023104202232">"Жылдыруу"</string>
+    <string name="button_dismiss" msgid="3714065566893946085">"Этибарга албоо"</string>
+    <!-- no translation found for button_retry (4392027584153752797) -->
+    <skip />
     <string name="sort_name" msgid="9183560467917256779">"Аты боюнча"</string>
     <string name="sort_date" msgid="586080032956151448">"Өзгөртүлгөн күнү боюнча"</string>
     <string name="sort_size" msgid="3350681319735474741">"Өлчөмү боюнча"</string>
diff --git a/packages/DocumentsUI/res/values-lo-rLA/strings.xml b/packages/DocumentsUI/res/values-lo-rLA/strings.xml
index 2d099f4..dedfa05 100644
--- a/packages/DocumentsUI/res/values-lo-rLA/strings.xml
+++ b/packages/DocumentsUI/res/values-lo-rLA/strings.xml
@@ -44,6 +44,9 @@
     <string name="button_select" msgid="527196987259139214">"ເລືອກ"</string>
     <string name="button_copy" msgid="8706475544635021302">"ສຳເນົາ"</string>
     <string name="button_move" msgid="2202666023104202232">"ຍ້າຍ"</string>
+    <string name="button_dismiss" msgid="3714065566893946085">"ປິດໄວ້"</string>
+    <!-- no translation found for button_retry (4392027584153752797) -->
+    <skip />
     <string name="sort_name" msgid="9183560467917256779">"ຕາມຊື່"</string>
     <string name="sort_date" msgid="586080032956151448">"ຕາມວັນທີທີ່ແກ້ໄຂ"</string>
     <string name="sort_size" msgid="3350681319735474741">"ຕາມຂະໜາດ"</string>
diff --git a/packages/DocumentsUI/res/values-lt/strings.xml b/packages/DocumentsUI/res/values-lt/strings.xml
index 7b458add..0c8b443 100644
--- a/packages/DocumentsUI/res/values-lt/strings.xml
+++ b/packages/DocumentsUI/res/values-lt/strings.xml
@@ -44,6 +44,9 @@
     <string name="button_select" msgid="527196987259139214">"Pasirinkti"</string>
     <string name="button_copy" msgid="8706475544635021302">"Kopijuoti"</string>
     <string name="button_move" msgid="2202666023104202232">"Perkelti"</string>
+    <string name="button_dismiss" msgid="3714065566893946085">"Atsisakyti"</string>
+    <!-- no translation found for button_retry (4392027584153752797) -->
+    <skip />
     <string name="sort_name" msgid="9183560467917256779">"Pagal pavadinimą"</string>
     <string name="sort_date" msgid="586080032956151448">"Pagal keitimo datą"</string>
     <string name="sort_size" msgid="3350681319735474741">"Pagal dydį"</string>
diff --git a/packages/DocumentsUI/res/values-lv/strings.xml b/packages/DocumentsUI/res/values-lv/strings.xml
index 44909ce..5c79554 100644
--- a/packages/DocumentsUI/res/values-lv/strings.xml
+++ b/packages/DocumentsUI/res/values-lv/strings.xml
@@ -44,6 +44,9 @@
     <string name="button_select" msgid="527196987259139214">"Atlasīt"</string>
     <string name="button_copy" msgid="8706475544635021302">"Kopēt"</string>
     <string name="button_move" msgid="2202666023104202232">"Pārvietot"</string>
+    <string name="button_dismiss" msgid="3714065566893946085">"Noraidīt"</string>
+    <!-- no translation found for button_retry (4392027584153752797) -->
+    <skip />
     <string name="sort_name" msgid="9183560467917256779">"Pēc nosaukuma"</string>
     <string name="sort_date" msgid="586080032956151448">"Pēc pārveidošanas datuma"</string>
     <string name="sort_size" msgid="3350681319735474741">"Pēc lieluma"</string>
diff --git a/packages/DocumentsUI/res/values-mk-rMK/strings.xml b/packages/DocumentsUI/res/values-mk-rMK/strings.xml
index 7408d0b..b2cb9f1 100644
--- a/packages/DocumentsUI/res/values-mk-rMK/strings.xml
+++ b/packages/DocumentsUI/res/values-mk-rMK/strings.xml
@@ -44,6 +44,9 @@
     <string name="button_select" msgid="527196987259139214">"Избери"</string>
     <string name="button_copy" msgid="8706475544635021302">"Копирај"</string>
     <string name="button_move" msgid="2202666023104202232">"Премести"</string>
+    <string name="button_dismiss" msgid="3714065566893946085">"Отфрли"</string>
+    <!-- no translation found for button_retry (4392027584153752797) -->
+    <skip />
     <string name="sort_name" msgid="9183560467917256779">"По име"</string>
     <string name="sort_date" msgid="586080032956151448">"Изменети по датум"</string>
     <string name="sort_size" msgid="3350681319735474741">"По големина"</string>
diff --git a/packages/DocumentsUI/res/values-ml-rIN/strings.xml b/packages/DocumentsUI/res/values-ml-rIN/strings.xml
index af21db9..07efa66 100644
--- a/packages/DocumentsUI/res/values-ml-rIN/strings.xml
+++ b/packages/DocumentsUI/res/values-ml-rIN/strings.xml
@@ -44,6 +44,9 @@
     <string name="button_select" msgid="527196987259139214">"തിരഞ്ഞെടുക്കുക"</string>
     <string name="button_copy" msgid="8706475544635021302">"പകര്‍ത്തുക"</string>
     <string name="button_move" msgid="2202666023104202232">"നീക്കുക"</string>
+    <string name="button_dismiss" msgid="3714065566893946085">"ഡിസ്മിസ് ചെയ്യുക"</string>
+    <!-- no translation found for button_retry (4392027584153752797) -->
+    <skip />
     <string name="sort_name" msgid="9183560467917256779">"പേര് പ്രകാരം"</string>
     <string name="sort_date" msgid="586080032956151448">"പരിഷ്‌ക്കരിച്ച തീയതി പ്രകാരം"</string>
     <string name="sort_size" msgid="3350681319735474741">"വലുപ്പം പ്രകാരം"</string>
diff --git a/packages/DocumentsUI/res/values-mn-rMN/strings.xml b/packages/DocumentsUI/res/values-mn-rMN/strings.xml
index 1475e08..d45778c 100644
--- a/packages/DocumentsUI/res/values-mn-rMN/strings.xml
+++ b/packages/DocumentsUI/res/values-mn-rMN/strings.xml
@@ -44,6 +44,9 @@
     <string name="button_select" msgid="527196987259139214">"Сонгох"</string>
     <string name="button_copy" msgid="8706475544635021302">"Хуулах"</string>
     <string name="button_move" msgid="2202666023104202232">"Зөөх"</string>
+    <string name="button_dismiss" msgid="3714065566893946085">"Алгасах"</string>
+    <!-- no translation found for button_retry (4392027584153752797) -->
+    <skip />
     <string name="sort_name" msgid="9183560467917256779">"Нэрээр"</string>
     <string name="sort_date" msgid="586080032956151448">"Өөрчлөгдсөн огноогоор"</string>
     <string name="sort_size" msgid="3350681319735474741">"Хэмжээгээр"</string>
diff --git a/packages/DocumentsUI/res/values-mr-rIN/strings.xml b/packages/DocumentsUI/res/values-mr-rIN/strings.xml
index b7654881..8d70143 100644
--- a/packages/DocumentsUI/res/values-mr-rIN/strings.xml
+++ b/packages/DocumentsUI/res/values-mr-rIN/strings.xml
@@ -44,6 +44,8 @@
     <string name="button_select" msgid="527196987259139214">"निवडा"</string>
     <string name="button_copy" msgid="8706475544635021302">"कॉपी करा"</string>
     <string name="button_move" msgid="2202666023104202232">"हलवा"</string>
+    <string name="button_dismiss" msgid="3714065566893946085">"डिसमिस करा"</string>
+    <string name="button_retry" msgid="4392027584153752797">"पुन्‍हा प्रयत्न करा"</string>
     <string name="sort_name" msgid="9183560467917256779">"नावानुसार"</string>
     <string name="sort_date" msgid="586080032956151448">"सुधारित केलेल्‍या तारखेनुसार"</string>
     <string name="sort_size" msgid="3350681319735474741">"आकारानुसार"</string>
diff --git a/packages/DocumentsUI/res/values-ms-rMY/strings.xml b/packages/DocumentsUI/res/values-ms-rMY/strings.xml
index 4682957..2250109 100644
--- a/packages/DocumentsUI/res/values-ms-rMY/strings.xml
+++ b/packages/DocumentsUI/res/values-ms-rMY/strings.xml
@@ -44,6 +44,9 @@
     <string name="button_select" msgid="527196987259139214">"Pilih"</string>
     <string name="button_copy" msgid="8706475544635021302">"Salin"</string>
     <string name="button_move" msgid="2202666023104202232">"Alihkan"</string>
+    <string name="button_dismiss" msgid="3714065566893946085">"Tolak"</string>
+    <!-- no translation found for button_retry (4392027584153752797) -->
+    <skip />
     <string name="sort_name" msgid="9183560467917256779">"Mengikut nama"</string>
     <string name="sort_date" msgid="586080032956151448">"Mengikut tarikh diubah"</string>
     <string name="sort_size" msgid="3350681319735474741">"Mengikut saiz"</string>
diff --git a/packages/DocumentsUI/res/values-my-rMM/strings.xml b/packages/DocumentsUI/res/values-my-rMM/strings.xml
index a422898..480c27b 100644
--- a/packages/DocumentsUI/res/values-my-rMM/strings.xml
+++ b/packages/DocumentsUI/res/values-my-rMM/strings.xml
@@ -44,6 +44,8 @@
     <string name="button_select" msgid="527196987259139214">"ရွေးရန်"</string>
     <string name="button_copy" msgid="8706475544635021302">"ကူးယူရန်"</string>
     <string name="button_move" msgid="2202666023104202232">"ရွေ့မည်"</string>
+    <string name="button_dismiss" msgid="3714065566893946085">"ပယ်ရန်"</string>
+    <string name="button_retry" msgid="4392027584153752797">"ထပ် စမ်းကြည့်ပါ"</string>
     <string name="sort_name" msgid="9183560467917256779">"အမည်ဖြင့်"</string>
     <string name="sort_date" msgid="586080032956151448">"ပြင်ဆင်မှု ရက်စွဲဖြင့်"</string>
     <string name="sort_size" msgid="3350681319735474741">"အရွယ်အစားဖြင့်"</string>
diff --git a/packages/DocumentsUI/res/values-nb/strings.xml b/packages/DocumentsUI/res/values-nb/strings.xml
index 5bcc50f..48355aa 100644
--- a/packages/DocumentsUI/res/values-nb/strings.xml
+++ b/packages/DocumentsUI/res/values-nb/strings.xml
@@ -44,6 +44,8 @@
     <string name="button_select" msgid="527196987259139214">"Velg"</string>
     <string name="button_copy" msgid="8706475544635021302">"Kopiér"</string>
     <string name="button_move" msgid="2202666023104202232">"Flytt"</string>
+    <string name="button_dismiss" msgid="3714065566893946085">"Avvis"</string>
+    <string name="button_retry" msgid="4392027584153752797">"Prøv på nytt"</string>
     <string name="sort_name" msgid="9183560467917256779">"Etter navn"</string>
     <string name="sort_date" msgid="586080032956151448">"Etter endringsdato"</string>
     <string name="sort_size" msgid="3350681319735474741">"Etter størrelse"</string>
diff --git a/packages/DocumentsUI/res/values-ne-rNP/strings.xml b/packages/DocumentsUI/res/values-ne-rNP/strings.xml
index b121035..53942c1 100644
--- a/packages/DocumentsUI/res/values-ne-rNP/strings.xml
+++ b/packages/DocumentsUI/res/values-ne-rNP/strings.xml
@@ -44,6 +44,9 @@
     <string name="button_select" msgid="527196987259139214">"चयन गर्नुहोस्"</string>
     <string name="button_copy" msgid="8706475544635021302">"प्रतिलिपि बनाउनुहोस्"</string>
     <string name="button_move" msgid="2202666023104202232">"सार्नुहोस्"</string>
+    <string name="button_dismiss" msgid="3714065566893946085">"खारेज गर्नुहोस्"</string>
+    <!-- no translation found for button_retry (4392027584153752797) -->
+    <skip />
     <string name="sort_name" msgid="9183560467917256779">"नाम अनुसार"</string>
     <string name="sort_date" msgid="586080032956151448">"परिमार्जित मिति अनुसार"</string>
     <string name="sort_size" msgid="3350681319735474741">"आकार अनुसार"</string>
diff --git a/packages/DocumentsUI/res/values-nl/strings.xml b/packages/DocumentsUI/res/values-nl/strings.xml
index 88ef0bf..d3e6bbe 100644
--- a/packages/DocumentsUI/res/values-nl/strings.xml
+++ b/packages/DocumentsUI/res/values-nl/strings.xml
@@ -44,6 +44,8 @@
     <string name="button_select" msgid="527196987259139214">"Selecteren"</string>
     <string name="button_copy" msgid="8706475544635021302">"Kopiëren"</string>
     <string name="button_move" msgid="2202666023104202232">"Verplaatsen"</string>
+    <string name="button_dismiss" msgid="3714065566893946085">"Sluiten"</string>
+    <string name="button_retry" msgid="4392027584153752797">"Opnieuw proberen"</string>
     <string name="sort_name" msgid="9183560467917256779">"Op naam"</string>
     <string name="sort_date" msgid="586080032956151448">"Op aanpassingsdatum"</string>
     <string name="sort_size" msgid="3350681319735474741">"Op grootte"</string>
diff --git a/packages/DocumentsUI/res/values-pa-rIN/strings.xml b/packages/DocumentsUI/res/values-pa-rIN/strings.xml
index a55fc27..3b14396 100644
--- a/packages/DocumentsUI/res/values-pa-rIN/strings.xml
+++ b/packages/DocumentsUI/res/values-pa-rIN/strings.xml
@@ -44,6 +44,9 @@
     <string name="button_select" msgid="527196987259139214">"ਚੁਣੋ"</string>
     <string name="button_copy" msgid="8706475544635021302">"ਕਾਪੀ ਕਰੋ"</string>
     <string name="button_move" msgid="2202666023104202232">"ਮੂਵ ਕਰੋ"</string>
+    <string name="button_dismiss" msgid="3714065566893946085">"ਬਰਖਾਸਤ ਕਰੋ"</string>
+    <!-- no translation found for button_retry (4392027584153752797) -->
+    <skip />
     <string name="sort_name" msgid="9183560467917256779">"ਨਾਮ ਮੁਤਾਬਕ"</string>
     <string name="sort_date" msgid="586080032956151448">"ਤਾਰੀਖ ਮੁਤਾਬਕ ਸੰਸ਼ੋਧਿਤ"</string>
     <string name="sort_size" msgid="3350681319735474741">"ਆਕਾਰ ਮੁਤਾਬਕ"</string>
diff --git a/packages/DocumentsUI/res/values-pl/strings.xml b/packages/DocumentsUI/res/values-pl/strings.xml
index 0099e5a..3e4ef68 100644
--- a/packages/DocumentsUI/res/values-pl/strings.xml
+++ b/packages/DocumentsUI/res/values-pl/strings.xml
@@ -44,6 +44,9 @@
     <string name="button_select" msgid="527196987259139214">"Wybierz"</string>
     <string name="button_copy" msgid="8706475544635021302">"Kopiuj"</string>
     <string name="button_move" msgid="2202666023104202232">"Przenieś"</string>
+    <string name="button_dismiss" msgid="3714065566893946085">"Zamknij"</string>
+    <!-- no translation found for button_retry (4392027584153752797) -->
+    <skip />
     <string name="sort_name" msgid="9183560467917256779">"Według nazwy"</string>
     <string name="sort_date" msgid="586080032956151448">"Według daty edycji"</string>
     <string name="sort_size" msgid="3350681319735474741">"Według rozmiaru"</string>
diff --git a/packages/DocumentsUI/res/values-pt-rBR/strings.xml b/packages/DocumentsUI/res/values-pt-rBR/strings.xml
index 18506dc..ca984cf 100644
--- a/packages/DocumentsUI/res/values-pt-rBR/strings.xml
+++ b/packages/DocumentsUI/res/values-pt-rBR/strings.xml
@@ -44,6 +44,8 @@
     <string name="button_select" msgid="527196987259139214">"Selecionar"</string>
     <string name="button_copy" msgid="8706475544635021302">"Copiar"</string>
     <string name="button_move" msgid="2202666023104202232">"Mover"</string>
+    <string name="button_dismiss" msgid="3714065566893946085">"Dispensar"</string>
+    <string name="button_retry" msgid="4392027584153752797">"Tentar novamente"</string>
     <string name="sort_name" msgid="9183560467917256779">"Por nome"</string>
     <string name="sort_date" msgid="586080032956151448">"Por data de modificação"</string>
     <string name="sort_size" msgid="3350681319735474741">"Por tamanho"</string>
diff --git a/packages/DocumentsUI/res/values-pt-rPT/strings.xml b/packages/DocumentsUI/res/values-pt-rPT/strings.xml
index adf6ec9..ab67358 100644
--- a/packages/DocumentsUI/res/values-pt-rPT/strings.xml
+++ b/packages/DocumentsUI/res/values-pt-rPT/strings.xml
@@ -44,6 +44,8 @@
     <string name="button_select" msgid="527196987259139214">"Selecionar"</string>
     <string name="button_copy" msgid="8706475544635021302">"Copiar"</string>
     <string name="button_move" msgid="2202666023104202232">"Mover"</string>
+    <string name="button_dismiss" msgid="3714065566893946085">"Ignorar"</string>
+    <string name="button_retry" msgid="4392027584153752797">"Tentar novamente"</string>
     <string name="sort_name" msgid="9183560467917256779">"Por nome"</string>
     <string name="sort_date" msgid="586080032956151448">"Por data de modificação"</string>
     <string name="sort_size" msgid="3350681319735474741">"Por tamanho"</string>
diff --git a/packages/DocumentsUI/res/values-pt/strings.xml b/packages/DocumentsUI/res/values-pt/strings.xml
index 18506dc..ca984cf 100644
--- a/packages/DocumentsUI/res/values-pt/strings.xml
+++ b/packages/DocumentsUI/res/values-pt/strings.xml
@@ -44,6 +44,8 @@
     <string name="button_select" msgid="527196987259139214">"Selecionar"</string>
     <string name="button_copy" msgid="8706475544635021302">"Copiar"</string>
     <string name="button_move" msgid="2202666023104202232">"Mover"</string>
+    <string name="button_dismiss" msgid="3714065566893946085">"Dispensar"</string>
+    <string name="button_retry" msgid="4392027584153752797">"Tentar novamente"</string>
     <string name="sort_name" msgid="9183560467917256779">"Por nome"</string>
     <string name="sort_date" msgid="586080032956151448">"Por data de modificação"</string>
     <string name="sort_size" msgid="3350681319735474741">"Por tamanho"</string>
diff --git a/packages/DocumentsUI/res/values-ro/strings.xml b/packages/DocumentsUI/res/values-ro/strings.xml
index 1b046a3..e927b78 100644
--- a/packages/DocumentsUI/res/values-ro/strings.xml
+++ b/packages/DocumentsUI/res/values-ro/strings.xml
@@ -44,6 +44,8 @@
     <string name="button_select" msgid="527196987259139214">"Selectați"</string>
     <string name="button_copy" msgid="8706475544635021302">"Copiați"</string>
     <string name="button_move" msgid="2202666023104202232">"Mutați"</string>
+    <string name="button_dismiss" msgid="3714065566893946085">"Închideți"</string>
+    <string name="button_retry" msgid="4392027584153752797">"Încercați din nou"</string>
     <string name="sort_name" msgid="9183560467917256779">"După nume"</string>
     <string name="sort_date" msgid="586080032956151448">"După data modificării"</string>
     <string name="sort_size" msgid="3350681319735474741">"După dimensiune"</string>
diff --git a/packages/DocumentsUI/res/values-ru/strings.xml b/packages/DocumentsUI/res/values-ru/strings.xml
index 84ae160..cdf20ca 100644
--- a/packages/DocumentsUI/res/values-ru/strings.xml
+++ b/packages/DocumentsUI/res/values-ru/strings.xml
@@ -44,6 +44,9 @@
     <string name="button_select" msgid="527196987259139214">"Выбрать"</string>
     <string name="button_copy" msgid="8706475544635021302">"Копировать"</string>
     <string name="button_move" msgid="2202666023104202232">"Переместить"</string>
+    <string name="button_dismiss" msgid="3714065566893946085">"Скрыть"</string>
+    <!-- no translation found for button_retry (4392027584153752797) -->
+    <skip />
     <string name="sort_name" msgid="9183560467917256779">"По названию"</string>
     <string name="sort_date" msgid="586080032956151448">"По дате изменения"</string>
     <string name="sort_size" msgid="3350681319735474741">"По размеру"</string>
diff --git a/packages/DocumentsUI/res/values-si-rLK/strings.xml b/packages/DocumentsUI/res/values-si-rLK/strings.xml
index d9d5818..138f8a6 100644
--- a/packages/DocumentsUI/res/values-si-rLK/strings.xml
+++ b/packages/DocumentsUI/res/values-si-rLK/strings.xml
@@ -44,6 +44,9 @@
     <string name="button_select" msgid="527196987259139214">"තෝරන්න"</string>
     <string name="button_copy" msgid="8706475544635021302">"පිටපත් කිරීම"</string>
     <string name="button_move" msgid="2202666023104202232">"ගෙන යන්න"</string>
+    <string name="button_dismiss" msgid="3714065566893946085">"ඉවතලන්න"</string>
+    <!-- no translation found for button_retry (4392027584153752797) -->
+    <skip />
     <string name="sort_name" msgid="9183560467917256779">"නමින්"</string>
     <string name="sort_date" msgid="586080032956151448">"වෙනස් කරන ලද දිනයෙන්"</string>
     <string name="sort_size" msgid="3350681319735474741">"ප්‍රමාණය මගින්"</string>
diff --git a/packages/DocumentsUI/res/values-sk/strings.xml b/packages/DocumentsUI/res/values-sk/strings.xml
index e0ff5fc..4310819 100644
--- a/packages/DocumentsUI/res/values-sk/strings.xml
+++ b/packages/DocumentsUI/res/values-sk/strings.xml
@@ -44,6 +44,9 @@
     <string name="button_select" msgid="527196987259139214">"Vybrať"</string>
     <string name="button_copy" msgid="8706475544635021302">"Kopírovať"</string>
     <string name="button_move" msgid="2202666023104202232">"Presunúť"</string>
+    <string name="button_dismiss" msgid="3714065566893946085">"Odmietnuť"</string>
+    <!-- no translation found for button_retry (4392027584153752797) -->
+    <skip />
     <string name="sort_name" msgid="9183560467917256779">"Podľa názvu"</string>
     <string name="sort_date" msgid="586080032956151448">"Podľa dátumu zmeny"</string>
     <string name="sort_size" msgid="3350681319735474741">"Podľa veľkosti"</string>
diff --git a/packages/DocumentsUI/res/values-sl/strings.xml b/packages/DocumentsUI/res/values-sl/strings.xml
index 83e5124..c9982a7 100644
--- a/packages/DocumentsUI/res/values-sl/strings.xml
+++ b/packages/DocumentsUI/res/values-sl/strings.xml
@@ -44,6 +44,9 @@
     <string name="button_select" msgid="527196987259139214">"Izberi"</string>
     <string name="button_copy" msgid="8706475544635021302">"Kopiraj"</string>
     <string name="button_move" msgid="2202666023104202232">"Premik"</string>
+    <string name="button_dismiss" msgid="3714065566893946085">"Opusti"</string>
+    <!-- no translation found for button_retry (4392027584153752797) -->
+    <skip />
     <string name="sort_name" msgid="9183560467917256779">"Po imenu"</string>
     <string name="sort_date" msgid="586080032956151448">"Po datumu spremembe"</string>
     <string name="sort_size" msgid="3350681319735474741">"Po velikosti"</string>
diff --git a/packages/DocumentsUI/res/values-sq-rAL/strings.xml b/packages/DocumentsUI/res/values-sq-rAL/strings.xml
index 981daf2..f1ee1bc 100644
--- a/packages/DocumentsUI/res/values-sq-rAL/strings.xml
+++ b/packages/DocumentsUI/res/values-sq-rAL/strings.xml
@@ -44,6 +44,9 @@
     <string name="button_select" msgid="527196987259139214">"Zgjidh"</string>
     <string name="button_copy" msgid="8706475544635021302">"Kopjo"</string>
     <string name="button_move" msgid="2202666023104202232">"Zhvendos"</string>
+    <string name="button_dismiss" msgid="3714065566893946085">"Largoje"</string>
+    <!-- no translation found for button_retry (4392027584153752797) -->
+    <skip />
     <string name="sort_name" msgid="9183560467917256779">"Sipas emrit"</string>
     <string name="sort_date" msgid="586080032956151448">"Sipas datës së modifikimit"</string>
     <string name="sort_size" msgid="3350681319735474741">"Sipas madhësisë"</string>
diff --git a/packages/DocumentsUI/res/values-sr/strings.xml b/packages/DocumentsUI/res/values-sr/strings.xml
index bd0e9af..c5116c2 100644
--- a/packages/DocumentsUI/res/values-sr/strings.xml
+++ b/packages/DocumentsUI/res/values-sr/strings.xml
@@ -44,6 +44,9 @@
     <string name="button_select" msgid="527196987259139214">"Изабери"</string>
     <string name="button_copy" msgid="8706475544635021302">"Копирај"</string>
     <string name="button_move" msgid="2202666023104202232">"Премести"</string>
+    <string name="button_dismiss" msgid="3714065566893946085">"Одбаци"</string>
+    <!-- no translation found for button_retry (4392027584153752797) -->
+    <skip />
     <string name="sort_name" msgid="9183560467917256779">"Према имену"</string>
     <string name="sort_date" msgid="586080032956151448">"Према датуму измене"</string>
     <string name="sort_size" msgid="3350681319735474741">"Према величини"</string>
diff --git a/packages/DocumentsUI/res/values-sv/strings.xml b/packages/DocumentsUI/res/values-sv/strings.xml
index 2569e00..06e6514 100644
--- a/packages/DocumentsUI/res/values-sv/strings.xml
+++ b/packages/DocumentsUI/res/values-sv/strings.xml
@@ -44,6 +44,8 @@
     <string name="button_select" msgid="527196987259139214">"Välj"</string>
     <string name="button_copy" msgid="8706475544635021302">"Kopiera"</string>
     <string name="button_move" msgid="2202666023104202232">"Flytta"</string>
+    <string name="button_dismiss" msgid="3714065566893946085">"Ta bort permanent"</string>
+    <string name="button_retry" msgid="4392027584153752797">"Försök igen"</string>
     <string name="sort_name" msgid="9183560467917256779">"Efter namn"</string>
     <string name="sort_date" msgid="586080032956151448">"Efter ändringsdatum"</string>
     <string name="sort_size" msgid="3350681319735474741">"Efter storlek"</string>
diff --git a/packages/DocumentsUI/res/values-sw/strings.xml b/packages/DocumentsUI/res/values-sw/strings.xml
index ce186d7..79bae1f 100644
--- a/packages/DocumentsUI/res/values-sw/strings.xml
+++ b/packages/DocumentsUI/res/values-sw/strings.xml
@@ -44,6 +44,8 @@
     <string name="button_select" msgid="527196987259139214">"Teua"</string>
     <string name="button_copy" msgid="8706475544635021302">"Nakili"</string>
     <string name="button_move" msgid="2202666023104202232">"Hamisha"</string>
+    <string name="button_dismiss" msgid="3714065566893946085">"Ondoa"</string>
+    <string name="button_retry" msgid="4392027584153752797">"Jaribu Tena"</string>
     <string name="sort_name" msgid="9183560467917256779">"Kwa jina"</string>
     <string name="sort_date" msgid="586080032956151448">"Kwa tarehe viliporekebishwa"</string>
     <string name="sort_size" msgid="3350681319735474741">"Kwa ukubwa"</string>
diff --git a/packages/DocumentsUI/res/values-ta-rIN/strings.xml b/packages/DocumentsUI/res/values-ta-rIN/strings.xml
index b7b9c09..117aabc 100644
--- a/packages/DocumentsUI/res/values-ta-rIN/strings.xml
+++ b/packages/DocumentsUI/res/values-ta-rIN/strings.xml
@@ -44,6 +44,8 @@
     <string name="button_select" msgid="527196987259139214">"தேர்ந்தெடு"</string>
     <string name="button_copy" msgid="8706475544635021302">"நகலெடு"</string>
     <string name="button_move" msgid="2202666023104202232">"நகர்த்து"</string>
+    <string name="button_dismiss" msgid="3714065566893946085">"நிராகரி"</string>
+    <string name="button_retry" msgid="4392027584153752797">"மீண்டும் முயற்சிக்கவும்"</string>
     <string name="sort_name" msgid="9183560467917256779">"பெயரின்படி"</string>
     <string name="sort_date" msgid="586080032956151448">"திருத்தப்பட்ட தேதியின்படி"</string>
     <string name="sort_size" msgid="3350681319735474741">"அளவின்படி"</string>
diff --git a/packages/DocumentsUI/res/values-te-rIN/strings.xml b/packages/DocumentsUI/res/values-te-rIN/strings.xml
index 7a81486..21b7f58 100644
--- a/packages/DocumentsUI/res/values-te-rIN/strings.xml
+++ b/packages/DocumentsUI/res/values-te-rIN/strings.xml
@@ -44,6 +44,8 @@
     <string name="button_select" msgid="527196987259139214">"ఎంచుకోండి"</string>
     <string name="button_copy" msgid="8706475544635021302">"కాపీ చేయి"</string>
     <string name="button_move" msgid="2202666023104202232">"తరలించు"</string>
+    <string name="button_dismiss" msgid="3714065566893946085">"తీసివేయి"</string>
+    <string name="button_retry" msgid="4392027584153752797">"మళ్లీ ప్రయత్నించు"</string>
     <string name="sort_name" msgid="9183560467917256779">"పేరు ద్వారా"</string>
     <string name="sort_date" msgid="586080032956151448">"సవరించిన తేదీ ద్వారా"</string>
     <string name="sort_size" msgid="3350681319735474741">"పరిమాణం ద్వారా"</string>
diff --git a/packages/DocumentsUI/res/values-th/strings.xml b/packages/DocumentsUI/res/values-th/strings.xml
index 87c0a73..8a49708 100644
--- a/packages/DocumentsUI/res/values-th/strings.xml
+++ b/packages/DocumentsUI/res/values-th/strings.xml
@@ -44,6 +44,9 @@
     <string name="button_select" msgid="527196987259139214">"เลือก"</string>
     <string name="button_copy" msgid="8706475544635021302">"คัดลอก"</string>
     <string name="button_move" msgid="2202666023104202232">"ย้าย"</string>
+    <string name="button_dismiss" msgid="3714065566893946085">"ปิด"</string>
+    <!-- no translation found for button_retry (4392027584153752797) -->
+    <skip />
     <string name="sort_name" msgid="9183560467917256779">"ตามชื่อ"</string>
     <string name="sort_date" msgid="586080032956151448">"ตามวันที่ที่ปรับเปลี่ยน"</string>
     <string name="sort_size" msgid="3350681319735474741">"ตามขนาด"</string>
diff --git a/packages/DocumentsUI/res/values-tl/strings.xml b/packages/DocumentsUI/res/values-tl/strings.xml
index eaef936..5c0dc12 100644
--- a/packages/DocumentsUI/res/values-tl/strings.xml
+++ b/packages/DocumentsUI/res/values-tl/strings.xml
@@ -44,6 +44,9 @@
     <string name="button_select" msgid="527196987259139214">"Pumili"</string>
     <string name="button_copy" msgid="8706475544635021302">"Kopyahin"</string>
     <string name="button_move" msgid="2202666023104202232">"Ilipat"</string>
+    <string name="button_dismiss" msgid="3714065566893946085">"I-dismiss"</string>
+    <!-- no translation found for button_retry (4392027584153752797) -->
+    <skip />
     <string name="sort_name" msgid="9183560467917256779">"Ayon sa pangalan"</string>
     <string name="sort_date" msgid="586080032956151448">"Ayon sa petsa ng pagbago"</string>
     <string name="sort_size" msgid="3350681319735474741">"Ayon sa laki"</string>
diff --git a/packages/DocumentsUI/res/values-tr/strings.xml b/packages/DocumentsUI/res/values-tr/strings.xml
index 8c0596f..092b38e 100644
--- a/packages/DocumentsUI/res/values-tr/strings.xml
+++ b/packages/DocumentsUI/res/values-tr/strings.xml
@@ -44,6 +44,9 @@
     <string name="button_select" msgid="527196987259139214">"Seç"</string>
     <string name="button_copy" msgid="8706475544635021302">"Kopyala"</string>
     <string name="button_move" msgid="2202666023104202232">"Taşı"</string>
+    <string name="button_dismiss" msgid="3714065566893946085">"Kapat"</string>
+    <!-- no translation found for button_retry (4392027584153752797) -->
+    <skip />
     <string name="sort_name" msgid="9183560467917256779">"Ada göre"</string>
     <string name="sort_date" msgid="586080032956151448">"Değişiklik tarihine göre"</string>
     <string name="sort_size" msgid="3350681319735474741">"Boyuta göre"</string>
diff --git a/packages/DocumentsUI/res/values-uk/strings.xml b/packages/DocumentsUI/res/values-uk/strings.xml
index 9bbc59a..dff37c3 100644
--- a/packages/DocumentsUI/res/values-uk/strings.xml
+++ b/packages/DocumentsUI/res/values-uk/strings.xml
@@ -44,6 +44,8 @@
     <string name="button_select" msgid="527196987259139214">"Вибрати"</string>
     <string name="button_copy" msgid="8706475544635021302">"Копіювати"</string>
     <string name="button_move" msgid="2202666023104202232">"Перемістити"</string>
+    <string name="button_dismiss" msgid="3714065566893946085">"Закрити"</string>
+    <string name="button_retry" msgid="4392027584153752797">"Повторити спробу"</string>
     <string name="sort_name" msgid="9183560467917256779">"За назвою"</string>
     <string name="sort_date" msgid="586080032956151448">"За датою змінення"</string>
     <string name="sort_size" msgid="3350681319735474741">"За розміром"</string>
diff --git a/packages/DocumentsUI/res/values-ur-rPK/strings.xml b/packages/DocumentsUI/res/values-ur-rPK/strings.xml
index 0c12aa1..82822ec 100644
--- a/packages/DocumentsUI/res/values-ur-rPK/strings.xml
+++ b/packages/DocumentsUI/res/values-ur-rPK/strings.xml
@@ -44,6 +44,8 @@
     <string name="button_select" msgid="527196987259139214">"منتخب کریں"</string>
     <string name="button_copy" msgid="8706475544635021302">"کاپی کریں"</string>
     <string name="button_move" msgid="2202666023104202232">"منتقل کریں"</string>
+    <string name="button_dismiss" msgid="3714065566893946085">"برخاست کریں"</string>
+    <string name="button_retry" msgid="4392027584153752797">"دوبارہ کوشش کریں"</string>
     <string name="sort_name" msgid="9183560467917256779">"نام کے لحاظ سے"</string>
     <string name="sort_date" msgid="586080032956151448">"ترمیم کی تاریخ کے لحاظ سے"</string>
     <string name="sort_size" msgid="3350681319735474741">"سائز کے لحاظ سے"</string>
diff --git a/packages/DocumentsUI/res/values-uz-rUZ/strings.xml b/packages/DocumentsUI/res/values-uz-rUZ/strings.xml
index ec91885..d0cfacc 100644
--- a/packages/DocumentsUI/res/values-uz-rUZ/strings.xml
+++ b/packages/DocumentsUI/res/values-uz-rUZ/strings.xml
@@ -24,7 +24,7 @@
     <string name="menu_grid" msgid="6878021334497835259">"Katak ko‘rinishida"</string>
     <string name="menu_list" msgid="7279285939892417279">"Ro‘yxat ko‘rinishida"</string>
     <string name="menu_sort" msgid="7677740407158414452">"Saralash"</string>
-    <string name="menu_search" msgid="3816712084502856974">"Izlash"</string>
+    <string name="menu_search" msgid="3816712084502856974">"Qidirish"</string>
     <string name="menu_settings" msgid="6008033148948428823">"Sozlamalar"</string>
     <string name="menu_open" msgid="432922957274920903">"Ochish"</string>
     <string name="menu_save" msgid="2394743337684426338">"Saqlash"</string>
@@ -44,6 +44,9 @@
     <string name="button_select" msgid="527196987259139214">"Tanlash"</string>
     <string name="button_copy" msgid="8706475544635021302">"Nusxalash"</string>
     <string name="button_move" msgid="2202666023104202232">"Ko‘chirib o‘tkazish"</string>
+    <string name="button_dismiss" msgid="3714065566893946085">"O‘chirish"</string>
+    <!-- no translation found for button_retry (4392027584153752797) -->
+    <skip />
     <string name="sort_name" msgid="9183560467917256779">"Nomi bo‘yicha"</string>
     <string name="sort_date" msgid="586080032956151448">"Tahrir sanasi bo‘yicha"</string>
     <string name="sort_size" msgid="3350681319735474741">"Hajmi bo‘yicha"</string>
diff --git a/packages/DocumentsUI/res/values-vi/strings.xml b/packages/DocumentsUI/res/values-vi/strings.xml
index a652ecc..4583362 100644
--- a/packages/DocumentsUI/res/values-vi/strings.xml
+++ b/packages/DocumentsUI/res/values-vi/strings.xml
@@ -44,6 +44,9 @@
     <string name="button_select" msgid="527196987259139214">"Chọn"</string>
     <string name="button_copy" msgid="8706475544635021302">"Sao chép"</string>
     <string name="button_move" msgid="2202666023104202232">"Di chuyển"</string>
+    <string name="button_dismiss" msgid="3714065566893946085">"Loại bỏ"</string>
+    <!-- no translation found for button_retry (4392027584153752797) -->
+    <skip />
     <string name="sort_name" msgid="9183560467917256779">"Theo tên"</string>
     <string name="sort_date" msgid="586080032956151448">"Theo ngày sửa đổi"</string>
     <string name="sort_size" msgid="3350681319735474741">"Theo kích thước"</string>
diff --git a/packages/DocumentsUI/res/values-zh-rCN/strings.xml b/packages/DocumentsUI/res/values-zh-rCN/strings.xml
index b0f5480..d577e14 100644
--- a/packages/DocumentsUI/res/values-zh-rCN/strings.xml
+++ b/packages/DocumentsUI/res/values-zh-rCN/strings.xml
@@ -44,6 +44,9 @@
     <string name="button_select" msgid="527196987259139214">"选择"</string>
     <string name="button_copy" msgid="8706475544635021302">"复制"</string>
     <string name="button_move" msgid="2202666023104202232">"移动"</string>
+    <string name="button_dismiss" msgid="3714065566893946085">"关闭"</string>
+    <!-- no translation found for button_retry (4392027584153752797) -->
+    <skip />
     <string name="sort_name" msgid="9183560467917256779">"按名称"</string>
     <string name="sort_date" msgid="586080032956151448">"按修改日期"</string>
     <string name="sort_size" msgid="3350681319735474741">"按大小"</string>
diff --git a/packages/DocumentsUI/res/values-zh-rHK/strings.xml b/packages/DocumentsUI/res/values-zh-rHK/strings.xml
index 220b716..3d44047 100644
--- a/packages/DocumentsUI/res/values-zh-rHK/strings.xml
+++ b/packages/DocumentsUI/res/values-zh-rHK/strings.xml
@@ -44,6 +44,9 @@
     <string name="button_select" msgid="527196987259139214">"選取"</string>
     <string name="button_copy" msgid="8706475544635021302">"複製"</string>
     <string name="button_move" msgid="2202666023104202232">"移動"</string>
+    <string name="button_dismiss" msgid="3714065566893946085">"關閉"</string>
+    <!-- no translation found for button_retry (4392027584153752797) -->
+    <skip />
     <string name="sort_name" msgid="9183560467917256779">"按名稱"</string>
     <string name="sort_date" msgid="586080032956151448">"按修改日期"</string>
     <string name="sort_size" msgid="3350681319735474741">"按大小"</string>
diff --git a/packages/DocumentsUI/res/values-zh-rTW/strings.xml b/packages/DocumentsUI/res/values-zh-rTW/strings.xml
index 9c21aa5..aaad98c 100644
--- a/packages/DocumentsUI/res/values-zh-rTW/strings.xml
+++ b/packages/DocumentsUI/res/values-zh-rTW/strings.xml
@@ -44,6 +44,9 @@
     <string name="button_select" msgid="527196987259139214">"選取"</string>
     <string name="button_copy" msgid="8706475544635021302">"複製"</string>
     <string name="button_move" msgid="2202666023104202232">"移動"</string>
+    <string name="button_dismiss" msgid="3714065566893946085">"關閉"</string>
+    <!-- no translation found for button_retry (4392027584153752797) -->
+    <skip />
     <string name="sort_name" msgid="9183560467917256779">"依名稱"</string>
     <string name="sort_date" msgid="586080032956151448">"依修改日期"</string>
     <string name="sort_size" msgid="3350681319735474741">"依大小"</string>
diff --git a/packages/DocumentsUI/res/values-zu/strings.xml b/packages/DocumentsUI/res/values-zu/strings.xml
index 5ce9f12..8f20cc4 100644
--- a/packages/DocumentsUI/res/values-zu/strings.xml
+++ b/packages/DocumentsUI/res/values-zu/strings.xml
@@ -44,6 +44,8 @@
     <string name="button_select" msgid="527196987259139214">"Khetha"</string>
     <string name="button_copy" msgid="8706475544635021302">"Kopisha"</string>
     <string name="button_move" msgid="2202666023104202232">"Hambisa"</string>
+    <string name="button_dismiss" msgid="3714065566893946085">"Cashisa"</string>
+    <string name="button_retry" msgid="4392027584153752797">"Zama futhi"</string>
     <string name="sort_name" msgid="9183560467917256779">"Ngegama"</string>
     <string name="sort_date" msgid="586080032956151448">"Ngedethi yokuguqula"</string>
     <string name="sort_size" msgid="3350681319735474741">"Ngosayizi"</string>
diff --git a/packages/DocumentsUI/res/values/strings.xml b/packages/DocumentsUI/res/values/strings.xml
index 4b44d7c..a4acb60 100644
--- a/packages/DocumentsUI/res/values/strings.xml
+++ b/packages/DocumentsUI/res/values/strings.xml
@@ -54,6 +54,8 @@
     <!-- Menu item title that moves the selected documents [CHAR LIMIT=24] -->
     <string name="menu_move">Move to\u2026</string>
 
+    <!-- Menu item title that creates a new window in the activity [CHAR LIMIT=24] -->
+    <string name="menu_new_window">New window</string>
     <!-- Menu item title that copies the selected documents to clipboard [CHAR LIMIT=24] -->
     <string name="menu_copy_to_clipboard">Copy</string>
     <!-- Menu item title that pastes files from the clipboard [CHAR LIMIT=24] -->
@@ -79,7 +81,10 @@
     <string name="button_copy">Copy</string>
     <!-- Button label that moves files to the current directory [CHAR LIMIT=24] -->
     <string name="button_move">Move</string>
-
+    <!-- Button label that hides the error bar [CHAR LIMIT=24] -->
+    <string name="button_dismiss">Dismiss</string>
+    <string name="button_retry">Try Again</string>
+    
     <!-- Mode that sorts documents by their display name alphabetically [CHAR LIMIT=24] -->
     <string name="sort_name">By name</string>
     <!-- Mode that sorts documents by their last modified time in descending order; most recent first [CHAR LIMIT=24] -->
diff --git a/packages/DocumentsUI/res/values/styles.xml b/packages/DocumentsUI/res/values/styles.xml
index 8301816..c13f144 100644
--- a/packages/DocumentsUI/res/values/styles.xml
+++ b/packages/DocumentsUI/res/values/styles.xml
@@ -77,14 +77,20 @@
         <item name="android:colorPrimaryDark">@color/platform_blue_700</item>
         <item name="android:colorPrimary">@color/platform_blue_500</item>
         <item name="android:colorAccent">@color/platform_blue_700</item>
+        <item name="colorControlActivated">@color/platform_blue_a100</item>
         <item name="android:actionModeStyle">@style/FilesActionModeStyle</item>
         <item name="colorActionMode">@color/platform_blue_700</item>
-
         <item name="android:alertDialogTheme">@style/AlertDialogTheme</item>
     </style>
 
     <style name="FilesActionModeStyle" parent="@android:style/Widget.Material.Light.ActionMode">
         <item name="android:background">@color/platform_blue_100</item>
     </style>
-    
+
+    <style name="TrimmedHorizontalProgressBar" parent="android:Widget.Material.ProgressBar.Horizontal">
+        <item name="android:indeterminateDrawable">@drawable/progress_indeterminate_horizontal_material_trimmed</item>
+        <item name="android:minHeight">3dp</item>
+        <item name="android:maxHeight">3dp</item>    
+    </style>
+
 </resources>
diff --git a/packages/DocumentsUI/src/com/android/documentsui/BaseActivity.java b/packages/DocumentsUI/src/com/android/documentsui/BaseActivity.java
index 1e7a42f..a6a45e5 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;
@@ -40,14 +33,11 @@
 import android.net.Uri;
 import android.os.AsyncTask;
 import android.os.Bundle;
-import android.os.Parcel;
-import android.os.Parcelable;
 import android.provider.DocumentsContract;
 import android.provider.DocumentsContract.Root;
 import android.support.annotation.LayoutRes;
 import android.support.annotation.Nullable;
 import android.util.Log;
-import android.util.SparseArray;
 import android.view.LayoutInflater;
 import android.view.Menu;
 import android.view.MenuItem;
@@ -74,7 +64,6 @@
 import java.io.IOException;
 import java.util.ArrayList;
 import java.util.Collection;
-import java.util.HashMap;
 import java.util.List;
 import java.util.concurrent.Executor;
 
@@ -86,10 +75,12 @@
     RootsCache mRoots;
     SearchManager mSearchManager;
     DrawerController mDrawer;
+    boolean mProductivityDevice;
 
+    private final String mTag;
     @LayoutRes
     private int mLayoutId;
-    private final String mTag;
+    private DirectoryContainerView mDirectoryContainer;
 
     public abstract void onDocumentPicked(DocumentInfo doc, @Nullable DocumentContext siblings);
     public abstract void onDocumentsPicked(List<DocumentInfo> docs);
@@ -109,6 +100,7 @@
     public void onCreate(Bundle icicle) {
         super.onCreate(icicle);
 
+        mProductivityDevice = getResources().getBoolean(R.bool.productivity_device);
         mState = (icicle != null)
                 ? icicle.<State>getParcelable(EXTRA_STATE)
                         : buildState();
@@ -116,6 +108,7 @@
         setContentView(mLayoutId);
 
         mRoots = DocumentsApplication.getRootsCache(this);
+        mDirectoryContainer = (DirectoryContainerView) findViewById(R.id.container_directory);
         mSearchManager = new SearchManager();
 
         // Base classes must update result in their onCreate.
@@ -123,22 +116,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);
 
@@ -176,8 +153,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;
@@ -187,13 +166,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;
@@ -217,7 +200,7 @@
         if (mRoots.isRecentsRoot(root)) {
             onCurrentDirectoryChanged(ANIM_SIDE);
         } else {
-            new PickRootTask(root).executeOnExecutor(getCurrentExecutor());
+            new PickRootTask(root).executeOnExecutor(getExecutorForCurrentDirectory());
         }
     }
 
@@ -227,6 +210,7 @@
             switch (item.getItemId()) {
                 case R.id.menu_advanced:
                 case R.id.menu_file_size:
+                case R.id.menu_new_window:
                     break;
                 default:
                     item.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);
@@ -316,6 +300,7 @@
      * @param anim
      */
     final void onCurrentDirectoryChanged(int anim) {
+        mDirectoryContainer.setDrawDisappearingFirst(anim == ANIM_DOWN);
         onDirectoryChanged(anim);
 
         final RootsFragment roots = RootsFragment.get(getFragmentManager());
@@ -381,118 +366,6 @@
         public static String EXTRA_DIRECTORY_COPY = "com.android.documentsui.DIRECTORY_COPY";
     }
 
-    public static class State implements android.os.Parcelable {
-        public int action;
-        public String[] acceptMimes;
-
-        /** Explicit user choice */
-        public int userMode = MODE_UNKNOWN;
-        /** Derived after loader */
-        public int derivedMode = MODE_LIST;
-
-        /** Explicit user choice */
-        public int userSortOrder = SORT_ORDER_UNKNOWN;
-        /** Derived after loader */
-        public int derivedSortOrder = SORT_ORDER_DISPLAY_NAME;
-
-        public boolean allowMultiple;
-        public boolean showSize;
-        public boolean localOnly ;
-        public boolean forceAdvanced ;
-        public boolean showAdvanced ;
-        public boolean stackTouched ;
-        public boolean restored ;
-        public boolean directoryCopy ;
-        /** Transfer mode for file copy/move operations. */
-        public int transferMode;
-
-        /** Current user navigation stack; empty implies recents. */
-        public DocumentStack stack = new DocumentStack();
-        /** Currently active search, overriding any stack. */
-        public String currentSearch;
-
-        /** Instance state for every shown directory */
-        public HashMap<String, SparseArray<Parcelable>> dirState = new HashMap<>();
-
-        /** Currently copying file */
-        public List<DocumentInfo> selectedDocumentsForCopy = new ArrayList<DocumentInfo>();
-
-        /** Name of the package that started DocsUI */
-        public List<String> excludedAuthorities = new ArrayList<>();
-
-        public static final int ACTION_OPEN = 1;
-        public static final int ACTION_CREATE = 2;
-        public static final int ACTION_GET_CONTENT = 3;
-        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;
-        public static final int MODE_LIST = 1;
-        public static final int MODE_GRID = 2;
-
-        public static final int SORT_ORDER_UNKNOWN = 0;
-        public static final int SORT_ORDER_DISPLAY_NAME = 1;
-        public static final int SORT_ORDER_LAST_MODIFIED = 2;
-        public static final int SORT_ORDER_SIZE = 3;
-
-        @Override
-        public int describeContents() {
-            return 0;
-        }
-
-        @Override
-        public void writeToParcel(Parcel out, int flags) {
-            out.writeInt(action);
-            out.writeInt(userMode);
-            out.writeStringArray(acceptMimes);
-            out.writeInt(userSortOrder);
-            out.writeInt(allowMultiple ? 1 : 0);
-            out.writeInt(showSize ? 1 : 0);
-            out.writeInt(localOnly ? 1 : 0);
-            out.writeInt(forceAdvanced ? 1 : 0);
-            out.writeInt(showAdvanced ? 1 : 0);
-            out.writeInt(stackTouched ? 1 : 0);
-            out.writeInt(restored ? 1 : 0);
-            DurableUtils.writeToParcel(out, stack);
-            out.writeString(currentSearch);
-            out.writeMap(dirState);
-            out.writeList(selectedDocumentsForCopy);
-            out.writeList(excludedAuthorities);
-        }
-
-        public static final Creator<State> CREATOR = new Creator<State>() {
-            @Override
-            public State createFromParcel(Parcel in) {
-                final State state = new State();
-                state.action = in.readInt();
-                state.userMode = in.readInt();
-                state.acceptMimes = in.readStringArray();
-                state.userSortOrder = in.readInt();
-                state.allowMultiple = in.readInt() != 0;
-                state.showSize = in.readInt() != 0;
-                state.localOnly = in.readInt() != 0;
-                state.forceAdvanced = in.readInt() != 0;
-                state.showAdvanced = in.readInt() != 0;
-                state.stackTouched = in.readInt() != 0;
-                state.restored = in.readInt() != 0;
-                DurableUtils.readFromParcel(in, state.stack);
-                state.currentSearch = in.readString();
-                in.readMap(state.dirState, null);
-                in.readList(state.selectedDocumentsForCopy, null);
-                in.readList(state.excludedAuthorities, null);
-                return state;
-            }
-
-            @Override
-            public State[] newArray(int size) {
-                return new State[size];
-            }
-        };
-    }
-
     void setDisplayAdvancedDevices(boolean display) {
         State state = getDisplayState();
         LocalPreferences.setDisplayAdvancedDevices(this, display);
@@ -559,7 +432,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/CopyService.java b/packages/DocumentsUI/src/com/android/documentsui/CopyService.java
index f8ec8f1..f1492dc 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/CopyService.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/CopyService.java
@@ -16,6 +16,7 @@
 
 package com.android.documentsui;
 
+import static com.android.documentsui.Shared.DEBUG;
 import static com.android.documentsui.model.DocumentInfo.getCursorLong;
 import static com.android.documentsui.model.DocumentInfo.getCursorString;
 
@@ -56,7 +57,6 @@
 
 public class CopyService extends IntentService {
     public static final String TAG = "CopyService";
-    public static final boolean DEBUG = false;
 
     private static final String EXTRA_CANCEL = "com.android.documentsui.CANCEL";
     public static final String EXTRA_SRC_LIST = "com.android.documentsui.SRC_LIST";
diff --git a/packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java b/packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java
index 046f3df..5eacf21 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java
@@ -16,15 +16,15 @@
 
 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;
-import static com.android.documentsui.BaseActivity.State.MODE_LIST;
-import static com.android.documentsui.BaseActivity.State.MODE_UNKNOWN;
-import static com.android.documentsui.BaseActivity.State.SORT_ORDER_UNKNOWN;
+import static com.android.documentsui.Shared.DEBUG;
 import static com.android.documentsui.Shared.TAG;
+import static com.android.documentsui.State.ACTION_BROWSE;
+import static com.android.documentsui.State.ACTION_CREATE;
+import static com.android.documentsui.State.ACTION_MANAGE;
+import static com.android.documentsui.State.MODE_GRID;
+import static com.android.documentsui.State.MODE_LIST;
+import static com.android.documentsui.State.MODE_UNKNOWN;
+import static com.android.documentsui.State.SORT_ORDER_UNKNOWN;
 import static com.android.documentsui.model.DocumentInfo.getCursorInt;
 import static com.android.documentsui.model.DocumentInfo.getCursorLong;
 import static com.android.documentsui.model.DocumentInfo.getCursorString;
@@ -94,14 +94,13 @@
 import android.widget.Toast;
 
 import com.android.documentsui.BaseActivity.DocumentContext;
-import com.android.documentsui.BaseActivity.State;
 import com.android.documentsui.MultiSelectManager.Selection;
 import com.android.documentsui.ProviderExecutor.Preemptable;
 import com.android.documentsui.RecentsProvider.StateColumns;
 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;
 
 import java.util.ArrayList;
@@ -125,7 +124,6 @@
     public static final int REQUEST_COPY_DESTINATION = 1;
 
     private static final int LOADER_ID = 42;
-    private static final boolean DEBUG = false;
     private static final boolean DEBUG_ENABLE_DND = false;
 
     private static final String EXTRA_TYPE = "type";
@@ -135,6 +133,7 @@
     private static final String EXTRA_IGNORE_STATE = "ignoreState";
 
     private Model mModel;
+    private Model.UpdateListener mModelUpdateListener = new ModelUpdateListener();
 
     private final Handler mHandler = new Handler(Looper.getMainLooper());
 
@@ -159,6 +158,9 @@
     private GridLayoutManager mGridLayout;
     private int mColumnCount = 1;  // This will get updated when layout changes.
 
+    private MessageBar mMessageBar;
+    private View mProgressBar;
+
     public static void showNormal(FragmentManager fm, RootInfo root, DocumentInfo doc, int anim) {
         show(fm, TYPE_NORMAL, root, doc, null, anim);
     }
@@ -220,6 +222,9 @@
         final Resources res = context.getResources();
         final View view = inflater.inflate(R.layout.fragment_directory, container, false);
 
+        mMessageBar = MessageBar.create(getChildFragmentManager());
+        mProgressBar = view.findViewById(R.id.progressbar);
+
         mEmptyView = view.findViewById(android.R.id.empty);
 
         mRecView = (RecyclerView) view.findViewById(R.id.recyclerView);
@@ -307,9 +312,8 @@
                     : MultiSelectManager.MODE_SINGLE);
         selMgr.addCallback(new SelectionModeListener());
 
-        mModel = new Model(context, selMgr);
-        mModel.setSelectionManager(selMgr);
-        mModel.addUpdateListener(mAdapter);
+        mModel = new Model(context, selMgr, mAdapter);
+        mModel.addUpdateListener(mModelUpdateListener);
 
         mType = getArguments().getInt(EXTRA_TYPE);
         mStateKey = buildStateKey(root, doc);
@@ -362,21 +366,6 @@
 
             @Override
             public void onLoadFinished(Loader<DirectoryResult> loader, DirectoryResult result) {
-                if (result == null || result.exception != null) {
-                    // onBackPressed does a fragment transaction, which can't be done inside
-                    // onLoadFinished
-                    mHandler.post(new Runnable() {
-                        @Override
-                        public void run() {
-                            final Activity activity = getActivity();
-                            if (activity != null) {
-                                activity.onBackPressed();
-                            }
-                        }
-                    });
-                    return;
-                }
-
                 if (!isAdded()) return;
 
                 mModel.update(result);
@@ -611,7 +600,7 @@
 
         @Override
         public boolean onBeforeItemStateChange(int position, boolean selected) {
-            // Directories and footer items cannot be checked
+            // Directories cannot be checked
             if (selected) {
                 final Cursor cursor = mModel.getItem(position);
                 checkNotNull(cursor, "Cursor cannot be null.");
@@ -624,7 +613,6 @@
 
         @Override
         public void onItemStateChanged(int position, boolean selected) {
-
             final Cursor cursor = mModel.getItem(position);
             checkNotNull(cursor, "Cursor cannot be null.");
 
@@ -710,8 +698,9 @@
                 return true;
 
             } else if (id == R.id.menu_delete) {
-                deleteDocuments(selection);
+                // Exit selection mode first, so we avoid deselecting deleted documents.
                 mode.finish();
+                deleteDocuments(selection);
                 return true;
 
             } else if (id == R.id.menu_copy_to) {
@@ -720,8 +709,9 @@
                 return true;
 
             } else if (id == R.id.menu_move_to) {
-                transferDocuments(selection, CopyService.TRANSFER_MODE_MOVE);
+                // Exit selection mode first, so we avoid deselecting deleted documents.
                 mode.finish();
+                transferDocuments(selection, CopyService.TRANSFER_MODE_MOVE);
                 return true;
 
             } else if (id == R.id.menu_copy_to_clipboard) {
@@ -880,79 +870,6 @@
         return ((BaseActivity) fragment.getActivity()).getDisplayState();
     }
 
-    private static abstract class Footer {
-        private final int mItemViewType;
-
-        public Footer(int itemViewType) {
-            mItemViewType = itemViewType;
-        }
-
-        public abstract View getView(View convertView, ViewGroup parent);
-
-        public int getItemViewType() {
-            return mItemViewType;
-        }
-    }
-
-    private class LoadingFooter extends Footer {
-        public LoadingFooter() {
-            super(1);
-        }
-
-        @Override
-        public View getView(View convertView, ViewGroup parent) {
-            final Context context = parent.getContext();
-            final State state = getDisplayState(DirectoryFragment.this);
-
-            if (convertView == null) {
-                final LayoutInflater inflater = LayoutInflater.from(context);
-                if (state.derivedMode == MODE_LIST) {
-                    convertView = inflater.inflate(R.layout.item_loading_list, parent, false);
-                } else if (state.derivedMode == MODE_GRID) {
-                    convertView = inflater.inflate(R.layout.item_loading_grid, parent, false);
-                } else {
-                    throw new IllegalStateException();
-                }
-            }
-
-            return convertView;
-        }
-    }
-
-    private class MessageFooter extends Footer {
-        private final int mIcon;
-        private final String mMessage;
-
-        public MessageFooter(int itemViewType, int icon, String message) {
-            super(itemViewType);
-            mIcon = icon;
-            mMessage = message;
-        }
-
-        @Override
-        public View getView(View convertView, ViewGroup parent) {
-            final Context context = parent.getContext();
-            final State state = getDisplayState(DirectoryFragment.this);
-
-            if (convertView == null) {
-                final LayoutInflater inflater = LayoutInflater.from(context);
-                if (state.derivedMode == MODE_LIST) {
-                    convertView = inflater.inflate(R.layout.item_message_list, parent, false);
-                } else if (state.derivedMode == MODE_GRID) {
-                    convertView = inflater.inflate(R.layout.item_message_grid, parent, false);
-                } else {
-                    throw new IllegalStateException();
-                }
-            }
-
-            final ImageView icon = (ImageView) convertView.findViewById(android.R.id.icon);
-            final TextView title = (TextView) convertView.findViewById(android.R.id.title);
-            icon.setImageResource(mIcon);
-            title.setText(mMessage);
-            return convertView;
-        }
-    }
-
     // Provide a reference to the views for each data item
     // Complex data items may need more than one view per item, and
     // you provide access to all the views for a data item in a view holder
@@ -966,46 +883,39 @@
         }
     }
 
-    private final class DocumentsAdapter extends RecyclerView.Adapter<DocumentHolder>
-            implements Model.UpdateListener {
+    void showEmptyView() {
+        mEmptyView.setVisibility(View.VISIBLE);
+        mRecView.setVisibility(View.GONE);
+        TextView msg = (TextView) mEmptyView.findViewById(R.id.message);
+        msg.setText(R.string.empty);
+        // No retry button for the empty view.
+        mEmptyView.findViewById(R.id.button_retry).setVisibility(View.GONE);
+    }
+
+    void showErrorView() {
+        mEmptyView.setVisibility(View.VISIBLE);
+        mRecView.setVisibility(View.GONE);
+        TextView msg = (TextView) mEmptyView.findViewById(R.id.message);
+        msg.setText(R.string.query_error);
+        // TODO: Enable this once the retry button does something.
+        mEmptyView.findViewById(R.id.button_retry).setVisibility(View.GONE);
+    }
+
+    void showRecyclerView() {
+        mEmptyView.setVisibility(View.GONE);
+        mRecView.setVisibility(View.VISIBLE);
+    }
+
+    private final class DocumentsAdapter extends RecyclerView.Adapter<DocumentHolder> {
 
         private final Context mContext;
         private final LayoutInflater mInflater;
-        // TODO: Bring back support for footers.
-        private final List<Footer> mFooters = new ArrayList<>();
 
         public DocumentsAdapter(Context context) {
             mContext = context;
             mInflater = LayoutInflater.from(context);
         }
 
-        public void onModelUpdate(Model model) {
-            mFooters.clear();
-            if (model.info != null) {
-                mFooters.add(new MessageFooter(2, R.drawable.ic_dialog_info, model.info));
-            }
-            if (model.error != null) {
-                mFooters.add(new MessageFooter(3, R.drawable.ic_dialog_alert, model.error));
-            }
-            if (model.isLoading()) {
-                mFooters.add(new LoadingFooter());
-            }
-
-            if (model.isEmpty()) {
-                mEmptyView.setVisibility(View.VISIBLE);
-            } else {
-                mEmptyView.setVisibility(View.GONE);
-            }
-
-            notifyDataSetChanged();
-        }
-
-        public void onModelUpdateFailed(Exception e) {
-            String error = getString(R.string.query_error);
-            mFooters.add(new MessageFooter(3, R.drawable.ic_dialog_alert, error));
-            notifyDataSetChanged();
-        }
-
         @Override
         public DocumentHolder onCreateViewHolder(ViewGroup parent, int viewType) {
             final State state = getDisplayState(DirectoryFragment.this);
@@ -1222,19 +1132,8 @@
         @Override
         public int getItemCount() {
             return mModel.getItemCount();
-            // return mCursorCount + mFooters.size();
         }
 
-        @Override
-        public int getItemViewType(int position) {
-            final int itemCount = mModel.getItemCount();
-            if (position < itemCount) {
-                return 0;
-            } else {
-                position -= itemCount;
-                return mFooters.get(position).getItemViewType();
-            }
-        }
     }
 
     private static String formatTime(Context context, long when) {
@@ -1519,7 +1418,8 @@
                     getClipDataFromDocuments(docs),
                     new DrawableShadowBuilder(getDragShadowIcon(docs)),
                     null,
-                    View.DRAG_FLAG_GLOBAL
+                    View.DRAG_FLAG_GLOBAL | View.DRAG_FLAG_GLOBAL_URI_READ |
+                            View.DRAG_FLAG_GLOBAL_URI_WRITE
             );
             return true;
         }
@@ -1660,9 +1560,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);
     }
 
     /**
@@ -1699,15 +1599,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);
@@ -1716,14 +1615,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.
@@ -1740,6 +1636,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);
@@ -1759,6 +1656,7 @@
     @VisibleForTesting
     public static final class Model implements DocumentContext {
         private MultiSelectManager mSelectionManager;
+        private RecyclerView.Adapter<?> mViewAdapter;
         private Context mContext;
         private int mCursorCount;
         private boolean mIsLoading;
@@ -1768,17 +1666,11 @@
         @Nullable private String info;
         @Nullable private String error;
 
-        Model(Context context, MultiSelectManager selectionManager) {
+        Model(Context context, MultiSelectManager selectionManager,
+                RecyclerView.Adapter<?> viewAdapter) {
             mContext = context;
             mSelectionManager = selectionManager;
-        }
-
-        /**
-         * Sets the selection manager used by the model.
-         * TODO: the model should instantiate the selection manager.  See onActivityCreated.
-         */
-        void setSelectionManager(MultiSelectManager mgr) {
-            mSelectionManager = mgr;
+            mViewAdapter = viewAdapter;
         }
 
         /**
@@ -1943,7 +1835,7 @@
                 int position = selected.get(i);
                 if (DEBUG) Log.d(TAG, "Marked position " + position + " for deletion");
                 mMarkedForDeletion.append(position, true);
-                mUpdateListener.notifyItemRemoved(position);
+                mViewAdapter.notifyItemRemoved(position);
             }
         }
 
@@ -1958,7 +1850,7 @@
             for (int i = 0; i < size; ++i) {
                 final int position = mMarkedForDeletion.keyAt(i);
                 mMarkedForDeletion.put(position, false);
-                mUpdateListener.notifyItemInserted(position);
+                mViewAdapter.notifyItemInserted(position);
             }
 
             // Then, clear the deletion list.
@@ -2041,26 +1933,41 @@
             mUpdateListener = listener;
         }
 
-        interface UpdateListener {
+        static class UpdateListener {
             /**
              * Called when a successful update has occurred.
              */
-            void onModelUpdate(Model model);
+            void onModelUpdate(Model model) {}
 
             /**
              * Called when an update has been attempted but failed.
              */
-            void onModelUpdateFailed(Exception e);
+            void onModelUpdateFailed(Exception e) {}
+        }
+    }
 
-            /**
-             * Called when an item has been removed from the model.
-             */
-            void notifyItemRemoved(int position);
+    private class ModelUpdateListener extends Model.UpdateListener {
+        @Override
+        public void onModelUpdate(Model model) {
+            if (model.info != null || model.error != null) {
+                mMessageBar.setInfo(model.info);
+                mMessageBar.setError(model.error);
+                mMessageBar.show();
+            }
 
-            /**
-             * Called when an item has been added to the model.
-             */
-            void notifyItemInserted(int position);
+            mProgressBar.setVisibility(model.isLoading() ? View.VISIBLE : View.GONE);
+
+            if (model.isEmpty()) {
+                showEmptyView();
+            } else {
+                showRecyclerView();
+                mAdapter.notifyDataSetChanged();
+            }
+        }
+
+        @Override
+        public void onModelUpdateFailed(Exception e) {
+            showErrorView();
         }
     }
 }
diff --git a/packages/DocumentsUI/src/com/android/documentsui/DirectoryLoader.java b/packages/DocumentsUI/src/com/android/documentsui/DirectoryLoader.java
index 0edb241..bb82b38 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/DirectoryLoader.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/DirectoryLoader.java
@@ -16,12 +16,12 @@
 
 package com.android.documentsui;
 
-import static com.android.documentsui.BaseActivity.State.MODE_UNKNOWN;
-import static com.android.documentsui.BaseActivity.State.SORT_ORDER_DISPLAY_NAME;
-import static com.android.documentsui.BaseActivity.State.SORT_ORDER_LAST_MODIFIED;
-import static com.android.documentsui.BaseActivity.State.SORT_ORDER_SIZE;
-import static com.android.documentsui.BaseActivity.State.SORT_ORDER_UNKNOWN;
 import static com.android.documentsui.Shared.TAG;
+import static com.android.documentsui.State.MODE_UNKNOWN;
+import static com.android.documentsui.State.SORT_ORDER_DISPLAY_NAME;
+import static com.android.documentsui.State.SORT_ORDER_LAST_MODIFIED;
+import static com.android.documentsui.State.SORT_ORDER_SIZE;
+import static com.android.documentsui.State.SORT_ORDER_UNKNOWN;
 import static com.android.documentsui.model.DocumentInfo.getCursorInt;
 
 import android.content.AsyncTaskLoader;
@@ -37,7 +37,6 @@
 import android.provider.DocumentsContract.Document;
 import android.util.Log;
 
-import com.android.documentsui.BaseActivity.State;
 import com.android.documentsui.RecentsProvider.StateColumns;
 import com.android.documentsui.model.DocumentInfo;
 import com.android.documentsui.model.RootInfo;
diff --git a/packages/DocumentsUI/src/com/android/documentsui/DirectoryView.java b/packages/DocumentsUI/src/com/android/documentsui/DirectoryView.java
index 4893652..000b92a 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/DirectoryView.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/DirectoryView.java
@@ -18,9 +18,9 @@
 
 import android.content.Context;
 import android.util.AttributeSet;
-import android.widget.FrameLayout;
+import android.widget.LinearLayout;
 
-public class DirectoryView extends FrameLayout {
+public class DirectoryView extends LinearLayout {
     private float mPosition = 0f;
 
     private int mWidth;
diff --git a/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java b/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java
index 1de1c6a..4658fe3 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java
@@ -16,19 +16,16 @@
 
 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;
-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.State.ACTION_CREATE;
+import static com.android.documentsui.State.ACTION_GET_CONTENT;
+import static com.android.documentsui.State.ACTION_OPEN;
+import static com.android.documentsui.State.ACTION_OPEN_COPY_DESTINATION;
+import static com.android.documentsui.State.ACTION_OPEN_TREE;
 
 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 +41,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;
@@ -75,8 +71,6 @@
 
     private Toolbar mRootsToolbar;
 
-    private DirectoryContainerView mDirectoryContainer;
-
     private ItemSelectedListener mStackListener;
     private BaseAdapter mStackAdapter;
 
@@ -89,7 +83,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);
@@ -114,8 +108,6 @@
             mDrawer = DrawerController.create(this);
         }
 
-        mDirectoryContainer = (DirectoryContainerView) findViewById(R.id.container_directory);
-
         mToolbar = (Toolbar) findViewById(R.id.toolbar);
 
         mStackAdapter = new StackAdapter();
@@ -127,14 +119,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);
@@ -160,12 +144,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);
         }
@@ -185,8 +164,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;
         }
@@ -196,20 +173,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);
@@ -361,11 +324,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) {
@@ -387,11 +346,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;
     }
@@ -407,8 +364,6 @@
         final RootInfo root = getCurrentRoot();
         final DocumentInfo cwd = getCurrentDirectory();
 
-        mDirectoryContainer.setDrawDisappearingFirst(anim == ANIM_DOWN);
-
         if (cwd == null) {
             // No directory means recents
             if (mState.action == ACTION_CREATE ||
@@ -452,11 +407,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
@@ -472,21 +427,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();
-            }
         }
     }
 
@@ -498,7 +442,7 @@
             for (int i = 0; i < size; i++) {
                 uris[i] = docs.get(i).derivedUri;
             }
-            new ExistingFinishTask(uris).executeOnExecutor(getCurrentExecutor());
+            new ExistingFinishTask(uris).executeOnExecutor(getExecutorForCurrentDirectory());
         }
     }
 
@@ -513,7 +457,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 450def7..e8d1088 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/FilesActivity.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/FilesActivity.java
@@ -16,8 +16,10 @@
 
 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 static com.android.internal.util.Preconditions.checkState;
 
 import android.app.Activity;
 import android.app.FragmentManager;
@@ -25,11 +27,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 +46,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,11 +57,9 @@
 public class FilesActivity extends BaseActivity {
 
     public static final String TAG = "FilesActivity";
-    static final boolean DEBUG = false;
 
     private Toolbar mToolbar;
     private Spinner mToolbarStack;
-    private DirectoryContainerView mDirectoryContainer;
     private ItemSelectedListener mStackListener;
     private BaseAdapter mStackAdapter;
     private DocumentClipper mClipper;
@@ -75,10 +72,6 @@
     public void onCreate(Bundle icicle) {
         super.onCreate(icicle);
 
-        final Context context = this;
-
-        mDirectoryContainer = (DirectoryContainerView) findViewById(R.id.container_directory);
-
         mToolbar = (Toolbar) findViewById(R.id.toolbar);
 
         mStackAdapter = new StackAdapter();
@@ -93,10 +86,19 @@
 
         RootsFragment.show(getFragmentManager(), null);
         if (!mState.restored) {
-            new RestoreStackTask().execute();
+            Intent intent = getIntent();
+            Uri rootUri = intent.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 && !LauncherActivity.isLaunchUri(rootUri)) {
+                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();
             final DocumentStack dstStack = intent.getParcelableExtra(CopyService.EXTRA_STACK);
             final int failure = intent.getIntExtra(CopyService.EXTRA_FAILURE, 0);
             final int transferMode = intent.getIntExtra(CopyService.EXTRA_TRANSFER_MODE,
@@ -118,22 +120,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;
     }
@@ -145,6 +141,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();
 
@@ -200,17 +211,22 @@
         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);
+        final MenuItem newWindow = menu.findItem(R.id.menu_new_window);
+        final MenuItem pasteFromCb = menu.findItem(R.id.menu_paste_from_clipboard);
 
         boolean canCreateDir = canCreateDirectory();
 
         createDir.setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER);
         createDir.setVisible(canCreateDir);
+        createDir.setEnabled(canCreateDir);
 
-        pasteFromCb.setVisible(true);
+        newWindow.setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER);
+        newWindow.setVisible(mProductivityDevice);
+        newWindow.setEnabled(mProductivityDevice);
+
         pasteFromCb.setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER);
+        pasteFromCb.setVisible(true);
         pasteFromCb.setEnabled(mClipper.hasItemsToPaste());
 
         return shown;
@@ -218,12 +234,19 @@
 
     @Override
     public boolean onOptionsItemSelected(MenuItem item) {
-        final int id = item.getItemId();
-        if (id == R.id.menu_paste_from_clipboard) {
-            DirectoryFragment dir = DirectoryFragment.get(getFragmentManager());
-            dir = DirectoryFragment.get(getFragmentManager());
-            dir.pasteFromClipboard();
-            return true;
+        switch (item.getItemId()) {
+            case R.id.menu_create_dir:
+                checkState(canCreateDirectory());
+                showCreateDirectoryDialog();
+                return true;
+            case R.id.menu_new_window:
+                startActivity(LauncherActivity.createLaunchIntent(this));
+                return true;
+            case R.id.menu_paste_from_clipboard:
+                DirectoryFragment dir = DirectoryFragment.get(getFragmentManager());
+                dir = DirectoryFragment.get(getFragmentManager());
+                dir.pasteFromClipboard();
+                return true;
         }
 
         return super.onOptionsItemSelected(item);
@@ -235,8 +258,6 @@
         final RootInfo root = getCurrentRoot();
         final DocumentInfo cwd = getCurrentDirectory();
 
-        mDirectoryContainer.setDrawDisappearingFirst(anim == ANIM_DOWN);
-
         if (cwd == null) {
             DirectoryFragment.showRecentsOpen(fm, anim);
 
@@ -313,19 +334,13 @@
                 dir = DirectoryFragment.get(getFragmentManager());
                 dir.selectAllFiles();
                 return true;
-            case KeyEvent.KEYCODE_N:
-                if (event.isShiftPressed() && canCreateDirectory()) {
-                    showCreateDirectoryDialog();
-                    return true;
-                }
             case KeyEvent.KEYCODE_C:
                 // TODO: Should be statically bound using alphabeticShortcut. See b/21330356.
                 dir = DirectoryFragment.get(getFragmentManager());
                 dir.copySelectedToClipboard();
-                // TODO: Cancel action mode in directory fragment.
         }
 
-        return super.onKeyUp(keyCode, event);
+        return super.onKeyShortcut(keyCode, event);
     }
 
     @Override
diff --git a/packages/DocumentsUI/src/com/android/documentsui/IconUtils.java b/packages/DocumentsUI/src/com/android/documentsui/IconUtils.java
index ec1cb1d..a1213d2 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/IconUtils.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/IconUtils.java
@@ -222,7 +222,7 @@
                 return context.getDrawable(R.drawable.ic_doc_album);
             }
 
-            if (mode == BaseActivity.State.MODE_GRID) {
+            if (mode == State.MODE_GRID) {
                 return context.getDrawable(R.drawable.ic_grid_folder);
             } else {
                 return context.getDrawable(R.drawable.ic_doc_folder);
diff --git a/packages/DocumentsUI/src/com/android/documentsui/LauncherActivity.java b/packages/DocumentsUI/src/com/android/documentsui/LauncherActivity.java
new file mode 100644
index 0000000..c29937d
--- /dev/null
+++ b/packages/DocumentsUI/src/com/android/documentsui/LauncherActivity.java
@@ -0,0 +1,93 @@
+/*
+ * 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.documentsui;
+
+import android.app.Activity;
+import android.app.ActivityManager;
+import android.app.ActivityManager.AppTask;
+import android.app.ActivityManager.RecentTaskInfo;
+import android.content.Context;
+import android.content.Intent;
+import android.net.Uri;
+import android.os.Bundle;
+import android.support.annotation.Nullable;
+
+import java.util.List;
+
+/**
+ * Provides FilesActivity task grouping support. This allows multiple FilesActivities to be
+ * launched (a behavior imparted by way of {@code documentLaunchMode="intoExisting"} and
+ * our use of pseudo document {@link Uri}s. This also lets us move an existing task
+ * to the foreground when a suitable task exists.
+ *
+ * Requires that {@code documentLaunchMode="intoExisting"} be set on target activity.
+ *
+ */
+public class LauncherActivity extends Activity {
+
+    public static final String LAUNCH_CONTROL_AUTHORITY = "com.android.documentsui.launchControl";
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        ActivityManager activities = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
+        List<AppTask> tasks = activities.getAppTasks();
+
+        AppTask raiseTask = null;
+        for (AppTask task : tasks) {
+            Uri taskUri = task.getTaskInfo().baseIntent.getData();
+            if (taskUri != null && isLaunchUri(taskUri)) {
+                raiseTask = task;
+            }
+        }
+
+        if (raiseTask == null) {
+            launchFilesTask();
+        } else {
+            raiseFilesTask(activities, raiseTask.getTaskInfo());
+        }
+
+        finish();
+    }
+
+    private void launchFilesTask() {
+        Intent intent = createLaunchIntent(this);
+        startActivity(intent);
+    }
+
+    private void raiseFilesTask(ActivityManager activities, RecentTaskInfo task) {
+        activities.moveTaskToFront(task.id, 0);
+    }
+
+    static Intent createLaunchIntent(Context context) {
+        Intent intent = new Intent(context, FilesActivity.class);
+        intent.setData(buildLaunchUri());
+        return intent;
+    }
+
+    private static Uri buildLaunchUri() {
+        return new Uri.Builder()
+                .authority(LAUNCH_CONTROL_AUTHORITY)
+                .fragment(String.valueOf(System.currentTimeMillis()))
+                .build();
+    }
+
+    static boolean isLaunchUri(@Nullable Uri uri) {
+        return uri != null && LAUNCH_CONTROL_AUTHORITY.equals(uri.getAuthority());
+    }
+}
diff --git a/packages/DocumentsUI/src/com/android/documentsui/ManageRootActivity.java b/packages/DocumentsUI/src/com/android/documentsui/ManageRootActivity.java
index 7401578..4754899 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/ManageRootActivity.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/ManageRootActivity.java
@@ -16,9 +16,8 @@
 
 package com.android.documentsui;
 
-import static com.android.documentsui.BaseActivity.State.ACTION_MANAGE;
-import static com.android.documentsui.DirectoryFragment.ANIM_DOWN;
 import static com.android.documentsui.DirectoryFragment.ANIM_NONE;
+import static com.android.documentsui.State.ACTION_MANAGE;
 
 import android.app.Activity;
 import android.app.Fragment;
@@ -56,8 +55,6 @@
     private Toolbar mToolbar;
     private Spinner mToolbarStack;
 
-    private DirectoryContainerView mDirectoryContainer;
-
     private ItemSelectedListener mStackListener;
     private BaseAdapter mStackAdapter;
 
@@ -73,8 +70,6 @@
 
         mDrawer = DrawerController.createDummy();
 
-        mDirectoryContainer = (DirectoryContainerView) findViewById(R.id.container_directory);
-
         mToolbar = (Toolbar) findViewById(R.id.toolbar);
         mToolbar.setTitleTextAppearance(context,
                 android.R.style.TextAppearance_DeviceDefault_Widget_ActionBar_Title);
@@ -91,7 +86,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);
         }
@@ -157,7 +152,6 @@
         // If started in manage roots mode, there has to be a cwd (i.e. the root dir of the managed
         // root).
         Preconditions.checkNotNull(cwd);
-        mDirectoryContainer.setDrawDisappearingFirst(anim == ANIM_DOWN);
 
         if (mState.currentSearch != null) {
             // Ongoing search
diff --git a/packages/DocumentsUI/src/com/android/documentsui/MessageBar.java b/packages/DocumentsUI/src/com/android/documentsui/MessageBar.java
new file mode 100644
index 0000000..312d53b
--- /dev/null
+++ b/packages/DocumentsUI/src/com/android/documentsui/MessageBar.java
@@ -0,0 +1,129 @@
+/*
+ * 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.documentsui;
+
+import android.annotation.Nullable;
+import android.app.Fragment;
+import android.app.FragmentManager;
+import android.app.FragmentTransaction;
+import android.os.Bundle;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.Button;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+/**
+ * A message bar displaying some info/error messages and a Dismiss button.
+ */
+public class MessageBar extends Fragment {
+    private View mView;
+    private ViewGroup mContainer;
+
+    /**
+     * Creates an instance of a MessageBar. Note that the new MessagBar is not visible by default,
+     * and has to be shown by calling MessageBar.show.
+     */
+    public static MessageBar create(FragmentManager fm) {
+        final MessageBar fragment = new MessageBar();
+
+        final FragmentTransaction ft = fm.beginTransaction();
+        ft.replace(R.id.container_message_bar, fragment);
+        ft.commitAllowingStateLoss();
+
+        return fragment;
+    }
+
+    /**
+     * Sets the info message. Can be null, in which case no info message will be displayed. The
+     * message bar layout will be adjusted accordingly.
+     */
+    public void setInfo(@Nullable String info) {
+        View infoContainer = mView.findViewById(R.id.container_info);
+        if (info != null) {
+            TextView infoText = (TextView) mView.findViewById(R.id.textview_info);
+            infoText.setText(info);
+            infoContainer.setVisibility(View.VISIBLE);
+        } else {
+            infoContainer.setVisibility(View.GONE);
+        }
+    }
+
+    /**
+     * Sets the error message. Can be null, in which case no error message will be displayed. The
+     * message bar layout will be adjusted accordingly.
+     */
+    public void setError(@Nullable String error) {
+        View errorView = mView.findViewById(R.id.container_error);
+        if (error != null) {
+            TextView errorText = (TextView) mView.findViewById(R.id.textview_error);
+            errorText.setText(error);
+            errorView.setVisibility(View.VISIBLE);
+        } else {
+            errorView.setVisibility(View.GONE);
+        }
+    }
+
+    @Override
+    public View onCreateView(
+            LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
+
+        mView = inflater.inflate(R.layout.fragment_message_bar, container, false);
+
+        ImageView infoIcon = (ImageView) mView.findViewById(R.id.icon_info);
+        infoIcon.setImageResource(R.drawable.ic_dialog_info);
+
+        ImageView errorIcon = (ImageView) mView.findViewById(R.id.icon_error);
+        errorIcon.setImageResource(R.drawable.ic_dialog_alert);
+
+        Button dismiss = (Button) mView.findViewById(R.id.button_dismiss);
+        dismiss.setOnClickListener(
+                new View.OnClickListener() {
+                    @Override
+                    public void onClick(View v) {
+                        hide();
+                    }
+                });
+
+        mContainer = container;
+
+        return mView;
+    }
+
+    void hide() {
+        // The container view is used to show/hide the error bar. If a container is not provided,
+        // fall back to showing/hiding the error bar View, which also works, but does not provide
+        // the same animated transition.
+        if (mContainer != null) {
+            mContainer.setVisibility(View.GONE);
+        } else {
+            mView.setVisibility(View.GONE);
+        }
+    }
+
+    void show() {
+        // The container view is used to show/hide the error bar. If a container is not provided,
+        // fall back to showing/hiding the error bar View, which also works, but does not provide
+        // the same animated transition.
+        if (mContainer != null) {
+            mContainer.setVisibility(View.VISIBLE);
+        } else {
+            mView.setVisibility(View.VISIBLE);
+        }
+    }
+}
diff --git a/packages/DocumentsUI/src/com/android/documentsui/MultiSelectManager.java b/packages/DocumentsUI/src/com/android/documentsui/MultiSelectManager.java
index 5930056..858fb42 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);
 
@@ -360,8 +365,8 @@
             // To make this more correct, we'd need to update the Ranger class to return
             // information about what has changed.
             notifySelectionChanged();
-        } else if (toggleSelection(input.getItemPosition())) {
-            notifySelectionChanged();
+        } else {
+            toggleSelection(input.getItemPosition());
         }
     }
 
@@ -370,14 +375,13 @@
      * a new Ranger (range selection manager) at that point is created.
      *
      * @param position
-     * @return True if state changed.
      */
-    private boolean toggleSelection(int position) {
+    private void toggleSelection(int position) {
         // Position may be special "no position" during certain
         // transitional phases. If so, skip handling of the event.
         if (position == RecyclerView.NO_POSITION) {
             if (DEBUG) Log.d(TAG, "Ignoring toggle for element with no position.");
-            return false;
+            return;
         }
 
         boolean changed = false;
@@ -386,7 +390,7 @@
         } else {
             boolean canSelect = notifyBeforeItemStateChange(position, true);
             if (!canSelect) {
-                return false;
+                return;
             }
             if (mSingleSelect && !mSelection.isEmpty()) {
                 clearSelectionQuietly();
@@ -402,7 +406,9 @@
             changed = true;
         }
 
-        return changed;
+        if (changed) {
+            notifySelectionChanged();
+        }
     }
 
     /**
@@ -1060,21 +1066,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 +1091,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 +1108,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 +1118,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 +1135,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 +1145,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 +1155,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/PickFragment.java b/packages/DocumentsUI/src/com/android/documentsui/PickFragment.java
index 5f6a5e9..48e28dc 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/PickFragment.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/PickFragment.java
@@ -21,7 +21,6 @@
 import android.app.FragmentManager;
 import android.app.FragmentTransaction;
 import android.os.Bundle;
-import android.text.TextUtils;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
@@ -29,8 +28,6 @@
 
 import com.android.documentsui.model.DocumentInfo;
 
-import java.util.Locale;
-
 /**
  * Display pick confirmation bar, usually for selecting a directory.
  */
@@ -93,7 +90,7 @@
     };
 
     /**
-     * @param action Which action defined in BaseActivity.State is the picker shown for.
+     * @param action Which action defined in State is the picker shown for.
      */
     public void setPickTarget(int action, int transferMode, DocumentInfo pickTarget) {
         mAction = action;
@@ -109,11 +106,11 @@
      */
     private void updateView() {
         switch (mAction) {
-            case BaseActivity.State.ACTION_OPEN_TREE:
+            case State.ACTION_OPEN_TREE:
                 mPick.setText(R.string.button_select);
                 mCancel.setVisibility(View.GONE);
                 break;
-            case BaseActivity.State.ACTION_OPEN_COPY_DESTINATION:
+            case State.ACTION_OPEN_COPY_DESTINATION:
                 mPick.setText(R.string.button_copy);
                 mCancel.setVisibility(View.VISIBLE);
                 break;
@@ -123,7 +120,7 @@
         }
 
         if (mPickTarget != null && (
-                mAction == BaseActivity.State.ACTION_OPEN_TREE ||
+                mAction == State.ACTION_OPEN_TREE ||
                 mPickTarget.isCreateSupported())) {
             mContainer.setVisibility(View.VISIBLE);
         } else {
diff --git a/packages/DocumentsUI/src/com/android/documentsui/QuickViewIntentBuilder.java b/packages/DocumentsUI/src/com/android/documentsui/QuickViewIntentBuilder.java
index 4685c41..607cb95 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/QuickViewIntentBuilder.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/QuickViewIntentBuilder.java
@@ -16,6 +16,8 @@
 
 package com.android.documentsui;
 
+import static com.android.documentsui.Shared.DEBUG;
+import static com.android.documentsui.Shared.TAG;
 import static com.android.documentsui.model.DocumentInfo.getCursorString;
 
 import android.content.ClipData;
@@ -38,9 +40,6 @@
  */
 final class QuickViewIntentBuilder {
 
-    private static final String TAG = "QvIntentBuilder";
-    private static final boolean DEBUG = false;
-
     private final DocumentInfo mDocument;
     private final DocumentContext mContext;
 
diff --git a/packages/DocumentsUI/src/com/android/documentsui/RecentLoader.java b/packages/DocumentsUI/src/com/android/documentsui/RecentLoader.java
index 1a7095a..c2b64fb 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/RecentLoader.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/RecentLoader.java
@@ -16,8 +16,9 @@
 
 package com.android.documentsui;
 
-import static com.android.documentsui.BaseActivity.State.SORT_ORDER_LAST_MODIFIED;
+import static com.android.documentsui.Shared.DEBUG;
 import static com.android.documentsui.Shared.TAG;
+import static com.android.documentsui.State.SORT_ORDER_LAST_MODIFIED;
 
 import android.app.ActivityManager;
 import android.content.AsyncTaskLoader;
@@ -34,7 +35,6 @@
 import android.text.format.DateUtils;
 import android.util.Log;
 
-import com.android.documentsui.BaseActivity.State;
 import com.android.documentsui.model.RootInfo;
 
 import com.google.common.util.concurrent.AbstractFuture;
@@ -53,8 +53,6 @@
 import java.util.concurrent.TimeUnit;
 
 public class RecentLoader extends AsyncTaskLoader<DirectoryResult> {
-    private static final boolean DEBUG = false;
-
     // TODO: clean up cursor ownership so background thread doesn't traverse
     // previously returned cursors for filtering/sorting; this currently races
     // with the UI thread.
diff --git a/packages/DocumentsUI/src/com/android/documentsui/RecentsCreateFragment.java b/packages/DocumentsUI/src/com/android/documentsui/RecentsCreateFragment.java
index 662822e..cf682fa 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/RecentsCreateFragment.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/RecentsCreateFragment.java
@@ -30,22 +30,21 @@
 import android.net.Uri;
 import android.os.Bundle;
 import android.os.CancellationSignal;
+import android.support.annotation.Nullable;
+import android.support.v7.widget.LinearLayoutManager;
+import android.support.v7.widget.RecyclerView;
 import android.text.Spannable;
 import android.text.SpannableStringBuilder;
 import android.text.TextUtils.TruncateAt;
 import android.text.style.ImageSpan;
 import android.util.Log;
 import android.view.LayoutInflater;
+import android.view.MotionEvent;
 import android.view.View;
 import android.view.ViewGroup;
-import android.widget.AdapterView;
-import android.widget.AdapterView.OnItemClickListener;
-import android.widget.BaseAdapter;
 import android.widget.ImageView;
-import android.widget.ListView;
 import android.widget.TextView;
 
-import com.android.documentsui.BaseActivity.State;
 import com.android.documentsui.RecentsProvider.RecentColumns;
 import com.android.documentsui.model.DocumentStack;
 import com.android.documentsui.model.DurableUtils;
@@ -64,8 +63,7 @@
 public class RecentsCreateFragment extends Fragment {
 
     private View mEmptyView;
-    private ListView mListView;
-
+    private RecyclerView mRecView;
     private DocumentStackAdapter mAdapter;
     private LoaderCallbacks<List<DocumentStack>> mCallbacks;
 
@@ -85,13 +83,14 @@
 
         final View view = inflater.inflate(R.layout.fragment_directory, container, false);
 
+        mRecView = (RecyclerView) view.findViewById(R.id.recyclerView);
+        mRecView.setLayoutManager(new LinearLayoutManager(getContext()));
+        mRecView.addOnItemTouchListener(mItemListener);
+
         mEmptyView = view.findViewById(android.R.id.empty);
 
-        mListView = (ListView) view.findViewById(R.id.list);
-        mListView.setOnItemClickListener(mItemListener);
-
         mAdapter = new DocumentStackAdapter();
-        mListView.setAdapter(mAdapter);
+        mRecView.setAdapter(mAdapter);
 
         final RootsCache roots = DocumentsApplication.getRootsCache(context);
         final State state = ((BaseActivity) getActivity()).getDisplayState();
@@ -105,7 +104,7 @@
             @Override
             public void onLoadFinished(
                     Loader<List<DocumentStack>> loader, List<DocumentStack> data) {
-                mAdapter.swapStacks(data);
+                mAdapter.update(data);
 
                 // When launched into empty recents, show drawer
                 if (mAdapter.isEmpty() && !state.stackTouched &&
@@ -116,7 +115,7 @@
 
             @Override
             public void onLoaderReset(Loader<List<DocumentStack>> loader) {
-                mAdapter.swapStacks(null);
+                mAdapter.update(null);
             }
         };
 
@@ -135,13 +134,24 @@
         getLoaderManager().destroyLoader(LOADER_RECENTS);
     }
 
-    private OnItemClickListener mItemListener = new OnItemClickListener() {
-        @Override
-        public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
-            final DocumentStack stack = mAdapter.getItem(position);
-            ((BaseActivity) getActivity()).onStackPicked(stack);
-        }
-    };
+    private RecyclerView.OnItemTouchListener mItemListener =
+            new RecyclerView.OnItemTouchListener() {
+                @Override
+                public boolean onInterceptTouchEvent(RecyclerView rv, MotionEvent e) {
+                    Events.MotionInputEvent event = new Events.MotionInputEvent(e, mRecView);
+                    if (event.isOverItem() && event.isActionUp()) {
+                        final DocumentStack stack = mAdapter.getItem(event.getItemPosition());
+                        ((BaseActivity) getActivity()).onStackPicked(stack);
+                        return true;
+                    }
+                    return false;
+                }
+
+                @Override
+                public void onTouchEvent(RecyclerView rv, MotionEvent e) {}
+                @Override
+                public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) {}
+            };
 
     public static class RecentsCreateLoader extends UriDerivativeLoader<Uri, List<DocumentStack>> {
         private final RootsCache mRoots;
@@ -187,14 +197,32 @@
         }
     }
 
-    private class DocumentStackAdapter extends BaseAdapter {
-        private List<DocumentStack> mStacks;
+    private static final class StackHolder extends RecyclerView.ViewHolder {
+        public View view;
+        public StackHolder(View view) {
+            super(view);
+            this.view = view;
+        }
+    }
 
-        public DocumentStackAdapter() {
+    private class DocumentStackAdapter extends RecyclerView.Adapter<StackHolder> {
+        @Nullable private List<DocumentStack> mItems;
+
+        DocumentStack getItem(int position) {
+            return mItems.get(position);
         }
 
-        public void swapStacks(List<DocumentStack> stacks) {
-            mStacks = stacks;
+        @Override
+        public int getItemCount() {
+            return mItems == null ? 0 : mItems.size();
+        }
+
+        boolean isEmpty() {
+            return mItems == null ? true : mItems.isEmpty();
+        }
+
+        void update(@Nullable List<DocumentStack> items) {
+            mItems = items;
 
             if (isEmpty()) {
                 mEmptyView.setVisibility(View.VISIBLE);
@@ -206,17 +234,22 @@
         }
 
         @Override
-        public View getView(int position, View convertView, ViewGroup parent) {
-            final Context context = parent.getContext();
+        public StackHolder onCreateViewHolder(ViewGroup parent, int viewType) {
+          final Context context = parent.getContext();
 
-            if (convertView == null) {
-                final LayoutInflater inflater = LayoutInflater.from(context);
-                convertView = inflater.inflate(R.layout.item_doc_list, parent, false);
-            }
+          final LayoutInflater inflater = LayoutInflater.from(context);
+          return new StackHolder(
+                  (View) inflater.inflate(R.layout.item_doc_list, parent, false));
+        }
 
-            final ImageView iconMime = (ImageView) convertView.findViewById(R.id.icon_mime);
-            final TextView title = (TextView) convertView.findViewById(android.R.id.title);
-            final View line2 = convertView.findViewById(R.id.line2);
+        @Override
+        public void onBindViewHolder(StackHolder holder, int position) {
+            Context context = getContext();
+            View view = holder.view;
+
+            final ImageView iconMime = (ImageView) view.findViewById(R.id.icon_mime);
+            final TextView title = (TextView) view.findViewById(android.R.id.title);
+            final View line2 = view.findViewById(R.id.line2);
 
             final DocumentStack stack = getItem(position);
             iconMime.setImageDrawable(stack.root.loadIcon(context));
@@ -234,23 +267,6 @@
             title.setEllipsize(TruncateAt.MIDDLE);
 
             if (line2 != null) line2.setVisibility(View.GONE);
-
-            return convertView;
-        }
-
-        @Override
-        public int getCount() {
-            return mStacks != null ? mStacks.size() : 0;
-        }
-
-        @Override
-        public DocumentStack getItem(int position) {
-            return mStacks.get(position);
-        }
-
-        @Override
-        public long getItemId(int position) {
-            return getItem(position).hashCode();
         }
     }
 
diff --git a/packages/DocumentsUI/src/com/android/documentsui/RecentsProvider.java b/packages/DocumentsUI/src/com/android/documentsui/RecentsProvider.java
index f6e4349..82eb732 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/RecentsProvider.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/RecentsProvider.java
@@ -39,6 +39,7 @@
 import com.android.documentsui.model.DocumentStack;
 import com.android.documentsui.model.DurableUtils;
 import com.android.internal.util.Predicate;
+
 import com.google.android.collect.Sets;
 
 import libcore.io.IoUtils;
diff --git a/packages/DocumentsUI/src/com/android/documentsui/RootsCache.java b/packages/DocumentsUI/src/com/android/documentsui/RootsCache.java
index 05f7d8d..de35cef 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/RootsCache.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/RootsCache.java
@@ -16,6 +16,7 @@
 
 package com.android.documentsui;
 
+import static com.android.documentsui.Shared.DEBUG;
 import static com.android.documentsui.Shared.TAG;
 
 import android.content.ContentProviderClient;
@@ -34,12 +35,11 @@
 import android.os.SystemClock;
 import android.provider.DocumentsContract;
 import android.provider.DocumentsContract.Root;
+import android.support.annotation.VisibleForTesting;
 import android.util.Log;
 
-import com.android.documentsui.BaseActivity.State;
 import com.android.documentsui.model.RootInfo;
 import com.android.internal.annotations.GuardedBy;
-import android.support.annotation.VisibleForTesting;
 
 import com.google.common.collect.ArrayListMultimap;
 import com.google.common.collect.Multimap;
@@ -58,8 +58,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 +89,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 +146,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 +197,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 +212,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 +222,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 +240,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 +369,13 @@
             // 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 ||
-                 state.action != State.ACTION_OPEN_TREE ||
-                 state.action != State.ACTION_OPEN_COPY_DESTINATION) && empty) continue;
+
+            // Only show empty roots when creating, or in browse mode.
+            if (empty && (state.action == State.ACTION_OPEN
+                    || state.action == State.ACTION_GET_CONTENT)) {
+                if (DEBUG) Log.i(TAG, "Skipping empty root: " + root);
+                continue;
+            }
 
             // Only include roots that serve requested content
             final boolean overlap =
@@ -385,7 +387,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/RootsFragment.java b/packages/DocumentsUI/src/com/android/documentsui/RootsFragment.java
index c02184b..c98da47 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/RootsFragment.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/RootsFragment.java
@@ -41,7 +41,6 @@
 import android.widget.ListView;
 import android.widget.TextView;
 
-import com.android.documentsui.BaseActivity.State;
 import com.android.documentsui.model.DocumentInfo;
 import com.android.documentsui.model.RootInfo;
 
diff --git a/packages/DocumentsUI/src/com/android/documentsui/RootsLoader.java b/packages/DocumentsUI/src/com/android/documentsui/RootsLoader.java
index 49651b4..c81377a 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/RootsLoader.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/RootsLoader.java
@@ -19,7 +19,6 @@
 import android.content.AsyncTaskLoader;
 import android.content.Context;
 
-import com.android.documentsui.BaseActivity.State;
 import com.android.documentsui.model.RootInfo;
 
 import java.util.Collection;
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/DocumentsUI/src/com/android/documentsui/SortingCursorWrapper.java b/packages/DocumentsUI/src/com/android/documentsui/SortingCursorWrapper.java
index 3ec3d1c..6698ff1 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/SortingCursorWrapper.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/SortingCursorWrapper.java
@@ -16,9 +16,9 @@
 
 package com.android.documentsui;
 
-import static com.android.documentsui.BaseActivity.State.SORT_ORDER_DISPLAY_NAME;
-import static com.android.documentsui.BaseActivity.State.SORT_ORDER_LAST_MODIFIED;
-import static com.android.documentsui.BaseActivity.State.SORT_ORDER_SIZE;
+import static com.android.documentsui.State.SORT_ORDER_DISPLAY_NAME;
+import static com.android.documentsui.State.SORT_ORDER_LAST_MODIFIED;
+import static com.android.documentsui.State.SORT_ORDER_SIZE;
 import static com.android.documentsui.model.DocumentInfo.getCursorLong;
 import static com.android.documentsui.model.DocumentInfo.getCursorString;
 
diff --git a/packages/DocumentsUI/src/com/android/documentsui/State.java b/packages/DocumentsUI/src/com/android/documentsui/State.java
new file mode 100644
index 0000000..4306a0e
--- /dev/null
+++ b/packages/DocumentsUI/src/com/android/documentsui/State.java
@@ -0,0 +1,153 @@
+/*
+ * Copyright (C) 2013 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.documentsui;
+
+import android.content.Intent;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.SparseArray;
+
+import com.android.documentsui.model.DocumentInfo;
+import com.android.documentsui.model.DocumentStack;
+import com.android.documentsui.model.DurableUtils;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+
+public class State implements android.os.Parcelable {
+    public int action;
+    public String[] acceptMimes;
+
+    /** Explicit user choice */
+    public int userMode = MODE_UNKNOWN;
+    /** Derived after loader */
+    public int derivedMode = MODE_LIST;
+
+    /** Explicit user choice */
+    public int userSortOrder = SORT_ORDER_UNKNOWN;
+    /** Derived after loader */
+    public int derivedSortOrder = SORT_ORDER_DISPLAY_NAME;
+
+    public boolean allowMultiple;
+    public boolean forceSize;
+    public boolean showSize;
+    public boolean localOnly;
+    public boolean forceAdvanced;
+    public boolean showAdvanced;
+    public boolean stackTouched;
+    public boolean restored;
+    public boolean directoryCopy;
+    /** Transfer mode for file copy/move operations. */
+    public int transferMode;
+
+    /** Current user navigation stack; empty implies recents. */
+    public DocumentStack stack = new DocumentStack();
+    /** Currently active search, overriding any stack. */
+    public String currentSearch;
+
+    /** Instance state for every shown directory */
+    public HashMap<String, SparseArray<Parcelable>> dirState = new HashMap<>();
+
+    /** Currently copying file */
+    public List<DocumentInfo> selectedDocumentsForCopy = new ArrayList<DocumentInfo>();
+
+    /** Name of the package that started DocsUI */
+    public List<String> excludedAuthorities = new ArrayList<>();
+
+    public static final int ACTION_OPEN = 1;
+    public static final int ACTION_CREATE = 2;
+    public static final int ACTION_GET_CONTENT = 3;
+    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_OPEN_COPY_DESTINATION = 8;
+
+    public static final int MODE_UNKNOWN = 0;
+    public static final int MODE_LIST = 1;
+    public static final int MODE_GRID = 2;
+
+    public static final int SORT_ORDER_UNKNOWN = 0;
+    public static final int SORT_ORDER_DISPLAY_NAME = 1;
+    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;
+    }
+
+    @Override
+    public void writeToParcel(Parcel out, int flags) {
+        out.writeInt(action);
+        out.writeInt(userMode);
+        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);
+        out.writeInt(showAdvanced ? 1 : 0);
+        out.writeInt(stackTouched ? 1 : 0);
+        out.writeInt(restored ? 1 : 0);
+        DurableUtils.writeToParcel(out, stack);
+        out.writeString(currentSearch);
+        out.writeMap(dirState);
+        out.writeList(selectedDocumentsForCopy);
+        out.writeList(excludedAuthorities);
+    }
+
+    public static final Creator<State> CREATOR = new Creator<State>() {
+        @Override
+        public State createFromParcel(Parcel in) {
+            final State state = new State();
+            state.action = in.readInt();
+            state.userMode = in.readInt();
+            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;
+            state.showAdvanced = in.readInt() != 0;
+            state.stackTouched = in.readInt() != 0;
+            state.restored = in.readInt() != 0;
+            DurableUtils.readFromParcel(in, state.stack);
+            state.currentSearch = in.readString();
+            in.readMap(state.dirState, null);
+            in.readList(state.selectedDocumentsForCopy, null);
+            in.readList(state.excludedAuthorities, null);
+            return state;
+        }
+
+        @Override
+        public State[] newArray(int size) {
+            return new State[size];
+        }
+    };
+}
\ No newline at end of file
diff --git a/packages/DocumentsUI/tests/src/com/android/documentsui/DirectoryFragmentModelTest.java b/packages/DocumentsUI/tests/src/com/android/documentsui/DirectoryFragmentModelTest.java
index 5505f35..1895a6e 100644
--- a/packages/DocumentsUI/tests/src/com/android/documentsui/DirectoryFragmentModelTest.java
+++ b/packages/DocumentsUI/tests/src/com/android/documentsui/DirectoryFragmentModelTest.java
@@ -22,9 +22,12 @@
 import android.database.Cursor;
 import android.database.MatrixCursor;
 import android.provider.DocumentsContract.Document;
+import android.support.v7.widget.RecyclerView.ViewHolder;
+import android.support.v7.widget.RecyclerView;
 import android.test.AndroidTestCase;
 import android.test.MoreAsserts;
 import android.test.mock.MockContentResolver;
+import android.view.ViewGroup;
 
 import com.android.documentsui.DirectoryFragment.Model;
 import com.android.documentsui.MultiSelectManager.Selection;
@@ -57,7 +60,8 @@
         DirectoryResult r = new DirectoryResult();
         r.cursor = cursor;
 
-        model = new Model(mContext, null);
+        // Instantiate the model with a dummy view adapter and listener that (for now) do nothing.
+        model = new Model(mContext, null, new DummyAdapter());
         model.addUpdateListener(new DummyListener());
         model.update(r);
     }
@@ -160,11 +164,16 @@
         return model.getDocuments(sel);
     }
 
-    private static class DummyListener implements Model.UpdateListener {
+    private static class DummyListener extends Model.UpdateListener {
         public void onModelUpdate(Model model) {}
         public void onModelUpdateFailed(Exception e) {}
-        public void notifyItemRemoved(int position) {}
-        public void notifyItemInserted(int position) {}
     }
 
+    private static class DummyAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
+        public int getItemCount() { return 0; }
+        public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {}
+        public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
+            return null;
+        }
+    }
 }
diff --git a/packages/DocumentsUI/tests/src/com/android/documentsui/RootsCacheTest.java b/packages/DocumentsUI/tests/src/com/android/documentsui/RootsCacheTest.java
index 1325706..7d3498e 100644
--- a/packages/DocumentsUI/tests/src/com/android/documentsui/RootsCacheTest.java
+++ b/packages/DocumentsUI/tests/src/com/android/documentsui/RootsCacheTest.java
@@ -19,7 +19,6 @@
 import android.test.AndroidTestCase;
 import android.test.suitebuilder.annotation.SmallTest;
 
-import com.android.documentsui.BaseActivity.State;
 import com.android.documentsui.model.RootInfo;
 
 import com.google.common.collect.Lists;
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 47d8e28..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;
@@ -129,6 +128,7 @@
     private static final int MSG_REPORT_EMERGENCY_CALL_ACTION = 318;
     private static final int MSG_STARTED_WAKING_UP = 319;
     private static final int MSG_FINISHED_GOING_TO_SLEEP = 320;
+    private static final int MSG_STARTED_GOING_TO_SLEEP = 321;
     private static final int MSG_KEYGUARD_BOUNCER_CHANGED = 322;
     private static final int MSG_FACE_UNLOCK_STATE_CHANGED = 327;
     private static final int MSG_SIM_SUBSCRIPTION_INFO_CHANGED = 328;
@@ -170,6 +170,7 @@
      * until the Keyguard has been dismissed.
      */
     private boolean mFingerprintAlreadyAuthenticated;
+    private boolean mGoingToSleep;
     private boolean mBouncer;
     private boolean mBootCompleted;
 
@@ -231,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;
@@ -249,6 +247,9 @@
                 case MSG_REPORT_EMERGENCY_CALL_ACTION:
                     handleReportEmergencyCallAction();
                     break;
+                case MSG_STARTED_GOING_TO_SLEEP:
+                    handleStartedGoingToSleep(msg.arg1);
+                    break;
                 case MSG_FINISHED_GOING_TO_SLEEP:
                     handleFinishedGoingToSleep(msg.arg1);
                     break;
@@ -884,16 +885,29 @@
         }
     }
 
-    protected void handleFinishedGoingToSleep(int arg1) {
+    protected void handleStartedGoingToSleep(int arg1) {
         clearFingerprintRecognized();
         final int count = mCallbacks.size();
         for (int i = 0; i < count; i++) {
             KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
             if (cb != null) {
+                cb.onStartedGoingToSleep(arg1);
+            }
+        }
+        mGoingToSleep = true;
+        mFingerprintAlreadyAuthenticated = false;
+        updateFingerprintListeningState();
+    }
+
+    protected void handleFinishedGoingToSleep(int arg1) {
+        mGoingToSleep = false;
+        final int count = mCallbacks.size();
+        for (int i = 0; i < count; i++) {
+            KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
+            if (cb != null) {
                 cb.onFinishedGoingToSleep(arg1);
             }
         }
-        mFingerprintAlreadyAuthenticated = false;
         updateFingerprintListeningState();
     }
 
@@ -1032,8 +1046,9 @@
     }
 
     private boolean shouldListenForFingerprint() {
-        return (mKeyguardIsVisible || !mDeviceInteractive || mBouncer) && !mSwitchingUser
-                && !mFingerprintAlreadyAuthenticated && !isFingerprintDisabled(getCurrentUser());
+        return (mKeyguardIsVisible || !mDeviceInteractive || mBouncer || mGoingToSleep)
+                && !mSwitchingUser && !mFingerprintAlreadyAuthenticated
+                && !isFingerprintDisabled(getCurrentUser());
     }
 
     private void startListeningForFingerprint() {
@@ -1325,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();
@@ -1458,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();
     }
@@ -1605,6 +1614,10 @@
         mHandler.sendEmptyMessage(MSG_STARTED_WAKING_UP);
     }
 
+    public void dispatchStartedGoingToSleep(int why) {
+        mHandler.sendMessage(mHandler.obtainMessage(MSG_STARTED_GOING_TO_SLEEP, why, 0));
+    }
+
     public void dispatchFinishedGoingToSleep(int why) {
         synchronized(this) {
             mDeviceInteractive = false;
@@ -1630,6 +1643,10 @@
         return mDeviceInteractive;
     }
 
+    public boolean isGoingToSleep() {
+        return mGoingToSleep;
+    }
+
     /**
      * Find the next SubscriptionId for a SIM in the given state, favoring lower slot numbers first.
      * @param state
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java b/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java
index 15ffe9f..bd6c51c 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java
@@ -153,6 +153,12 @@
     public void onStartedWakingUp() { }
 
     /**
+     * Called when the device has started going to sleep.
+     * @param why see {@link #onFinishedGoingToSleep(int)}
+     */
+    public void onStartedGoingToSleep(int why) { }
+
+    /**
      * Called when the device has finished going to sleep.
      * @param why either {@link WindowManagerPolicy#OFF_BECAUSE_OF_ADMIN},
      * {@link WindowManagerPolicy#OFF_BECAUSE_OF_USER}, or
diff --git a/packages/PrintSpooler/jni/com_android_printspooler_util_BitmapSerializeUtils.cpp b/packages/PrintSpooler/jni/com_android_printspooler_util_BitmapSerializeUtils.cpp
index b5d9138..1530a02 100644
--- a/packages/PrintSpooler/jni/com_android_printspooler_util_BitmapSerializeUtils.cpp
+++ b/packages/PrintSpooler/jni/com_android_printspooler_util_BitmapSerializeUtils.cpp
@@ -166,7 +166,7 @@
     }
 }
 
-static JNINativeMethod sMethods[] = {
+static const JNINativeMethod sMethods[] = {
     {"nativeReadBitmapPixels", "(Landroid/graphics/Bitmap;I)V", (void *) readBitmapPixels},
     {"nativeWriteBitmapPixels", "(Landroid/graphics/Bitmap;I)V", (void *) writeBitmapPixels},
 };
diff --git a/packages/PrintSpooler/res/values-uz-rUZ/strings.xml b/packages/PrintSpooler/res/values-uz-rUZ/strings.xml
index 8e41a34..f88eca5 100644
--- a/packages/PrintSpooler/res/values-uz-rUZ/strings.xml
+++ b/packages/PrintSpooler/res/values-uz-rUZ/strings.xml
@@ -46,7 +46,7 @@
     <string name="savetopdf_button" msgid="2976186791686924743">"PDF sifatida saqlash"</string>
     <string name="print_options_expanded" msgid="6944679157471691859">"Chop qilish tanlamalari yoyildi"</string>
     <string name="print_options_collapsed" msgid="7455930445670414332">"Chop qilish tanlamalari yig‘ildi"</string>
-    <string name="search" msgid="5421724265322228497">"Izlash"</string>
+    <string name="search" msgid="5421724265322228497">"Qidirish"</string>
     <string name="all_printers_label" msgid="3178848870161526399">"Barcha printerlar"</string>
     <string name="add_print_service_label" msgid="5356702546188981940">"Xizmat qo‘shish"</string>
     <string name="print_search_box_shown_utterance" msgid="7967404953901376090">"Izlash oynasi ko‘rsatildi"</string>
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/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index e4fc075..8fc7ad0 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -205,7 +205,8 @@
                   android:stateNotNeeded="true"
                   android:resumeWhilePausing="true"
                   android:screenOrientation="behind"
-                  android:theme="@style/config_recents_activity_theme">
+                  android:resizeableActivity="true"
+                  android:theme="@style/RecentsTheme.Wallpaper">
             <intent-filter>
                 <action android:name="com.android.systemui.recents.TOGGLE_RECENTS" />
             </intent-filter>
diff --git a/packages/SystemUI/res/drawable/vector_drawable_place_dock_bottom.xml b/packages/SystemUI/res/drawable/vector_drawable_place_dock_bottom.xml
new file mode 100644
index 0000000..f11b690
--- /dev/null
+++ b/packages/SystemUI/res/drawable/vector_drawable_place_dock_bottom.xml
@@ -0,0 +1,24 @@
+<!--
+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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24.0dp"
+        android:height="24.0dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF0000FF"
+        android:pathData="M0.0,0.0l0.0,24.0l24.0,0.0L24.0,0.0L0.0,0.0zM4.0,10.0l16.0,0.0l0.0,10.0L4.0,20.0L4.0,10.0z"/>
+</vector>
diff --git a/packages/SystemUI/res/drawable/vector_drawable_place_dock_left.xml b/packages/SystemUI/res/drawable/vector_drawable_place_dock_left.xml
new file mode 100644
index 0000000..79ade42
--- /dev/null
+++ b/packages/SystemUI/res/drawable/vector_drawable_place_dock_left.xml
@@ -0,0 +1,24 @@
+<!--
+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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24.0dp"
+        android:height="24.0dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF0000FF"
+        android:pathData="M24.0,0.0L0.0,0.0l0.0,24.0l24.0,0.0L24.0,0.0zM14.0,4.0l0.0,16.0L4.0,20.0L4.0,4.0L14.0,4.0z"/>
+</vector>
diff --git a/packages/SystemUI/res/drawable/vector_drawable_place_dock_right.xml b/packages/SystemUI/res/drawable/vector_drawable_place_dock_right.xml
new file mode 100644
index 0000000..49c2a38
--- /dev/null
+++ b/packages/SystemUI/res/drawable/vector_drawable_place_dock_right.xml
@@ -0,0 +1,24 @@
+<!--
+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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24.0dp"
+        android:height="24.0dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF0000FF"
+        android:pathData="M0.0,24.0l24.0,0.0L24.0,0.0L0.0,0.0L0.0,24.0zM10.0,20.0L10.0,4.0l10.0,0.0l0.0,16.0L10.0,20.0z"/>
+</vector>
diff --git a/packages/SystemUI/res/drawable/vector_drawable_place_dock_top.xml b/packages/SystemUI/res/drawable/vector_drawable_place_dock_top.xml
new file mode 100644
index 0000000..c3abec2
--- /dev/null
+++ b/packages/SystemUI/res/drawable/vector_drawable_place_dock_top.xml
@@ -0,0 +1,24 @@
+<!--
+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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24.0dp"
+        android:height="24.0dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF0000FF"
+        android:pathData="M24.0,24.0L24.0,0.0L0.0,0.0l0.0,24.0L24.0,24.0zM20.0,14.0L4.0,14.0L4.0,4.0l16.0,0.0L20.0,14.0z"/>
+</vector>
diff --git a/packages/SystemUI/res/layout-land/recents_task_resize_dialog.xml b/packages/SystemUI/res/layout-land/recents_task_resize_dialog.xml
index a718d4d..0a4c086 100644
--- a/packages/SystemUI/res/layout-land/recents_task_resize_dialog.xml
+++ b/packages/SystemUI/res/layout-land/recents_task_resize_dialog.xml
@@ -27,6 +27,20 @@
         android:layout_height="wrap_content"
         android:orientation="horizontal">
         <Button
+            android:id="@+id/place_dock_left"
+            android:layout_width="36dp"
+            android:layout_height="36dp"
+            android:layout_weight="1"
+            android:layout_margin="10dp"
+            android:background="@drawable/vector_drawable_place_dock_left" />
+        <Button
+            android:id="@+id/place_dock_right"
+            android:layout_width="36dp"
+            android:layout_height="36dp"
+            android:layout_weight="1"
+            android:layout_margin="10dp"
+            android:background="@drawable/vector_drawable_place_dock_right" />
+        <Button
             android:id="@+id/place_left"
             android:layout_width="36dp"
             android:layout_height="36dp"
diff --git a/packages/SystemUI/res/layout-port/recents_task_resize_dialog.xml b/packages/SystemUI/res/layout-port/recents_task_resize_dialog.xml
index 250f53d..bf5207a 100644
--- a/packages/SystemUI/res/layout-port/recents_task_resize_dialog.xml
+++ b/packages/SystemUI/res/layout-port/recents_task_resize_dialog.xml
@@ -27,6 +27,20 @@
         android:layout_height="wrap_content"
         android:orientation="horizontal">
         <Button
+            android:id="@+id/place_dock_top"
+            android:layout_width="36dp"
+            android:layout_height="36dp"
+            android:layout_weight="1"
+            android:layout_margin="10dp"
+            android:background="@drawable/vector_drawable_place_dock_top" />
+        <Button
+            android:id="@+id/place_dock_bottom"
+            android:layout_width="36dp"
+            android:layout_height="36dp"
+            android:layout_weight="1"
+            android:layout_margin="10dp"
+            android:background="@drawable/vector_drawable_place_dock_bottom" />
+        <Button
             android:id="@+id/place_top"
             android:layout_width="36dp"
             android:layout_height="36dp"
diff --git a/packages/SystemUI/res/layout-sw600dp-land/recents_task_resize_dialog.xml b/packages/SystemUI/res/layout-sw600dp-land/recents_task_resize_dialog.xml
index 26c9b1a..6e92afc 100644
--- a/packages/SystemUI/res/layout-sw600dp-land/recents_task_resize_dialog.xml
+++ b/packages/SystemUI/res/layout-sw600dp-land/recents_task_resize_dialog.xml
@@ -27,6 +27,20 @@
         android:layout_height="wrap_content"
         android:orientation="horizontal">
         <Button
+            android:id="@+id/place_dock_left"
+            android:layout_width="36dp"
+            android:layout_height="36dp"
+            android:layout_weight="1"
+            android:layout_margin="10dp"
+            android:background="@drawable/vector_drawable_place_dock_left" />
+        <Button
+            android:id="@+id/place_dock_right"
+            android:layout_width="36dp"
+            android:layout_height="36dp"
+            android:layout_weight="1"
+            android:layout_margin="10dp"
+            android:background="@drawable/vector_drawable_place_dock_right" />
+        <Button
             android:id="@+id/place_left"
             android:layout_width="36dp"
             android:layout_height="36dp"
diff --git a/packages/SystemUI/res/layout-sw600dp-port/recents_task_resize_dialog.xml b/packages/SystemUI/res/layout-sw600dp-port/recents_task_resize_dialog.xml
index e180daa..faa5f4b 100644
--- a/packages/SystemUI/res/layout-sw600dp-port/recents_task_resize_dialog.xml
+++ b/packages/SystemUI/res/layout-sw600dp-port/recents_task_resize_dialog.xml
@@ -27,6 +27,20 @@
         android:layout_height="wrap_content"
         android:orientation="horizontal">
         <Button
+            android:id="@+id/place_dock_top"
+            android:layout_width="36dp"
+            android:layout_height="36dp"
+            android:layout_weight="1"
+            android:layout_margin="10dp"
+            android:background="@drawable/vector_drawable_place_dock_top" />
+        <Button
+            android:id="@+id/place_dock_bottom"
+            android:layout_width="36dp"
+            android:layout_height="36dp"
+            android:layout_weight="1"
+            android:layout_margin="10dp"
+            android:background="@drawable/vector_drawable_place_dock_bottom" />
+        <Button
             android:id="@+id/place_top"
             android:layout_width="36dp"
             android:layout_height="36dp"
diff --git a/packages/SystemUI/res/layout/recents.xml b/packages/SystemUI/res/layout/recents.xml
index 8140dd6..064d225 100644
--- a/packages/SystemUI/res/layout/recents.xml
+++ b/packages/SystemUI/res/layout/recents.xml
@@ -39,12 +39,6 @@
            android:layout_width="match_parent"
            android:layout_height="match_parent" />
 
-    <!-- Debug Overlay View -->
-    <ViewStub android:id="@+id/debug_overlay_stub"
-           android:layout="@layout/recents_debug_overlay"
-           android:layout_width="match_parent"
-           android:layout_height="match_parent" />
-
     <!-- Nav Bar Scrim View -->
     <ImageView
         android:id="@+id/nav_bar_scrim"
diff --git a/packages/SystemUI/res/layout/recents_debug_overlay.xml b/packages/SystemUI/res/layout/recents_debug_overlay.xml
deleted file mode 100644
index d23495e..0000000
--- a/packages/SystemUI/res/layout/recents_debug_overlay.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2014 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.
--->
-<com.android.systemui.recents.views.DebugOverlayView
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:layout_width="match_parent" 
-    android:layout_height="match_parent"
-    android:focusable="false">
-    <SeekBar
-        android:id="@+id/debug_seek_bar_1"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:layout_gravity="top"
-        android:layout_marginTop="25dp" />
-    <SeekBar
-        android:id="@+id/debug_seek_bar_2"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:layout_gravity="top"
-        android:layout_marginTop="50dp" />
-</com.android.systemui.recents.views.DebugOverlayView>
-
-
diff --git a/packages/SystemUI/res/values-ca/strings.xml b/packages/SystemUI/res/values-ca/strings.xml
index cc37162..8b2951a 100644
--- a/packages/SystemUI/res/values-ca/strings.xml
+++ b/packages/SystemUI/res/values-ca/strings.xml
@@ -406,7 +406,7 @@
     <string name="volumeui_notification_title" msgid="4906770126345910955">"<xliff:g id="APP_NAME">%1$s</xliff:g> és el diàleg de volum"</string>
     <string name="volumeui_notification_text" msgid="1826889705095768656">"Toca per restaurar l\'original."</string>
     <string name="managed_profile_foreground_toast" msgid="5421487114739245972">"Estàs utilitzant el perfil professional"</string>
-    <string name="system_ui_tuner" msgid="708224127392452018">"Configurador de la IU del sistema"</string>
+    <string name="system_ui_tuner" msgid="708224127392452018">"Personalitzador d\'interfície d\'usuari"</string>
     <string name="show_battery_percentage" msgid="5444136600512968798">"Mostra el percentatge de la bateria inserit"</string>
     <string name="show_battery_percentage_summary" msgid="3215025775576786037">"Mostra el percentatge del nivell de bateria dins de la icona de la barra d\'estat quan no s\'estigui carregant"</string>
     <string name="quick_settings" msgid="10042998191725428">"Configuració ràpida"</string>
@@ -428,12 +428,12 @@
     <string name="accessibility_status_bar_hotspot" msgid="4099381329956402865">"Zona Wi-Fi"</string>
     <string name="accessibility_managed_profile" msgid="6613641363112584120">"Perfil professional"</string>
     <string name="tuner_warning_title" msgid="7094689930793031682">"Diversió per a uns quants, però no per a tothom"</string>
-    <string name="tuner_warning" msgid="8730648121973575701">"El Configurador de la IU del sistema presenta opcions addicionals per canviar i personalitzar la interfície d\'usuari d\'Android. És possible que aquestes funcions experimentals canviïn, deixin de funcionar o desapareguin en versions futures. Continua amb precaució."</string>
+    <string name="tuner_warning" msgid="8730648121973575701">"El Personalitzador d\'interfície d\'usuari presenta opcions addicionals per canviar i personalitzar la interfície d\'usuari d\'Android. És possible que aquestes funcions experimentals canviïn, deixin de funcionar o desapareguin en versions futures. Continua amb precaució."</string>
     <string name="tuner_persistent_warning" msgid="8597333795565621795">"És possible que aquestes funcions experimentals canviïn, deixin de funcionar o desapareguin en versions futures. Continua amb precaució."</string>
     <string name="got_it" msgid="2239653834387972602">"D\'acord"</string>
-    <string name="tuner_toast" msgid="603429811084428439">"Enhorabona! El Configurador de la IU del sistema s\'ha afegit a Configuració."</string>
+    <string name="tuner_toast" msgid="603429811084428439">"Enhorabona! El Personalitzador d\'interfície d\'usuari s\'ha afegit a Configuració."</string>
     <string name="remove_from_settings" msgid="8389591916603406378">"Treu de Configuració"</string>
-    <string name="remove_from_settings_prompt" msgid="6069085993355887748">"Vols treure el Configurador de la UI del sistema de Configuració i deixar d\'utilitzar-ne totes les funcions?"</string>
+    <string name="remove_from_settings_prompt" msgid="6069085993355887748">"Vols suprimir el Personalitzador d\'interfície d\'usuari de Configuració i deixar d\'utilitzar-ne totes les funcions?"</string>
     <string name="activity_not_found" msgid="348423244327799974">"L\'aplicació no està instal·lada al dispositiu"</string>
     <string name="clock_seconds" msgid="7689554147579179507">"Mostra els segons del rellotge"</string>
     <string name="clock_seconds_desc" msgid="6282693067130470675">"Mostra els segons del rellotge a la barra d\'estat. Això pot afectar la durada de la bateria."</string>
diff --git a/packages/SystemUI/res/values-da/strings.xml b/packages/SystemUI/res/values-da/strings.xml
index 6f49187..91d975b 100644
--- a/packages/SystemUI/res/values-da/strings.xml
+++ b/packages/SystemUI/res/values-da/strings.xml
@@ -183,12 +183,12 @@
     <string name="accessibility_quick_settings_airplane_on" msgid="6406141469157599296">"Flytilstand er slået til."</string>
     <string name="accessibility_quick_settings_airplane_changed_off" msgid="66846307818850664">"Flytilstand er slået fra."</string>
     <string name="accessibility_quick_settings_airplane_changed_on" msgid="8983005603505087728">"Flytilstand er slået til."</string>
-    <string name="accessibility_quick_settings_dnd_priority_on" msgid="1448402297221249355">"\"Vil ikke forstyrres\" er slået til, kun prioritet."</string>
-    <string name="accessibility_quick_settings_dnd_none_on" msgid="6882582132662613537">"\"Vil ikke forstyrres\" er slået til, total stilhed."</string>
-    <string name="accessibility_quick_settings_dnd_alarms_on" msgid="9152834845587554157">"\"Vil ikke forstyrres\" er slået til, kun alarmer."</string>
-    <string name="accessibility_quick_settings_dnd_off" msgid="2371832603753738581">"\"Vil ikke forstyrres\" er slået fra."</string>
-    <string name="accessibility_quick_settings_dnd_changed_off" msgid="898107593453022935">"\"Vil ikke forstyrres\" er slået fra."</string>
-    <string name="accessibility_quick_settings_dnd_changed_on" msgid="4483780856613561039">"\"Vil ikke forstyrres\" er slået til."</string>
+    <string name="accessibility_quick_settings_dnd_priority_on" msgid="1448402297221249355">"\"Forstyr ikke\" er slået til, kun prioritet."</string>
+    <string name="accessibility_quick_settings_dnd_none_on" msgid="6882582132662613537">"\"Forstyr ikke\" er slået til, total stilhed."</string>
+    <string name="accessibility_quick_settings_dnd_alarms_on" msgid="9152834845587554157">"\"Forstyr ikke\" er slået til, kun alarmer."</string>
+    <string name="accessibility_quick_settings_dnd_off" msgid="2371832603753738581">"\"Forstyr ikke\" er slået fra."</string>
+    <string name="accessibility_quick_settings_dnd_changed_off" msgid="898107593453022935">"\"Forstyr ikke\" er slået fra."</string>
+    <string name="accessibility_quick_settings_dnd_changed_on" msgid="4483780856613561039">"\"Forstyr ikke\" er slået til."</string>
     <string name="accessibility_quick_settings_bluetooth_off" msgid="2133631372372064339">"Bluetooth er slået fra."</string>
     <string name="accessibility_quick_settings_bluetooth_on" msgid="7681999166216621838">"Bluetooth er slået til."</string>
     <string name="accessibility_quick_settings_bluetooth_connecting" msgid="6953242966685343855">"Opretter forbindelse til Bluetooth."</string>
@@ -236,7 +236,7 @@
     <string name="dessert_case" msgid="1295161776223959221">"Dessertcase"</string>
     <string name="start_dreams" msgid="7219575858348719790">"Dagdrøm"</string>
     <string name="ethernet_label" msgid="7967563676324087464">"Ethernet"</string>
-    <string name="quick_settings_dnd_label" msgid="8735855737575028208">"Vil ikke forstyrres"</string>
+    <string name="quick_settings_dnd_label" msgid="8735855737575028208">"Forstyr ikke"</string>
     <string name="quick_settings_dnd_priority_label" msgid="483232950670692036">"Kun prioritet"</string>
     <string name="quick_settings_dnd_alarms_label" msgid="2559229444312445858">"Kun Alarmer"</string>
     <string name="quick_settings_dnd_none_label" msgid="5025477807123029478">"Total stilhed"</string>
diff --git a/packages/SystemUI/res/values-en/donottranslate.xml b/packages/SystemUI/res/values-en/donottranslate.xml
new file mode 100644
index 0000000..9f04e1f
--- /dev/null
+++ b/packages/SystemUI/res/values-en/donottranslate.xml
@@ -0,0 +1,23 @@
+<?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
+  -->
+
+<resources>
+    <!-- DO NOT TRANSLATE - temporary hack to show the speed-less label if no translation is available -->
+    <item type="string" name="keyguard_indication_charging_time_fast_if_translated">@string/keyguard_indication_charging_time_fast</item>
+    <!-- DO NOT TRANSLATE - temporary hack to show the speed-less label if no translation is available -->
+    <item type="string" name="keyguard_indication_charging_time_slowly_if_translated">@string/keyguard_indication_charging_time_slowly</item>
+</resources>
\ No newline at end of file
diff --git a/packages/SystemUI/res/values-fa/strings.xml b/packages/SystemUI/res/values-fa/strings.xml
index 7593f46..147cbe4 100644
--- a/packages/SystemUI/res/values-fa/strings.xml
+++ b/packages/SystemUI/res/values-fa/strings.xml
@@ -275,7 +275,7 @@
     <string name="quick_settings_inversion_label" msgid="8790919884718619648">"برگردان رنگ‌ها"</string>
     <string name="quick_settings_color_space_label" msgid="853443689745584770">"حالت تصحیح رنگ"</string>
     <string name="quick_settings_more_settings" msgid="326112621462813682">"تنظیمات بیشتر"</string>
-    <string name="quick_settings_done" msgid="3402999958839153376">"انجام شد"</string>
+    <string name="quick_settings_done" msgid="3402999958839153376">"تمام"</string>
     <string name="quick_settings_connected" msgid="1722253542984847487">"متصل"</string>
     <string name="quick_settings_connecting" msgid="47623027419264404">"در حال اتصال..."</string>
     <string name="quick_settings_tethering_label" msgid="7153452060448575549">"اتصال به اینترنت با تلفن همراه"</string>
diff --git a/packages/SystemUI/res/values-it/strings.xml b/packages/SystemUI/res/values-it/strings.xml
index 8ee5f07..0d06636 100644
--- a/packages/SystemUI/res/values-it/strings.xml
+++ b/packages/SystemUI/res/values-it/strings.xml
@@ -354,7 +354,7 @@
     <string name="user_remove_user_title" msgid="4681256956076895559">"Rimuovere l\'utente?"</string>
     <string name="user_remove_user_message" msgid="1453218013959498039">"Tutte le app e i dati di questo utente verranno eliminati."</string>
     <string name="user_remove_user_remove" msgid="7479275741742178297">"Rimuovi"</string>
-    <string name="battery_saver_notification_title" msgid="237918726750955859">"Risparmio batteria attivo"</string>
+    <string name="battery_saver_notification_title" msgid="237918726750955859">"Risparmio energetico attivo"</string>
     <string name="battery_saver_notification_text" msgid="820318788126672692">"Riduce le prestazioni e i dati in background"</string>
     <string name="battery_saver_notification_action_text" msgid="109158658238110382">"Disattiva risparmio energetico"</string>
     <string name="notification_hidden_text" msgid="1135169301897151909">"Contenuti nascosti"</string>
diff --git a/packages/SystemUI/res/values-ja/strings.xml b/packages/SystemUI/res/values-ja/strings.xml
index a73f4c1..a034f4f 100644
--- a/packages/SystemUI/res/values-ja/strings.xml
+++ b/packages/SystemUI/res/values-ja/strings.xml
@@ -330,7 +330,7 @@
     <string name="accessibility_multi_user_switch_switcher" msgid="7305948938141024937">"ユーザーを切り替える"</string>
     <string name="accessibility_multi_user_switch_switcher_with_current" msgid="8434880595284601601">"ユーザーを切り替える、現在のユーザーは<xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
     <string name="accessibility_multi_user_switch_inactive" msgid="1424081831468083402">"現在のユーザー: <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
-    <string name="accessibility_multi_user_switch_quick_contact" msgid="3020367729287990475">"プロフィールを表示"</string>
+    <string name="accessibility_multi_user_switch_quick_contact" msgid="3020367729287990475">"プロファイルを表示"</string>
     <string name="user_add_user" msgid="5110251524486079492">"ユーザーを追加"</string>
     <string name="user_new_user_name" msgid="426540612051178753">"新しいユーザー"</string>
     <string name="guest_nickname" msgid="8059989128963789678">"ゲスト"</string>
@@ -364,10 +364,10 @@
     <string name="media_projection_action_text" msgid="8470872969457985954">"今すぐ開始"</string>
     <string name="empty_shade_text" msgid="708135716272867002">"通知はありません"</string>
     <string name="device_owned_footer" msgid="3802752663326030053">"端末が監視されている可能性があります"</string>
-    <string name="profile_owned_footer" msgid="8021888108553696069">"プロフィールが監視されている可能性があります"</string>
+    <string name="profile_owned_footer" msgid="8021888108553696069">"プロファイルが監視されている可能性があります"</string>
     <string name="vpn_footer" msgid="2388611096129106812">"ネットワークが監視されている可能性があります"</string>
     <string name="monitoring_title_device_owned" msgid="7121079311903859610">"端末の監視"</string>
-    <string name="monitoring_title_profile_owned" msgid="6790109874733501487">"プロフィールの監視"</string>
+    <string name="monitoring_title_profile_owned" msgid="6790109874733501487">"プロファイルの監視"</string>
     <string name="monitoring_title" msgid="169206259253048106">"ネットワーク監視"</string>
     <string name="disable_vpn" msgid="4435534311510272506">"VPNを無効にする"</string>
     <string name="disconnect_vpn" msgid="1324915059568548655">"VPNを切断"</string>
diff --git a/packages/SystemUI/res/values-land/config.xml b/packages/SystemUI/res/values-land/config.xml
index d608e25..f7e2344 100644
--- a/packages/SystemUI/res/values-land/config.xml
+++ b/packages/SystemUI/res/values-land/config.xml
@@ -20,10 +20,6 @@
 <!-- These resources are around just to allow their values to be customized
      for different hardware and product builds. -->
 <resources>
-    <!-- Whether we're using the tablet-optimized recents interface (we use this
-     value at runtime for some things) -->
-    <integer name="status_bar_recents_bg_gradient_degrees">90</integer>
-
     <!-- The maximum number of rows in the QuickSettings -->
     <integer name="quick_settings_max_rows">2</integer>
 
diff --git a/packages/SystemUI/res/values-land/dimens.xml b/packages/SystemUI/res/values-land/dimens.xml
index a9e7735..2893b99 100644
--- a/packages/SystemUI/res/values-land/dimens.xml
+++ b/packages/SystemUI/res/values-land/dimens.xml
@@ -19,25 +19,6 @@
     <!-- thickness (width) of the navigation bar on phones that require it -->
     <dimen name="navigation_bar_size">@*android:dimen/navigation_bar_width</dimen>
 
-    <!-- Recent Applications parameters -->
-    <!-- How far the thumbnail for a recent app appears from left edge -->
-    <dimen name="status_bar_recents_thumbnail_left_margin">0dp</dimen>
-    <!-- How far the thumbnail for a recent app appears from top edge -->
-    <dimen name="status_bar_recents_thumbnail_top_margin">28dp</dimen>
-    <!-- Padding for text descriptions -->
-    <dimen name="status_bar_recents_text_description_padding">8dp</dimen>
-    <!-- Width of application label text -->
-    <dimen name="status_bar_recents_app_label_width">156dip</dimen>
-    <!-- Left margin of application label text -->
-    <dimen name="status_bar_recents_app_label_left_margin">12dip</dimen>
-    <!-- Margin between recents container and glow on the right -->
-    <dimen name="status_bar_recents_right_glow_margin">0dip</dimen>
-    <!-- Padding between recents items -->
-    <dimen name="status_bar_recents_item_padding">2dip</dimen>
-    <!-- Where to place the app icon over the thumbnail -->
-    <dimen name="status_bar_recents_app_icon_left_margin">8dp</dimen>
-    <dimen name="status_bar_recents_app_icon_top_margin">8dp</dimen>
-
     <!-- The side padding for the task stack as a percentage of the width. -->
     <item name="recents_stack_width_padding_percentage" format="float" type="dimen">0.26</item>
 
diff --git a/packages/SystemUI/res/values-sw600dp-land/dimens.xml b/packages/SystemUI/res/values-sw600dp-land/dimens.xml
index 3a62ad9..81ca86b 100644
--- a/packages/SystemUI/res/values-sw600dp-land/dimens.xml
+++ b/packages/SystemUI/res/values-sw600dp-land/dimens.xml
@@ -17,8 +17,6 @@
 -->
 <resources>
     <!-- Recent Applications parameters -->
-    <dimen name="status_bar_recents_app_label_width">190dip</dimen>
-
     <!-- The side padding for the task stack as a percentage of the width. -->
     <item name="recents_stack_width_padding_percentage" format="float" type="dimen">0.25</item>
 
diff --git a/packages/SystemUI/res/values-sw600dp/dimens.xml b/packages/SystemUI/res/values-sw600dp/dimens.xml
index 1af4ab1..6deb818 100644
--- a/packages/SystemUI/res/values-sw600dp/dimens.xml
+++ b/packages/SystemUI/res/values-sw600dp/dimens.xml
@@ -32,10 +32,6 @@
     <!-- The width of the view containing the menu/ime navigation bar icons -->
     <dimen name="navigation_extra_key_width">48dip</dimen>
 
-    <!-- Size of application thumbnail -->
-    <dimen name="status_bar_recents_thumbnail_width">200dp</dimen>
-    <dimen name="status_bar_recents_thumbnail_height">177dp</dimen>
-
     <!-- Minimum fraction of the screen that should be taken up by the notification panel. -->
     <item type="dimen" name="notification_panel_min_height_frac">40%</item>
 
diff --git a/packages/SystemUI/res/values-sw720dp/config.xml b/packages/SystemUI/res/values-sw720dp/config.xml
index fbeadcd..1efae42 100644
--- a/packages/SystemUI/res/values-sw720dp/config.xml
+++ b/packages/SystemUI/res/values-sw720dp/config.xml
@@ -22,20 +22,6 @@
 <resources>
     <integer name="status_bar_config_maxNotificationIcons">5</integer>
 
-    <!-- Whether we're using the tablet-optimized recents interface (we use this
-     value at runtime for some things) -->
-    <bool name="config_recents_interface_for_tablets">true</bool>
-
-    <!-- Whether recents thumbnails should stretch in both x and y to fill their
-     ImageView -->
-    <bool name="config_recents_thumbnail_image_fits_to_xy">true</bool>
-
-    <!-- Min alpha % that recent items will fade to while being dismissed -->
-    <integer name="config_recent_item_min_alpha">0</integer>
-
-    <!-- Transposes the search bar layout in landscape -->
-    <bool name="recents_transpose_search_layout_with_orientation">false</bool>
-
     <!-- The maximum count of notifications on Keyguard. The rest will be collapsed in an overflow
          card. -->
     <integer name="keyguard_max_notification_count">5</integer>
diff --git a/packages/SystemUI/res/values-sw720dp/dimens.xml b/packages/SystemUI/res/values-sw720dp/dimens.xml
index dd158c2..7cee381 100644
--- a/packages/SystemUI/res/values-sw720dp/dimens.xml
+++ b/packages/SystemUI/res/values-sw720dp/dimens.xml
@@ -29,42 +29,9 @@
     <!-- Bottom margin (from display edge) for status bar panels -->
     <dimen name="panel_float">56dp</dimen>
 
-    <!-- Recent Applications parameters -->
-    <!-- How far the thumbnail for a recent app appears from left edge -->
-    <dimen name="status_bar_recents_thumbnail_left_margin">28dp</dimen>
-    <!-- Upper width limit for application icon -->
-    <dimen name="status_bar_recents_app_icon_max_width">64dp</dimen>
-    <!-- Upper height limit for application icon -->
-    <dimen name="status_bar_recents_app_icon_max_height">64dp</dimen>
-
-    <!-- Size of application icon -->
-    <dimen name="status_bar_recents_thumbnail_width">208dp</dimen>
-    <dimen name="status_bar_recents_thumbnail_height">130dp</dimen>
-
-    <!-- Width of recents panel -->
-    <dimen name="status_bar_recents_width">600dp</dimen>
-    <!-- Padding for text descriptions -->
-    <dimen name="status_bar_recents_text_description_padding">8dp</dimen>
-    <!-- Size of application label text -->
-    <dimen name="status_bar_recents_app_label_text_size">18dip</dimen>
-    <!-- Size of application description text -->
-    <dimen name="status_bar_recents_app_description_text_size">18dip</dimen>
-    <!-- Width of application label text -->
-    <dimen name="status_bar_recents_app_label_width">97dip</dimen>
-    <!-- Left margin for application label -->
-    <dimen name="status_bar_recents_app_label_left_margin">16dip</dimen>
-    <!-- Size of fading edge for text -->
-    <dimen name="status_bar_recents_text_fading_edge_length">20dip</dimen>
-    <!-- Size of fading edge for scrolling -->
-    <dimen name="status_bar_recents_scroll_fading_edge_length">10dip</dimen>
-
     <!-- The radius of the rounded corners on a task view. -->
     <dimen name="recents_task_view_rounded_corners_radius">3dp</dimen>
 
-    <!-- Where to place the app icon over the thumbnail -->
-    <dimen name="status_bar_recents_app_icon_left_margin">0dp</dimen>
-    <dimen name="status_bar_recents_app_icon_top_margin">8dp</dimen>
-
     <!-- The fraction of the screen height where the clock on the Keyguard has its center. The
      max value is used when no notifications are displaying, and the min value is when the
      highest possible number of notifications are showing. -->
diff --git a/packages/SystemUI/res/values-uz-rUZ/strings.xml b/packages/SystemUI/res/values-uz-rUZ/strings.xml
index 04ec78b..9f9fe5f 100644
--- a/packages/SystemUI/res/values-uz-rUZ/strings.xml
+++ b/packages/SystemUI/res/values-uz-rUZ/strings.xml
@@ -82,7 +82,7 @@
     <string name="accessibility_home" msgid="8217216074895377641">"Uyga"</string>
     <string name="accessibility_menu" msgid="316839303324695949">"Menyu"</string>
     <string name="accessibility_recent" msgid="5208608566793607626">"Umumiy nazar"</string>
-    <string name="accessibility_search_light" msgid="1103867596330271848">"Izlash"</string>
+    <string name="accessibility_search_light" msgid="1103867596330271848">"Qidirish"</string>
     <string name="accessibility_camera_button" msgid="8064671582820358152">"Kamera"</string>
     <string name="accessibility_phone_button" msgid="6738112589538563574">"Telefon"</string>
     <string name="accessibility_voice_assist_button" msgid="487611083884852965">"Ovozli yordam"</string>
@@ -303,7 +303,7 @@
     <string name="expanded_header_battery_charging_with_time" msgid="457559884275395376">"<xliff:g id="CHARGING_TIME">%s</xliff:g>da to‘ladi"</string>
     <string name="expanded_header_battery_not_charging" msgid="4798147152367049732">"Quvvat olmayapti"</string>
     <string name="ssl_ca_cert_warning" msgid="9005954106902053641">"Tarmoq nazorat\nostida bo‘lishi mumkin"</string>
-    <string name="description_target_search" msgid="3091587249776033139">"Izlash"</string>
+    <string name="description_target_search" msgid="3091587249776033139">"Qidirish"</string>
     <string name="description_direction_up" msgid="7169032478259485180">"<xliff:g id="TARGET_DESCRIPTION">%s</xliff:g> uchun yuqoriga suring."</string>
     <string name="description_direction_left" msgid="7207478719805562165">"<xliff:g id="TARGET_DESCRIPTION">%s</xliff:g> uchun chapga suring."</string>
     <string name="zen_priority_introduction" msgid="3070506961866919502">"Turli ovoz va tebranishlar endi sizni bezovta qilmaydi. Biroq uyg‘otkich signallari, eslatmalar, tadbirlar haqidagi bildirishnomalar va siz tanlagan abonentlardan kelgan qo‘ng‘iroqlar bundan mustasno."</string>
diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml
index da21084..f40f0d9 100644
--- a/packages/SystemUI/res/values/colors.xml
+++ b/packages/SystemUI/res/values/colors.xml
@@ -24,11 +24,8 @@
     <color name="system_bar_background_semi_transparent">#66000000</color> <!-- 40% black -->
     <color name="system_bar_background_transparent">#00000000</color>
     <color name="notification_panel_solid_background">#ff000000</color>
-    <drawable name="status_bar_recents_app_thumbnail_background">#88000000</drawable>
-    <color name="status_bar_recents_app_label_color">#ffffffff</color>
     <drawable name="status_bar_notification_row_background_color">#ff090909</drawable>
     <color name="notification_list_shadow_top">#80000000</color>
-    <drawable name="recents_callout_line">#99ffffff</drawable>
     <drawable name="heads_up_notification_bg_pressed">#ff33B5E5</drawable>
     <color name="batterymeter_frame_color">#4DFFFFFF</color><!-- 30% white -->
     <color name="batterymeter_charge_color">#FFFFFFFF</color>
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index 8da1148..f28c9d3 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -20,15 +20,6 @@
 <!-- These resources are around just to allow their values to be customized
      for different hardware and product builds. -->
 <resources>
-
-    <!-- Whether we're using the tablet-optimized recents interface (we use this
-     value at runtime for some things) -->
-    <bool name="config_recents_interface_for_tablets">false</bool>
-
-    <!-- Whether recents thumbnails should stretch in both x and y to fill their
-     ImageView -->
-    <bool name="config_recents_thumbnail_image_fits_to_xy">false</bool>
-
     <!-- Whether recents should use hardware layers for its taskviews. This flag can be enabled
     for devices where the java drawing of round rects may be slow -->
     <bool name="config_recents_use_hardware_layers">false</bool>
@@ -46,9 +37,6 @@
          certain GPU's and thus can be turned off with only minimal visual impact. -->
     <bool name="config_notifications_round_rect_clipping">true</bool>
 
-    <!-- The theme to use for RecentsActivity. -->
-    <item type="style" name="config_recents_activity_theme">@style/RecentsTheme.Wallpaper</item>
-
     <!-- Control whether status bar should distinguish HSPA data icon form UMTS
     data icon on devices -->
     <bool name="config_hspa_data_distinguishable">false</bool>
@@ -92,10 +80,6 @@
     <!-- The length of the vibration when the notification pops open. -->
     <integer name="one_finger_pop_duration_ms">10</integer>
 
-    <!-- Whether we're using the tablet-optimized recents interface (we use this
-     value at runtime for some things) -->
-    <integer name="status_bar_recents_bg_gradient_degrees">90</integer>
-
     <!-- decay duration (from size_max -> size), in ms -->
     <integer name="navigation_bar_deadzone_hold">333</integer>
     <integer name="navigation_bar_deadzone_decay">333</integer>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 96a77256..abfd863 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -16,45 +16,6 @@
 */
 -->
 <resources>
-    <!-- Recent Applications parameters -->
-    <!-- Upper width limit for application icon -->
-    <dimen name="status_bar_recents_app_icon_max_width">48dp</dimen>
-    <!-- Upper height limit for application icon -->
-    <dimen name="status_bar_recents_app_icon_max_height">48dp</dimen>
-
-    <!-- Size of application thumbnail -->
-    <dimen name="status_bar_recents_thumbnail_width">164dp</dimen>
-    <dimen name="status_bar_recents_thumbnail_height">145dp</dimen>
-    <dimen name="status_bar_recents_thumbnail_bg_padding">4dp</dimen>
-
-    <!-- Size of application label text -->
-    <dimen name="status_bar_recents_app_label_text_size">14dip</dimen>
-    <!-- Size of application description text -->
-    <dimen name="status_bar_recents_app_description_text_size">14dip</dimen>
-    <!-- Size of fading edge for text -->
-    <dimen name="status_bar_recents_text_fading_edge_length">20dip</dimen>
-    <!-- Size of fading edge for scrolling -->
-    <dimen name="status_bar_recents_scroll_fading_edge_length">10dip</dimen>
-    <!-- Margin between recents container and glow on the right -->
-    <dimen name="status_bar_recents_right_glow_margin">100dip</dimen>
-    <!-- How far the thumbnail for a recent app appears from left edge -->
-    <dimen name="status_bar_recents_thumbnail_left_margin">20dp</dimen>
-    <!-- Padding for text descriptions -->
-    <dimen name="status_bar_recents_text_description_padding">8dp</dimen>
-    <!-- Width of application label text -->
-    <dimen name="status_bar_recents_app_label_width">88dip</dimen>
-    <!-- Left margin of application label text -->
-    <dimen name="status_bar_recents_app_label_left_margin">0dip</dimen>
-    <!-- Padding between recents items -->
-    <dimen name="status_bar_recents_item_padding">0dip</dimen>
-    <!-- When recents first appears, how far the icon and label of the primary activity
-         travel -->
-    <dimen name="status_bar_recents_app_icon_translate_distance">35dip</dimen>
-
-    <!-- Where to place the app icon over the thumbnail -->
-    <dimen name="status_bar_recents_app_icon_left_margin">0dp</dimen>
-    <dimen name="status_bar_recents_app_icon_top_margin">8dp</dimen>
-
     <!-- Amount to offset bottom of notification peek window from top of status bar. -->
     <dimen name="peek_window_y_offset">-12dp</dimen>
 
diff --git a/packages/SystemUI/res/values/donottranslate.xml b/packages/SystemUI/res/values/donottranslate.xml
index 351a1fd..30ff704 100644
--- a/packages/SystemUI/res/values/donottranslate.xml
+++ b/packages/SystemUI/res/values/donottranslate.xml
@@ -20,4 +20,10 @@
     <!-- Date format for display: should match the lockscreen in /policy.  -->
     <string name="system_ui_date_pattern">@*android:string/system_ui_date_pattern</string>
 
+    <!-- DO NOT TRANSLATE - temporary hack to show the speed-less label if no translation is available -->
+    <item type="string" name="keyguard_indication_charging_time_fast_if_translated">@string/keyguard_indication_charging_time</item>
+
+    <!-- DO NOT TRANSLATE - temporary hack to show the speed-less label if no translation is available -->
+    <item type="string" name="keyguard_indication_charging_time_slowly_if_translated">@string/keyguard_indication_charging_time</item>
+
 </resources>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index db1e688..d567e97 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -793,6 +793,12 @@
     <!-- Indication on the keyguard that is shown when the device is charging. [CHAR LIMIT=40]-->
     <string name="keyguard_indication_charging_time">Charging (<xliff:g id="charging_time_left" example="4 hours and 2 minutes">%s</xliff:g> until full)</string>
 
+    <!-- Indication on the keyguard that is shown when the device is charging rapidly. Should match keyguard_plugged_in_charging_fast [CHAR LIMIT=40]-->
+    <string name="keyguard_indication_charging_time_fast">Charging rapidly (<xliff:g id="charging_time_left" example="4 hours and 2 minutes">%s</xliff:g> until full)</string>
+
+    <!-- Indication on the keyguard that is shown when the device is charging slowly. Should match keyguard_plugged_in_charging_slowly [CHAR LIMIT=40]-->
+    <string name="keyguard_indication_charging_time_slowly">Charging slowly (<xliff:g id="charging_time_left" example="4 hours and 2 minutes">%s</xliff:g> until full)</string>
+
     <!-- Related to user switcher --><skip/>
 
     <!-- Accessibility label for the button that opens the user switcher. -->
diff --git a/packages/SystemUI/src/com/android/systemui/RecentsComponent.java b/packages/SystemUI/src/com/android/systemui/RecentsComponent.java
index dd93389..1d0bfe7 100644
--- a/packages/SystemUI/src/com/android/systemui/RecentsComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/RecentsComponent.java
@@ -20,10 +20,6 @@
 import android.view.View;
 
 public interface RecentsComponent {
-    public interface Callbacks {
-        public void onVisibilityChanged(boolean visible);
-    }
-
     void showRecents(boolean triggeredFromAltTab, View statusBarView);
     void hideRecents(boolean triggeredFromAltTab, boolean triggeredFromHomeKey);
     void toggleRecents(Display display, int layoutDirection, View statusBarView);
@@ -31,5 +27,4 @@
     void cancelPreloadingRecents();
     void showNextAffiliatedTask();
     void showPrevAffiliatedTask();
-    void setCallback(Callbacks cb);
 }
diff --git a/packages/SystemUI/src/com/android/systemui/SwipeHelper.java b/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
index 33f6564..6207324 100644
--- a/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
@@ -34,6 +34,8 @@
 import android.view.animation.Interpolator;
 import android.view.animation.LinearInterpolator;
 
+import com.android.systemui.classifier.FalsingManager;
+
 public class SwipeHelper implements Gefingerpoken {
     static final String TAG = "com.android.systemui.SwipeHelper";
     private static final boolean DEBUG = false;
@@ -67,6 +69,7 @@
     private Handler mHandler;
     private int mSwipeDirection;
     private VelocityTracker mVelocityTracker;
+    private FalsingManager mFalsingManager;
 
     private float mInitialTouchPos;
     private boolean mDragging;
@@ -97,6 +100,7 @@
                 android.R.interpolator.fast_out_linear_in);
         mFalsingThreshold = context.getResources().getDimensionPixelSize(
                 R.dimen.swipe_helper_falsing_threshold);
+        mFalsingManager = FalsingManager.getInstance(context);
     }
 
     public void setLongPressListener(LongPressListener listener) {
@@ -449,8 +453,13 @@
                     boolean childSwipedFastEnough = (Math.abs(velocity) > escapeVelocity) &&
                             (Math.abs(velocity) > Math.abs(perpendicularVelocity)) &&
                             (velocity > 0) == (getTranslation(mCurrAnimView) > 0);
-                    boolean falsingDetected = mCallback.isAntiFalsingNeeded()
-                            && !mTouchAboveFalsingThreshold;
+                    boolean falsingDetected = mCallback.isAntiFalsingNeeded();
+
+                    if (mFalsingManager.isClassiferEnabled()) {
+                        falsingDetected = falsingDetected && mFalsingManager.isFalseTouch();
+                    } else {
+                        falsingDetected = falsingDetected && !mTouchAboveFalsingThreshold;
+                    }
 
                     boolean dismissChild = mCallback.canChildBeDismissed(mCurrView)
                             && !falsingDetected && (childSwipedFastEnough || childSwipedFarEnough)
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java b/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
index 33bd726..5bf251b 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
@@ -22,7 +22,9 @@
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.content.res.Configuration;
+import android.os.Process;
 import android.os.SystemProperties;
+import android.os.UserHandle;
 import android.util.Log;
 
 import java.util.HashMap;
@@ -51,12 +53,20 @@
     };
 
     /**
+     * The classes of the stuff to start for each user.  This is a subset of the services listed
+     * above.
+     */
+    private final Class<?>[] SERVICES_PER_USER = new Class[] {
+            com.android.systemui.recents.Recents.class
+    };
+
+    /**
      * Hold a reference on the stuff we start.
      */
     private final SystemUI[] mServices = new SystemUI[SERVICES.length];
     private boolean mServicesStarted;
     private boolean mBootCompleted;
-    private final Map<Class<?>, Object> mComponents = new HashMap<Class<?>, Object>();
+    private final Map<Class<?>, Object> mComponents = new HashMap<>();
 
     @Override
     public void onCreate() {
@@ -66,24 +76,32 @@
         // the theme set there.
         setTheme(R.style.systemui_theme);
 
-        IntentFilter filter = new IntentFilter(Intent.ACTION_BOOT_COMPLETED);
-        filter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
-        registerReceiver(new BroadcastReceiver() {
-            @Override
-            public void onReceive(Context context, Intent intent) {
-                if (mBootCompleted) return;
+        if (Process.myUserHandle().equals(UserHandle.SYSTEM)) {
+            IntentFilter filter = new IntentFilter(Intent.ACTION_BOOT_COMPLETED);
+            filter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
+            registerReceiver(new BroadcastReceiver() {
+                @Override
+                public void onReceive(Context context, Intent intent) {
+                    if (mBootCompleted) return;
 
-                if (DEBUG) Log.v(TAG, "BOOT_COMPLETED received");
-                unregisterReceiver(this);
-                mBootCompleted = true;
-                if (mServicesStarted) {
-                    final int N = mServices.length;
-                    for (int i = 0; i < N; i++) {
-                        mServices[i].onBootCompleted();
+                    if (DEBUG) Log.v(TAG, "BOOT_COMPLETED received");
+                    unregisterReceiver(this);
+                    mBootCompleted = true;
+                    if (mServicesStarted) {
+                        final int N = mServices.length;
+                        for (int i = 0; i < N; i++) {
+                            mServices[i].onBootCompleted();
+                        }
                     }
                 }
-            }
-        }, filter);
+            }, filter);
+        } else {
+            // For a secondary user, boot-completed will never be called because it has already
+            // been broadcasted on startup for the primary SystemUI process.  Instead, for
+            // components which require the SystemUI component to be initialized per-user, we
+            // start those components now for the current non-system user.
+            startServicesIfNeeded(SERVICES_PER_USER);
+        }
     }
 
     /**
@@ -94,6 +112,10 @@
      * <p>This method must only be called from the main thread.</p>
      */
     public void startServicesIfNeeded() {
+        startServicesIfNeeded(SERVICES);
+    }
+
+    private void startServicesIfNeeded(Class<?>[] services) {
         if (mServicesStarted) {
             return;
         }
@@ -107,13 +129,14 @@
             }
         }
 
-        Log.v(TAG, "Starting SystemUI services.");
-        final int N = SERVICES.length;
+        Log.v(TAG, "Starting SystemUI services for user " +
+                Process.myUserHandle().getIdentifier() + ".");
+        final int N = services.length;
         for (int i=0; i<N; i++) {
-            Class<?> cl = SERVICES[i];
+            Class<?> cl = services[i];
             if (DEBUG) Log.d(TAG, "loading: " + cl);
             try {
-                mServices[i] = (SystemUI)cl.newInstance();
+                mServices[i] = (SystemUI) cl.newInstance();
             } catch (IllegalAccessException ex) {
                 throw new RuntimeException(ex);
             } catch (InstantiationException ex) {
@@ -136,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/analytics/LockedPhoneAnalytics.java b/packages/SystemUI/src/com/android/systemui/analytics/DataCollector.java
similarity index 75%
rename from packages/SystemUI/src/com/android/systemui/analytics/LockedPhoneAnalytics.java
rename to packages/SystemUI/src/com/android/systemui/analytics/DataCollector.java
index e458862..91f6520 100644
--- a/packages/SystemUI/src/com/android/systemui/analytics/LockedPhoneAnalytics.java
+++ b/packages/SystemUI/src/com/android/systemui/analytics/DataCollector.java
@@ -21,7 +21,6 @@
 import android.hardware.Sensor;
 import android.hardware.SensorEvent;
 import android.hardware.SensorEventListener;
-import android.hardware.SensorManager;
 import android.os.AsyncTask;
 import android.os.Build;
 import android.os.Handler;
@@ -30,8 +29,6 @@
 import android.util.Log;
 import android.view.MotionEvent;
 
-import com.android.systemui.statusbar.StatusBarState;
-
 import java.io.File;
 import java.io.FileOutputStream;
 import java.io.IOException;
@@ -47,24 +44,29 @@
  * A session starts when the screen is turned on.
  * A session ends when the screen is turned off or user unlocks the phone.
  */
-public class LockedPhoneAnalytics implements SensorEventListener {
-    private static final String TAG = "LockedPhoneAnalytics";
-    private static final String ANALYTICS_ENABLE = "locked_phone_analytics_enable";
-    private static final String ENFORCE_BOUNCER = "locked_phone_analytics_enforce_bouncer";
-    private static final String COLLECT_BAD_TOCUHES = "locked_phone_analytics_collect_bad_touches";
+public class DataCollector implements SensorEventListener {
+    private static final String TAG = "DataCollector";
+    private static final String COLLECTOR_ENABLE = "data_collector_enable";
+    private static final String COLLECT_BAD_TOUCHES = "data_collector_collect_bad_touches";
 
     private static final long TIMEOUT_MILLIS = 11000; // 11 seconds.
     public static final boolean DEBUG = false;
 
-    private static final int[] SENSORS = new int[] {
-            Sensor.TYPE_ACCELEROMETER,
-            Sensor.TYPE_GYROSCOPE,
-            Sensor.TYPE_PROXIMITY,
-            Sensor.TYPE_LIGHT,
-            Sensor.TYPE_ROTATION_VECTOR,
-    };
-
     private final Handler mHandler = new Handler();
+    private final Context mContext;
+
+    // Err on the side of caution, so logging is not started after a crash even tough the screen
+    // is off.
+    private SensorLoggerSession mCurrentSession = null;
+
+    private boolean mEnableCollector = false;
+    private boolean mTimeoutActive = false;
+    private boolean mCollectBadTouches = false;
+    private boolean mCornerSwiping = false;
+    private boolean mTrackingStarted = false;
+
+    private static DataCollector sInstance = null;
+
     protected final ContentObserver mSettingsObserver = new ContentObserver(mHandler) {
         @Override
         public void onChange(boolean selfChange) {
@@ -72,69 +74,40 @@
         }
     };
 
-    private final SensorManager mSensorManager;
-    private final Context mContext;
-
-    // Err on the side of caution, so logging is not started after a crash even tough the screen
-    // is off.
-    private SensorLoggerSession mCurrentSession = null;
-
-    private boolean mEnableAnalytics = false;
-    private boolean mEnforceBouncer = false;
-    private boolean mTimeoutActive = false;
-    private boolean mCollectBadTouches = false;
-    private boolean mBouncerOn = false;
-    private boolean mCornerSwiping = false;
-    private boolean mTrackingStarted = false;
-
-    private int mState = StatusBarState.SHADE;
-
-    private static LockedPhoneAnalytics sInstance = null;
-
-    private LockedPhoneAnalytics(Context context) {
-        mSensorManager = (SensorManager) context.getSystemService(Context.SENSOR_SERVICE);
+    private DataCollector(Context context) {
         mContext = context;
 
         mContext.getContentResolver().registerContentObserver(
-                Settings.Secure.getUriFor(ANALYTICS_ENABLE), false,
+                Settings.Secure.getUriFor(COLLECTOR_ENABLE), false,
                 mSettingsObserver,
                 UserHandle.USER_ALL);
 
         mContext.getContentResolver().registerContentObserver(
-                Settings.Secure.getUriFor(ENFORCE_BOUNCER), false,
-                mSettingsObserver,
-                UserHandle.USER_ALL);
-
-        mContext.getContentResolver().registerContentObserver(
-                Settings.Secure.getUriFor(COLLECT_BAD_TOCUHES), false,
+                Settings.Secure.getUriFor(COLLECT_BAD_TOUCHES), false,
                 mSettingsObserver,
                 UserHandle.USER_ALL);
 
         updateConfiguration();
     }
 
-    public static LockedPhoneAnalytics getInstance(Context context) {
+    public static DataCollector getInstance(Context context) {
         if (sInstance == null) {
-            sInstance = new LockedPhoneAnalytics(context);
+            sInstance = new DataCollector(context);
         }
         return sInstance;
     }
 
     private void updateConfiguration() {
-        mEnableAnalytics = Build.IS_DEBUGGABLE && 0 != Settings.Secure.getInt(
+        mEnableCollector = Build.IS_DEBUGGABLE && 0 != Settings.Secure.getInt(
                 mContext.getContentResolver(),
-                ANALYTICS_ENABLE, 0);
-        mEnforceBouncer = mEnableAnalytics && 0 != Settings.Secure.getInt(
+                COLLECTOR_ENABLE, 0);
+        mCollectBadTouches = mEnableCollector && 0 != Settings.Secure.getInt(
                 mContext.getContentResolver(),
-                ENFORCE_BOUNCER, 0);
-        mCollectBadTouches = mEnableAnalytics && 0 != Settings.Secure.getInt(
-                mContext.getContentResolver(),
-                COLLECT_BAD_TOCUHES, 0);
+                COLLECT_BAD_TOUCHES, 0);
     }
 
     private boolean sessionEntrypoint() {
-        if ((mState == StatusBarState.KEYGUARD || mState == StatusBarState.SHADE_LOCKED)
-                && mEnableAnalytics && mCurrentSession == null) {
+        if (mEnableCollector && mCurrentSession == null) {
             onSessionStart();
             return true;
         }
@@ -142,22 +115,15 @@
     }
 
     private void sessionExitpoint(int result) {
-        if (mEnableAnalytics && mCurrentSession != null) {
+        if (mEnableCollector && mCurrentSession != null) {
             onSessionEnd(result);
         }
     }
 
     private void onSessionStart() {
-        mBouncerOn = false;
         mCornerSwiping = false;
         mTrackingStarted = false;
         mCurrentSession = new SensorLoggerSession(System.currentTimeMillis(), System.nanoTime());
-        for (int sensorType : SENSORS) {
-            Sensor s = mSensorManager.getDefaultSensor(sensorType);
-            if (s != null) {
-                mSensorManager.registerListener(this, s, SensorManager.SENSOR_DELAY_GAME);
-            }
-        }
     }
 
     private void onSessionEnd(int result) {
@@ -196,10 +162,9 @@
         });
     }
 
-
     @Override
     public synchronized void onSensorChanged(SensorEvent event) {
-        if (mEnableAnalytics && mCurrentSession != null) {
+        if (mEnableCollector && mCurrentSession != null) {
             mCurrentSession.addSensorEvent(event, System.nanoTime());
             enforceTimeout();
         }
@@ -221,18 +186,14 @@
     public void onAccuracyChanged(Sensor sensor, int accuracy) {
     }
 
-    public boolean shouldEnforceBouncer() {
-        return mEnforceBouncer;
+    public boolean isEnabled() {
+        return mEnableCollector;
     }
 
-    public void setStatusBarState(int state) {
-        mState = state;
-    }
-
-    public void onScreenOn() {
+    public void onScreenTurningOn() {
         if (sessionEntrypoint()) {
             if (DEBUG) {
-                Log.d(TAG, "onScreenOn");
+                Log.d(TAG, "onScreenTurningOn");
             }
             addEvent(PhoneEvent.ON_SCREEN_ON);
         }
@@ -264,23 +225,17 @@
     }
 
     public void onBouncerShown() {
-        if (!mBouncerOn) {
-            if (DEBUG) {
-                Log.d(TAG, "onBouncerShown");
-            }
-            mBouncerOn = true;
-            addEvent(PhoneEvent.ON_BOUNCER_SHOWN);
+        if (DEBUG) {
+            Log.d(TAG, "onBouncerShown");
         }
+        addEvent(PhoneEvent.ON_BOUNCER_SHOWN);
     }
 
     public void onBouncerHidden() {
-        if (mBouncerOn) {
-            if (DEBUG) {
-                Log.d(TAG, "onBouncerHidden");
-            }
-            mBouncerOn = false;
-            addEvent(PhoneEvent.ON_BOUNCER_HIDDEN);
+        if (DEBUG) {
+            Log.d(TAG, "onBouncerHidden");
         }
+        addEvent(PhoneEvent.ON_BOUNCER_HIDDEN);
     }
 
     public void onQsDown() {
@@ -433,20 +388,20 @@
         addEvent(PhoneEvent.ON_LEFT_AFFORDANCE_HINT_STARTED);
     }
 
-    public void onTouchEvent(MotionEvent ev, int width, int height) {
-        if (!mBouncerOn && mCurrentSession != null) {
+    public void onTouchEvent(MotionEvent event, int width, int height) {
+        if (mCurrentSession != null) {
             if (DEBUG) {
                 Log.v(TAG, "onTouchEvent(ev.action="
-                        + MotionEvent.actionToString(ev.getAction()) + ")");
+                        + MotionEvent.actionToString(event.getAction()) + ")");
             }
-            mCurrentSession.addMotionEvent(ev);
+            mCurrentSession.addMotionEvent(event);
             mCurrentSession.setTouchArea(width, height);
             enforceTimeout();
         }
     }
 
     private void addEvent(int eventType) {
-        if (mEnableAnalytics && mCurrentSession != null) {
+        if (mEnableCollector && mCurrentSession != null) {
             mCurrentSession.addPhoneEvent(eventType, System.nanoTime());
         }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/analytics/SensorLoggerSession.java b/packages/SystemUI/src/com/android/systemui/analytics/SensorLoggerSession.java
index 09383f6a..0e28002 100644
--- a/packages/SystemUI/src/com/android/systemui/analytics/SensorLoggerSession.java
+++ b/packages/SystemUI/src/com/android/systemui/analytics/SensorLoggerSession.java
@@ -57,7 +57,7 @@
         mResult = result;
         mEndTimestampMillis = endTimestampMillis;
 
-        if (LockedPhoneAnalytics.DEBUG) {
+        if (DataCollector.DEBUG) {
             Log.d(TAG, "Ending session result=" + result + " it lasted for " +
                     (float) (mEndTimestampMillis - mStartTimestampMillis) / 1000f + "s");
         }
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/AccelerationClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/AccelerationClassifier.java
new file mode 100644
index 0000000..86bea87
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/classifier/AccelerationClassifier.java
@@ -0,0 +1,94 @@
+/*
+ * 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 android.view.MotionEvent;
+
+import java.util.HashMap;
+
+/**
+ * A classifier which looks at the speed and distance between successive points of a Stroke.
+ * It looks at two consecutive speeds between two points and calculates the ratio between them.
+ * The final result is the maximum of these values. It does the same for distances. If some speed
+ * or distance is equal to zero then the ratio between this and the next part is not calculated. To
+ * the duration of each part there is added one nanosecond so that it is always possible to
+ * calculate the speed of a part.
+ */
+public class AccelerationClassifier extends StrokeClassifier {
+    private final HashMap<Stroke, Data> mStrokeMap = new HashMap<>();
+
+    public AccelerationClassifier(ClassifierData classifierData) {
+        mClassifierData = classifierData;
+    }
+
+    @Override
+    public void onTouchEvent(MotionEvent event) {
+        int action = event.getActionMasked();
+
+        if (action == MotionEvent.ACTION_DOWN) {
+            mStrokeMap.clear();
+        }
+
+        for (int i = 0; i < event.getPointerCount(); i++) {
+            Stroke stroke = mClassifierData.getStroke(event.getPointerId(i));
+            Point point = stroke.getPoints().get(stroke.getPoints().size() - 1);
+            if (mStrokeMap.get(stroke) == null) {
+                mStrokeMap.put(stroke, new Data(point));
+            } else {
+                mStrokeMap.get(stroke).addPoint(point);
+            }
+        }
+    }
+
+    @Override
+    public float getFalseTouchEvaluation(int type, Stroke stroke) {
+        Data data = mStrokeMap.get(stroke);
+        return SpeedRatioEvaluator.evaluate(data.maxSpeedRatio)
+                + DistanceRatioEvaluator.evaluate(data.maxDistanceRatio);
+    }
+
+    private static class Data {
+        public Point previousPoint;
+        public float previousSpeed;
+        public float previousDistance;
+        public float maxSpeedRatio;
+        public float maxDistanceRatio;
+
+        public Data(Point point) {
+            previousPoint = point;
+            previousSpeed = previousDistance = 0.0f;
+            maxDistanceRatio = maxSpeedRatio = 0.0f;
+        }
+
+        public void addPoint(Point point) {
+            float distance = previousPoint.dist(point);
+            float duration = (float) (point.timeOffsetNano - previousPoint.timeOffsetNano + 1);
+            float speed = distance / duration;
+            if (previousDistance != 0.0f) {
+                maxDistanceRatio = Math.max(maxDistanceRatio, distance / previousDistance);
+            }
+
+            if (previousSpeed != 0.0f) {
+                maxSpeedRatio = Math.max(maxSpeedRatio, speed / previousSpeed);
+            }
+
+            previousDistance = distance;
+            previousSpeed = speed;
+            previousPoint = point;
+        }
+    }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/AnglesVarianceClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/AnglesVarianceClassifier.java
new file mode 100644
index 0000000..c74339b
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/classifier/AnglesVarianceClassifier.java
@@ -0,0 +1,140 @@
+/*
+ * 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 android.view.MotionEvent;
+
+import java.lang.Math;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+
+/**
+ * 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. Then it calculates 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. This classifier also tries to split the stroke into two parts
+ * int the place in which the biggest angle is. It calculates the angle variance of the two parts
+ * and sums them up. The reason the classifier is doing this, is because some human swipes at the
+ * beginning go for a moment in one direction and then they rapidly change direction for the rest
+ * of the stroke (like a tick). The final result is the minimum of angle variance of the whole
+ * stroke and the sum of angle variances of the two parts split up.
+ */
+public class AnglesVarianceClassifier extends StrokeClassifier {
+    private HashMap<Stroke, Data> mStrokeMap = new HashMap<>();
+
+    public AnglesVarianceClassifier(ClassifierData classifierData) {
+        mClassifierData = classifierData;
+    }
+
+    @Override
+    public void onTouchEvent(MotionEvent event) {
+        int action = event.getActionMasked();
+
+        if (action == MotionEvent.ACTION_DOWN) {
+            mStrokeMap.clear();
+        }
+
+        for (int i = 0; i < event.getPointerCount(); i++) {
+            Stroke stroke = mClassifierData.getStroke(event.getPointerId(i));
+
+            if (mStrokeMap.get(stroke) == null) {
+                mStrokeMap.put(stroke, new Data());
+            }
+            mStrokeMap.get(stroke).addPoint(stroke.getPoints().get(stroke.getPoints().size() - 1));
+        }
+    }
+
+    @Override
+    public float getFalseTouchEvaluation(int type, Stroke stroke) {
+        return AnglesVarianceEvaluator.evaluate(mStrokeMap.get(stroke).getAnglesVariance());
+    }
+
+    private static class Data {
+        private List<Point> mLastThreePoints = new ArrayList<>();
+        private float mFirstAngleVariance;
+        private float mPreviousAngle;
+        private float mBiggestAngle;
+        private float mSumSquares;
+        private float mSecondSumSquares;
+        private float mSum;
+        private float mSecondSum;
+        private float mCount;
+        private float mSecondCount;
+
+        public Data() {
+            mFirstAngleVariance = 0.0f;
+            mPreviousAngle = (float) Math.PI;
+            mBiggestAngle = 0.0f;
+            mSumSquares = mSecondSumSquares = 0.0f;
+            mSum = mSecondSum = 0.0f;
+            mCount = mSecondCount = 1.0f;
+        }
+
+        public void addPoint(Point point) {
+            // Checking if the added point is different than the previously added point
+            // Repetitions are being ignored so that proper angles are calculated.
+            if (mLastThreePoints.isEmpty()
+                    || !mLastThreePoints.get(mLastThreePoints.size() - 1).equals(point)) {
+                mLastThreePoints.add(point);
+                if (mLastThreePoints.size() == 4) {
+                    mLastThreePoints.remove(0);
+
+                    float angle = mLastThreePoints.get(1).getAngle(mLastThreePoints.get(0),
+                            mLastThreePoints.get(2));
+
+                    float difference = angle - mPreviousAngle;
+
+                    // If this is the biggest angle of the stroke so then we save the value of
+                    // the angle variance so far and start to count the values for the angle
+                    // variance of the second part.
+                    if (mBiggestAngle < angle) {
+                        mBiggestAngle = angle;
+                        mFirstAngleVariance = getAnglesVariance(mSumSquares, mSum, mCount);
+                        mSecondSumSquares = 0.0f;
+                        mSecondSum = 0.0f;
+                        mSecondCount = 1.0f;
+                    } else {
+                        mSecondSum += difference;
+                        mSecondSumSquares += difference * difference;
+                        mSecondCount += 1.0;
+                    }
+
+                    mSum += difference;
+                    mSumSquares += difference * difference;
+                    mCount += 1.0;
+                    mPreviousAngle = angle;
+                }
+            }
+        }
+
+        public float getAnglesVariance(float sumSquares, float sum, float count) {
+            return sumSquares / count - (sum / count) * (sum / count);
+        }
+
+        public float getAnglesVariance() {
+            return Math.min(getAnglesVariance(mSumSquares, mSum, mCount),
+                    mFirstAngleVariance + getAnglesVariance(mSecondSumSquares, mSecondSum,
+                            mSecondCount));
+        }
+    }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/AnglesVarianceEvaluator.java b/packages/SystemUI/src/com/android/systemui/classifier/AnglesVarianceEvaluator.java
new file mode 100644
index 0000000..99cc1a6
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/classifier/AnglesVarianceEvaluator.java
@@ -0,0 +1,30 @@
+/*
+ * 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;
+
+public class AnglesVarianceEvaluator {
+    public static float evaluate(float value) {
+        float evaluation = 0.0f;
+        if (value > 0.05) evaluation++;
+        if (value > 0.10) evaluation++;
+        if (value > 0.20) evaluation++;
+        if (value > 0.40) evaluation++;
+        if (value > 0.80) evaluation++;
+        if (value > 1.50) evaluation++;
+        return evaluation;
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/Classifier.java b/packages/SystemUI/src/com/android/systemui/classifier/Classifier.java
new file mode 100644
index 0000000..89d20de
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/classifier/Classifier.java
@@ -0,0 +1,51 @@
+/*
+ * 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 android.hardware.SensorEvent;
+import android.view.MotionEvent;
+
+/**
+ * An abstract class for classifiers for touch and sensor events.
+ */
+public abstract class Classifier {
+    public static final int QUICK_SETTINGS = 0;
+    public static final int NOTIFICATION_DISMISS = 1;
+    public static final int NOTIFICATION_DRAG_DOWN = 2;
+    public static final int NOTIFICATION_DOUBLE_TAP = 3;
+    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
+     */
+    protected ClassifierData mClassifierData;
+
+    /**
+     * Informs the classifier that a new touch event has occurred
+     */
+    public void onTouchEvent(MotionEvent event) {
+    }
+
+    /**
+     * Informs the classifier that a sensor change occurred
+     */
+    public void onSensorChanged(SensorEvent event) {
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/ClassifierData.java b/packages/SystemUI/src/com/android/systemui/classifier/ClassifierData.java
new file mode 100644
index 0000000..c83c74f
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/classifier/ClassifierData.java
@@ -0,0 +1,85 @@
+/*
+ * 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 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<>();
+    private final float mDpi;
+
+    public ClassifierData(float dpi) {
+        mDpi = dpi;
+    }
+
+    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) {
+                mCurrentStrokes.put(id, new Stroke(event.getEventTimeNano(), mDpi));
+            }
+            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);
+            if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL
+                    || (action == MotionEvent.ACTION_POINTER_UP && i == event.getActionIndex())) {
+                mCurrentStrokes.remove(id);
+            }
+        }
+    }
+
+    /**
+     * @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
+     */
+    public Stroke getStroke(int id) {
+        return mCurrentStrokes.get(id);
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/DistanceRatioEvaluator.java b/packages/SystemUI/src/com/android/systemui/classifier/DistanceRatioEvaluator.java
new file mode 100644
index 0000000..8acb009
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/classifier/DistanceRatioEvaluator.java
@@ -0,0 +1,29 @@
+/*
+ * 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;
+
+public class DistanceRatioEvaluator {
+    public static float evaluate(float value) {
+        float evaluation = 0.0f;
+        if (value <= 1.0) evaluation++;
+        if (value <= 0.5) evaluation++;
+        if (value > 4.0) evaluation++;
+        if (value > 7.0) evaluation++;
+        if (value > 14.0) evaluation++;
+        return evaluation;
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/DurationCountClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/DurationCountClassifier.java
new file mode 100644
index 0000000..8924694
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/classifier/DurationCountClassifier.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;
+
+/**
+ * A classifier which looks at the ratio between the duration of the stroke and its number of
+ * points.
+ */
+public class DurationCountClassifier extends StrokeClassifier {
+    public DurationCountClassifier(ClassifierData classifierData) {
+    }
+
+    @Override
+    public float getFalseTouchEvaluation(int type, Stroke stroke) {
+        return DurationCountEvaluator.evaluate(stroke.getDurationSeconds() / stroke.getCount());
+    }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/DurationCountEvaluator.java b/packages/SystemUI/src/com/android/systemui/classifier/DurationCountEvaluator.java
new file mode 100644
index 0000000..5395983
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/classifier/DurationCountEvaluator.java
@@ -0,0 +1,30 @@
+/*
+ * 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;
+
+
+public class DurationCountEvaluator {
+    public static float evaluate(float value) {
+        float evaluation = 0.0f;
+        if (value < 0.0105) evaluation++;
+        if (value < 0.00909) evaluation++;
+        if (value < 0.00667) evaluation++;
+        if (value > 0.0333) evaluation++;
+        if (value > 0.0500) evaluation++;
+        return evaluation;
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/EndPointLengthClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/EndPointLengthClassifier.java
new file mode 100644
index 0000000..78bc0dd
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/classifier/EndPointLengthClassifier.java
@@ -0,0 +1,30 @@
+/*
+ * 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;
+
+/**
+ * A classifier which looks at the distance between the first and the last point from the stroke.
+ */
+public class EndPointLengthClassifier extends StrokeClassifier {
+    public EndPointLengthClassifier(ClassifierData classifierData) {
+    }
+
+    @Override
+    public float getFalseTouchEvaluation(int type, Stroke stroke) {
+        return EndPointLengthEvaluator.evaluate(stroke.getEndPointLength());
+    }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/EndPointLengthEvaluator.java b/packages/SystemUI/src/com/android/systemui/classifier/EndPointLengthEvaluator.java
new file mode 100644
index 0000000..bb2f1c4
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/classifier/EndPointLengthEvaluator.java
@@ -0,0 +1,30 @@
+/*
+ * 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;
+
+public class EndPointLengthEvaluator {
+    public static float evaluate(float value) {
+        float evaluation = 0.0f;
+        if (value < 0.05) evaluation += 2.0;
+        if (value < 0.1) evaluation += 2.0;
+        if (value < 0.2) evaluation += 2.0;
+        if (value < 0.3) evaluation += 2.0;
+        if (value < 0.4) evaluation += 2.0;
+        if (value < 0.5) evaluation += 2.0;
+        return evaluation;
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/EndPointRatioClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/EndPointRatioClassifier.java
new file mode 100644
index 0000000..c125e00
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/classifier/EndPointRatioClassifier.java
@@ -0,0 +1,36 @@
+/*
+ * 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;
+
+/**
+ * A classifier which looks at the ratio between the total length covered by the stroke and the
+ * distance between the first and last point from this stroke.
+ */
+public class EndPointRatioClassifier extends StrokeClassifier {
+    public EndPointRatioClassifier(ClassifierData classifierData) {
+        mClassifierData = classifierData;
+    }
+
+    @Override
+    public float getFalseTouchEvaluation(int type, Stroke stroke) {
+        if (stroke.getTotalLength() == 0.0f) {
+            return 1.0f;
+        }
+        return EndPointRatioEvaluator.evaluate(
+                stroke.getEndPointLength() / stroke.getTotalLength());
+    }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/EndPointRatioEvaluator.java b/packages/SystemUI/src/com/android/systemui/classifier/EndPointRatioEvaluator.java
new file mode 100644
index 0000000..529fcec
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/classifier/EndPointRatioEvaluator.java
@@ -0,0 +1,30 @@
+/*
+ * 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;
+
+public class EndPointRatioEvaluator {
+    public static float evaluate(float value) {
+        float evaluation = 0.0f;
+        if (value < 0.85) evaluation++;
+        if (value < 0.75) evaluation++;
+        if (value < 0.65) evaluation++;
+        if (value < 0.55) evaluation++;
+        if (value < 0.45) evaluation++;
+        if (value < 0.35) evaluation++;
+        return evaluation;
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/FalsingManager.java b/packages/SystemUI/src/com/android/systemui/classifier/FalsingManager.java
new file mode 100644
index 0000000..735a7c4
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/classifier/FalsingManager.java
@@ -0,0 +1,299 @@
+/*
+ * 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 android.content.Context;
+import android.database.ContentObserver;
+import android.hardware.Sensor;
+import android.hardware.SensorEvent;
+import android.hardware.SensorEventListener;
+import android.hardware.SensorManager;
+import android.os.Handler;
+import android.os.UserHandle;
+import android.provider.Settings;
+import android.view.MotionEvent;
+
+import com.android.systemui.analytics.DataCollector;
+import com.android.systemui.statusbar.StatusBarState;
+
+/**
+ * When the phone is locked, listens to touch, sensor and phone events and sends them to
+ * DataCollector and HumanInteractionClassifier.
+ *
+ * It does not collect touch events when the bouncer shows up.
+ */
+public class FalsingManager implements SensorEventListener {
+    private static final String ENFORCE_BOUNCER = "falsing_manager_enforce_bouncer";
+
+    private static final int[] CLASSIFIER_SENSORS = new int[] {
+            Sensor.TYPE_PROXIMITY,
+    };
+
+    private static final int[] COLLECTOR_SENSORS = new int[] {
+            Sensor.TYPE_ACCELEROMETER,
+            Sensor.TYPE_GYROSCOPE,
+            Sensor.TYPE_PROXIMITY,
+            Sensor.TYPE_LIGHT,
+            Sensor.TYPE_ROTATION_VECTOR,
+    };
+
+    private final Handler mHandler = new Handler();
+    private final Context mContext;
+
+    private final SensorManager mSensorManager;
+    private final DataCollector mDataCollector;
+    private final HumanInteractionClassifier mHumanInteractionClassifier;
+
+    private static FalsingManager sInstance = null;
+
+    private boolean mEnforceBouncer = false;
+    private boolean mBouncerOn = false;
+    private boolean mSessionActive = false;
+    private int mState = StatusBarState.SHADE;
+
+    protected final ContentObserver mSettingsObserver = new ContentObserver(mHandler) {
+        @Override
+        public void onChange(boolean selfChange) {
+            updateConfiguration();
+        }
+    };
+
+    private FalsingManager(Context context) {
+        mContext = context;
+        mSensorManager = (SensorManager) mContext.getSystemService(Context.SENSOR_SERVICE);
+        mDataCollector = DataCollector.getInstance(mContext);
+        mHumanInteractionClassifier = HumanInteractionClassifier.getInstance(mContext);
+
+        mContext.getContentResolver().registerContentObserver(
+                Settings.Secure.getUriFor(ENFORCE_BOUNCER), false,
+                mSettingsObserver,
+                UserHandle.USER_ALL);
+
+        updateConfiguration();
+    }
+
+    public static FalsingManager getInstance(Context context) {
+        if (sInstance == null) {
+            sInstance = new FalsingManager(context);
+        }
+        return sInstance;
+    }
+
+    private void updateConfiguration() {
+        mEnforceBouncer = 0 != Settings.Secure.getInt(mContext.getContentResolver(),
+                ENFORCE_BOUNCER, 0);
+    }
+
+    private boolean sessionEntrypoint() {
+        if (!mSessionActive && isEnabled() &&
+                (mState == StatusBarState.KEYGUARD || mState == StatusBarState.SHADE_LOCKED)) {
+            onSessionStart();
+            return true;
+        }
+        return false;
+    }
+
+    private void sessionExitpoint() {
+        if (mSessionActive) {
+            mSessionActive = false;
+            mSensorManager.unregisterListener(this);
+        }
+    }
+
+    private void onSessionStart() {
+        mBouncerOn = false;
+        mSessionActive = true;
+
+        if (mHumanInteractionClassifier.isEnabled()) {
+            registerSensors(CLASSIFIER_SENSORS);
+        }
+        if (mDataCollector.isEnabled()) {
+            registerSensors(COLLECTOR_SENSORS);
+        }
+    }
+
+    private void registerSensors(int [] sensors) {
+        for (int sensorType : sensors) {
+            Sensor s = mSensorManager.getDefaultSensor(sensorType);
+            if (s != null) {
+                mSensorManager.registerListener(this, s, SensorManager.SENSOR_DELAY_GAME);
+            }
+        }
+    }
+
+    public boolean isClassiferEnabled() {
+        return mHumanInteractionClassifier.isEnabled();
+    }
+
+    private boolean isEnabled() {
+        return mHumanInteractionClassifier.isEnabled() || mDataCollector.isEnabled();
+    }
+
+    /**
+     * @return true if the classifier determined that this is not a human interacting with the phone
+     */
+    public boolean isFalseTouch() {
+        return mHumanInteractionClassifier.isFalseTouch();
+    }
+
+    @Override
+    public synchronized void onSensorChanged(SensorEvent event) {
+        mDataCollector.onSensorChanged(event);
+        mHumanInteractionClassifier.onSensorChanged(event);
+    }
+
+    @Override
+    public void onAccuracyChanged(Sensor sensor, int accuracy) {
+        mDataCollector.onAccuracyChanged(sensor, accuracy);
+    }
+
+    public boolean shouldEnforceBouncer() {
+        return mEnforceBouncer;
+    }
+
+    public void setStatusBarState(int state) {
+        mState = state;
+    }
+
+    public void onScreenTurningOn() {
+        if (sessionEntrypoint()) {
+            mDataCollector.onScreenTurningOn();
+        }
+    }
+
+    public void onScreenOnFromTouch() {
+        if (sessionEntrypoint()) {
+            mDataCollector.onScreenOnFromTouch();
+        }
+    }
+
+    public void onScreenOff() {
+        mDataCollector.onScreenOff();
+        sessionExitpoint();
+    }
+
+    public void onSucccessfulUnlock() {
+        mDataCollector.onSucccessfulUnlock();
+        sessionExitpoint();
+    }
+
+    public void onBouncerShown() {
+        if (!mBouncerOn) {
+            mBouncerOn = true;
+            mDataCollector.onBouncerShown();
+        }
+    }
+
+    public void onBouncerHidden() {
+        if (mBouncerOn) {
+            mBouncerOn = false;
+            mDataCollector.onBouncerHidden();
+        }
+    }
+
+    public void onQsDown() {
+        mHumanInteractionClassifier.setType(Classifier.QUICK_SETTINGS);
+        mDataCollector.onQsDown();
+    }
+
+    public void setQsExpanded(boolean expanded) {
+        mDataCollector.setQsExpanded(expanded);
+    }
+
+    public void onTrackingStarted() {
+        mHumanInteractionClassifier.setType(Classifier.UNLOCK);
+        mDataCollector.onTrackingStarted();
+    }
+
+    public void onTrackingStopped() {
+        mDataCollector.onTrackingStopped();
+    }
+
+    public void onNotificationActive() {
+        mDataCollector.onNotificationActive();
+    }
+
+    public void onNotificationDoubleTap() {
+        mDataCollector.onNotificationDoubleTap();
+    }
+
+    public void setNotificationExpanded() {
+        mDataCollector.setNotificationExpanded();
+    }
+
+    public void onNotificatonStartDraggingDown() {
+        mHumanInteractionClassifier.setType(Classifier.NOTIFICATION_DRAG_DOWN);
+        mDataCollector.onNotificatonStartDraggingDown();
+    }
+
+    public void onNotificatonStopDraggingDown() {
+        mDataCollector.onNotificatonStopDraggingDown();
+    }
+
+    public void onNotificationDismissed() {
+        mDataCollector.onNotificationDismissed();
+    }
+
+    public void onNotificatonStartDismissing() {
+        mHumanInteractionClassifier.setType(Classifier.NOTIFICATION_DISMISS);
+        mDataCollector.onNotificatonStartDismissing();
+    }
+
+    public void onNotificatonStopDismissing() {
+        mDataCollector.onNotificatonStopDismissing();
+    }
+
+    public void onCameraOn() {
+        mDataCollector.onCameraOn();
+    }
+
+    public void onLeftAffordanceOn() {
+        mDataCollector.onLeftAffordanceOn();
+    }
+
+    public void onAffordanceSwipingStarted(boolean rightCorner) {
+        if (rightCorner) {
+            mHumanInteractionClassifier.setType(Classifier.RIGHT_AFFORDANCE);
+        } else {
+            mHumanInteractionClassifier.setType(Classifier.LEFT_AFFORDANCE);
+        }
+        mDataCollector.onAffordanceSwipingStarted(rightCorner);
+    }
+
+    public void onAffordanceSwipingAborted() {
+        mDataCollector.onAffordanceSwipingAborted();
+    }
+
+    public void onUnlockHintStarted() {
+        mDataCollector.onUnlockHintStarted();
+    }
+
+    public void onCameraHintStarted() {
+        mDataCollector.onCameraHintStarted();
+    }
+
+    public void onLeftAffordanceHintStarted() {
+        mDataCollector.onLeftAffordanceHintStarted();
+    }
+
+    public void onTouchEvent(MotionEvent event, int width, int height) {
+        if (mSessionActive && !mBouncerOn) {
+            mDataCollector.onTouchEvent(event, width, height);
+            mHumanInteractionClassifier.onTouchEvent(event);
+        }
+    }
+}
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..11388fc
--- /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..85a9bee
--- /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 static 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
new file mode 100644
index 0000000..0e45ac1
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/classifier/HumanInteractionClassifier.java
@@ -0,0 +1,202 @@
+/*
+ * 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 android.content.Context;
+import android.database.ContentObserver;
+import android.hardware.SensorEvent;
+import android.os.Build;
+import android.os.Handler;
+import android.os.UserHandle;
+import android.provider.Settings;
+import android.util.DisplayMetrics;
+import android.view.MotionEvent;
+
+import java.util.ArrayDeque;
+import java.util.ArrayList;
+
+/**
+ * An classifier trying to determine whether it is a human interacting with the phone or not.
+ */
+public class HumanInteractionClassifier extends Classifier {
+    private static final String HIC_ENABLE = "HIC_enable";
+    private static final float FINGER_DISTANCE = 0.1f;
+    private static HumanInteractionClassifier sInstance = null;
+
+    private final Handler mHandler = new Handler();
+    private final Context mContext;
+
+    private ArrayList<StrokeClassifier> mStrokeClassifiers = new ArrayList<>();
+    private ArrayList<GestureClassifier> mGestureClassifiers = new ArrayList<>();
+    private ArrayDeque<MotionEvent> mBufferedEvents = new ArrayDeque<>();
+    private final int mStrokeClassifiersSize;
+    private final int mGestureClassifiersSize;
+    private final float mDpi;
+
+    private HistoryEvaluator mHistoryEvaluator;
+    private boolean mEnableClassifier = true;
+    private int mCurrentType = Classifier.GENERIC;
+
+    protected final ContentObserver mSettingsObserver = new ContentObserver(mHandler) {
+        @Override
+        public void onChange(boolean selfChange) {
+            updateConfiguration();
+        }
+    };
+
+    private HumanInteractionClassifier(Context context) {
+        mContext = context;
+        DisplayMetrics displayMetrics = mContext.getResources().getDisplayMetrics();
+
+        // If the phone is rotated to landscape, the calculations would be wrong if xdpi and ydpi
+        // were to be used separately. Due negligible differences in xdpi and ydpi we can just
+        // take the average.
+        mDpi = (displayMetrics.xdpi + displayMetrics.ydpi) / 2.0f;
+        mClassifierData = new ClassifierData(mDpi);
+        mHistoryEvaluator = new HistoryEvaluator();
+
+        mStrokeClassifiers.add(new AnglesVarianceClassifier(mClassifierData));
+        mStrokeClassifiers.add(new SpeedClassifier(mClassifierData));
+        mStrokeClassifiers.add(new DurationCountClassifier(mClassifierData));
+        mStrokeClassifiers.add(new EndPointRatioClassifier(mClassifierData));
+        mStrokeClassifiers.add(new EndPointLengthClassifier(mClassifierData));
+        mStrokeClassifiers.add(new AccelerationClassifier(mClassifierData));
+        mStrokeClassifiers.add(new SpeedVarianceClassifier(mClassifierData));
+        mStrokeClassifiers.add(new LengthCountClassifier(mClassifierData));
+
+        mGestureClassifiers.add(new PointerCountClassifier(mClassifierData));
+        mGestureClassifiers.add(new ProximityClassifier(mClassifierData));
+
+        mStrokeClassifiersSize = mStrokeClassifiers.size();
+        mGestureClassifiersSize = mGestureClassifiers.size();
+
+        mContext.getContentResolver().registerContentObserver(
+                Settings.Global.getUriFor(HIC_ENABLE), false,
+                mSettingsObserver,
+                UserHandle.USER_ALL);
+
+        updateConfiguration();
+    }
+
+    public static HumanInteractionClassifier getInstance(Context context) {
+        if (sInstance == null) {
+            sInstance = new HumanInteractionClassifier(context);
+        }
+        return sInstance;
+    }
+
+    private void updateConfiguration() {
+        mEnableClassifier = Build.IS_DEBUGGABLE && 0 != Settings.Global.getInt(
+                mContext.getContentResolver(),
+                HIC_ENABLE, 0);
+    }
+
+    public void setType(int type) {
+        mCurrentType = type;
+    }
+
+    @Override
+    public void onTouchEvent(MotionEvent event) {
+        if (!mEnableClassifier) {
+            return;
+        }
+
+        // If the user is dragging down the notification, he might want to drag it down
+        // enough to see the content, read it for a while and then lift the finger to open
+        // the notification. This kind of motion scores very bad in the Classifier so the
+        // MotionEvents which are close to the current position of the finger are not
+        // sent to the classifiers until the finger moves far enough. When the finger if lifted
+        // up, the last MotionEvent which was far enough from the finger is set as the final
+        // MotionEvent and sent to the Classifiers.
+        if (mCurrentType == Classifier.NOTIFICATION_DRAG_DOWN) {
+            mBufferedEvents.add(MotionEvent.obtain(event));
+            Point pointEnd = new Point(event.getX() / mDpi, event.getY() / mDpi);
+
+            while (pointEnd.dist(new Point(mBufferedEvents.getFirst().getX() / mDpi,
+                    mBufferedEvents.getFirst().getY() / mDpi)) > FINGER_DISTANCE) {
+                addTouchEvent(mBufferedEvents.getFirst());
+                mBufferedEvents.remove();
+            }
+
+            int action = event.getActionMasked();
+            if (action == MotionEvent.ACTION_UP) {
+                mBufferedEvents.getFirst().setAction(MotionEvent.ACTION_UP);
+                addTouchEvent(mBufferedEvents.getFirst());
+                mBufferedEvents.clear();
+            }
+        } else {
+            addTouchEvent(event);
+        }
+    }
+
+    private void addTouchEvent(MotionEvent event) {
+        mClassifierData.update(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);
+    }
+
+    @Override
+    public void onSensorChanged(SensorEvent event) {
+        for (int i = 0; i < mStrokeClassifiers.size(); i++) {
+            mStrokeClassifiers.get(i).onSensorChanged(event);
+        }
+
+        for (int i = 0; i < mGestureClassifiers.size(); i++) {
+            mGestureClassifiers.get(i).onSensorChanged(event);
+        }
+    }
+
+    public boolean isFalseTouch() {
+        if (mEnableClassifier) {
+            return mHistoryEvaluator.getEvaluation() >= 5.0f;
+        }
+        return false;
+    }
+
+    public boolean isEnabled() {
+        return mEnableClassifier;
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/LengthCountClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/LengthCountClassifier.java
new file mode 100644
index 0000000..1ea467b
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/classifier/LengthCountClassifier.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;
+
+/**
+ * A classifier which looks at the ratio between the duration of the stroke and its number of
+ * points.
+ */
+public class LengthCountClassifier extends StrokeClassifier {
+    public LengthCountClassifier(ClassifierData classifierData) {
+    }
+
+    @Override
+    public float getFalseTouchEvaluation(int type, Stroke stroke) {
+        return LengthCountEvaluator.evaluate(stroke.getTotalLength() / stroke.getCount());
+    }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/LengthCountEvaluator.java b/packages/SystemUI/src/com/android/systemui/classifier/LengthCountEvaluator.java
new file mode 100644
index 0000000..68f163d1
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/classifier/LengthCountEvaluator.java
@@ -0,0 +1,33 @@
+/*
+ * 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;
+
+/**
+ * A classifier which looks at the ratio between the length of the stroke and its number of
+ * points.
+ */
+public class LengthCountEvaluator {
+    public static float evaluate(float value) {
+        float evaluation = 0.0f;
+        if (value < 0.07) evaluation++;
+        if (value < 0.05) evaluation++;
+        if (value < 0.02) evaluation++;
+        if (value > 0.6) evaluation++;
+        if (value > 1.2) evaluation++;
+        return evaluation;
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/Point.java b/packages/SystemUI/src/com/android/systemui/classifier/Point.java
new file mode 100644
index 0000000..f3dc2be
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/classifier/Point.java
@@ -0,0 +1,83 @@
+/*
+ * 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;
+
+public class Point {
+    public float x;
+    public float y;
+    public long timeOffsetNano;
+
+    public Point(float x, float y) {
+        this.x = x;
+        this.y = y;
+        this.timeOffsetNano = 0;
+    }
+
+    public Point(float x, float y, long timeOffsetNano) {
+        this.x = x;
+        this.y = y;
+        this.timeOffsetNano = timeOffsetNano;
+    }
+
+    public boolean equals(Point p) {
+        return x == p.x && y == p.y;
+    }
+
+    public float dist(Point a) {
+        return (float) Math.hypot(a.x - x, a.y - y);
+    }
+
+    /**
+     * Calculates the cross product of vec(this, a) and vec(this, b) where vec(x,y) is the
+     * vector from point x to point y
+     */
+    public float crossProduct(Point a, Point b) {
+        return (a.x - x) * (b.y - y) - (a.y - y) * (b.x - x);
+    }
+
+    /**
+     * Calculates the dot product of vec(this, a) and vec(this, b) where vec(x,y) is the
+     * vector from point x to point y
+     */
+    public float dotProduct(Point a, Point b) {
+        return (a.x - x) * (b.x - x) + (a.y - y) * (b.y - y);
+    }
+
+    /**
+     * Calculates the angle in radians created by points (a, this, b). If any two of these points
+     * are the same, the method will return 0.0f
+     *
+     * @return the angle in radians
+     */
+    public float getAngle(Point a, Point b) {
+        float dist1 = dist(a);
+        float dist2 = dist(b);
+
+        if (dist1 == 0.0f || dist2 == 0.0f) {
+            return 0.0f;
+        }
+
+        float crossProduct = crossProduct(a, b);
+        float dotProduct = dotProduct(a, b);
+        float cos = Math.min(1.0f, Math.max(-1.0f, dotProduct / dist1 / dist2));
+        float angle = (float) Math.acos(cos);
+        if (crossProduct < 0.0) {
+            angle = 2.0f * (float) Math.PI - angle;
+        }
+        return angle;
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/PointerCountClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/PointerCountClassifier.java
new file mode 100644
index 0000000..5097b63
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/classifier/PointerCountClassifier.java
@@ -0,0 +1,48 @@
+/*
+ * 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 android.view.MotionEvent;
+
+/**
+ * A classifier which looks at the total number of traces in the whole gesture.
+ */
+public class PointerCountClassifier extends GestureClassifier {
+    private int mCount;
+
+    public PointerCountClassifier(ClassifierData classifierData) {
+        mCount = 0;
+    }
+
+    @Override
+    public void onTouchEvent(MotionEvent event) {
+        int action = event.getActionMasked();
+
+        if (action == MotionEvent.ACTION_DOWN) {
+            mCount = 1;
+        }
+
+        if (action == MotionEvent.ACTION_POINTER_DOWN) {
+            ++mCount;
+        }
+    }
+
+    @Override
+    public float getFalseTouchEvaluation(int type) {
+        return PointerCountEvaluator.evaluate(mCount);
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/PointerCountEvaluator.java b/packages/SystemUI/src/com/android/systemui/classifier/PointerCountEvaluator.java
new file mode 100644
index 0000000..d7a3af0
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/classifier/PointerCountEvaluator.java
@@ -0,0 +1,23 @@
+/*
+ * 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;
+
+public class PointerCountEvaluator {
+    public static float evaluate(int value) {
+        return (value - 1) * (value - 1);
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/ProximityClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/ProximityClassifier.java
new file mode 100644
index 0000000..6995064
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/classifier/ProximityClassifier.java
@@ -0,0 +1,93 @@
+/*
+ * 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 android.hardware.Sensor;
+import android.hardware.SensorEvent;
+import android.view.MotionEvent;
+
+/**
+ * A classifier which looks at the proximity sensor during the gesture. It calculates the percentage
+ * the proximity sensor showing the near state during the whole gesture
+ */
+public class ProximityClassifier extends GestureClassifier {
+    private long mGestureStartTimeNano;
+    private long mNearStartTimeNano;
+    private long mNearDuration;
+    private boolean mNear;
+    private float mAverageNear;
+
+    public ProximityClassifier(ClassifierData classifierData) {
+    }
+
+    @Override
+    public void onSensorChanged(SensorEvent event) {
+        if (event.sensor.getType() == Sensor.TYPE_PROXIMITY) {
+            update(event.values[0] < event.sensor.getMaximumRange(), event.timestamp);
+        }
+    }
+
+    @Override
+    public void onTouchEvent(MotionEvent event) {
+        int action = event.getActionMasked();
+
+        if (action == MotionEvent.ACTION_DOWN) {
+            mGestureStartTimeNano = event.getEventTimeNano();
+            mNearStartTimeNano = event.getEventTimeNano();
+            mNearDuration = 0;
+        }
+
+        if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL) {
+            update(mNear, event.getEventTimeNano());
+            long duration = event.getEventTimeNano() - mGestureStartTimeNano;
+
+            if (duration == 0) {
+                mAverageNear = mNear ? 1.0f : 0.0f;
+            } else {
+                mAverageNear = (float) mNearDuration / (float) duration;
+            }
+        }
+    }
+
+
+    /**
+     * @param near is the sensor showing the near state right now
+     * @param timestampNano time of this event in nanoseconds
+     */
+    private void update(boolean near, long timestampNano) {
+        // This if is necessary because MotionEvents and SensorEvents do not come in
+        // chronological order
+        if (timestampNano > mNearStartTimeNano) {
+            // if the state before was near then add the difference of the current time and
+            // mNearStartTimeNano to mNearDuration.
+            if (mNear) {
+                mNearDuration += timestampNano - mNearStartTimeNano;
+            }
+
+            // if the new state is near, set mNearStartTimeNano equal to this moment.
+            if (near) {
+                mNearStartTimeNano = timestampNano;
+            }
+        }
+        mNear = near;
+    }
+
+    @Override
+    public float getFalseTouchEvaluation(int type) {
+        return ProximityEvaluator.evaluate(mAverageNear, type);
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/ProximityEvaluator.java b/packages/SystemUI/src/com/android/systemui/classifier/ProximityEvaluator.java
new file mode 100644
index 0000000..91002bf
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/classifier/ProximityEvaluator.java
@@ -0,0 +1,29 @@
+/*
+ * 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;
+
+public class ProximityEvaluator {
+    public static float evaluate(float value, int type) {
+        float evaluation = 0.0f;
+        float threshold = 0.1f;
+        if (type == Classifier.QUICK_SETTINGS) {
+            threshold = 1.0f;
+        }
+        if (value >= threshold) evaluation += 2.0;
+        return evaluation;
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/SpeedClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/SpeedClassifier.java
new file mode 100644
index 0000000..81b78c7
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/classifier/SpeedClassifier.java
@@ -0,0 +1,37 @@
+/*
+ * 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;
+
+/**
+ * A classifier that looks at the speed of the stroke. It calculates the speed of a stroke in
+ * inches per second.
+ */
+public class SpeedClassifier extends StrokeClassifier {
+    private final float NANOS_TO_SECONDS = 1e9f;
+
+    public SpeedClassifier(ClassifierData classifierData) {
+    }
+
+    @Override
+    public float getFalseTouchEvaluation(int type, Stroke stroke) {
+        float duration = (float) stroke.getDurationNanos() / NANOS_TO_SECONDS;
+        if (duration == 0.0f) {
+            return SpeedEvaluator.evaluate(0.0f);
+        }
+        return SpeedEvaluator.evaluate(stroke.getTotalLength() / duration);
+    }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/SpeedEvaluator.java b/packages/SystemUI/src/com/android/systemui/classifier/SpeedEvaluator.java
new file mode 100644
index 0000000..c0e4a2d
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/classifier/SpeedEvaluator.java
@@ -0,0 +1,27 @@
+/*
+ * 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;
+
+public class SpeedEvaluator {
+    public static float evaluate(float value) {
+        float evaluation = 0.0f;
+        if (value < 4.0 || value > 35.0) evaluation += 1.0;
+        if (value < 2.2) evaluation += 1.0;
+        if (value > 50.0) evaluation += 1.0;
+        return evaluation;
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/SpeedRatioEvaluator.java b/packages/SystemUI/src/com/android/systemui/classifier/SpeedRatioEvaluator.java
new file mode 100644
index 0000000..349aa9e
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/classifier/SpeedRatioEvaluator.java
@@ -0,0 +1,26 @@
+/*
+ * 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;
+
+public class SpeedRatioEvaluator {
+    public static float evaluate(float value) {
+        float evaluation = 0.0f;
+        if (value > 9.0) ++evaluation;
+        if (value > 18.0) ++evaluation;
+        return evaluation;
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/SpeedVarianceClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/SpeedVarianceClassifier.java
new file mode 100644
index 0000000..9a30fe1
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/classifier/SpeedVarianceClassifier.java
@@ -0,0 +1,124 @@
+/*
+ * 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 android.view.MotionEvent;
+
+import java.lang.Math;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+
+/**
+ * A classifier which for each point from a stroke, it creates a point on plane with coordinates
+ * (timeOffsetNano, distanceCoveredUpToThisPoint) (scaled by DURATION_SCALE and LENGTH_SCALE)
+ * and then it calculates the angle variance of these points like the class
+ * {@link AnglesVarianceClassifier} (without splitting it into two parts). The classifier ignores
+ * the last point of a stroke because the UP event comes in with some delay and this ruins the
+ * smoothness of this curve
+ */
+public class SpeedVarianceClassifier extends StrokeClassifier {
+    private HashMap<Stroke, Data> mStrokeMap = new HashMap<>();
+
+    public SpeedVarianceClassifier(ClassifierData classifierData) {
+        mClassifierData = classifierData;
+    }
+
+    @Override
+    public void onTouchEvent(MotionEvent event) {
+        int action = event.getActionMasked();
+
+        if (action == MotionEvent.ACTION_DOWN) {
+            mStrokeMap.clear();
+        }
+
+        for (int i = 0; i < event.getPointerCount(); i++) {
+            Stroke stroke = mClassifierData.getStroke(event.getPointerId(i));
+
+            if (mStrokeMap.get(stroke) == null) {
+                mStrokeMap.put(stroke, new Data());
+            }
+
+            if (action != MotionEvent.ACTION_UP && action != MotionEvent.ACTION_CANCEL
+                    && !(action == MotionEvent.ACTION_POINTER_UP && i == event.getActionIndex())) {
+                mStrokeMap.get(stroke).addPoint(
+                        stroke.getPoints().get(stroke.getPoints().size() - 1));
+            }
+        }
+    }
+
+    @Override
+    public float getFalseTouchEvaluation(int type, Stroke stroke) {
+        return SpeedVarianceEvaluator.evaluate(mStrokeMap.get(stroke).getAnglesVariance());
+    }
+
+    private static class Data {
+        private final float DURATION_SCALE = 1e8f;
+        private final float LENGTH_SCALE = 1.0f;
+
+        private List<Point> mLastThreePoints = new ArrayList<>();
+        private Point mPreviousPoint;
+        private float mPreviousAngle;
+        private float mSumSquares;
+        private float mSum;
+        private float mCount;
+        private float mDist;
+
+        public Data() {
+            mPreviousPoint = null;
+            mPreviousAngle = (float) Math.PI;
+            mSumSquares = 0.0f;
+            mSum = 0.0f;
+            mCount = 1.0f;
+            mDist = 0.0f;
+        }
+
+        public void addPoint(Point point) {
+            if (mPreviousPoint != null) {
+                mDist += mPreviousPoint.dist(point);
+            }
+
+            mPreviousPoint = point;
+            Point speedPoint = new Point((float) point.timeOffsetNano / DURATION_SCALE,
+                    mDist / LENGTH_SCALE);
+
+            // Checking if the added point is different than the previously added point
+            // Repetitions are being ignored so that proper angles are calculated.
+            if (mLastThreePoints.isEmpty()
+                    || !mLastThreePoints.get(mLastThreePoints.size() - 1).equals(speedPoint)) {
+                mLastThreePoints.add(speedPoint);
+                if (mLastThreePoints.size() == 4) {
+                    mLastThreePoints.remove(0);
+
+                    float angle = mLastThreePoints.get(1).getAngle(mLastThreePoints.get(0),
+                            mLastThreePoints.get(2));
+
+                    float difference = angle - mPreviousAngle;
+                    mSum += difference;
+                    mSumSquares += difference * difference;
+                    mCount += 1.0;
+                    mPreviousAngle = angle;
+                }
+            }
+        }
+
+        public float getAnglesVariance() {
+            return mSumSquares / mCount - (mSum / mCount) * (mSum / mCount);
+        }
+    }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/SpeedVarianceEvaluator.java b/packages/SystemUI/src/com/android/systemui/classifier/SpeedVarianceEvaluator.java
new file mode 100644
index 0000000..8f9a7e1
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/classifier/SpeedVarianceEvaluator.java
@@ -0,0 +1,28 @@
+/*
+ * 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;
+
+public class SpeedVarianceEvaluator {
+    public static float evaluate(float value) {
+        float evaluation = 0.0f;
+        if (value > 0.06) evaluation += 1.0;
+        if (value > 0.15) evaluation += 1.0;
+        if (value > 0.3) evaluation += 1.0;
+        if (value > 0.6) evaluation += 1.0;
+        return evaluation;
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/Stroke.java b/packages/SystemUI/src/com/android/systemui/classifier/Stroke.java
new file mode 100644
index 0000000..fb04d3e
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/classifier/Stroke.java
@@ -0,0 +1,71 @@
+/*
+ * 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;
+
+/**
+ * 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 final float NANOS_TO_SECONDS = 1e9f;
+
+    private ArrayList<Point> mPoints = new ArrayList<>();
+    private long mStartTimeNano;
+    private long mEndTimeNano;
+    private float mLength;
+    private final float mDpi;
+
+    public Stroke(long eventTimeNano, float dpi) {
+        mDpi = dpi;
+        mStartTimeNano = mEndTimeNano = eventTimeNano;
+    }
+
+    public void addPoint(float x, float y, long eventTimeNano) {
+        mEndTimeNano = eventTimeNano;
+        Point point = new Point(x / mDpi, y / mDpi, eventTimeNano - mStartTimeNano);
+        if (!mPoints.isEmpty()) {
+            mLength += mPoints.get(mPoints.size() - 1).dist(point);
+        }
+        mPoints.add(point);
+    }
+
+    public int getCount() {
+        return mPoints.size();
+    }
+
+    public float getTotalLength() {
+        return mLength;
+    }
+
+    public float getEndPointLength() {
+        return mPoints.get(0).dist(mPoints.get(mPoints.size() - 1));
+    }
+
+    public long getDurationNanos() {
+        return mEndTimeNano - mStartTimeNano;
+    }
+
+    public float getDurationSeconds() {
+        return (float) getDurationNanos() / NANOS_TO_SECONDS;
+    }
+
+    public ArrayList<Point> getPoints() {
+        return mPoints;
+    }
+}
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..5da392f
--- /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 3514c5d..2ea1e43 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -68,8 +68,8 @@
 import com.android.keyguard.KeyguardUpdateMonitorCallback;
 import com.android.keyguard.ViewMediatorCallback;
 import com.android.systemui.SystemUI;
+import com.android.systemui.classifier.FalsingManager;
 import com.android.systemui.statusbar.phone.FingerprintUnlockController;
-import com.android.systemui.analytics.LockedPhoneAnalytics;
 import com.android.systemui.statusbar.phone.PhoneStatusBar;
 import com.android.systemui.statusbar.phone.ScrimController;
 import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
@@ -155,6 +155,7 @@
     private static final int NOTIFY_STARTED_WAKING_UP = 21;
     private static final int NOTIFY_SCREEN_TURNED_ON = 22;
     private static final int NOTIFY_SCREEN_TURNED_OFF = 23;
+    private static final int NOTIFY_STARTED_GOING_TO_SLEEP = 24;
 
     /**
      * The default amount of time we stay awake (used for all key input)
@@ -652,6 +653,7 @@
             final boolean lockImmediately =
                     mLockPatternUtils.getPowerButtonInstantlyLocks(currentUser)
                             || !mLockPatternUtils.isSecure(currentUser);
+            long timeout = getLockTimeout();
 
             if (mExitSecureCallback != null) {
                 if (DEBUG) Log.d(TAG, "pending exit secure callback cancelled");
@@ -666,9 +668,9 @@
                 }
             } else if (mShowing) {
                 mPendingReset = true;
-            } else if (why == WindowManagerPolicy.OFF_BECAUSE_OF_TIMEOUT
+            } else if ((why == WindowManagerPolicy.OFF_BECAUSE_OF_TIMEOUT && timeout > 0)
                     || (why == WindowManagerPolicy.OFF_BECAUSE_OF_USER && !lockImmediately)) {
-                doKeyguardLaterLocked();
+                doKeyguardLaterLocked(timeout);
             } else if (!mLockPatternUtils.isLockScreenDisabled(currentUser)) {
                 mPendingLock = true;
             }
@@ -677,6 +679,8 @@
                 playSounds(true);
             }
         }
+        KeyguardUpdateMonitor.getInstance(mContext).dispatchStartedGoingToSleep(why);
+        notifyStartedGoingToSleep();
     }
 
     public void onFinishedGoingToSleep(int why) {
@@ -702,7 +706,7 @@
         KeyguardUpdateMonitor.getInstance(mContext).dispatchFinishedGoingToSleep(why);
     }
 
-    private void doKeyguardLaterLocked() {
+    private long getLockTimeout() {
         // if the screen turned off because of timeout or the user hit the power button
         // and we don't need to lock immediately, set an alarm
         // to enable it a little bit later (i.e, give the user a chance
@@ -731,23 +735,30 @@
         } else {
             timeout = lockAfterTimeout;
         }
+        return timeout;
+    }
 
-        if (timeout <= 0) {
-            // Lock now
+    private void doKeyguardLaterLocked() {
+        long timeout = getLockTimeout();
+        if (timeout == 0) {
             doKeyguardLocked(null);
         } else {
-            // Lock in the future
-            long when = SystemClock.elapsedRealtime() + timeout;
-            Intent intent = new Intent(DELAYED_KEYGUARD_ACTION);
-            intent.putExtra("seq", mDelayedShowingSequence);
-            PendingIntent sender = PendingIntent.getBroadcast(mContext,
-                    0, intent, PendingIntent.FLAG_CANCEL_CURRENT);
-            mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, when, sender);
-            if (DEBUG) Log.d(TAG, "setting alarm to turn off keyguard, seq = "
-                             + mDelayedShowingSequence);
+            doKeyguardLaterLocked(timeout);
         }
     }
 
+    private void doKeyguardLaterLocked(long timeout) {
+        // Lock in the future
+        long when = SystemClock.elapsedRealtime() + timeout;
+        Intent intent = new Intent(DELAYED_KEYGUARD_ACTION);
+        intent.putExtra("seq", mDelayedShowingSequence);
+        PendingIntent sender = PendingIntent.getBroadcast(mContext,
+                0, intent, PendingIntent.FLAG_CANCEL_CURRENT);
+        mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, when, sender);
+        if (DEBUG) Log.d(TAG, "setting alarm to turn off keyguard, seq = "
+                         + mDelayedShowingSequence);
+    }
+
     private void cancelDoKeyguardLaterLocked() {
         mDelayedShowingSequence++;
     }
@@ -910,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);
+                }
             }
         }
     }
@@ -1098,6 +1127,11 @@
         mHandler.sendEmptyMessage(VERIFY_UNLOCK);
     }
 
+    private void notifyStartedGoingToSleep() {
+        if (DEBUG) Log.d(TAG, "notifyStartedGoingToSleep");
+        mHandler.sendEmptyMessage(NOTIFY_STARTED_GOING_TO_SLEEP);
+    }
+
     private void notifyFinishedGoingToSleep() {
         if (DEBUG) Log.d(TAG, "notifyFinishedGoingToSleep");
         mHandler.sendEmptyMessage(NOTIFY_FINISHED_GOING_TO_SLEEP);
@@ -1209,6 +1243,9 @@
                 case VERIFY_UNLOCK:
                     handleVerifyUnlock();
                     break;
+                case NOTIFY_STARTED_GOING_TO_SLEEP:
+                    handleNotifyStartedGoingToSleep();
+                    break;
                 case NOTIFY_FINISHED_GOING_TO_SLEEP:
                     handleNotifyFinishedGoingToSleep();
                     break;
@@ -1244,7 +1281,7 @@
                 case START_KEYGUARD_EXIT_ANIM:
                     StartKeyguardExitAnimParams params = (StartKeyguardExitAnimParams) msg.obj;
                     handleStartKeyguardExitAnimation(params.startTime, params.fadeoutDuration);
-                    LockedPhoneAnalytics.getInstance(mContext).onSucccessfulUnlock();
+                    FalsingManager.getInstance(mContext).onSucccessfulUnlock();
                     break;
                 case KEYGUARD_DONE_PENDING_TIMEOUT:
                     Log.w(TAG, "Timeout while waiting for activity drawn!");
@@ -1546,6 +1583,13 @@
         }
     }
 
+    private void handleNotifyStartedGoingToSleep() {
+        synchronized (KeyguardViewMediator.this) {
+            if (DEBUG) Log.d(TAG, "handleNotifyStartedGoingToSleep");
+            mStatusBarKeyguardViewManager.onStartedGoingToSleep();
+        }
+    }
+
     /**
      * Handle message sent by {@link #notifyFinishedGoingToSleep()}
      * @see #NOTIFY_FINISHED_GOING_TO_SLEEP
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/recents/Constants.java b/packages/SystemUI/src/com/android/systemui/recents/Constants.java
index a4acf83..9781664 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/Constants.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/Constants.java
@@ -29,8 +29,6 @@
     }
 
     public static class DebugFlags {
-        // Enable this with any other debug flag to see more info
-        public static final boolean Verbose = false;
 
         public static class App {
             // Enables debug drawing for the transition thumbnail
@@ -39,14 +37,8 @@
             public static final boolean EnableTaskFiltering = false;
             // Enables dismiss-all
             public static final boolean EnableDismissAll = false;
-            // Enables debug mode
-            public static final boolean EnableDebugMode = false;
-            // Enables the search bar layout
-            public static final boolean EnableSearchLayout = true;
             // Enables the thumbnail alpha on the front-most task
             public static final boolean EnableThumbnailAlphaOnFrontmost = false;
-            // Enables all system stacks to show up in the same recents stack
-            public static final boolean EnableMultiStackToSingleStack = true;
             // This disables the bitmap and icon caches
             public static final boolean DisableBackgroundCache = false;
             // Enables the simulated task affiliations
@@ -65,7 +57,6 @@
     public static class Values {
         public static class App {
             public static int AppWidgetHostId = 1024;
-            public static String DebugModeVersion = "A";
         }
 
         public static class TaskStackView {
diff --git a/packages/SystemUI/src/com/android/systemui/recents/Recents.java b/packages/SystemUI/src/com/android/systemui/recents/Recents.java
index 0cfa7a1..a3e89f2 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/Recents.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/Recents.java
@@ -116,7 +116,7 @@
         /** Preloads the next task */
         public void run() {
             // Temporarily skip this if multi stack is enabled
-            if (mConfig.multiStackEnabled) return;
+            if (mConfig.multiWindowEnabled) return;
 
             RecentsConfiguration config = RecentsConfiguration.getInstance();
             if (config.svelteLevel == RecentsConfiguration.SVELTE_NONE) {
@@ -151,7 +151,8 @@
         public void onReceive(Context context, Intent intent) {
             switch (intent.getAction()) {
                 case ACTION_PROXY_NOTIFY_RECENTS_VISIBLITY_TO_OWNER:
-                    visibilityChanged(intent.getBooleanExtra(EXTRA_RECENTS_VISIBILITY, false));
+                    visibilityChanged(context,
+                            intent.getBooleanExtra(EXTRA_RECENTS_VISIBILITY, false));
                     break;
                 case ACTION_PROXY_SCREEN_PINNING_REQUEST_TO_OWNER:
                     onStartScreenPinning(context);
@@ -160,7 +161,6 @@
         }
     }
 
-    static RecentsComponent.Callbacks sRecentsComponentCallbacks;
     static RecentsTaskLoadPlan sInstanceLoadPlan;
     static Recents sInstance;
 
@@ -176,7 +176,6 @@
 
     // Task launching
     RecentsConfiguration mConfig;
-    Rect mWindowRect = new Rect();
     Rect mTaskStackBounds = new Rect();
     Rect mSystemInsets = new Rect();
     TaskViewTransform mTmpTransform = new TaskViewTransform();
@@ -372,9 +371,9 @@
         if (topTask != null && !mSystemServicesProxy.isRecentsTopMost(topTask, topTaskHome)) {
             sInstanceLoadPlan.preloadRawTasks(topTaskHome.value);
             loader.preloadTasks(sInstanceLoadPlan, topTaskHome.value);
-            TaskStack top = sInstanceLoadPlan.getAllTaskStacks().get(0);
-            if (top.getTaskCount() > 0) {
-                preCacheThumbnailTransitionBitmapAsync(topTask, top, mDummyStackView,
+            TaskStack stack = sInstanceLoadPlan.getTaskStack();
+            if (stack.getTaskCount() > 0) {
+                preCacheThumbnailTransitionBitmapAsync(topTask, stack, mDummyStackView,
                         topTaskHome.value);
             }
         }
@@ -388,16 +387,10 @@
     void showRelativeAffiliatedTask(boolean showNextTask) {
         // Return early if there is no focused stack
         int focusedStackId = mSystemServicesProxy.getFocusedStack();
-        TaskStack focusedStack = null;
         RecentsTaskLoader loader = RecentsTaskLoader.getInstance();
         RecentsTaskLoadPlan plan = loader.createLoadPlan(mContext);
         loader.preloadTasks(plan, true /* isTopTaskHome */);
-        if (mConfig.multiStackEnabled) {
-            if (focusedStackId < 0) return;
-            focusedStack = plan.getTaskStack(focusedStackId);
-        } else {
-            focusedStack = plan.getAllTaskStacks().get(0);
-        }
+        TaskStack focusedStack = plan.getTaskStack();
 
         // Return early if there are no tasks in the focused stack
         if (focusedStack == null || focusedStack.getTaskCount() == 0) return;
@@ -502,10 +495,13 @@
     /** Prepares the header bar layout. */
     void reloadHeaderBarLayout() {
         Resources res = mContext.getResources();
-        mWindowRect = mSystemServicesProxy.getWindowRect();
+        Rect windowRect = mSystemServicesProxy.getWindowRect();
+
         mStatusBarHeight = res.getDimensionPixelSize(com.android.internal.R.dimen.status_bar_height);
         mNavBarHeight = res.getDimensionPixelSize(com.android.internal.R.dimen.navigation_bar_height);
         mNavBarWidth = res.getDimensionPixelSize(com.android.internal.R.dimen.navigation_bar_width);
+        // TODO: We can't rely on this anymore since the activity context will yield different
+        //      resources while multiwindow is enabled
         mConfig = RecentsConfiguration.reinitialize(mContext, mSystemServicesProxy);
         mConfig.updateOnConfigurationChange();
         Rect searchBarBounds = new Rect();
@@ -513,10 +509,10 @@
         // have the right thumbnail bounds to animate to.
         // Note: We have to reload the widget id before we get the task stack bounds below
         if (mSystemServicesProxy.getOrBindSearchAppWidget(mContext, mAppWidgetHost) != null) {
-            mConfig.getSearchBarBounds(mWindowRect.width(), mWindowRect.height(),
+            mConfig.getSearchBarBounds(windowRect,
                     mStatusBarHeight, searchBarBounds);
         }
-        mConfig.getAvailableTaskStackBounds(mWindowRect.width(), mWindowRect.height(),
+        mConfig.getAvailableTaskStackBounds(windowRect,
                 mStatusBarHeight, (mConfig.hasTransposedNavBar ? mNavBarWidth : 0), searchBarBounds,
                 mTaskStackBounds);
         if (mConfig.isLandscape && mConfig.hasTransposedNavBar) {
@@ -531,7 +527,7 @@
         TaskStackViewLayoutAlgorithm algo = mDummyStackView.getStackAlgorithm();
         Rect taskStackBounds = new Rect(mTaskStackBounds);
         taskStackBounds.bottom -= mSystemInsets.bottom;
-        algo.computeRects(mWindowRect.width(), mWindowRect.height(), taskStackBounds);
+        algo.computeRects(windowRect.width(), windowRect.height(), taskStackBounds);
         Rect taskViewSize = algo.getUntransformedTaskViewSize();
         int taskBarHeight = res.getDimensionPixelSize(R.dimen.recents_task_bar_height);
         synchronized (mHeaderBarLock) {
@@ -540,6 +536,7 @@
             mHeaderBar.measure(
                     View.MeasureSpec.makeMeasureSpec(taskViewSize.width(), View.MeasureSpec.EXACTLY),
                     View.MeasureSpec.makeMeasureSpec(taskBarHeight, View.MeasureSpec.EXACTLY));
+            // TODO: may not be needed
             mHeaderBar.layout(0, 0, taskViewSize.width(), taskBarHeight);
         }
     }
@@ -740,7 +737,10 @@
     /** Starts the recents activity */
     void startRecentsActivity(ActivityManager.RunningTaskInfo topTask, boolean isTopTaskHome) {
         RecentsTaskLoader loader = RecentsTaskLoader.getInstance();
-        RecentsConfiguration.reinitialize(mContext, mSystemServicesProxy);
+        // Don't reinitialize the configuration completely here, since it has the wrong context,
+        // only update the parts that we can get from any context
+        RecentsConfiguration config = RecentsConfiguration.getInstance();
+        config.reinitializeWithApplicationContext(mContext, mSystemServicesProxy);
 
         if (sInstanceLoadPlan == null) {
             // Create a new load plan if onPreloadRecents() was never triggered
@@ -749,10 +749,9 @@
 
         // Temporarily skip the transition (use a dummy fade) if multi stack is enabled.
         // For multi-stack we need to figure out where each of the tasks are going.
-        if (mConfig.multiStackEnabled) {
+        if (mConfig.multiWindowEnabled) {
             loader.preloadTasks(sInstanceLoadPlan, true);
-            ArrayList<TaskStack> stacks = sInstanceLoadPlan.getAllTaskStacks();
-            TaskStack stack = stacks.get(0);
+            TaskStack stack = sInstanceLoadPlan.getTaskStack();
             mDummyStackView.updateMinMaxScrollForStack(stack, mTriggeredFromAltTab, true);
             TaskStackViewLayoutAlgorithm.VisibilityReport stackVr =
                     mDummyStackView.computeStackVisibilityReport();
@@ -765,8 +764,7 @@
         if (!sInstanceLoadPlan.hasTasks()) {
             loader.preloadTasks(sInstanceLoadPlan, isTopTaskHome);
         }
-        ArrayList<TaskStack> stacks = sInstanceLoadPlan.getAllTaskStacks();
-        TaskStack stack = stacks.get(0);
+        TaskStack stack = sInstanceLoadPlan.getTaskStack();
 
         // Prepare the dummy stack for the transition
         mDummyStackView.updateMinMaxScrollForStack(stack, mTriggeredFromAltTab, isTopTaskHome);
@@ -841,18 +839,12 @@
         mCanReuseTaskStackViews = true;
     }
 
-    /** Sets the RecentsComponent callbacks. */
-    @Override
-    public void setCallback(RecentsComponent.Callbacks cb) {
-        sRecentsComponentCallbacks = cb;
-    }
-
     /** Notifies the callbacks that the visibility of Recents has changed. */
     @ProxyFromAnyToSystemUser
     public static void notifyVisibilityChanged(Context context, SystemServicesProxy ssp,
             boolean visible) {
         if (ssp.isForegroundUserSystem()) {
-            visibilityChanged(visible);
+            visibilityChanged(context, visible);
         } else {
             Intent intent = createLocalBroadcastIntent(context,
                     ACTION_PROXY_NOTIFY_RECENTS_VISIBLITY_TO_OWNER);
@@ -860,9 +852,13 @@
             context.sendBroadcastAsUser(intent, UserHandle.SYSTEM);
         }
     }
-    static void visibilityChanged(boolean visible) {
-        if (sRecentsComponentCallbacks != null) {
-            sRecentsComponentCallbacks.onVisibilityChanged(visible);
+    static void visibilityChanged(Context context, boolean visible) {
+        // For the primary user, the context for the SystemUI component is the SystemUIApplication
+        SystemUIApplication app = (SystemUIApplication)
+                getInstanceAndStartIfNeeded(context.getApplicationContext()).mContext;
+        PhoneStatusBar statusBar = app.getComponent(PhoneStatusBar.class);
+        if (statusBar != null) {
+            statusBar.updateRecentsVisibility(visible);
         }
     }
 
@@ -880,7 +876,7 @@
     static void onStartScreenPinning(Context context) {
         // For the primary user, the context for the SystemUI component is the SystemUIApplication
         SystemUIApplication app = (SystemUIApplication)
-                getInstanceAndStartIfNeeded(context).mContext;
+                getInstanceAndStartIfNeeded(context.getApplicationContext()).mContext;
         PhoneStatusBar statusBar = app.getComponent(PhoneStatusBar.class);
         if (statusBar != null) {
             statusBar.showScreenPinningRequest(false);
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
index d0876fa..c53e573 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
@@ -25,6 +25,7 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
+import android.content.res.Configuration;
 import android.os.Bundle;
 import android.os.SystemClock;
 import android.os.UserHandle;
@@ -37,14 +38,12 @@
 import com.android.systemui.Prefs;
 import com.android.systemui.R;
 import com.android.systemui.recents.misc.Console;
-import com.android.systemui.recents.misc.DebugTrigger;
 import com.android.systemui.recents.misc.ReferenceCountedTrigger;
 import com.android.systemui.recents.misc.SystemServicesProxy;
 import com.android.systemui.recents.model.RecentsTaskLoadPlan;
 import com.android.systemui.recents.model.RecentsTaskLoader;
 import com.android.systemui.recents.model.Task;
 import com.android.systemui.recents.model.TaskStack;
-import com.android.systemui.recents.views.DebugOverlayView;
 import com.android.systemui.recents.views.RecentsView;
 import com.android.systemui.recents.views.SystemBarScrimViews;
 import com.android.systemui.recents.views.ViewAnimation;
@@ -56,8 +55,7 @@
  * The main Recents activity that is started from AlternateRecentsComponent.
  */
 public class RecentsActivity extends Activity implements RecentsView.RecentsViewCallbacks,
-        RecentsAppWidgetHost.RecentsAppWidgetHostCallbacks,
-        DebugOverlayView.DebugOverlayViewCallbacks {
+        RecentsAppWidgetHost.RecentsAppWidgetHostCallbacks {
 
     RecentsConfiguration mConfig;
     long mLastTabKeyEventTime;
@@ -66,9 +64,7 @@
     RecentsView mRecentsView;
     SystemBarScrimViews mScrimViews;
     ViewStub mEmptyViewStub;
-    ViewStub mDebugOverlayStub;
     View mEmptyView;
-    DebugOverlayView mDebugOverlay;
 
     // Resize task debug
     RecentsResizeTaskDialog mResizeTaskDebugDialog;
@@ -175,16 +171,6 @@
         }
     };
 
-    /**
-     * A custom debug trigger to listen for a debug key chord.
-     */
-    final DebugTrigger mDebugTrigger = new DebugTrigger(new Runnable() {
-        @Override
-        public void run() {
-            onDebugModeTriggered();
-        }
-    });
-
     /** Updates the set of recent tasks */
     void updateRecentsTasks() {
         // If AlternateRecentsComponent has preloaded a load plan, then use that to prevent
@@ -205,10 +191,10 @@
         loadOpts.numVisibleTaskThumbnails = mConfig.launchedNumVisibleThumbnails;
         loader.loadTasks(this, plan, loadOpts);
 
-        ArrayList<TaskStack> stacks = plan.getAllTaskStacks();
+        TaskStack stack = plan.getTaskStack();
         mConfig.launchedWithNoRecentTasks = !plan.hasTasks();
         if (!mConfig.launchedWithNoRecentTasks) {
-            mRecentsView.setTaskStacks(stacks);
+            mRecentsView.setTaskStack(stack);
         }
 
         // Create the home intent runnable
@@ -224,20 +210,16 @@
                         R.anim.recents_to_launcher_exit));
 
         // Mark the task that is the launch target
-        int taskStackCount = stacks.size();
         int launchTaskIndexInStack = 0;
         if (mConfig.launchedToTaskId != -1) {
-            for (int i = 0; i < taskStackCount; i++) {
-                TaskStack stack = stacks.get(i);
-                ArrayList<Task> tasks = stack.getTasks();
-                int taskCount = tasks.size();
-                for (int j = 0; j < taskCount; j++) {
-                    Task t = tasks.get(j);
-                    if (t.key.id == mConfig.launchedToTaskId) {
-                        t.isLaunchTarget = true;
-                        launchTaskIndexInStack = tasks.size() - j - 1;
-                        break;
-                    }
+            ArrayList<Task> tasks = stack.getTasks();
+            int taskCount = tasks.size();
+            for (int j = 0; j < taskCount; j++) {
+                Task t = tasks.get(j);
+                if (t.key.id == mConfig.launchedToTaskId) {
+                    t.isLaunchTarget = true;
+                    launchTaskIndexInStack = tasks.size() - j - 1;
+                    break;
                 }
             }
         }
@@ -278,11 +260,7 @@
             MetricsLogger.count(this, "overview_source_home", 1);
         }
         // Keep track of the total stack task count
-        int taskCount = 0;
-        for (int i = 0; i < stacks.size(); i++) {
-            TaskStack stack = stacks.get(i);
-            taskCount += stack.getTaskCount();
-        }
+        int taskCount = stack.getTaskCount();
         MetricsLogger.histogram(this, "overview_task_count", taskCount);
     }
 
@@ -359,9 +337,7 @@
                 View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN |
                 View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION);
         mEmptyViewStub = (ViewStub) findViewById(R.id.empty_view_stub);
-        mDebugOverlayStub = (ViewStub) findViewById(R.id.debug_overlay_stub);
         mScrimViews = new SystemBarScrimViews(this, mConfig);
-        inflateDebugOverlay();
 
         // Bind the search app widget when we first start up
         mSearchWidgetInfo = ssp.getOrBindSearchAppWidget(this, mAppWidgetHost);
@@ -373,27 +349,10 @@
         registerReceiver(mSystemBroadcastReceiver, filter);
     }
 
-    /** Inflates the debug overlay if debug mode is enabled. */
-    void inflateDebugOverlay() {
-        if (!Constants.DebugFlags.App.EnableDebugMode) return;
-
-        if (mConfig.debugModeEnabled && mDebugOverlay == null) {
-            // Inflate the overlay and seek bars
-            mDebugOverlay = (DebugOverlayView) mDebugOverlayStub.inflate();
-            mDebugOverlay.setCallbacks(this);
-            mRecentsView.setDebugOverlay(mDebugOverlay);
-        }
-    }
-
     @Override
     protected void onNewIntent(Intent intent) {
         super.onNewIntent(intent);
         setIntent(intent);
-
-        // Clear any debug rects
-        if (mDebugOverlay != null) {
-            mDebugOverlay.clear();
-        }
     }
 
     @Override
@@ -545,8 +504,6 @@
             default:
                 break;
         }
-        // Pass through the debug trigger
-        mDebugTrigger.onKeyEvent(keyCode);
         return super.onKeyDown(keyCode, event);
     }
 
@@ -564,33 +521,6 @@
         dismissRecentsToFocusedTaskOrHome(true);
     }
 
-    /** Called when debug mode is triggered */
-    public void onDebugModeTriggered() {
-        if (mConfig.developerOptionsEnabled) {
-            if (Prefs.getBoolean(this, Prefs.Key.DEBUG_MODE_ENABLED, false /* boolean */)) {
-                // Disable the debug mode
-                Prefs.remove(this, Prefs.Key.DEBUG_MODE_ENABLED);
-                mConfig.debugModeEnabled = false;
-                inflateDebugOverlay();
-                if (mDebugOverlay != null) {
-                    mDebugOverlay.disable();
-                }
-            } else {
-                // Enable the debug mode
-                Prefs.putBoolean(this, Prefs.Key.DEBUG_MODE_ENABLED, true);
-                mConfig.debugModeEnabled = true;
-                inflateDebugOverlay();
-                if (mDebugOverlay != null) {
-                    mDebugOverlay.enable();
-                }
-            }
-            Toast.makeText(this, "Debug mode (" + Constants.Values.App.DebugModeVersion + ") " +
-                (mConfig.debugModeEnabled ? "Enabled" : "Disabled") + ", please restart Recents now",
-                Toast.LENGTH_SHORT).show();
-        }
-    }
-
-
     /**** RecentsResizeTaskDialog ****/
 
     private RecentsResizeTaskDialog getResizeTaskDebugDialog() {
@@ -662,16 +592,4 @@
             mRecentsView.setSearchBar(null);
         }
     }
-
-    /**** DebugOverlayView.DebugOverlayViewCallbacks ****/
-
-    @Override
-    public void onPrimarySeekBarChanged(float progress) {
-        // Do nothing
-    }
-
-    @Override
-    public void onSecondarySeekBarChanged(float progress) {
-        // Do nothing
-    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java
index dfe7e96..a59eb30 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java
@@ -134,7 +134,7 @@
     public boolean fakeShadows;
 
     /** Dev options and global settings */
-    public boolean multiStackEnabled;
+    public boolean multiWindowEnabled;
     public boolean lockToAppEnabled;
     public boolean developerOptionsEnabled;
     public boolean debugModeEnabled;
@@ -166,7 +166,7 @@
             sInstance.update(context);
             sPrevConfigurationHashCode = configHashCode;
         }
-        sInstance.updateOnReinitialize(context, ssp);
+        sInstance.reinitializeWithApplicationContext(context.getApplicationContext(), ssp);
         return sInstance;
     }
 
@@ -276,14 +276,14 @@
         systemInsets.set(insets);
     }
 
-    /** Updates the states that need to be re-read whenever we re-initialize. */
-    void updateOnReinitialize(Context context, SystemServicesProxy ssp) {
+    /** Updates the states that need to be re-read from the application context. */
+    void reinitializeWithApplicationContext(Context context, SystemServicesProxy ssp) {
         // Check if the developer options are enabled
         developerOptionsEnabled = ssp.getGlobalSetting(context,
                 Settings.Global.DEVELOPMENT_SETTINGS_ENABLED) != 0;
         lockToAppEnabled = ssp.getSystemSetting(context,
                 Settings.System.LOCK_TO_APP_ENABLED) != 0;
-        multiStackEnabled = "true".equals(ssp.getSystemProperty("persist.sys.debug.multi_window"));
+        multiWindowEnabled = "true".equals(ssp.getSystemProperty("persist.sys.debug.multi_window"));
     }
 
     /** Called when the configuration has changed, and we want to reset any configuration specific
@@ -320,14 +320,16 @@
      * Returns the task stack bounds in the current orientation. These bounds do not account for
      * the system insets.
      */
-    public void getAvailableTaskStackBounds(int windowWidth, int windowHeight, int topInset,
+    public void getAvailableTaskStackBounds(Rect windowBounds, int topInset,
             int rightInset, Rect searchBarBounds, Rect taskStackBounds) {
         if (isLandscape && hasTransposedSearchBar) {
             // In landscape, the search bar appears on the left, but we overlay it on top
-            taskStackBounds.set(0, topInset, windowWidth - rightInset, windowHeight);
+            taskStackBounds.set(windowBounds.left, windowBounds.top + topInset,
+                    windowBounds.right - rightInset, windowBounds.bottom);
         } else {
             // In portrait, the search bar appears on the top (which already has the inset)
-            taskStackBounds.set(0, searchBarBounds.bottom, windowWidth, windowHeight);
+            taskStackBounds.set(windowBounds.left, searchBarBounds.bottom,
+                    windowBounds.right, windowBounds.bottom);
         }
     }
 
@@ -335,16 +337,17 @@
      * Returns the search bar bounds in the current orientation.  These bounds do not account for
      * the system insets.
      */
-    public void getSearchBarBounds(int windowWidth, int windowHeight, int topInset,
-            Rect searchBarSpaceBounds) {
+    public void getSearchBarBounds(Rect windowBounds, int topInset, Rect searchBarSpaceBounds) {
         // Return empty rects if search is not enabled
         int searchBarSize = searchBarSpaceHeightPx;
         if (isLandscape && hasTransposedSearchBar) {
             // In landscape, the search bar appears on the left
-            searchBarSpaceBounds.set(0, topInset, searchBarSize, windowHeight);
+            searchBarSpaceBounds.set(windowBounds.left, windowBounds.top + topInset,
+                    windowBounds.left + searchBarSize, windowBounds.bottom);
         } else {
             // In portrait, the search bar appears on the top
-            searchBarSpaceBounds.set(0, topInset, windowWidth, topInset + searchBarSize);
+            searchBarSpaceBounds.set(windowBounds.left, windowBounds.top + topInset,
+                    windowBounds.right, windowBounds.top + topInset + searchBarSize);
         }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsResizeTaskDialog.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsResizeTaskDialog.java
index 300ea2a..85f5b5d 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsResizeTaskDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsResizeTaskDialog.java
@@ -16,6 +16,7 @@
 
 package com.android.systemui.recents;
 
+import android.app.ActivityManager;
 import android.app.AlertDialog;
 import android.app.Dialog;
 import android.app.DialogFragment;
@@ -24,19 +25,16 @@
 import android.content.DialogInterface;
 import android.graphics.Rect;
 import android.os.Bundle;
-import android.util.Pair;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.widget.Button;
+import android.widget.Toast;
 import com.android.systemui.R;
 import com.android.systemui.recents.misc.SystemServicesProxy;
 import com.android.systemui.recents.model.RecentsTaskLoader;
 import com.android.systemui.recents.model.Task;
-import com.android.systemui.recents.RecentsActivity;
 import com.android.systemui.recents.views.RecentsView;
 
-import java.util.ArrayList;
-
 /**
  * A helper for the dialogs that show when task debugging is on.
  */
@@ -54,10 +52,18 @@
     private static final int PLACE_BOTTOM_LEFT = 7;
     private static final int PLACE_BOTTOM_RIGHT = 8;
     private static final int PLACE_FULL = 9;
+    private static final int PLACE_DOCK_LEFT = 10;
+    private static final int PLACE_DOCK_RIGHT = 11;
+    private static final int PLACE_DOCK_TOP = 12;
+    private static final int PLACE_DOCK_BOTTOM = 13;
 
     // The button resource ID combined with the arrangement command.
     private static final int[][] BUTTON_DEFINITIONS =
-           {{R.id.place_left, PLACE_LEFT},
+           {{R.id.place_dock_left, PLACE_DOCK_LEFT},
+            {R.id.place_dock_right, PLACE_DOCK_RIGHT},
+            {R.id.place_dock_top, PLACE_DOCK_TOP},
+            {R.id.place_dock_bottom, PLACE_DOCK_BOTTOM},
+            {R.id.place_left, PLACE_LEFT},
             {R.id.place_right, PLACE_RIGHT},
             {R.id.place_top, PLACE_TOP},
             {R.id.place_bottom, PLACE_BOTTOM},
@@ -76,6 +82,12 @@
     private Rect[] mBounds = {new Rect(), new Rect(), new Rect(), new Rect()};
     private Task[] mTasks = {null, null, null, null};
 
+    /**
+     * Called by FragmentManager
+     */
+    public RecentsResizeTaskDialog() {
+    }
+
     public RecentsResizeTaskDialog(FragmentManager mgr, RecentsActivity activity) {
         mFragmentManager = mgr;
         mRecentsActivity = activity;
@@ -86,13 +98,11 @@
     void showResizeTaskDialog(Task mainTask, RecentsView rv) {
         mTasks[0] = mainTask;
         mRecentsView = rv;
-
-        show(mFragmentManager, TAG);
+        showAllowingStateLoss(mFragmentManager, TAG);
     }
 
     /** Creates a new resize-task dialog. */
-    private void createResizeTaskDialog(final Context context, LayoutInflater inflater,
-            AlertDialog.Builder builder) {
+    private void createResizeTaskDialog(LayoutInflater inflater, AlertDialog.Builder builder) {
         builder.setTitle(R.string.recents_caption_resize);
         mResizeTaskDialogContent =
                 inflater.inflate(R.layout.recents_task_resize_dialog, null, false);
@@ -104,7 +114,17 @@
                 b.setOnClickListener(
                         new View.OnClickListener() {
                             public void onClick(View v) {
-                                placeTasks(action);
+                                switch (action) {
+                                    case PLACE_DOCK_LEFT:
+                                    case PLACE_DOCK_RIGHT:
+                                    case PLACE_DOCK_TOP:
+                                    case PLACE_DOCK_BOTTOM:
+                                        placeDockTasks(action);
+                                        break;
+                                    default:
+                                        placeTasks(action);
+                                        break;
+                                }
                             }
                         });
             }
@@ -113,7 +133,7 @@
         builder.setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() {
             @Override
             public void onClick(DialogInterface dialog, int which) {
-                dismiss();
+                dismissAllowingStateLoss();
             }
         });
 
@@ -122,7 +142,7 @@
 
     /** Helper function to place window(s) on the display according to an arrangement request. */
     private void placeTasks(int arrangement) {
-        Rect rect = mSsp.getWindowRect();
+        Rect rect = mSsp.getDisplayRect();
         for (int i = 0; i < mBounds.length; ++i) {
             mBounds[i].set(rect);
             if (i != 0) {
@@ -197,7 +217,7 @@
                 break;
             case PLACE_FULL:
                 // Nothing to change.
-                mBounds[0] = null;
+                mBounds[0] = new Rect();
                 break;
         }
 
@@ -211,12 +231,12 @@
         }
 
         // Get rid of the dialog.
-        dismiss();
+        dismissAllowingStateLoss();
         mRecentsActivity.dismissRecentsToHomeWithoutTransitionAnimation();
 
         // In debug mode, we force all task to be resizeable regardless of the
         // current app configuration.
-        if (RecentsConfiguration.getInstance().multiStackEnabled) {
+        if (RecentsConfiguration.getInstance().multiWindowEnabled) {
             for (int i = additionalTasks; i >= 0; --i) {
                 if (mTasks[i] != null) {
                     mSsp.setTaskResizeable(mTasks[i].key.id);
@@ -233,12 +253,44 @@
         }
     }
 
+    /**
+     * Helper function to place docked window(s) on the display according to an arrangement request.
+     */
+    private void placeDockTasks(int arrangement) {
+        int createMode = ActivityManager.DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT;
+        switch (arrangement) {
+            case PLACE_DOCK_LEFT:
+                createMode = ActivityManager.DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT;
+                break;
+            case PLACE_DOCK_TOP:
+                createMode = ActivityManager.DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT;
+                break;
+            case PLACE_DOCK_RIGHT:
+                createMode = ActivityManager.DOCKED_STACK_CREATE_MODE_BOTTOM_OR_RIGHT;
+                break;
+            case PLACE_DOCK_BOTTOM:
+                createMode = ActivityManager.DOCKED_STACK_CREATE_MODE_BOTTOM_OR_RIGHT;
+                break;
+        }
+
+        // Dismiss the dialog before trying to launch the task
+        dismissAllowingStateLoss();
+
+        if (mTasks[0].key.stackId != ActivityManager.DOCKED_STACK_ID) {
+            int taskId = mTasks[0].key.id;
+            mSsp.setTaskResizeable(taskId);
+            mSsp.dockTask(taskId, createMode);
+            mRecentsView.launchTask(mTasks[0], null);
+        } else {
+            Toast.makeText(getContext(), "Already docked", Toast.LENGTH_SHORT);
+        }
+    }
+
     @Override
     public Dialog onCreateDialog(Bundle args) {
-        final Context context = this.getActivity();
         LayoutInflater inflater = getActivity().getLayoutInflater();
         AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
-        createResizeTaskDialog(context, inflater, builder);
+        createResizeTaskDialog(inflater, builder);
         return builder.create();
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/recents/misc/DebugTrigger.java b/packages/SystemUI/src/com/android/systemui/recents/misc/DebugTrigger.java
deleted file mode 100644
index fbf8a86..0000000
--- a/packages/SystemUI/src/com/android/systemui/recents/misc/DebugTrigger.java
+++ /dev/null
@@ -1,70 +0,0 @@
-/*
- * Copyright (C) 2014 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.recents.misc;
-
-import android.os.Handler;
-import android.os.SystemClock;
-import android.view.KeyEvent;
-import com.android.systemui.recents.Constants;
-
-/**
- * A trigger for catching a debug chord.
- * We currently use volume up then volume down to trigger this mode.
- */
-public class DebugTrigger {
-
-    Handler mHandler;
-    Runnable mTriggeredRunnable;
-
-    int mLastKeyCode;
-    long mLastKeyCodeTime;
-
-    public DebugTrigger(Runnable triggeredRunnable) {
-        mHandler = new Handler();
-        mTriggeredRunnable = triggeredRunnable;
-    }
-
-    /** Resets the debug trigger */
-    void reset() {
-        mLastKeyCode = 0;
-        mLastKeyCodeTime = 0;
-    }
-
-    /**
-     * Processes a key event and tests if it is a part of the trigger. If the chord is complete,
-     * then we just call the callback.
-     */
-    public void onKeyEvent(int keyCode) {
-        if (!Constants.DebugFlags.App.EnableDebugMode) return;
-
-        if (mLastKeyCode == 0) {
-            if (keyCode == KeyEvent.KEYCODE_VOLUME_UP) {
-                mLastKeyCode = keyCode;
-                mLastKeyCodeTime = SystemClock.uptimeMillis();
-                return;
-            }
-        } else {
-            if (mLastKeyCode == KeyEvent.KEYCODE_VOLUME_UP &&
-                    keyCode == KeyEvent.KEYCODE_VOLUME_DOWN) {
-                if ((SystemClock.uptimeMillis() - mLastKeyCodeTime) < 750) {
-                    mTriggeredRunnable.run();
-                }
-            }
-        }
-        reset();
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
index bead1b0..a2d7d01 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
@@ -32,6 +32,7 @@
 import android.content.pm.ActivityInfo;
 import android.content.pm.IPackageManager;
 import android.content.pm.PackageManager;
+import android.content.pm.PackageParser;
 import android.content.pm.ResolveInfo;
 import android.content.res.Resources;
 import android.graphics.Bitmap;
@@ -282,6 +283,30 @@
         }
     }
 
+    /**
+     * Resizes the given task to the new bounds.
+     */
+    public void resizeTask(int taskId, Rect bounds) {
+        if (mIam == null) return;
+
+        try {
+            mIam.resizeTask(taskId, bounds, ActivityManager.RESIZE_MODE_FORCED);
+        } catch (RemoteException e) {
+            e.printStackTrace();
+        }
+    }
+
+    /** Docks a task to the side of the screen. */
+    public void dockTask(int taskId, int createMode) {
+        if (mIam == null) return;
+
+        try {
+            mIam.moveTaskToDockedStack(taskId, createMode, true);
+        } catch (RemoteException e) {
+            e.printStackTrace();
+        }
+    }
+
     /** Returns the focused stack id. */
     public int getFocusedStack() {
         if (mIam == null) return -1;
@@ -637,16 +662,36 @@
     }
 
     /**
+     * Returns the display rect.
+     */
+    public Rect getDisplayRect() {
+        Rect displayRect = new Rect();
+        if (mWm == null) return displayRect;
+
+        Point p = new Point();
+        mWm.getDefaultDisplay().getRealSize(p);
+        displayRect.set(0, 0, p.x, p.y);
+        return displayRect;
+    }
+
+    /**
      * Returns the window rect.
      */
     public Rect getWindowRect() {
         Rect windowRect = new Rect();
-        if (mWm == null) return windowRect;
+        if (mIam == null) return windowRect;
 
-        Point p = new Point();
-        mWm.getDefaultDisplay().getRealSize(p);
-        windowRect.set(0, 0, p.x, p.y);
-        return windowRect;
+        try {
+            // Use the home stack bounds
+            ActivityManager.StackInfo stackInfo = mIam.getStackInfo(ActivityManager.HOME_STACK_ID);
+            if (stackInfo != null) {
+                windowRect.set(stackInfo.bounds);
+            }
+        } catch (RemoteException e) {
+            e.printStackTrace();
+        } finally {
+            return windowRect;
+        }
     }
 
     /** Starts an activity from recents. */
diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoadPlan.java b/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoadPlan.java
index b8015c0..649cb4d 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoadPlan.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoadPlan.java
@@ -20,12 +20,10 @@
 import android.content.Context;
 import android.content.res.Resources;
 import android.graphics.Bitmap;
-import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
 import android.os.UserHandle;
 import android.util.Log;
 import android.util.SparseArray;
-import com.android.systemui.recents.Constants;
 import com.android.systemui.recents.RecentsConfiguration;
 import com.android.systemui.recents.misc.SystemServicesProxy;
 
@@ -63,7 +61,7 @@
     SystemServicesProxy mSystemServicesProxy;
 
     List<ActivityManager.RecentTaskInfo> mRawTasks;
-    SparseArray<TaskStack> mStacks = new SparseArray<>();
+    TaskStack mStack;
     HashMap<Task.ComponentNameKey, ActivityInfoHandle> mActivityInfoCache =
             new HashMap<Task.ComponentNameKey, ActivityInfoHandle>();
 
@@ -96,11 +94,8 @@
         // This activity info cache will be used for both preloadPlan() and executePlan()
         mActivityInfoCache.clear();
 
-        // TODO (multi-display): Currently assume the primary display
-        Rect displayBounds = mSystemServicesProxy.getWindowRect();
-
         Resources res = mContext.getResources();
-        SparseArray<ArrayList<Task>> stacksTasks = new SparseArray<>();
+        ArrayList<Task> stackTasks = new ArrayList<>();
         if (mRawTasks == null) {
             preloadRawTasks(isTopTaskHome);
         }
@@ -152,37 +147,13 @@
             task.thumbnail = loader.getAndUpdateThumbnail(taskKey, mSystemServicesProxy, false);
             if (DEBUG) Log.d(TAG, "\tthumbnail: " + taskKey + ", " + task.thumbnail);
 
-            if (!mConfig.multiStackEnabled ||
-                    Constants.DebugFlags.App.EnableMultiStackToSingleStack) {
-                int firstStackId = 0;
-                ArrayList<Task> stackTasks = stacksTasks.get(firstStackId);
-                if (stackTasks == null) {
-                    stackTasks = new ArrayList<>();
-                    stacksTasks.put(firstStackId, stackTasks);
-                }
-                stackTasks.add(task);
-            } else {
-                ArrayList<Task> stackTasks = stacksTasks.get(t.stackId);
-                if (stackTasks == null) {
-                    stackTasks = new ArrayList<>();
-                    stacksTasks.put(t.stackId, stackTasks);
-                }
-                stackTasks.add(task);
-            }
+            stackTasks.add(task);
         }
 
         // Initialize the stacks
-        mStacks.clear();
-        int stackCount = stacksTasks.size();
-        for (int i = 0; i < stackCount; i++) {
-            int stackId = stacksTasks.keyAt(i);
-            ArrayList<Task> stackTasks = stacksTasks.valueAt(i);
-            TaskStack stack = new TaskStack(stackId);
-            stack.setBounds(displayBounds, displayBounds);
-            stack.setTasks(stackTasks);
-            stack.createAffiliatedGroupings(mConfig);
-            mStacks.put(stackId, stack);
-        }
+        mStack = new TaskStack();
+        mStack.setTasks(stackTasks);
+        mStack.createAffiliatedGroupings(mConfig);
     }
 
     /**
@@ -197,92 +168,70 @@
         Resources res = mContext.getResources();
 
         // Iterate through each of the tasks and load them according to the load conditions.
-        int stackCount = mStacks.size();
-        for (int j = 0; j < stackCount; j++) {
-            ArrayList<Task> tasks = mStacks.valueAt(j).getTasks();
-            int taskCount = tasks.size();
-            for (int i = 0; i < taskCount; i++) {
-                ActivityManager.RecentTaskInfo t = mRawTasks.get(i);
-                Task task = tasks.get(i);
-                Task.TaskKey taskKey = task.key;
+        ArrayList<Task> tasks = mStack.getTasks();
+        int taskCount = tasks.size();
+        for (int i = 0; i < taskCount; i++) {
+            ActivityManager.RecentTaskInfo t = mRawTasks.get(i);
+            Task task = tasks.get(i);
+            Task.TaskKey taskKey = task.key;
 
-                // Get an existing activity info handle if possible
-                Task.ComponentNameKey cnKey = taskKey.getComponentNameKey();
-                ActivityInfoHandle infoHandle;
-                boolean hadCachedActivityInfo = false;
-                if (mActivityInfoCache.containsKey(cnKey)) {
-                    infoHandle = mActivityInfoCache.get(cnKey);
-                    hadCachedActivityInfo = true;
-                } else {
-                    infoHandle = new ActivityInfoHandle();
+            // Get an existing activity info handle if possible
+            Task.ComponentNameKey cnKey = taskKey.getComponentNameKey();
+            ActivityInfoHandle infoHandle;
+            boolean hadCachedActivityInfo = false;
+            if (mActivityInfoCache.containsKey(cnKey)) {
+                infoHandle = mActivityInfoCache.get(cnKey);
+                hadCachedActivityInfo = true;
+            } else {
+                infoHandle = new ActivityInfoHandle();
+            }
+
+            boolean isRunningTask = (task.key.id == opts.runningTaskId);
+            boolean isVisibleTask = i >= (taskCount - opts.numVisibleTasks);
+            boolean isVisibleThumbnail = i >= (taskCount - opts.numVisibleTaskThumbnails);
+
+            // If requested, skip the running task
+            if (opts.onlyLoadPausedActivities && isRunningTask) {
+                continue;
+            }
+
+            if (opts.loadIcons && (isRunningTask || isVisibleTask)) {
+                if (task.activityIcon == null) {
+                    if (DEBUG) Log.d(TAG, "\tLoading icon: " + taskKey);
+                    task.activityIcon = loader.getAndUpdateActivityIcon(taskKey,
+                            t.taskDescription, mSystemServicesProxy, res, infoHandle, true);
                 }
-
-                boolean isRunningTask = (task.key.id == opts.runningTaskId);
-                boolean isVisibleTask = i >= (taskCount - opts.numVisibleTasks);
-                boolean isVisibleThumbnail = i >= (taskCount - opts.numVisibleTaskThumbnails);
-
-                // If requested, skip the running task
-                if (opts.onlyLoadPausedActivities && isRunningTask) {
-                    continue;
-                }
-
-                if (opts.loadIcons && (isRunningTask || isVisibleTask)) {
-                    if (task.activityIcon == null) {
-                        if (DEBUG) Log.d(TAG, "\tLoading icon: " + taskKey);
-                        task.activityIcon = loader.getAndUpdateActivityIcon(taskKey,
-                                t.taskDescription, mSystemServicesProxy, res, infoHandle, true);
+            }
+            if (opts.loadThumbnails && (isRunningTask || isVisibleThumbnail)) {
+                if (task.thumbnail == null || isRunningTask) {
+                    if (DEBUG) Log.d(TAG, "\tLoading thumbnail: " + taskKey);
+                    if (mConfig.svelteLevel <= RecentsConfiguration.SVELTE_LIMIT_CACHE) {
+                        task.thumbnail = loader.getAndUpdateThumbnail(taskKey,
+                                mSystemServicesProxy, true);
+                    } else if (mConfig.svelteLevel == RecentsConfiguration.SVELTE_DISABLE_CACHE) {
+                        loadQueue.addTask(task);
                     }
                 }
-                if (opts.loadThumbnails && (isRunningTask || isVisibleThumbnail)) {
-                    if (task.thumbnail == null || isRunningTask) {
-                        if (DEBUG) Log.d(TAG, "\tLoading thumbnail: " + taskKey);
-                        if (mConfig.svelteLevel <= RecentsConfiguration.SVELTE_LIMIT_CACHE) {
-                            task.thumbnail = loader.getAndUpdateThumbnail(taskKey,
-                                    mSystemServicesProxy, true);
-                        } else if (mConfig.svelteLevel == RecentsConfiguration.SVELTE_DISABLE_CACHE) {
-                            loadQueue.addTask(task);
-                        }
-                    }
-                }
+            }
 
-                // Update the activity info cache
-                if (!hadCachedActivityInfo && infoHandle.info != null) {
-                    mActivityInfoCache.put(cnKey, infoHandle);
-                }
+            // Update the activity info cache
+            if (!hadCachedActivityInfo && infoHandle.info != null) {
+                mActivityInfoCache.put(cnKey, infoHandle);
             }
         }
     }
 
     /**
-     * Returns all TaskStacks from the preloaded list of recent tasks.
+     * Returns the TaskStack from the preloaded list of recent tasks.
      */
-    public ArrayList<TaskStack> getAllTaskStacks() {
-        ArrayList<TaskStack> stacks = new ArrayList<TaskStack>();
-        int stackCount = mStacks.size();
-        for (int i = 0; i < stackCount; i++) {
-            stacks.add(mStacks.valueAt(i));
-        }
-        // Ensure that we have at least one stack
-        if (stacks.isEmpty()) {
-            stacks.add(new TaskStack());
-        }
-        return stacks;
-    }
-
-    /**
-     * Returns a specific TaskStack from the preloaded list of recent tasks.
-     */
-    public TaskStack getTaskStack(int stackId) {
-        return mStacks.get(stackId);
+    public TaskStack getTaskStack() {
+        return mStack;
     }
 
     /** Returns whether there are any tasks in any stacks. */
     public boolean hasTasks() {
-        int stackCount = mStacks.size();
-        for (int i = 0; i < stackCount; i++) {
-            if (mStacks.valueAt(i).getTaskCount() > 0) {
-                return true;
-            }
+        if (mStack != null) {
+            return mStack.getTaskCount() > 0;
         }
         return false;
     }
diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/TaskStack.java b/packages/SystemUI/src/com/android/systemui/recents/model/TaskStack.java
index 5aaea15..515e578 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/model/TaskStack.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/model/TaskStack.java
@@ -17,7 +17,6 @@
 package com.android.systemui.recents.model;
 
 import android.graphics.Color;
-import android.graphics.Rect;
 import com.android.systemui.recents.Constants;
 import com.android.systemui.recents.RecentsConfiguration;
 import com.android.systemui.recents.misc.NamedCounter;
@@ -177,35 +176,17 @@
     // The task offset to apply to a task id as a group affiliation
     static final int IndividualTaskIdOffset = 1 << 16;
 
-    public final int id;
-    public final Rect stackBounds = new Rect();
-    public final Rect displayBounds = new Rect();
-
     FilteredTaskList mTaskList = new FilteredTaskList();
     TaskStackCallbacks mCb;
 
     ArrayList<TaskGrouping> mGroups = new ArrayList<TaskGrouping>();
     HashMap<Integer, TaskGrouping> mAffinitiesGroups = new HashMap<Integer, TaskGrouping>();
 
-    public TaskStack() {
-        this(0);
-    }
-
-    public TaskStack(int stackId) {
-        id = stackId;
-    }
-
     /** Sets the callbacks for this task stack. */
     public void setCallbacks(TaskStackCallbacks cb) {
         mCb = cb;
     }
 
-    /** Sets the bounds of this stack. */
-    public void setBounds(Rect stackBounds, Rect displayBounds) {
-        this.stackBounds.set(stackBounds);
-        this.displayBounds.set(displayBounds);
-    }
-
     /** Resets this TaskStack. */
     public void reset() {
         mCb = null;
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/DebugOverlayView.java b/packages/SystemUI/src/com/android/systemui/recents/views/DebugOverlayView.java
deleted file mode 100644
index 452830d..0000000
--- a/packages/SystemUI/src/com/android/systemui/recents/views/DebugOverlayView.java
+++ /dev/null
@@ -1,190 +0,0 @@
-/*
- * Copyright (C) 2014 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.recents.views;
-
-import android.content.Context;
-import android.graphics.Canvas;
-import android.graphics.Paint;
-import android.graphics.Rect;
-import android.util.AttributeSet;
-import android.util.Pair;
-import android.view.View;
-import android.widget.FrameLayout;
-import android.widget.SeekBar;
-import com.android.systemui.R;
-import com.android.systemui.recents.RecentsConfiguration;
-
-import java.util.ArrayList;
-
-/**
- * A full screen overlay layer that allows us to draw views from throughout the system on the top
- * most layer.
- */
-public class DebugOverlayView extends FrameLayout implements SeekBar.OnSeekBarChangeListener {
-
-    public interface DebugOverlayViewCallbacks {
-        public void onPrimarySeekBarChanged(float progress);
-        public void onSecondarySeekBarChanged(float progress);
-    }
-
-    final static int sCornerRectSize = 50;
-
-    RecentsConfiguration mConfig;
-    DebugOverlayViewCallbacks mCb;
-
-    ArrayList<Pair<Rect, Integer>> mRects = new ArrayList<Pair<Rect, Integer>>();
-    String mText;
-    Paint mDebugOutline = new Paint();
-    Paint mTmpPaint = new Paint();
-    Rect mTmpRect = new Rect();
-    boolean mEnabled = true;
-
-    SeekBar mPrimarySeekBar;
-    SeekBar mSecondarySeekBar;
-
-    public DebugOverlayView(Context context) {
-        this(context, null);
-    }
-
-    public DebugOverlayView(Context context, AttributeSet attrs) {
-        this(context, attrs, 0);
-    }
-
-    public DebugOverlayView(Context context, AttributeSet attrs, int defStyleAttr) {
-        this(context, attrs, defStyleAttr, 0);
-    }
-
-    public DebugOverlayView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
-        super(context, attrs, defStyleAttr, defStyleRes);
-        mConfig = RecentsConfiguration.getInstance();
-        mDebugOutline.setColor(0xFFff0000);
-        mDebugOutline.setStyle(Paint.Style.STROKE);
-        mDebugOutline.setStrokeWidth(8f);
-        setWillNotDraw(false);
-    }
-
-    public void setCallbacks(DebugOverlayViewCallbacks cb) {
-        mCb = cb;
-    }
-
-    @Override
-    protected void onFinishInflate() {
-        mPrimarySeekBar = (SeekBar) findViewById(R.id.debug_seek_bar_1);
-        mPrimarySeekBar.setOnSeekBarChangeListener(this);
-        mSecondarySeekBar = (SeekBar) findViewById(R.id.debug_seek_bar_2);
-        mSecondarySeekBar.setOnSeekBarChangeListener(this);
-    }
-
-    /** Enables the debug overlay drawing. */
-    public void enable() {
-        mEnabled = true;
-        setVisibility(View.VISIBLE);
-    }
-
-    /** Disables the debug overlay drawing. */
-    public void disable() {
-        mEnabled = false;
-        setVisibility(View.GONE);
-    }
-
-    /** Clears all debug rects. */
-    public void clear() {
-        mRects.clear();
-    }
-
-    /** Adds a rect to be drawn. */
-    void addRect(Rect r, int color) {
-        mRects.add(new Pair<Rect, Integer>(r, color));
-        invalidate();
-    }
-
-    /** Adds a view's global rect to be drawn. */
-    void addViewRect(View v, int color) {
-        Rect vr = new Rect();
-        v.getGlobalVisibleRect(vr);
-        mRects.add(new Pair<Rect, Integer>(vr, color));
-        invalidate();
-    }
-
-    /** Adds a rect, relative to a given view to be drawn. */
-    void addRectRelativeToView(View v, Rect r, int color) {
-        Rect vr = new Rect();
-        v.getGlobalVisibleRect(vr);
-        r.offsetTo(vr.left, vr.top);
-        mRects.add(new Pair<Rect, Integer>(r, color));
-        invalidate();
-    }
-
-    /** Sets the debug text at the bottom of the screen. */
-    void setText(String message) {
-        mText = message;
-        invalidate();
-    }
-
-    @Override
-    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
-        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
-        addRect(new Rect(0, 0, sCornerRectSize, sCornerRectSize), 0xFFff0000);
-        addRect(new Rect(getMeasuredWidth() - sCornerRectSize, getMeasuredHeight() - sCornerRectSize,
-                getMeasuredWidth(), getMeasuredHeight()), 0xFFff0000);
-    }
-
-    @Override
-    protected void onDraw(Canvas canvas) {
-        if (mEnabled) {
-            // Draw the outline
-            canvas.drawRect(0, 0, getMeasuredWidth(), getMeasuredHeight(), mDebugOutline);
-
-            // Draw the rects
-            int numRects = mRects.size();
-            for (int i = 0; i < numRects; i++) {
-                Pair<Rect, Integer> r = mRects.get(i);
-                mTmpPaint.setColor(r.second);
-                canvas.drawRect(r.first, mTmpPaint);
-            }
-
-            // Draw the text
-            if (mText != null && mText.length() > 0) {
-                mTmpPaint.setColor(0xFFff0000);
-                mTmpPaint.setTextSize(60);
-                mTmpPaint.getTextBounds(mText, 0, 1, mTmpRect);
-                canvas.drawText(mText, 10f, getMeasuredHeight() - mTmpRect.height() - mConfig.systemInsets.bottom, mTmpPaint);
-            }
-        }
-    }
-
-    /**** SeekBar.OnSeekBarChangeListener Implementation ****/
-
-    @Override
-    public void onStopTrackingTouch(SeekBar seekBar) {
-        // Do nothing
-    }
-
-    @Override
-    public void onStartTrackingTouch(SeekBar seekBar) {
-        // Do nothing
-    }
-
-    @Override
-    public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
-        if (seekBar == mPrimarySeekBar) {
-            mCb.onPrimarySeekBarChanged((float) progress / mPrimarySeekBar.getMax());
-        } else if (seekBar == mSecondarySeekBar) {
-            mCb.onSecondarySeekBarChanged((float) progress / mSecondarySeekBar.getMax());
-        }
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/FakeShadowDrawable.java b/packages/SystemUI/src/com/android/systemui/recents/views/FakeShadowDrawable.java
index 509ad1b..41adbed 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/FakeShadowDrawable.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/FakeShadowDrawable.java
@@ -28,7 +28,6 @@
 import android.graphics.Shader;
 import android.graphics.drawable.Drawable;
 import android.util.Log;
-
 import com.android.systemui.R;
 import com.android.systemui.recents.RecentsConfiguration;
 
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
index 651b29a..9685a24 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
@@ -78,11 +78,9 @@
 
     RecentsConfiguration mConfig;
     LayoutInflater mInflater;
-    DebugOverlayView mDebugOverlay;
-    RecentsViewLayoutAlgorithm mLayoutAlgorithm;
 
     ArrayList<TaskStack> mStacks;
-    List<TaskStackView> mTaskStackViews = new ArrayList<>();
+    TaskStackView mTaskStackView;
     RecentsAppWidgetHostView mSearchBar;
     RecentsViewCallbacks mCb;
 
@@ -102,7 +100,6 @@
         super(context, attrs, defStyleAttr, defStyleRes);
         mConfig = RecentsConfiguration.getInstance();
         mInflater = LayoutInflater.from(context);
-        mLayoutAlgorithm = new RecentsViewLayoutAlgorithm(mConfig);
     }
 
     /** Sets the callbacks */
@@ -110,67 +107,37 @@
         mCb = cb;
     }
 
-    /** Sets the debug overlay */
-    public void setDebugOverlay(DebugOverlayView overlay) {
-        mDebugOverlay = overlay;
-    }
-
     /** Set/get the bsp root node */
-    public void setTaskStacks(ArrayList<TaskStack> stacks) {
-        int numStacks = stacks.size();
-
-        // Remove all/extra stack views
-        int numTaskStacksToKeep = 0; // Keep no tasks if we are recreating the layout
+    public void setTaskStack(TaskStack stack) {
         if (mConfig.launchedReuseTaskStackViews) {
-            numTaskStacksToKeep = Math.min(mTaskStackViews.size(), numStacks);
-        }
-        for (int i = mTaskStackViews.size() - 1; i >= numTaskStacksToKeep; i--) {
-            removeView(mTaskStackViews.remove(i));
-        }
-
-        // Update the stack views that we are keeping
-        for (int i = 0; i < numTaskStacksToKeep; i++) {
-            TaskStackView tsv = mTaskStackViews.get(i);
-            // If onRecentsHidden is not triggered, we need to the stack view again here
-            tsv.reset();
-            tsv.setStack(stacks.get(i));
-        }
-
-        // Add remaining/recreate stack views
-        mStacks = stacks;
-        for (int i = mTaskStackViews.size(); i < numStacks; i++) {
-            TaskStack stack = stacks.get(i);
-            TaskStackView stackView = new TaskStackView(getContext(), stack);
-            stackView.setCallbacks(this);
-            addView(stackView);
-            mTaskStackViews.add(stackView);
-        }
-
-        // Enable debug mode drawing on all the stacks if necessary
-        if (mConfig.debugModeEnabled) {
-            for (int i = mTaskStackViews.size() - 1; i >= 0; i--) {
-                TaskStackView stackView = mTaskStackViews.get(i);
-                stackView.setDebugOverlay(mDebugOverlay);
+            if (mTaskStackView != null) {
+                // If onRecentsHidden is not triggered, we need to the stack view again here
+                mTaskStackView.reset();
+                mTaskStackView.setStack(stack);
+            } else {
+                mTaskStackView = new TaskStackView(getContext(), stack);
+                mTaskStackView.setCallbacks(this);
+                addView(mTaskStackView);
             }
+        } else {
+            if (mTaskStackView != null) {
+                removeView(mTaskStackView);
+            }
+            mTaskStackView = new TaskStackView(getContext(), stack);
+            mTaskStackView.setCallbacks(this);
+            addView(mTaskStackView);
         }
 
         // Trigger a new layout
         requestLayout();
     }
 
-    /** Gets the list of task views */
-    List<TaskStackView> getTaskStackViews() {
-        return mTaskStackViews;
-    }
-
     /** Gets the next task in the stack - or if the last - the top task */
     public Task getNextTaskOrTopTask(Task taskToSearch) {
         Task returnTask = null;
         boolean found = false;
-        List<TaskStackView> stackViews = getTaskStackViews();
-        int stackCount = stackViews.size();
-        for (int i = stackCount - 1; i >= 0; --i) {
-            TaskStack stack = stackViews.get(i).getStack();
+        if (mTaskStackView != null) {
+            TaskStack stack = mTaskStackView.getStack();
             ArrayList<Task> taskList = stack.getTasks();
             // Iterate the stack views and try and find the focused task
             for (int j = taskList.size() - 1; j >= 0; --j) {
@@ -190,20 +157,16 @@
 
     /** Launches the focused task from the first stack if possible */
     public boolean launchFocusedTask() {
-        // Get the first stack view
-        List<TaskStackView> stackViews = getTaskStackViews();
-        int stackCount = stackViews.size();
-        for (int i = 0; i < stackCount; i++) {
-            TaskStackView stackView = stackViews.get(i);
-            TaskStack stack = stackView.getStack();
+        if (mTaskStackView != null) {
+            TaskStack stack = mTaskStackView.getStack();
             // Iterate the stack views and try and find the focused task
-            List<TaskView> taskViews = stackView.getTaskViews();
+            List<TaskView> taskViews = mTaskStackView.getTaskViews();
             int taskViewCount = taskViews.size();
             for (int j = 0; j < taskViewCount; j++) {
                 TaskView tv = taskViews.get(j);
                 Task task = tv.getTask();
                 if (tv.isFocusedTask()) {
-                    onTaskViewClicked(stackView, tv, stack, task, false, false, null);
+                    onTaskViewClicked(mTaskStackView, tv, stack, task, false, false, null);
                     return true;
                 }
             }
@@ -213,19 +176,16 @@
 
     /** Launches a given task. */
     public boolean launchTask(Task task, Rect taskBounds) {
-        // Get the first stack view
-        List<TaskStackView> stackViews = getTaskStackViews();
-        int stackCount = stackViews.size();
-        for (int i = 0; i < stackCount; i++) {
-            TaskStackView stackView = stackViews.get(i);
-            TaskStack stack = stackView.getStack();
+        if (mTaskStackView != null) {
+            TaskStack stack = mTaskStackView.getStack();
             // Iterate the stack views and try and find the given task.
-            List<TaskView> taskViews = stackView.getTaskViews();
+            List<TaskView> taskViews = mTaskStackView.getTaskViews();
             int taskViewCount = taskViews.size();
             for (int j = 0; j < taskViewCount; j++) {
                 TaskView tv = taskViews.get(j);
                 if (tv.getTask() == task) {
-                    onTaskViewClicked(stackView, tv, stack, task, false, true, taskBounds);
+                    onTaskViewClicked(mTaskStackView, tv, stack, task, false, taskBounds != null,
+                            taskBounds);
                     return true;
                 }
             }
@@ -235,12 +195,8 @@
 
     /** Launches the task that Recents was launched from, if possible */
     public boolean launchPreviousTask() {
-        // Get the first stack view
-        List<TaskStackView> stackViews = getTaskStackViews();
-        int stackCount = stackViews.size();
-        for (int i = 0; i < stackCount; i++) {
-            TaskStackView stackView = stackViews.get(i);
-            TaskStack stack = stackView.getStack();
+        if (mTaskStackView != null) {
+            TaskStack stack = mTaskStackView.getStack();
             ArrayList<Task> tasks = stack.getTasks();
 
             // Find the launch task in the stack
@@ -249,8 +205,8 @@
                 for (int j = 0; j < taskCount; j++) {
                     if (tasks.get(j).isLaunchTarget) {
                         Task task = tasks.get(j);
-                        TaskView tv = stackView.getChildViewForTask(task);
-                        onTaskViewClicked(stackView, tv, stack, task, false, false, null);
+                        TaskView tv = mTaskStackView.getChildViewForTask(task);
+                        onTaskViewClicked(mTaskStackView, tv, stack, task, false, false, null);
                         return true;
                     }
                 }
@@ -264,12 +220,8 @@
         // We have to increment/decrement the post animation trigger in case there are no children
         // to ensure that it runs
         ctx.postAnimationTrigger.increment();
-
-        List<TaskStackView> stackViews = getTaskStackViews();
-        int stackCount = stackViews.size();
-        for (int i = 0; i < stackCount; i++) {
-            TaskStackView stackView = stackViews.get(i);
-            stackView.startEnterRecentsAnimation(ctx);
+        if (mTaskStackView != null) {
+            mTaskStackView.startEnterRecentsAnimation(ctx);
         }
         ctx.postAnimationTrigger.decrement();
     }
@@ -279,11 +231,8 @@
         // We have to increment/decrement the post animation trigger in case there are no children
         // to ensure that it runs
         ctx.postAnimationTrigger.increment();
-        List<TaskStackView> stackViews = getTaskStackViews();
-        int stackCount = stackViews.size();
-        for (int i = 0; i < stackCount; i++) {
-            TaskStackView stackView = stackViews.get(i);
-            stackView.startExitToHomeAnimation(ctx);
+        if (mTaskStackView != null) {
+            mTaskStackView.startExitToHomeAnimation(ctx);
         }
         ctx.postAnimationTrigger.decrement();
 
@@ -329,31 +278,19 @@
         // Get the search bar bounds and measure the search bar layout
         Rect searchBarSpaceBounds = new Rect();
         if (mSearchBar != null) {
-            mConfig.getSearchBarBounds(width, height, mConfig.systemInsets.top, searchBarSpaceBounds);
+            mConfig.getSearchBarBounds(new Rect(0, 0, width, height), mConfig.systemInsets.top,
+                    searchBarSpaceBounds);
             mSearchBar.measure(
                     MeasureSpec.makeMeasureSpec(searchBarSpaceBounds.width(), MeasureSpec.EXACTLY),
                     MeasureSpec.makeMeasureSpec(searchBarSpaceBounds.height(), MeasureSpec.EXACTLY));
         }
 
         Rect taskStackBounds = new Rect();
-        mConfig.getAvailableTaskStackBounds(width, height, mConfig.systemInsets.top,
+        mConfig.getAvailableTaskStackBounds(new Rect(0, 0, width, height), mConfig.systemInsets.top,
                 mConfig.systemInsets.right, searchBarSpaceBounds, taskStackBounds);
-
-        // Measure each TaskStackView with the full width and height of the window since the
-        // transition view is a child of that stack view
-        List<TaskStackView> stackViews = getTaskStackViews();
-        List<Rect> stackViewsBounds = mLayoutAlgorithm.computeStackRects(stackViews,
-                taskStackBounds);
-        int stackCount = stackViews.size();
-        for (int i = 0; i < stackCount; i++) {
-            TaskStackView stackView = stackViews.get(i);
-            if (stackView.getVisibility() != GONE) {
-                // We are going to measure the TaskStackView with the whole RecentsView dimensions,
-                // but the actual stack is going to be inset to the bounds calculated by the layout
-                // algorithm
-                stackView.setStackInsetRect(stackViewsBounds.get(i));
-                stackView.measure(widthMeasureSpec, heightMeasureSpec);
-            }
+        if (mTaskStackView != null && mTaskStackView.getVisibility() != GONE) {
+            mTaskStackView.setTaskStackBounds(taskStackBounds);
+            mTaskStackView.measure(widthMeasureSpec, heightMeasureSpec);
         }
 
         setMeasuredDimension(width, height);
@@ -365,24 +302,17 @@
     @Override
     protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
         // Get the search bar bounds so that we lay it out
+        Rect measuredRect = new Rect(0, 0, getMeasuredWidth(), getMeasuredHeight());
+        Rect searchBarSpaceBounds = new Rect();
         if (mSearchBar != null) {
-            Rect searchBarSpaceBounds = new Rect();
-            mConfig.getSearchBarBounds(getMeasuredWidth(), getMeasuredHeight(),
+            mConfig.getSearchBarBounds(measuredRect,
                     mConfig.systemInsets.top, searchBarSpaceBounds);
             mSearchBar.layout(searchBarSpaceBounds.left, searchBarSpaceBounds.top,
                     searchBarSpaceBounds.right, searchBarSpaceBounds.bottom);
         }
 
-        // Layout each TaskStackView with the full width and height of the window since the
-        // transition view is a child of that stack view
-        List<TaskStackView> stackViews = getTaskStackViews();
-        int stackCount = stackViews.size();
-        for (int i = 0; i < stackCount; i++) {
-            TaskStackView stackView = stackViews.get(i);
-            if (stackView.getVisibility() != GONE) {
-                stackView.layout(left, top, left + stackView.getMeasuredWidth(),
-                        top + stackView.getMeasuredHeight());
-            }
+        if (mTaskStackView != null && mTaskStackView.getVisibility() != GONE) {
+            mTaskStackView.layout(left, top, left + getMeasuredWidth(), top + getMeasuredHeight());
         }
     }
 
@@ -397,29 +327,24 @@
     /** Notifies each task view of the user interaction. */
     public void onUserInteraction() {
         // Get the first stack view
-        List<TaskStackView> stackViews = getTaskStackViews();
-        int stackCount = stackViews.size();
-        for (int i = 0; i < stackCount; i++) {
-            TaskStackView stackView = stackViews.get(i);
-            stackView.onUserInteraction();
+        if (mTaskStackView != null) {
+            mTaskStackView.onUserInteraction();
         }
     }
 
     /** Focuses the next task in the first stack view */
     public void focusNextTask(boolean forward) {
         // Get the first stack view
-        List<TaskStackView> stackViews = getTaskStackViews();
-        if (!stackViews.isEmpty()) {
-            stackViews.get(0).focusNextTask(forward, true);
+        if (mTaskStackView != null) {
+            mTaskStackView.focusNextTask(forward, true);
         }
     }
 
     /** Dismisses the focused task. */
     public void dismissFocusedTask() {
         // Get the first stack view
-        List<TaskStackView> stackViews = getTaskStackViews();
-        if (!stackViews.isEmpty()) {
-            stackViews.get(0).dismissFocusedTask();
+        if (mTaskStackView != null) {
+            mTaskStackView.dismissFocusedTask();
         }
     }
 
@@ -442,9 +367,8 @@
     }
 
     public void disableLayersForOneFrame() {
-        List<TaskStackView> stackViews = getTaskStackViews();
-        for (int i = 0; i < stackViews.size(); i++) {
-            stackViews.get(i).disableLayersForOneFrame();
+        if (mTaskStackView != null) {
+            mTaskStackView.disableLayersForOneFrame();
         }
     }
 
@@ -655,7 +579,7 @@
             }
             postDrawHeaderThumbnailTransitionRunnable(stackView, tv, offsetX, offsetY, stackScroll,
                     animStartedListener);
-            if (mConfig.multiStackEnabled) {
+            if (mConfig.multiWindowEnabled) {
                 opts = ActivityOptions.makeCustomAnimation(sourceView.getContext(),
                         R.anim.recents_from_unknown_enter,
                         R.anim.recents_from_unknown_exit,
@@ -670,7 +594,7 @@
             opts = ActivityOptions.makeBasic();
         }
         if (boundsValid) {
-            opts.setBounds(bounds);
+            opts.setBounds(bounds.isEmpty() ? null : bounds);
         }
         final ActivityOptions launchOpts = opts;
         final boolean screenPinningRequested = (animStartedListener == null) && lockToTask;
@@ -767,11 +691,8 @@
     /** Final callback after Recents is finally hidden. */
     public void onRecentsHidden() {
         // Notify each task stack view
-        List<TaskStackView> stackViews = getTaskStackViews();
-        int stackCount = stackViews.size();
-        for (int i = 0; i < stackCount; i++) {
-            TaskStackView stackView = stackViews.get(i);
-            stackView.onRecentsHidden();
+        if (mTaskStackView != null) {
+            mTaskStackView.onRecentsHidden();
         }
     }
 
@@ -815,11 +736,8 @@
     @Override
     public void onPackagesChanged(RecentsPackageMonitor monitor, String packageName, int userId) {
         // Propagate this event down to each task stack view
-        List<TaskStackView> stackViews = getTaskStackViews();
-        int stackCount = stackViews.size();
-        for (int i = 0; i < stackCount; i++) {
-            TaskStackView stackView = stackViews.get(i);
-            stackView.onPackagesChanged(monitor, packageName, userId);
+        if (mTaskStackView != null) {
+            mTaskStackView.onPackagesChanged(monitor, packageName, userId);
         }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsViewLayoutAlgorithm.java b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsViewLayoutAlgorithm.java
deleted file mode 100644
index eea273c..0000000
--- a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsViewLayoutAlgorithm.java
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- * Copyright (C) 2014 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.recents.views;
-
-import android.graphics.Rect;
-import com.android.systemui.recents.RecentsConfiguration;
-import com.android.systemui.recents.model.TaskStack;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/* The layout logic for the RecentsView. */
-public class RecentsViewLayoutAlgorithm {
-
-    RecentsConfiguration mConfig;
-
-    public RecentsViewLayoutAlgorithm(RecentsConfiguration config) {
-        mConfig = config;
-    }
-
-    /** Return the relative coordinate given coordinates in another size. */
-    private int getRelativeCoordinate(int availableOffset, int availableSize, int otherCoord, int otherSize) {
-        float relPos = (float) otherCoord / otherSize;
-        return availableOffset + (int) (relPos * availableSize);
-    }
-
-    /**
-     * Computes and returns the bounds that each of the stack views should take up.
-     */
-    List<Rect> computeStackRects(List<TaskStackView> stackViews, Rect availableBounds) {
-        ArrayList<Rect> bounds = new ArrayList<Rect>(stackViews.size());
-        int stackViewsCount = stackViews.size();
-        for (int i = 0; i < stackViewsCount; i++) {
-            TaskStack stack = stackViews.get(i).getStack();
-            Rect sb = stack.stackBounds;
-            Rect db = stack.displayBounds;
-            Rect ab = availableBounds;
-            bounds.add(new Rect(getRelativeCoordinate(ab.left, ab.width(), sb.left, db.width()),
-                    getRelativeCoordinate(ab.top, ab.height(), sb.top, db.height()),
-                    getRelativeCoordinate(ab.left, ab.width(), sb.right, db.width()),
-                    getRelativeCoordinate(ab.top, ab.height(), sb.bottom, db.height())));
-        }
-        return bounds;
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
index 4e82c8a..4db8b37 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
@@ -79,8 +79,6 @@
     ViewPool<TaskView, Task> mViewPool;
     ArrayList<TaskViewTransform> mCurrentTaskTransforms = new ArrayList<TaskViewTransform>();
     DozeTrigger mUIDozeTrigger;
-    DebugOverlayView mDebugOverlay;
-    Rect mTaskStackBounds = new Rect();
     DismissView mDismissAllButton;
     boolean mDismissAllButtonAnimating;
     int mFocusedTaskIndex = -1;
@@ -93,6 +91,7 @@
     boolean mStartEnterAnimationRequestedAfterLayout;
     boolean mStartEnterAnimationCompleted;
     ViewAnimation.TaskViewEnterContext mStartEnterAnimationContext;
+    Rect mTaskStackBounds = new Rect();
     int[] mTmpVisibleRange = new int[2];
     float[] mTmpCoord = new float[2];
     Matrix mTmpMatrix = new Matrix();
@@ -161,11 +160,6 @@
         return mStack;
     }
 
-    /** Sets the debug overlay */
-    public void setDebugOverlay(DebugOverlayView overlay) {
-        mDebugOverlay = overlay;
-    }
-
     /** Updates the list of task views */
     void updateTaskViewsList() {
         mTaskViews.clear();
@@ -334,9 +328,6 @@
             int[] visibleRange = mTmpVisibleRange;
             boolean isValidVisibleRange = updateStackTransforms(mCurrentTaskTransforms, tasks,
                     stackScroll, visibleRange, false);
-            if (mDebugOverlay != null) {
-                mDebugOverlay.setText("vis[" + visibleRange[1] + "-" + visibleRange[0] + "]");
-            }
 
             // Inflate and add the dismiss button if necessary
             if (Constants.DebugFlags.App.EnableDismissAll && mDismissAllButton == null) {
@@ -477,11 +468,6 @@
         mStackViewsClipDirty = false;
     }
 
-    /** The stack insets to apply to the stack contents */
-    public void setStackInsetRect(Rect r) {
-        mTaskStackBounds.set(r);
-    }
-
     /** Updates the min and max virtual scroll bounds */
     void updateMinMaxScroll(boolean boundScrollToNewMinMax, boolean launchedWithAltTab,
             boolean launchedFromHome) {
@@ -692,11 +678,6 @@
         return mTouchHandler.onGenericMotionEvent(ev);
     }
 
-    /** Returns the region that touch gestures can be started in. */
-    Rect getTouchableRegion() {
-        return mTaskStackBounds;
-    }
-
     @Override
     public void computeScroll() {
         mStackScroller.computeScroll();
@@ -736,6 +717,10 @@
         return mLayoutAlgorithm.computeStackVisibilityReport(mStack.getTasks());
     }
 
+    public void setTaskStackBounds(Rect taskStackBounds) {
+        mTaskStackBounds.set(taskStackBounds);
+    }
+
     /**
      * This is called with the full window width and height to allow stack view children to
      * perform the full screen transition down.
@@ -746,9 +731,7 @@
         int height = MeasureSpec.getSize(heightMeasureSpec);
 
         // Compute our stack/task rects
-        Rect taskStackBounds = new Rect(mTaskStackBounds);
-        taskStackBounds.bottom -= mConfig.systemInsets.bottom;
-        computeRects(width, height, taskStackBounds, mConfig.launchedWithAltTab,
+        computeRects(width, height, mTaskStackBounds, mConfig.launchedWithAltTab,
                 mConfig.launchedFromHome);
 
         // If this is the first layout, then scroll to the front of the stack and synchronize the
@@ -875,7 +858,7 @@
         }
 
         // Start dozing
-        if (!mConfig.multiStackEnabled) {
+        if (!mConfig.multiWindowEnabled) {
             mUIDozeTrigger.startDozing();
         }
     }
@@ -1299,7 +1282,7 @@
         RecentsTaskLoader.getInstance().loadTaskData(task);
 
         // If the doze trigger has already fired, then update the state for this task view
-        if (mConfig.multiStackEnabled || mUIDozeTrigger.hasTriggered()) {
+        if (mConfig.multiWindowEnabled || mUIDozeTrigger.hasTriggered()) {
             tv.setNoUserInteractionState();
         }
 
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java
index 2e0b80a..7d079d9 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java
@@ -128,16 +128,6 @@
             return false;
         }
 
-        int action = ev.getAction();
-        if (mConfig.multiStackEnabled) {
-            // Check if we are within the bounds of the stack view contents
-            if ((action & MotionEvent.ACTION_MASK) == MotionEvent.ACTION_DOWN) {
-                if (!mSv.getTouchableRegion().contains((int) ev.getX(), (int) ev.getY())) {
-                    return false;
-                }
-            }
-        }
-
         // Pass through to swipe helper if we are swiping
         mInterceptedBySwipeHelper = mSwipeHelper.onInterceptTouchEvent(ev);
         if (mInterceptedBySwipeHelper) {
@@ -146,6 +136,7 @@
 
         boolean wasScrolling = mScroller.isScrolling() ||
                 (mScroller.mScrollAnimator != null && mScroller.mScrollAnimator.isRunning());
+        int action = ev.getAction();
         switch (action & MotionEvent.ACTION_MASK) {
             case MotionEvent.ACTION_DOWN: {
                 // Save the touch down info
@@ -212,16 +203,6 @@
             return false;
         }
 
-        int action = ev.getAction();
-        if (mConfig.multiStackEnabled) {
-            // Check if we are within the bounds of the stack view contents
-            if ((action & MotionEvent.ACTION_MASK) == MotionEvent.ACTION_DOWN) {
-                if (!mSv.getTouchableRegion().contains((int) ev.getX(), (int) ev.getY())) {
-                    return false;
-                }
-            }
-        }
-
         // Pass through to swipe helper if we are swiping
         if (mInterceptedBySwipeHelper && mSwipeHelper.onTouchEvent(ev)) {
             return true;
@@ -230,6 +211,7 @@
         // Update the velocity tracker
         initVelocityTrackerIfNotExists();
 
+        int action = ev.getAction();
         switch (action & MotionEvent.ACTION_MASK) {
             case MotionEvent.ACTION_DOWN: {
                 // Save the touch down info
@@ -372,7 +354,7 @@
         // Shift the tap position toward the center of the task stack and check to see if it would
         // have hit a view. The user might have tried to tap on a task and missed slightly.
         int shiftedX = x;
-        if (x > mSv.getTouchableRegion().centerX()) {
+        if (x > (mSv.getRight() - mSv.getLeft()) / 2) {
             shiftedX -= mWindowTouchSlop;
         } else {
             shiftedX += mWindowTouchSlop;
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
index cbfe842..373fe7b 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
@@ -682,7 +682,7 @@
                 mHeaderView.mApplicationIcon.setOnClickListener(this);
             }
             mHeaderView.mDismissButton.setOnClickListener(this);
-            if (mConfig.multiStackEnabled) {
+            if (mConfig.multiWindowEnabled) {
                 mHeaderView.mMoveTaskButton.setOnClickListener(this);
             }
             mActionButtonView.setOnClickListener(this);
@@ -701,7 +701,7 @@
             // Unbind any listeners
             mHeaderView.mApplicationIcon.setOnClickListener(null);
             mHeaderView.mDismissButton.setOnClickListener(null);
-            if (mConfig.multiStackEnabled) {
+            if (mConfig.multiWindowEnabled) {
                 mHeaderView.mMoveTaskButton.setOnClickListener(null);
             }
             mActionButtonView.setOnClickListener(null);
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java
index 353bcbe..3e9410e 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java
@@ -215,8 +215,8 @@
                 mLightDismissDrawable : mDarkDismissDrawable);
         mDismissButton.setContentDescription(String.format(mDismissContentDescription,
                 t.contentDescription));
-        mMoveTaskButton.setVisibility((mConfig.multiStackEnabled) ? View.VISIBLE : View.INVISIBLE);
-        if (mConfig.multiStackEnabled) {
+        mMoveTaskButton.setVisibility((mConfig.multiWindowEnabled) ? View.VISIBLE : View.INVISIBLE);
+        if (mConfig.multiWindowEnabled) {
             updateResizeTaskBarIcon(t);
         }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java b/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java
index c1dfec3..2e3e00c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java
@@ -34,7 +34,7 @@
 import android.view.animation.PathInterpolator;
 
 import com.android.systemui.R;
-import com.android.systemui.analytics.LockedPhoneAnalytics;
+import com.android.systemui.classifier.FalsingManager;
 
 /**
  * Base class for both {@link ExpandableNotificationRow} and {@link NotificationOverflowContainer}
@@ -129,7 +129,7 @@
     private final int mNormalColor;
     private final int mLowPriorityColor;
     private boolean mIsBelowSpeedBump;
-    private LockedPhoneAnalytics mLockedPhoneAnalytics;
+    private FalsingManager mFalsingManager;
 
     public ActivatableNotificationView(Context context, AttributeSet attrs) {
         super(context, attrs);
@@ -153,7 +153,7 @@
                 R.color.notification_ripple_color_low_priority);
         mNormalRippleColor = context.getColor(
                 R.color.notification_ripple_untinted_color);
-        mLockedPhoneAnalytics = LockedPhoneAnalytics.getInstance(context);
+        mFalsingManager = FalsingManager.getInstance(context);
     }
 
     @Override
@@ -222,7 +222,7 @@
                         makeActive();
                         postDelayed(mTapTimeoutRunnable, DOUBLETAP_TIMEOUT_MS);
                     } else {
-                        mLockedPhoneAnalytics.onNotificationDoubleTap();
+                        mFalsingManager.onNotificationDoubleTap();
                         boolean performed = performClick();
                         if (performed) {
                             removeCallbacks(mTapTimeoutRunnable);
@@ -242,7 +242,7 @@
     }
 
     private void makeActive() {
-        mLockedPhoneAnalytics.onNotificationActive();
+        mFalsingManager.onNotificationActive();
         startActivateAnimation(false /* reverse */);
         mActivated = true;
         if (mOnActivatedListener != null) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
index 2c964be..e7a3c8a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
@@ -115,8 +115,7 @@
 
 public abstract class BaseStatusBar extends SystemUI implements
         CommandQueue.Callbacks, ActivatableNotificationView.OnActivatedListener,
-        RecentsComponent.Callbacks, ExpandableNotificationRow.ExpansionLogger,
-        NotificationData.Environment {
+        ExpandableNotificationRow.ExpansionLogger, NotificationData.Environment {
     public static final String TAG = "StatusBar";
     public static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
     public static final boolean MULTIUSER_DEBUG = false;
@@ -577,7 +576,6 @@
                 ServiceManager.getService(Context.STATUS_BAR_SERVICE));
 
         mRecents = getComponent(Recents.class);
-        mRecents.setCallback(this);
 
         final Configuration currentConfig = mContext.getResources().getConfiguration();
         mLocale = currentConfig.locale;
@@ -1174,11 +1172,6 @@
         }
     }
 
-    @Override
-    public void onVisibilityChanged(boolean visible) {
-        // Do nothing
-    }
-
     /**
      * If there is an active heads-up notification and it has a fullscreen intent, fire it now.
      */
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
index b01a2a8..10d4a96 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
@@ -112,7 +112,7 @@
         public void appTransitionStarting(long startTime, long duration);
         public void showAssistDisclosure();
         public void startAssist(Bundle args);
-        public void onCameraLaunchGestureDetected();
+        public void onCameraLaunchGestureDetected(int source);
     }
 
     public CommandQueue(Callbacks callbacks, StatusBarIconList list) {
@@ -306,10 +306,10 @@
     }
 
     @Override
-    public void onCameraLaunchGestureDetected() {
+    public void onCameraLaunchGestureDetected(int source) {
         synchronized (mList) {
             mHandler.removeMessages(MSG_CAMERA_LAUNCH_GESTURE);
-            mHandler.obtainMessage(MSG_CAMERA_LAUNCH_GESTURE).sendToTarget();
+            mHandler.obtainMessage(MSG_CAMERA_LAUNCH_GESTURE, source, 0).sendToTarget();
         }
     }
 
@@ -415,7 +415,7 @@
                     mCallbacks.startAssist((Bundle) msg.obj);
                     break;
                 case MSG_CAMERA_LAUNCH_GESTURE:
-                    mCallbacks.onCameraLaunchGestureDetected();
+                    mCallbacks.onCameraLaunchGestureDetected(msg.arg1);
                     break;
             }
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/DragDownHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/DragDownHelper.java
index e2304c1..687f6c1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/DragDownHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/DragDownHelper.java
@@ -29,7 +29,7 @@
 import com.android.systemui.ExpandHelper;
 import com.android.systemui.Gefingerpoken;
 import com.android.systemui.R;
-import com.android.systemui.analytics.LockedPhoneAnalytics;
+import com.android.systemui.classifier.FalsingManager;
 
 /**
  * A utility class to enable the downward swipe on the lockscreen to go to the full shade and expand
@@ -55,7 +55,7 @@
     private ExpandableView mStartingChild;
     private Interpolator mInterpolator;
     private float mLastHeight;
-    private LockedPhoneAnalytics mLockedPhoneAnalytics;
+    private FalsingManager mFalsingManager;
 
     public DragDownHelper(Context context, View host, ExpandHelper.Callback callback,
             DragDownCallback dragDownCallback) {
@@ -67,7 +67,7 @@
         mCallback = callback;
         mDragDownCallback = dragDownCallback;
         mHost = host;
-        mLockedPhoneAnalytics = LockedPhoneAnalytics.getInstance(context);
+        mFalsingManager = FalsingManager.getInstance(context);
     }
 
     @Override
@@ -87,7 +87,7 @@
             case MotionEvent.ACTION_MOVE:
                 final float h = y - mInitialTouchY;
                 if (h > mTouchSlop && h > Math.abs(x - mInitialTouchX)) {
-                    mLockedPhoneAnalytics.onNotificatonStartDraggingDown();
+                    mFalsingManager.onNotificatonStartDraggingDown();
                     mDraggingDown = true;
                     captureStartingChild(mInitialTouchX, mInitialTouchY);
                     mInitialTouchY = y;
@@ -130,7 +130,7 @@
                 }
                 return true;
             case MotionEvent.ACTION_UP:
-                if (mDraggedFarEnough && mDragDownCallback.onDraggedDown(mStartingChild,
+                if (!isFalseTouch() && mDragDownCallback.onDraggedDown(mStartingChild,
                         (int) (y - mInitialTouchY))) {
                     if (mStartingChild == null) {
                         mDragDownCallback.setEmptyDragAmount(0f);
@@ -148,6 +148,13 @@
         return false;
     }
 
+    private boolean isFalseTouch() {
+        if (mFalsingManager.isClassiferEnabled()) {
+            return mFalsingManager.isFalseTouch();
+        }
+        return !mDraggedFarEnough;
+    }
+
     private void captureStartingChild(float x, float y) {
         if (mStartingChild == null) {
             mStartingChild = findView(x, y);
@@ -205,7 +212,7 @@
     }
 
     private void stopDragging() {
-        mLockedPhoneAnalytics.onNotificatonStopDraggingDown();
+        mFalsingManager.onNotificatonStopDraggingDown();
         if (mStartingChild != null) {
             cancelExpansion(mStartingChild);
         } else {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
index 564a60a..210be9f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
@@ -24,7 +24,6 @@
 import android.graphics.drawable.AnimationDrawable;
 import android.graphics.drawable.ColorDrawable;
 import android.graphics.drawable.Drawable;
-import android.graphics.drawable.RippleDrawable;
 import android.service.notification.StatusBarNotification;
 import android.util.AttributeSet;
 import android.view.MotionEvent;
@@ -35,7 +34,7 @@
 import android.widget.ImageView;
 
 import com.android.systemui.R;
-import com.android.systemui.analytics.LockedPhoneAnalytics;
+import com.android.systemui.classifier.FalsingManager;
 import com.android.systemui.statusbar.phone.NotificationGroupManager;
 import com.android.systemui.statusbar.phone.PhoneStatusBar;
 import com.android.systemui.statusbar.stack.NotificationChildrenContainer;
@@ -111,7 +110,7 @@
                     !mChildrenExpanded);
         }
     };
-    private LockedPhoneAnalytics mLockedPhoneAnalytics;
+    private FalsingManager mFalsingManager;
 
     private boolean mJustClicked;
 
@@ -327,7 +326,7 @@
 
     public ExpandableNotificationRow(Context context, AttributeSet attrs) {
         super(context, attrs);
-        mLockedPhoneAnalytics = LockedPhoneAnalytics.getInstance(context);
+        mFalsingManager = FalsingManager.getInstance(context);
     }
 
     /**
@@ -515,7 +514,7 @@
      * @param userExpanded whether the user wants this notification to be expanded
      */
     public void setUserExpanded(boolean userExpanded) {
-        mLockedPhoneAnalytics.setNotificationExpanded();
+        mFalsingManager.setNotificationExpanded();
         if (userExpanded && !mExpandable) return;
         final boolean wasExpanded = isExpanded();
         mHasUserChangedExpansion = true;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
index ac4dee2..5e6fdd0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
@@ -187,32 +187,41 @@
         }
 
         // Try fetching charging time from battery stats.
+        long chargingTimeRemaining = 0;
         try {
-            long chargingTimeRemaining = mBatteryInfo.computeChargeTimeRemaining();
-            if (chargingTimeRemaining > 0) {
-                String chargingTimeFormatted = Formatter.formatShortElapsedTimeRoundingUpToMinutes(
-                        mContext, chargingTimeRemaining);
-                return mContext.getResources().getString(
-                        R.string.keyguard_indication_charging_time, chargingTimeFormatted);
-            }
+            chargingTimeRemaining = mBatteryInfo.computeChargeTimeRemaining();
+
         } catch (RemoteException e) {
             Log.e(TAG, "Error calling IBatteryStats: ", e);
         }
+        final boolean hasChargingTime = chargingTimeRemaining > 0;
 
-        // Fall back to simple charging label.
         int chargingId;
         switch (mChargingSpeed) {
             case KeyguardUpdateMonitor.BatteryStatus.CHARGING_FAST:
-                chargingId = R.string.keyguard_plugged_in_charging_fast;
+                chargingId = hasChargingTime
+                        ? R.string.keyguard_indication_charging_time_fast_if_translated
+                        : R.string.keyguard_plugged_in_charging_fast;
                 break;
             case KeyguardUpdateMonitor.BatteryStatus.CHARGING_SLOWLY:
-                chargingId = R.string.keyguard_plugged_in_charging_slowly;
+                chargingId = hasChargingTime
+                        ? R.string.keyguard_indication_charging_time_slowly_if_translated
+                        : R.string.keyguard_plugged_in_charging_slowly;
                 break;
             default:
-                chargingId = R.string.keyguard_plugged_in;
+                chargingId = hasChargingTime
+                        ? R.string.keyguard_indication_charging_time
+                        : R.string.keyguard_plugged_in;
                 break;
         }
-        return mContext.getResources().getString(chargingId);
+
+        if (hasChargingTime) {
+            String chargingTimeFormatted = Formatter.formatShortElapsedTimeRoundingUpToMinutes(
+                    mContext, chargingTimeRemaining);
+            return mContext.getResources().getString(chargingId, chargingTimeFormatted);
+        } else {
+            return mContext.getResources().getString(chargingId);
+        }
     }
 
     KeyguardUpdateMonitorCallback mUpdateMonitor = new KeyguardUpdateMonitorCallback() {
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/FingerprintUnlockController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/FingerprintUnlockController.java
index 84082db..2912963 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/FingerprintUnlockController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/FingerprintUnlockController.java
@@ -93,6 +93,8 @@
     private KeyguardViewMediator mKeyguardViewMediator;
     private ScrimController mScrimController;
     private PhoneStatusBar mPhoneStatusBar;
+    private boolean mGoingToSleep;
+    private int mPendingAuthenticatedUserId = -1;
 
     public FingerprintUnlockController(Context context,
             StatusBarWindowManager statusBarWindowManager,
@@ -161,6 +163,10 @@
 
     @Override
     public void onFingerprintAuthenticated(int userId) {
+        if (mUpdateMonitor.isGoingToSleep()) {
+            mPendingAuthenticatedUserId = userId;
+            return;
+        }
         boolean wasDeviceInteractive = mUpdateMonitor.isDeviceInteractive();
         mMode = calculateMode();
         if (!wasDeviceInteractive) {
@@ -205,6 +211,26 @@
         mPhoneStatusBar.notifyFpAuthModeChanged();
     }
 
+    @Override
+    public void onStartedGoingToSleep(int why) {
+        mPendingAuthenticatedUserId = -1;
+    }
+
+    @Override
+    public void onFinishedGoingToSleep(int why) {
+        if (mPendingAuthenticatedUserId != -1) {
+
+            // Post this to make sure it's executed after the device is fully locked.
+            mHandler.post(new Runnable() {
+                @Override
+                public void run() {
+                    onFingerprintAuthenticated(mPendingAuthenticatedUserId);
+                }
+            });
+        }
+        mPendingAuthenticatedUserId = -1;
+    }
+
     public int getMode() {
         return mMode;
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardAffordanceHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardAffordanceHelper.java
index 60ebfdf..41adeb5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardAffordanceHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardAffordanceHelper.java
@@ -28,6 +28,7 @@
 import android.view.animation.Interpolator;
 
 import com.android.systemui.R;
+import com.android.systemui.classifier.FalsingManager;
 import com.android.systemui.statusbar.FlingAnimationUtils;
 import com.android.systemui.statusbar.KeyguardAffordanceView;
 
@@ -62,6 +63,7 @@
     private Interpolator mAppearInterpolator;
     private Interpolator mDisappearInterpolator;
     private Animator mSwipeAnimator;
+    private FalsingManager mFalsingManager;
     private int mMinBackgroundRadius;
     private boolean mMotionCancelled;
     private int mTouchTargetSize;
@@ -109,6 +111,7 @@
                 android.R.interpolator.linear_out_slow_in);
         mDisappearInterpolator = AnimationUtils.loadInterpolator(mContext,
                 android.R.interpolator.fast_out_linear_in);
+        mFalsingManager = FalsingManager.getInstance(mContext);
     }
 
     private void initIcons() {
@@ -322,7 +325,12 @@
         float vel = getCurrentVelocity(lastX, lastY);
 
         // We snap back if the current translation is not far enough
-        boolean snapBack = isBelowFalsingThreshold();
+        boolean snapBack;
+        if (mFalsingManager.isFalseTouch()) {
+            snapBack = mFalsingManager.isFalseTouch();
+        } else {
+            snapBack = isBelowFalsingThreshold();
+        }
 
         // or if the velocity is in the opposite direction.
         boolean velIsInWrongDirection = vel * mTranslation < 0;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
index 012dc9c..14176a6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
@@ -77,6 +77,13 @@
 
     final static String TAG = "PhoneStatusBar/KeyguardBottomAreaView";
 
+    public static final String CAMERA_LAUNCH_SOURCE_AFFORDANCE = "lockscreen_affordance";
+    public static final String CAMERA_LAUNCH_SOURCE_WIGGLE = "wiggle_gesture";
+    public static final String CAMERA_LAUNCH_SOURCE_POWER_DOUBLE_TAP = "power_double_tap";
+
+    public static final String EXTRA_CAMERA_LAUNCH_SOURCE
+            = "com.android.systemui.camera_launch_source";
+
     private static final Intent SECURE_CAMERA_INTENT =
             new Intent(MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA_SECURE)
                     .addFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
@@ -170,7 +177,7 @@
                             CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL, true /* force */);
                     return true;
                 } else if (host == mCameraImageView) {
-                    launchCamera();
+                    launchCamera(CAMERA_LAUNCH_SOURCE_AFFORDANCE);
                     return true;
                 } else if (host == mLeftAffordanceView) {
                     launchLeftAffordance();
@@ -349,7 +356,7 @@
     @Override
     public void onClick(View v) {
         if (v == mCameraImageView) {
-            launchCamera();
+            launchCamera(CAMERA_LAUNCH_SOURCE_AFFORDANCE);
         } else if (v == mLeftAffordanceView) {
             launchLeftAffordance();
         } if (v == mLockIcon) {
@@ -417,8 +424,9 @@
         }
     }
 
-    public void launchCamera() {
+    public void launchCamera(String source) {
         final Intent intent = getCameraIntent();
+        intent.putExtra(EXTRA_CAMERA_LAUNCH_SOURCE, source);
         boolean wouldLaunchResolverActivity = PreviewInflater.wouldLaunchResolverActivity(
                 mContext, intent, KeyguardUpdateMonitor.getCurrentUser());
         if (intent == SECURE_CAMERA_INTENT && !wouldLaunchResolverActivity) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
index cbd23bb..99436a1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
@@ -31,7 +31,7 @@
 import com.android.keyguard.R;
 import com.android.keyguard.ViewMediatorCallback;
 import com.android.systemui.DejankUtils;
-import com.android.systemui.analytics.LockedPhoneAnalytics;
+import com.android.systemui.classifier.FalsingManager;
 
 import static com.android.keyguard.KeyguardHostView.OnDismissAction;
 import static com.android.keyguard.KeyguardSecurityModel.SecurityMode;
@@ -50,7 +50,7 @@
     private ViewGroup mRoot;
     private boolean mShowingSoon;
     private int mBouncerPromptReason;
-    private LockedPhoneAnalytics mLockedPhoneAnalytics;
+    private FalsingManager mFalsingManager;
     private KeyguardUpdateMonitorCallback mUpdateMonitorCallback =
             new KeyguardUpdateMonitorCallback() {
                 @Override
@@ -68,11 +68,11 @@
         mContainer = container;
         mWindowManager = windowManager;
         KeyguardUpdateMonitor.getInstance(mContext).registerCallback(mUpdateMonitorCallback);
-        mLockedPhoneAnalytics = LockedPhoneAnalytics.getInstance(mContext);
+        mFalsingManager = FalsingManager.getInstance(mContext);
     }
 
     public void show(boolean resetSecuritySelection) {
-        mLockedPhoneAnalytics.onBouncerShown();
+        mFalsingManager.onBouncerShown();
         ensureView();
         if (resetSecuritySelection) {
             // showPrimarySecurityScreen() updates the current security method. This is needed in
@@ -132,7 +132,7 @@
     }
 
     public void hide(boolean destroyView) {
-        mLockedPhoneAnalytics.onBouncerHidden();
+        mFalsingManager.onBouncerHidden();
         cancelShowRunnable();
          if (mKeyguardView != null) {
             mKeyguardView.cancelDismissAction();
@@ -162,7 +162,7 @@
     public void reset() {
         cancelShowRunnable();
         inflateView();
-        mLockedPhoneAnalytics.onBouncerHidden();
+        mFalsingManager.onBouncerHidden();
     }
 
     public void onScreenTurnedOff() {
@@ -250,7 +250,7 @@
 
             // We need to show it in case it is secure. If not, it will get dismissed in any case.
             mRoot.setVisibility(View.VISIBLE);
-            mLockedPhoneAnalytics.onBouncerShown();
+            mFalsingManager.onBouncerShown();
             mKeyguardView.requestFocus();
             mKeyguardView.onResume();
             return true;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarApps.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarApps.java
index 34d3fcd..364e884 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarApps.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarApps.java
@@ -31,7 +31,6 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
-import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
 import android.graphics.PixelFormat;
@@ -69,7 +68,8 @@
  * Navigation bar contains both pinned and unpinned apps: pinned in the left part, unpinned in the
  * right part, with no separator in between.
  */
-class NavigationBarApps extends LinearLayout {
+class NavigationBarApps extends LinearLayout
+        implements NavigationBarAppsModel.OnAppsChangedListener {
     public final static boolean DEBUG = false;
     private final static String TAG = "NavigationBarApps";
 
@@ -120,11 +120,20 @@
     // has a child that will be moved to make the menu to appear where we need it.
     private final ViewGroup mPopupAnchor;
     private final PopupMenu mPopupMenu;
+
+    /**
+     * True if popup menu code is busy with a popup operation.
+     * Attempting  to show a popup menu or to add menu items while it's returning true will
+     * corrupt/crash the app.
+     */
+    private boolean mIsPopupInUse = false;
     private final int [] mClickedIconLocation = new int[2];
 
     public NavigationBarApps(Context context, AttributeSet attrs) {
         super(context, attrs);
-        sAppsModel = new NavigationBarAppsModel(context);
+        if (sAppsModel == null) {
+            sAppsModel = new NavigationBarAppsModel(context);
+        }
         mPackageManager = context.getPackageManager();
         mUserManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
         mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
@@ -237,7 +246,7 @@
             ComponentName appComponentName = appInfo.getComponentName();
             if (!appComponentName.getPackageName().equals(packageName)) continue;
 
-            if (sAppsModel.buildAppLaunchIntent(appInfo) != null) {
+            if (sAppsModel.resolveApp(appInfo) != null) {
                 continue;
             }
 
@@ -278,6 +287,7 @@
         mContext.registerReceiver(mBroadcastReceiver, filter);
 
         mAppPackageMonitor.register(mContext, null, UserHandle.ALL, true);
+        sAppsModel.addOnAppsChangedListener(this);
     }
 
     @Override
@@ -285,18 +295,35 @@
         super.onDetachedFromWindow();
         mContext.unregisterReceiver(mBroadcastReceiver);
         mAppPackageMonitor.unregister();
+        sAppsModel.removeOnAppsChangedListener(this);
+    }
+
+    @Override
+    protected void onVisibilityChanged(View changedView, int visibility) {
+        super.onVisibilityChanged(changedView, visibility);
+        if (mIsPopupInUse && !isShown()) {
+            // Hide the popup if current view became invisible.
+            shutdownPopupMenu();
+        }
     }
 
     private void addAppButton(AppButtonData appButtonData) {
-        ImageView button = createAppButton(appButtonData);
+        ImageView button = createAppButton();
+        updateApp(button, appButtonData);
         addView(button);
+    }
 
-        AppInfo app = appButtonData.appInfo;
-        CharSequence appLabel = getAppLabel(mPackageManager, app.getComponentName());
-        button.setContentDescription(appLabel);
-
-        // Load the icon asynchronously.
-        new GetActivityIconTask(mPackageManager, button).execute(appButtonData);
+    private List<AppInfo> getPinnedApps() {
+        List<AppInfo> apps = new ArrayList<AppInfo>();
+        int childCount = getChildCount();
+        for (int i = 0; i != childCount; ++i) {
+            View child = getChildAt(i);
+            AppButtonData appButtonData = (AppButtonData)child.getTag();
+            if (appButtonData == null) continue;  // Skip the drag placeholder.
+            if(!appButtonData.pinned) continue;
+            apps.add(appButtonData.appInfo);
+        }
+        return apps;
     }
 
     /**
@@ -319,22 +346,13 @@
      * Saves pinned apps stored in app icons into the data model.
      */
     private void savePinnedApps() {
-        List<AppInfo> apps = new ArrayList<AppInfo>();
-        int childCount = getChildCount();
-        for (int i = 0; i != childCount; ++i) {
-            View child = getChildAt(i);
-            AppButtonData appButtonData = (AppButtonData)child.getTag();
-            if (appButtonData == null) continue;  // Skip the drag placeholder.
-            if(!appButtonData.pinned) continue;
-            apps.add(appButtonData.appInfo);
-        }
-        sAppsModel.setApps(apps);
+        sAppsModel.setApps(getPinnedApps());
     }
 
     /**
      * Creates a new ImageView for an app, inflated from R.layout.navigation_bar_app_item.
      */
-    private ImageView createAppButton(AppButtonData appButtonData) {
+    private ImageView createAppButton() {
         ImageView button = (ImageView) mLayoutInflater.inflate(
                 R.layout.navigation_bar_app_item, this, false /* attachToRoot */);
         button.setOnHoverListener(new AppHoverListener());
@@ -343,7 +361,6 @@
         // TODO: Ripple effect. Use either KeyButtonRipple or the default ripple background.
         button.setOnLongClickListener(new AppLongClickListener());
         button.setOnDragListener(new AppIconDragListener());
-        button.setTag(appButtonData);
         return button;
     }
 
@@ -362,17 +379,12 @@
      * TODO: Cache the labels, perhaps in an LruCache.
      */
     @Nullable
-    static CharSequence getAppLabel(PackageManager packageManager,
-                                    ComponentName activityName) {
-        String packageName = activityName.getPackageName();
-        ApplicationInfo info;
-        try {
-            info = packageManager.getApplicationInfo(packageName, 0x0 /* flags */);
-        } catch (PackageManager.NameNotFoundException e) {
-            Slog.w(TAG, "Package not found " + packageName);
-            return null;
-        }
-        return packageManager.getApplicationLabel(info);
+    private CharSequence getAppLabel(AppInfo appInfo) {
+        NavigationBarAppsModel.ResolvedApp resolvedApp = sAppsModel.resolveApp(appInfo);
+        if (resolvedApp == null) return null;
+
+        CharSequence unbadgedLabel = resolvedApp.ri.loadLabel(mPackageManager);
+        return mUserManager.getBadgedLabelForUser(unbadgedLabel, appInfo.getUser());
     }
 
     /** Helper function to start dragging an app icon (either pinned or recent). */
@@ -459,7 +471,7 @@
      * Creates a blank icon-sized View to create an empty space during a drag.
      */
     private ImageView createPlaceholderDragView(int index) {
-        ImageView button = createAppButton(null);
+        ImageView button = createAppButton();
         addView(button, index);
         return button;
     }
@@ -612,7 +624,7 @@
             return null;
         }
         AppInfo appInfo = new AppInfo(componentName, appUser);
-        if (sAppsModel.buildAppLaunchIntent(appInfo) == null) {
+        if (sAppsModel.resolveApp(appInfo) == null) {
             return null;
         }
         return appInfo;
@@ -620,6 +632,9 @@
 
     /** Updates the app at a given view index. */
     private void updateApp(ImageView button, AppButtonData appButtonData) {
+        CharSequence appLabel = getAppLabel(appButtonData.appInfo);
+        button.setContentDescription(appLabel);
+
         button.setTag(appButtonData);
         new GetActivityIconTask(mPackageManager, button).execute(appButtonData);
     }
@@ -671,14 +686,12 @@
     }
 
     /**
-     * Returns true if popup menu code is busy with a popup operation.
-     * Attempting  to show a popup menu or to add menu items while it's returning true will
-     * corrupt/crash the app.
+     * Brings the menu popup to closed state.
+     * Can be called at any stage of the asynchronous process of showing a menu.
      */
-    boolean isPopupInUse() {
-        // mPopupAnchor's parent will be set to non-null/null by mWindowManager.add/RemoveView
-        // correspondingly.
-        return mPopupAnchor.getParent() != null;
+    private void shutdownPopupMenu() {
+        mWindowManager.removeView(mPopupAnchor);
+        mPopupMenu.dismiss();
     }
 
     /**
@@ -712,14 +725,19 @@
         mPopupMenu.setOnDismissListener(new PopupMenu.OnDismissListener() {
             @Override
             public void onDismiss(PopupMenu menu) {
+                // FYU: thorough testing for closing menu either by the user or via
+                // shutdownPopupMenu() called at various moments of the menu creation, revealed that
+                // 'onDismiss' is guaranteed to be called after each invocation of showPopupMenu.
                 mWindowManager.removeView(mPopupAnchor);
                 anchorButton.removeOnAttachStateChangeListener(onAttachStateChangeListener);
                 mPopupMenu.setOnDismissListener(null);
                 mPopupMenu.getMenu().clear();
+                mIsPopupInUse = false;
             }
         });
 
         mWindowManager.addView(mPopupAnchor, mPopupAnchorLayoutParams);
+        mIsPopupInUse = true;
     }
 
     private void activateTask(int taskPersistentId) {
@@ -758,7 +776,7 @@
      * tasks.
      */
     void maybeShowLaunchMenu(ImageView appIcon) {
-        if (isPopupInUse()) return;
+        if (mIsPopupInUse) return;
         AppButtonData appButtonData = (AppButtonData) appIcon.getTag();
         if (appButtonData.getTaskCount() <= 1) return;
 
@@ -801,13 +819,15 @@
      */
     private class AppClickListener implements View.OnClickListener {
         private void launchApp(AppInfo appInfo, View anchor) {
-            Intent launchIntent = sAppsModel.buildAppLaunchIntent(appInfo);
-            if (launchIntent == null) {
+            NavigationBarAppsModel.ResolvedApp resolvedApp = sAppsModel.resolveApp(appInfo);
+            if (resolvedApp == null) {
                 Toast.makeText(
                         getContext(), R.string.activity_not_found, Toast.LENGTH_SHORT).show();
                 return;
             }
 
+            Intent launchIntent = resolvedApp.launchIntent;
+
             // Play a scale-up animation while launching the activity.
             // TODO: Consider playing a different animation, or no animation, if the activity is
             // already open in a visible window. In that case we should move the task to front
@@ -889,7 +909,7 @@
 
         @Override
         public boolean onContextClick(View v) {
-            if (isPopupInUse()) return true;
+            if (mIsPopupInUse) return true;
             ImageView appIcon = (ImageView) v;
             populateContextMenu(appIcon);
             showPopupMenu(appIcon);
@@ -1050,7 +1070,7 @@
         UserHandle taskUser = new UserHandle(task.userId);
         AppInfo appInfo = new AppInfo(componentName, taskUser);
 
-        if (sAppsModel.buildAppLaunchIntent(appInfo) == null) {
+        if (sAppsModel.resolveApp(appInfo) == null) {
             // If task's activity is not launcheable, fall back to a launch component of the
             // task's package.
             ComponentName component = getLaunchComponentForPackage(
@@ -1083,4 +1103,11 @@
             });
         }
     }
+
+    @Override
+    public void onPinnedAppsChanged() {
+        if (getPinnedApps().equals(sAppsModel.getApps())) return;
+        recreatePinnedAppButtons();
+        updateRecentApps();
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarAppsModel.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarAppsModel.java
index 832a3e9..d527f29 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarAppsModel.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarAppsModel.java
@@ -44,6 +44,15 @@
  * ComponentName.
  */
 class NavigationBarAppsModel {
+    public interface OnAppsChangedListener {
+        void onPinnedAppsChanged();
+    }
+
+    public class ResolvedApp {
+        Intent launchIntent;
+        ResolveInfo ri;
+    }
+
     private final static String TAG = "NavigationBarAppsModel";
 
     // Default number of apps to load initially.
@@ -79,6 +88,9 @@
     // Apps are represented as an ordered list of app infos.
     private List<AppInfo> mApps = new ArrayList<AppInfo>();
 
+    private List<OnAppsChangedListener> mOnAppsChangedListeners =
+            new ArrayList<OnAppsChangedListener>();
+
     // Id of the current user.
     private int mCurrentUserId = -1;
 
@@ -105,8 +117,8 @@
         return AppGlobals.getPackageManager();
     }
 
-    // Returns a launch intent for a given app info, or null if the app info is unlauncheable.
-    public Intent buildAppLaunchIntent(AppInfo appInfo) {
+    // Returns a resolved app info for a given app info, or null if the app info is unlauncheable.
+    public ResolvedApp resolveApp(AppInfo appInfo) {
         ComponentName component = appInfo.getComponentName();
         int appUserId = appInfo.getUser().getIdentifier();
 
@@ -153,13 +165,17 @@
                 0 /* flags */, appUserId);
         final int size = apps.size();
         for (int i = 0; i < size; ++i) {
-            ActivityInfo activityInfo = apps.get(i).activityInfo;
+            ResolveInfo ri = apps.get(i);
+            ActivityInfo activityInfo = ri.activityInfo;
             if (activityInfo.packageName.equals(component.getPackageName()) &&
                     activityInfo.name.equals(component.getClassName())) {
                 // Found an activity with category launcher that matches
                 // this component so ok to launch.
                 launchIntent.setComponent(component);
-                return launchIntent;
+                ResolvedApp resolvedApp = new ResolvedApp();
+                resolvedApp.launchIntent = launchIntent;
+                resolvedApp.ri = ri;
+                return resolvedApp;
             }
         }
 
@@ -167,6 +183,14 @@
         return null;
     }
 
+    public void addOnAppsChangedListener(OnAppsChangedListener listener) {
+        mOnAppsChangedListeners.add(listener);
+    }
+
+    public void removeOnAppsChangedListener(OnAppsChangedListener listener) {
+        mOnAppsChangedListeners.remove(listener);
+    }
+
     /**
      * Reinitializes the model for a new user.
      */
@@ -239,6 +263,11 @@
     public void setApps(List<AppInfo> apps) {
         mApps = apps;
         savePrefs();
+
+        int size = mOnAppsChangedListeners.size();
+        for (int i = 0; i < size; ++i) {
+            mOnAppsChangedListeners.get(i).onPinnedAppsChanged();
+        }
     }
 
     /** Saves the current model to disk. */
@@ -280,7 +309,7 @@
             return null;
         }
         AppInfo appInfo = new AppInfo(componentName, appUser);
-        if (buildAppLaunchIntent(appInfo) == null) {
+        if (resolveApp(appInfo) == null) {
             return null;
         }
         return appInfo;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
index f47ec20..08353cb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -22,7 +22,7 @@
 import android.animation.PropertyValuesHolder;
 import android.animation.ValueAnimator;
 import android.app.ActivityManager;
-import android.app.ActivityManager.RunningTaskInfo;
+import android.app.StatusBarManager;
 import android.content.Context;
 import android.content.pm.ResolveInfo;
 import android.content.res.Configuration;
@@ -50,6 +50,7 @@
 import com.android.systemui.EventLogConstants;
 import com.android.systemui.EventLogTags;
 import com.android.systemui.R;
+import com.android.systemui.classifier.FalsingManager;
 import com.android.systemui.qs.QSContainer;
 import com.android.systemui.qs.QSPanel;
 import com.android.systemui.statusbar.ExpandableNotificationRow;
@@ -59,7 +60,6 @@
 import com.android.systemui.statusbar.KeyguardAffordanceView;
 import com.android.systemui.statusbar.NotificationData;
 import com.android.systemui.statusbar.StatusBarState;
-import com.android.systemui.analytics.LockedPhoneAnalytics;
 import com.android.systemui.statusbar.policy.HeadsUpManager;
 import com.android.systemui.statusbar.policy.KeyguardUserSwitcher;
 import com.android.systemui.statusbar.stack.NotificationStackScrollLayout;
@@ -204,7 +204,8 @@
     private boolean mClosingWithAlphaFadeOut;
     private boolean mHeadsUpAnimatingAway;
     private boolean mLaunchingAffordance;
-    private LockedPhoneAnalytics mLockedPhoneAnalytics;
+    private FalsingManager mFalsingManager;
+    private String mLastCameraLaunchSource = KeyguardBottomAreaView.CAMERA_LAUNCH_SOURCE_AFFORDANCE;
 
     private Runnable mHeadsUpExistenceChangedRunnable = new Runnable() {
         @Override
@@ -221,7 +222,7 @@
     public NotificationPanelView(Context context, AttributeSet attrs) {
         super(context, attrs);
         setWillNotDraw(!DEBUG);
-        mLockedPhoneAnalytics = LockedPhoneAnalytics.getInstance(context);
+        mFalsingManager = FalsingManager.getInstance(context);
     }
 
     public void setStatusBar(PhoneStatusBar bar) {
@@ -481,6 +482,7 @@
         mUnlockIconActive = false;
         if (!mLaunchingAffordance) {
             mAfforanceHelper.reset(false);
+            mLastCameraLaunchSource = KeyguardBottomAreaView.CAMERA_LAUNCH_SOURCE_AFFORDANCE;
         }
         closeQs();
         mStatusBar.dismissPopups();
@@ -693,7 +695,7 @@
     }
 
     private boolean flingExpandsQs(float vel) {
-        if (isBelowFalsingThreshold()) {
+        if (isFalseTouch()) {
             return false;
         }
         if (Math.abs(vel) < mFlingAnimationUtils.getMinVelocityPxPerSecond()) {
@@ -703,8 +705,14 @@
         }
     }
 
-    private boolean isBelowFalsingThreshold() {
-        return !mQsTouchAboveFalsingThreshold && mStatusBarState == StatusBarState.KEYGUARD;
+    private boolean isFalseTouch() {
+        if (mStatusBarState != StatusBarState.KEYGUARD) {
+            return false;
+        }
+        if (mFalsingManager.isClassiferEnabled()) {
+            return mFalsingManager.isFalseTouch();
+        }
+        return !mQsTouchAboveFalsingThreshold;
     }
 
     private float getQsExpansionFraction() {
@@ -813,7 +821,7 @@
     private void handleQsDown(MotionEvent event) {
         if (event.getActionMasked() == MotionEvent.ACTION_DOWN
                 && shouldQuickSettingsIntercept(event.getX(), event.getY(), -1)) {
-            mLockedPhoneAnalytics.onQsDown();
+            mFalsingManager.onQsDown();
             mQsTracking = true;
             onQsExpansionStarted();
             mInitialHeightOnTouch = mQsExpansionHeight;
@@ -981,7 +989,7 @@
             mQsExpanded = expanded;
             updateQsState();
             requestPanelHeightUpdate();
-            mLockedPhoneAnalytics.setQsExpanded(expanded);
+            mFalsingManager.setQsExpanded(expanded);
             mNotificationStackScroller.setInterceptDelegateEnabled(expanded);
             mStatusBar.setQsExpanded(expanded);
             mQsPanel.setExpanded(expanded);
@@ -1308,7 +1316,7 @@
                     R.string.accessibility_desc_quick_settings));
             mLastAnnouncementWasQuickSettings = true;
         }
-        if (mQsFullyExpanded && mLockedPhoneAnalytics.shouldEnforceBouncer()) {
+        if (mQsFullyExpanded && mFalsingManager.shouldEnforceBouncer()) {
             mStatusBar.executeRunnableDismissingKeyguard(null, null /* cancelAction */,
                     false /* dismissShade */, true /* afterKeyguardGone */);
         }
@@ -1429,7 +1437,7 @@
             }
             return;
         }
-        boolean belowFalsingThreshold = isBelowFalsingThreshold();
+        boolean belowFalsingThreshold = isFalseTouch();
         if (belowFalsingThreshold) {
             vel = 0;
         }
@@ -1839,7 +1847,7 @@
 
     @Override
     protected void onTrackingStarted() {
-        mLockedPhoneAnalytics.onTrackingStarted();
+        mFalsingManager.onTrackingStarted();
         super.onTrackingStarted();
         if (mQsFullyExpanded) {
             mQsExpandImmediate = true;
@@ -1853,7 +1861,7 @@
 
     @Override
     protected void onTrackingStopped(boolean expand) {
-        mLockedPhoneAnalytics.onTrackingStopped();
+        mFalsingManager.onTrackingStopped();
         super.onTrackingStopped(expand);
         if (expand) {
             mNotificationStackScroller.setOverScrolledPixels(
@@ -1953,8 +1961,8 @@
             EventLogTags.writeSysuiLockscreenGesture(
                     EventLogConstants.SYSUI_LOCKSCREEN_GESTURE_SWIPE_DIALER, lengthDp, velocityDp);
 
-            mLockedPhoneAnalytics.onLeftAffordanceOn();
-            if (mLockedPhoneAnalytics.shouldEnforceBouncer()) {
+            mFalsingManager.onLeftAffordanceOn();
+            if (mFalsingManager.shouldEnforceBouncer()) {
                 mStatusBar.executeRunnableDismissingKeyguard(new Runnable() {
                     @Override
                     public void run() {
@@ -1966,20 +1974,23 @@
                 mKeyguardBottomArea.launchLeftAffordance();
             }
         } else {
-            EventLogTags.writeSysuiLockscreenGesture(
-                    EventLogConstants.SYSUI_LOCKSCREEN_GESTURE_SWIPE_CAMERA, lengthDp, velocityDp);
-
-            mLockedPhoneAnalytics.onCameraOn();
-            if (mLockedPhoneAnalytics.shouldEnforceBouncer()) {
+            if (KeyguardBottomAreaView.CAMERA_LAUNCH_SOURCE_AFFORDANCE.equals(
+                    mLastCameraLaunchSource)) {
+                EventLogTags.writeSysuiLockscreenGesture(
+                        EventLogConstants.SYSUI_LOCKSCREEN_GESTURE_SWIPE_CAMERA,
+                        lengthDp, velocityDp);
+            }
+            mFalsingManager.onCameraOn();
+            if (mFalsingManager.shouldEnforceBouncer()) {
                 mStatusBar.executeRunnableDismissingKeyguard(new Runnable() {
                     @Override
                     public void run() {
-                        mKeyguardBottomArea.launchCamera();
+                        mKeyguardBottomArea.launchCamera(mLastCameraLaunchSource);
                     }
                 }, null, true /* dismissShade */, false /* afterKeyguardGone */);
             }
             else {
-                mKeyguardBottomArea.launchCamera();
+                mKeyguardBottomArea.launchCamera(mLastCameraLaunchSource);
             }
         }
         mStatusBar.startLaunchTransitionTimeout();
@@ -2024,7 +2035,7 @@
 
     @Override
     public void onSwipingStarted(boolean rightIcon) {
-        mLockedPhoneAnalytics.onAffordanceSwipingStarted(rightIcon);
+        mFalsingManager.onAffordanceSwipingStarted(rightIcon);
         boolean camera = getLayoutDirection() == LAYOUT_DIRECTION_RTL ? !rightIcon
                 : rightIcon;
         if (camera) {
@@ -2037,7 +2048,7 @@
 
     @Override
     public void onSwipingAborted() {
-        mLockedPhoneAnalytics.onAffordanceSwipingAborted();
+        mFalsingManager.onAffordanceSwipingAborted();
         mKeyguardBottomArea.unbindCameraPrewarmService(false /* launched */);
     }
 
@@ -2420,7 +2431,17 @@
         return !mDozing;
     }
 
-    public void launchCamera(boolean animate) {
+    public void launchCamera(boolean animate, int source) {
+        if (source == StatusBarManager.CAMERA_LAUNCH_SOURCE_POWER_DOUBLE_TAP) {
+            mLastCameraLaunchSource = KeyguardBottomAreaView.CAMERA_LAUNCH_SOURCE_POWER_DOUBLE_TAP;
+        } else if (source == StatusBarManager.CAMERA_LAUNCH_SOURCE_WIGGLE) {
+            mLastCameraLaunchSource = KeyguardBottomAreaView.CAMERA_LAUNCH_SOURCE_WIGGLE;
+        } else {
+
+            // Default.
+            mLastCameraLaunchSource = KeyguardBottomAreaView.CAMERA_LAUNCH_SOURCE_AFFORDANCE;
+        }
+
         // If we are launching it when we are occluded already we don't want it to animate,
         // nor setting these flags, since the occluded state doesn't change anymore, hence it's
         // never reset.
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 9cd6ea3..bd16257 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
@@ -35,6 +35,9 @@
 import com.android.systemui.EventLogConstants;
 import com.android.systemui.EventLogTags;
 import com.android.systemui.R;
+import com.android.systemui.classifier.Classifier;
+import com.android.systemui.classifier.FalsingManager;
+import com.android.systemui.classifier.HumanInteractionClassifier;
 import com.android.systemui.doze.DozeLog;
 import com.android.systemui.statusbar.FlingAnimationUtils;
 import com.android.systemui.statusbar.StatusBarState;
@@ -85,6 +88,7 @@
     private ObjectAnimator mPeekAnimator;
     private VelocityTrackerInterface mVelocityTracker;
     private FlingAnimationUtils mFlingAnimationUtils;
+    private FalsingManager mFalsingManager;
 
     /**
      * Whether an instant expand request is currently pending and we are just waiting for layout.
@@ -190,6 +194,7 @@
         mLinearOutSlowInInterpolator =
                 AnimationUtils.loadInterpolator(context, android.R.interpolator.linear_out_slow_in);
         mBounceInterpolator = new BounceInterpolator();
+        mFalsingManager = FalsingManager.getInstance(context);
     }
 
     protected void loadDimens() {
@@ -605,6 +610,9 @@
         if (!mStatusBar.isFalsingThresholdNeeded()) {
             return false;
         }
+        if (mFalsingManager.isClassiferEnabled()) {
+            return mFalsingManager.isFalseTouch();
+        }
         if (!mTouchAboveFalsingThreshold) {
             return true;
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
index 33ebfff..98f5444 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -108,6 +108,7 @@
 import com.android.systemui.EventLogTags;
 import com.android.systemui.Prefs;
 import com.android.systemui.R;
+import com.android.systemui.classifier.FalsingManager;
 import com.android.systemui.assist.AssistManager;
 import com.android.systemui.doze.DozeHost;
 import com.android.systemui.doze.DozeLog;
@@ -131,7 +132,6 @@
 import com.android.systemui.statusbar.SignalClusterView;
 import com.android.systemui.statusbar.SpeedBumpView;
 import com.android.systemui.statusbar.StatusBarState;
-import com.android.systemui.analytics.LockedPhoneAnalytics;
 import com.android.systemui.statusbar.phone.UnlockMethodCache.OnUnlockMethodChangedListener;
 import com.android.systemui.statusbar.policy.AccessibilityController;
 import com.android.systemui.statusbar.policy.BatteryController;
@@ -491,12 +491,19 @@
     private boolean mLaunchTransitionFadingAway;
     private ExpandableNotificationRow mDraggedDownRow;
     private boolean mLaunchCameraOnScreenTurningOn;
+    private boolean mLaunchCameraOnFinishedGoingToSleep;
+    private int mLastCameraLaunchSource;
     private PowerManager.WakeLock mGestureWakeLock;
     private Vibrator mVibrator;
 
     // Fingerprint (as computed by getLoggingFingerprint() of the last logged state.
     private int mLastLoggedStateFingerprint;
 
+    /**
+     * If set, the device has started going to sleep but isn't fully non-interactive yet.
+     */
+    protected boolean mStartedGoingToSleep;
+
     private static final int VISIBLE_LOCATIONS = StackViewState.LOCATION_FIRST_CARD
             | StackViewState.LOCATION_MAIN_AREA;
 
@@ -598,7 +605,7 @@
     private HashSet<Entry> mHeadsUpEntriesToRemoveOnSwitch = new HashSet<>();
     private RankingMap mLatestRankingMap;
     private boolean mNoAnimationOnNextBarModeChange;
-    private LockedPhoneAnalytics mLockedPhoneAnalytics;
+    private FalsingManager mFalsingManager;
 
     @Override
     public void start() {
@@ -646,7 +653,7 @@
         notifyUserAboutHiddenNotifications();
 
         mScreenPinningRequest = new ScreenPinningRequest(mContext);
-        mLockedPhoneAnalytics = LockedPhoneAnalytics.getInstance(mContext);
+        mFalsingManager = FalsingManager.getInstance(mContext);
     }
 
     // ================================================================================
@@ -3805,7 +3812,7 @@
         }
         mState = state;
         mGroupManager.setStatusBarState(state);
-        mLockedPhoneAnalytics.setStatusBarState(state);
+        mFalsingManager.setStatusBarState(state);
         mStatusBarWindowManager.setStatusBarState(state);
         updateDozing();
     }
@@ -3827,7 +3834,7 @@
     }
 
     public void onUnlockHintStarted() {
-        mLockedPhoneAnalytics.onUnlockHintStarted();
+        mFalsingManager.onUnlockHintStarted();
         mKeyguardIndicationController.showTransientIndication(R.string.keyguard_unlock);
     }
 
@@ -3837,17 +3844,17 @@
     }
 
     public void onCameraHintStarted() {
-        mLockedPhoneAnalytics.onCameraHintStarted();
+        mFalsingManager.onCameraHintStarted();
         mKeyguardIndicationController.showTransientIndication(R.string.camera_hint);
     }
 
     public void onVoiceAssistHintStarted() {
-        mLockedPhoneAnalytics.onLeftAffordanceHintStarted();
+        mFalsingManager.onLeftAffordanceHintStarted();
         mKeyguardIndicationController.showTransientIndication(R.string.voice_hint);
     }
 
     public void onPhoneHintStarted() {
-        mLockedPhoneAnalytics.onLeftAffordanceHintStarted();
+        mFalsingManager.onLeftAffordanceHintStarted();
         mKeyguardIndicationController.showTransientIndication(R.string.phone_hint);
     }
 
@@ -3922,7 +3929,7 @@
             row.setUserExpanded(true);
         }
         boolean fullShadeNeedsBouncer = !userAllowsPrivateNotificationsInPublic(mCurrentUserId)
-                || !mShowLockscreenNotifications || mLockedPhoneAnalytics.shouldEnforceBouncer();
+                || !mShowLockscreenNotifications || mFalsingManager.shouldEnforceBouncer();
         if (isLockscreenPublicMode() && fullShadeNeedsBouncer) {
             mLeaveOpenOnKeyguardHide = true;
             showBouncer();
@@ -3963,16 +3970,33 @@
         disable(mDisabledUnmodified1, mDisabledUnmodified2, true /* animate */);
     }
 
+    public void onStartedGoingToSleep() {
+        mStartedGoingToSleep = true;
+    }
+
     public void onFinishedGoingToSleep() {
         mNotificationPanel.onAffordanceLaunchEnded();
         releaseGestureWakeLock();
         mLaunchCameraOnScreenTurningOn = false;
+        mStartedGoingToSleep = false;
         mDeviceInteractive = false;
         mWakeUpComingFromTouch = false;
         mWakeUpTouchLocation = null;
-        mLockedPhoneAnalytics.onScreenOff();
+        mFalsingManager.onScreenOff();
         mStackScroller.setAnimationsEnabled(false);
         updateVisibleToUser();
+        if (mLaunchCameraOnFinishedGoingToSleep) {
+            mLaunchCameraOnFinishedGoingToSleep = false;
+
+            // This gets executed before we will show Keyguard, so post it in order that the state
+            // is correct.
+            mHandler.post(new Runnable() {
+                @Override
+                public void run() {
+                    onCameraLaunchGestureDetected(mLastCameraLaunchSource);
+                }
+            });
+        }
     }
 
     public void onStartedWakingUp() {
@@ -3980,20 +4004,21 @@
         mStackScroller.setAnimationsEnabled(true);
         mNotificationPanel.setTouchDisabled(false);
         updateVisibleToUser();
-        mLockedPhoneAnalytics.onScreenOn();
     }
 
     public void onScreenTurningOn() {
         mScreenTurningOn = true;
+        mFalsingManager.onScreenTurningOn();
         mNotificationPanel.onScreenTurningOn();
         if (mLaunchCameraOnScreenTurningOn) {
-            mNotificationPanel.launchCamera(false);
+            mNotificationPanel.launchCamera(false, mLastCameraLaunchSource);
             mLaunchCameraOnScreenTurningOn = false;
         }
     }
 
     private void vibrateForCameraGesture() {
-        mVibrator.vibrate(750L);
+        // Make sure to pass -1 for repeat so VibratorService doesn't stop us when going to sleep.
+        mVibrator.vibrate(new long[] { 0, 750L }, -1 /* repeat */);
     }
 
     public void onScreenTurnedOn() {
@@ -4082,8 +4107,7 @@
         super.toggleRecents();
     }
 
-    @Override
-    public void onVisibilityChanged(boolean visible) {
+    public void updateRecentsVisibility(boolean visible) {
         // Update the recents visibility flag
         if (visible) {
             mSystemUiVisibility |= View.RECENT_APPS_VISIBLE;
@@ -4119,7 +4143,7 @@
             mWakeUpTouchLocation = new PointF(event.getX(), event.getY());
             mNotificationPanel.setTouchDisabled(false);
             mStatusBarKeyguardViewManager.notifyDeviceWakeUpRequested();
-            mLockedPhoneAnalytics.onScreenOnFromTouch();
+            mFalsingManager.onScreenOnFromTouch();
         }
     }
 
@@ -4152,7 +4176,12 @@
     }
 
     @Override
-    public void onCameraLaunchGestureDetected() {
+    public void onCameraLaunchGestureDetected(int source) {
+        mLastCameraLaunchSource = source;
+        if (mStartedGoingToSleep) {
+            mLaunchCameraOnFinishedGoingToSleep = true;
+            return;
+        }
         if (!mNotificationPanel.canCameraGestureBeLaunched(
                 mStatusBarKeyguardViewManager.isShowing() && mExpandedVisible)) {
             return;
@@ -4174,7 +4203,7 @@
                 mGestureWakeLock.acquire(LAUNCH_TRANSITION_TIMEOUT_MS + 1000L);
             }
             if (mScreenTurningOn || mStatusBarKeyguardViewManager.isScreenTurnedOn()) {
-                mNotificationPanel.launchCamera(mDeviceInteractive /* animate */);
+                mNotificationPanel.launchCamera(mDeviceInteractive /* animate */, source);
             } else {
                 // We need to defer the camera launch until the screen comes on, since otherwise
                 // we will dismiss us too early since we are waiting on an activity to be drawn and
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 e26f423..05f6e57 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -164,6 +164,10 @@
         }
     }
 
+    public void onStartedGoingToSleep() {
+        mPhoneStatusBar.onStartedGoingToSleep();
+    }
+
     public void onFinishedGoingToSleep() {
         mDeviceInteractive = false;
         mPhoneStatusBar.onFinishedGoingToSleep();
@@ -462,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/phone/StatusBarWindowView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
index bbf981f..cfd3358 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
@@ -36,10 +36,10 @@
 import android.widget.FrameLayout;
 
 import com.android.systemui.R;
+import com.android.systemui.classifier.FalsingManager;
 import com.android.systemui.statusbar.BaseStatusBar;
 import com.android.systemui.statusbar.DragDownHelper;
 import com.android.systemui.statusbar.StatusBarState;
-import com.android.systemui.analytics.LockedPhoneAnalytics;
 import com.android.systemui.statusbar.stack.NotificationStackScrollLayout;
 
 
@@ -56,14 +56,14 @@
 
     private PhoneStatusBar mService;
     private final Paint mTransparentSrcPaint = new Paint();
-    private LockedPhoneAnalytics mLockedPhoneAnalytics;
+    private FalsingManager mFalsingManager;
 
     public StatusBarWindowView(Context context, AttributeSet attrs) {
         super(context, attrs);
         setMotionEventSplittingEnabled(false);
         mTransparentSrcPaint.setColor(0);
         mTransparentSrcPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC));
-        mLockedPhoneAnalytics = LockedPhoneAnalytics.getInstance(context);
+        mFalsingManager = FalsingManager.getInstance(context);
     }
 
     @Override
@@ -200,7 +200,7 @@
 
     @Override
     public boolean dispatchTouchEvent(MotionEvent ev) {
-        mLockedPhoneAnalytics.onTouchEvent(ev, getWidth(), getHeight());
+        mFalsingManager.onTouchEvent(ev, getWidth(), getHeight());
         if (mBrightnessMirror != null && mBrightnessMirror.getVisibility() == VISIBLE) {
             // Disallow new pointers while the brightness mirror is visible. This is so that you
             // can't touch anything other than the brightness slider while the mirror is showing
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/statusbar/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
index 51c2208..5e5f810 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
@@ -37,6 +37,7 @@
 import com.android.systemui.ExpandHelper;
 import com.android.systemui.R;
 import com.android.systemui.SwipeHelper;
+import com.android.systemui.classifier.FalsingManager;
 import com.android.systemui.statusbar.ActivatableNotificationView;
 import com.android.systemui.statusbar.DismissView;
 import com.android.systemui.statusbar.EmptyShadeView;
@@ -47,7 +48,6 @@
 import com.android.systemui.statusbar.SpeedBumpView;
 import com.android.systemui.statusbar.StackScrollerDecorView;
 import com.android.systemui.statusbar.StatusBarState;
-import com.android.systemui.analytics.LockedPhoneAnalytics;
 import com.android.systemui.statusbar.phone.NotificationGroupManager;
 import com.android.systemui.statusbar.phone.PhoneStatusBar;
 import com.android.systemui.statusbar.phone.ScrimController;
@@ -232,7 +232,7 @@
     private boolean mForceNoOverlappingRendering;
     private NotificationOverflowContainer mOverflowContainer;
     private final ArrayList<Pair<ExpandableNotificationRow, Boolean>> mTmpList = new ArrayList<>();
-    private LockedPhoneAnalytics mLockedPhoneAnalytics;
+    private FalsingManager mFalsingManager;
 
     public NotificationStackScrollLayout(Context context) {
         this(context, null);
@@ -266,7 +266,7 @@
             mDebugPaint.setStrokeWidth(2);
             mDebugPaint.setStyle(Paint.Style.STROKE);
         }
-        mLockedPhoneAnalytics = LockedPhoneAnalytics.getInstance(context);
+        mFalsingManager = FalsingManager.getInstance(context);
     }
 
     @Override
@@ -599,8 +599,8 @@
         }
         if (DEBUG) Log.v(TAG, "onChildDismissed: " + v);
 
-        mLockedPhoneAnalytics.onNotificationDismissed();
-        if (mLockedPhoneAnalytics.shouldEnforceBouncer()) {
+        mFalsingManager.onNotificationDismissed();
+        if (mFalsingManager.shouldEnforceBouncer()) {
             mPhoneStatusBar.executeRunnableDismissingKeyguard(null, null /* cancelAction */,
                     false /* dismissShade */, true /* afterKeyguardGone */);
         }
@@ -631,7 +631,7 @@
     }
 
     public void onBeginDrag(View v) {
-        mLockedPhoneAnalytics.onNotificatonStartDismissing();
+        mFalsingManager.onNotificatonStartDismissing();
         setSwipingInProgress(true);
         mAmbientState.onBeginDrag(v);
         if (mAnimationsEnabled && (mIsExpanded || !isPinnedHeadsUp(v))) {
@@ -658,7 +658,7 @@
     }
 
     public void onDragCancelled(View v) {
-        mLockedPhoneAnalytics.onNotificatonStopDismissing();
+        mFalsingManager.onNotificatonStopDismissing();
         setSwipingInProgress(false);
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java
index 2587b9f..bbe5dd9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java
@@ -171,7 +171,7 @@
     }
 
     @Override
-    public void onCameraLaunchGestureDetected() {
+    public void onCameraLaunchGestureDetected(int source) {
     }
 
     @Override
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/packages/SystemUI/src/com/android/systemui/volume/ZenFooter.java b/packages/SystemUI/src/com/android/systemui/volume/ZenFooter.java
index af7ee08..f156607 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/ZenFooter.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/ZenFooter.java
@@ -139,6 +139,7 @@
     }
 
     public void onConfigurationChanged() {
+        mEndNowButton.setText(mContext.getString(R.string.volume_zen_end_now));
         mSpTexts.update();
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NavigationBarAppsModelTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NavigationBarAppsModelTest.java
index e7a40d7..f51e8ff 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NavigationBarAppsModelTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NavigationBarAppsModelTest.java
@@ -44,7 +44,6 @@
 import java.util.ArrayList;
 import java.util.List;
 import java.util.HashMap;
-import java.util.List;
 import java.util.Map;
 
 /** Tests for the data model for the navigation bar app icons. */
@@ -109,8 +108,8 @@
         };
     }
 
-    /** Tests buildAppLaunchIntent(). */
-    public void testBuildAppLaunchIntent() {
+    /** Tests resolveApp(). */
+    public void testResolveApp() {
         ActivityInfo mockNonExportedActivityInfo = new ActivityInfo();
         mockNonExportedActivityInfo.exported = false;
         ActivityInfo mockExportedActivityInfo = new ActivityInfo();
@@ -149,25 +148,27 @@
 
         mModel.setCurrentUser(3);
         // Unlauncheable (for various reasons) apps.
-        assertEquals(null, mModel.buildAppLaunchIntent(
+        assertEquals(null, mModel.resolveApp(
                 new AppInfo(new ComponentName("package0", "class0"), new UserHandle(3))));
         mModel.setCurrentUser(4);
-        assertEquals(null, mModel.buildAppLaunchIntent(
+        assertEquals(null, mModel.resolveApp(
                 new AppInfo(new ComponentName("package1", "class1"), new UserHandle(4))));
         mModel.setCurrentUser(5);
-        assertEquals(null, mModel.buildAppLaunchIntent(
+        assertEquals(null, mModel.resolveApp(
                 new AppInfo(new ComponentName("package2", "class2"), new UserHandle(5))));
         mModel.setCurrentUser(6);
-        assertEquals(null, mModel.buildAppLaunchIntent(
+        assertEquals(null, mModel.resolveApp(
                 new AppInfo(new ComponentName("package3", "class3"), new UserHandle(6))));
 
         // A launcheable app.
         mModel.setCurrentUser(7);
-        Intent intent = mModel.buildAppLaunchIntent(
+        NavigationBarAppsModel.ResolvedApp resolvedApp = mModel.resolveApp(
                 new AppInfo(new ComponentName("package4", "class4"), new UserHandle(7)));
-        assertNotNull(intent);
+        assertNotNull(resolvedApp);
+        Intent intent = resolvedApp.launchIntent;
         assertEquals(new ComponentName("package4", "class4"), intent.getComponent());
         assertEquals("package4", intent.getPackage());
+        assertEquals(ri1, resolvedApp.ri);
     }
 
     /** Initializes the model from SharedPreferences for a few app activites. */
@@ -412,4 +413,19 @@
         verify(mMockEdit).apply();
         verifyNoMoreInteractions(mMockEdit);
     }
+
+    /** Tests the apps-changed listener. */
+    public void testAppsChangedListeners() {
+        NavigationBarAppsModel.OnAppsChangedListener listener =
+                mock(NavigationBarAppsModel.OnAppsChangedListener.class);
+
+        mModel.addOnAppsChangedListener(listener);
+        mModel.setApps(new ArrayList<AppInfo>());
+        verify(listener).onPinnedAppsChanged();
+        verifyNoMoreInteractions(listener);
+
+        mModel.removeOnAppsChangedListener(listener);
+        mModel.setApps(new ArrayList<AppInfo>());
+        verifyNoMoreInteractions(listener);
+    }
 }
diff --git a/packages/services/PacProcessor/jni/com_android_pacprocessor_PacNative.cpp b/packages/services/PacProcessor/jni/com_android_pacprocessor_PacNative.cpp
index 2727338..990d770 100644
--- a/packages/services/PacProcessor/jni/com_android_pacprocessor_PacNative.cpp
+++ b/packages/services/PacProcessor/jni/com_android_pacprocessor_PacNative.cpp
@@ -130,7 +130,7 @@
     return jret;
 }
 
-static JNINativeMethod gMethods[] = {
+static const JNINativeMethod gMethods[] = {
     { "createV8ParserNativeLocked", "()Z",
         (void*)com_android_pacprocessor_PacNative_createV8ParserNativeLocked},
     { "destroyV8ParserNativeLocked", "()Z",
diff --git a/rs/jni/android_renderscript_RenderScript.cpp b/rs/jni/android_renderscript_RenderScript.cpp
index 0419d33..8c830e8 100644
--- a/rs/jni/android_renderscript_RenderScript.cpp
+++ b/rs/jni/android_renderscript_RenderScript.cpp
@@ -2494,7 +2494,7 @@
 
 static const char *classPathName = "android/renderscript/RenderScript";
 
-static JNINativeMethod methods[] = {
+static const JNINativeMethod methods[] = {
 {"_nInit",                         "()V",                                     (void*)_nInit },
 
 {"nDeviceCreate",                  "()J",                                     (void*)nDeviceCreate },
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/accessibility/java/com/android/server/accessibility/AutoclickController.java b/services/accessibility/java/com/android/server/accessibility/AutoclickController.java
index 2e6136f..8989625 100644
--- a/services/accessibility/java/com/android/server/accessibility/AutoclickController.java
+++ b/services/accessibility/java/com/android/server/accessibility/AutoclickController.java
@@ -16,9 +16,16 @@
 
 package com.android.server.accessibility;
 
+import android.annotation.NonNull;
+import android.content.ContentResolver;
 import android.content.Context;
+import android.database.ContentObserver;
+import android.net.Uri;
 import android.os.Handler;
 import android.os.SystemClock;
+import android.os.UserHandle;
+import android.provider.Settings;
+import android.util.Slog;
 import android.view.InputDevice;
 import android.view.KeyEvent;
 import android.view.MotionEvent;
@@ -46,16 +53,17 @@
  * the class should handle cases where multiple mouse devices are present.
  */
 public class AutoclickController implements EventStreamTransformation {
+
+    public static final int DEFAULT_CLICK_DELAY_MS = 600;
+
     private static final String LOG_TAG = AutoclickController.class.getSimpleName();
 
-    // TODO: Control click delay via settings.
-    private static final int CLICK_DELAY_MS = 600;
-
     private EventStreamTransformation mNext;
-    private Context mContext;
+    private final Context mContext;
 
     // Lazily created on the first mouse motion event.
     private ClickScheduler mClickScheduler;
+    private ClickDelayObserver mClickDelayObserver;
 
     public AutoclickController(Context context) {
         mContext = context;
@@ -66,7 +74,9 @@
         if (event.isFromSource(InputDevice.SOURCE_MOUSE)) {
             if (mClickScheduler == null) {
                 Handler handler = new Handler(mContext.getMainLooper());
-                mClickScheduler = new ClickScheduler(handler, CLICK_DELAY_MS);
+                mClickScheduler = new ClickScheduler(handler, DEFAULT_CLICK_DELAY_MS);
+                mClickDelayObserver = new ClickDelayObserver(handler);
+                mClickDelayObserver.start(mContext.getContentResolver(), mClickScheduler);
             }
 
             handleMouseMotion(event, policyFlags);
@@ -119,8 +129,13 @@
 
     @Override
     public void onDestroy() {
+        if (mClickDelayObserver != null) {
+            mClickDelayObserver.stop();
+            mClickDelayObserver = null;
+        }
         if (mClickScheduler != null) {
             mClickScheduler.cancel();
+            mClickScheduler = null;
         }
     }
 
@@ -143,6 +158,80 @@
     }
 
     /**
+     * Observes setting value for autoclick delay, and updates ClickScheduler delay whenever the
+     * setting value changes.
+     */
+    final private static class ClickDelayObserver extends ContentObserver {
+        /** URI used to identify the autoclick delay setting with content resolver. */
+        private final Uri mAutoclickDelaySettingUri = Settings.Secure.getUriFor(
+                Settings.Secure.ACCESSIBILITY_AUTOCLICK_DELAY);
+
+        private ContentResolver mContentResolver;
+        private ClickScheduler mClickScheduler;
+
+        public ClickDelayObserver(Handler handler) {
+            super(handler);
+        }
+
+        /**
+         * Starts the observer. And makes sure up-to-date autoclick delay is propagated to
+         * |clickScheduler|.
+         *
+         * @param contentResolver Content resolver that should be observed for setting's value
+         *     changes.
+         * @param clickScheduler ClickScheduler that should be updated when click delay changes.
+         * @throws IllegalStateException If internal state is already setup when the method is
+         *         called.
+         * @throws NullPointerException If any of the arguments is a null pointer.
+         */
+        public void start(@NonNull ContentResolver contentResolver,
+                @NonNull ClickScheduler clickScheduler) {
+            if (mContentResolver != null || mClickScheduler != null) {
+                throw new IllegalStateException("Observer already started.");
+            }
+            if (contentResolver == null) {
+                throw new NullPointerException("contentResolver not set.");
+            }
+            if (clickScheduler == null) {
+                throw new NullPointerException("clickScheduler not set.");
+            }
+
+            mContentResolver = contentResolver;
+            mClickScheduler = clickScheduler;
+            mContentResolver.registerContentObserver(mAutoclickDelaySettingUri, false, this,
+                    UserHandle.USER_ALL);
+
+            // Initialize mClickScheduler's initial delay value.
+            onChange(true, mAutoclickDelaySettingUri);
+        }
+
+        /**
+         * Stops the the observer. Should only be called if the observer has been started.
+         *
+         * @throws IllegalStateException If internal state hasn't yet been initialized by calling
+         *         {@link #start}.
+         */
+        public void stop() {
+            if (mContentResolver == null || mClickScheduler == null) {
+                throw new IllegalStateException("ClickDelayObserver not started.");
+            }
+
+            mContentResolver.unregisterContentObserver(this);
+        }
+
+        @Override
+        public void onChange(boolean selfChange, Uri uri) {
+            if (mAutoclickDelaySettingUri.equals(uri)) {
+                // TODO: Plumb current user id down to here and use getIntForUser.
+                int delay = Settings.Secure.getInt(
+                        mContentResolver, Settings.Secure.ACCESSIBILITY_AUTOCLICK_DELAY,
+                        DEFAULT_CLICK_DELAY_MS);
+                mClickScheduler.updateDelay(delay);
+            }
+        }
+    }
+
+    /**
      * Schedules and performs click event sequence that should be initiated when mouse pointer stops
      * moving. The click is first scheduled when a mouse movement is detected, and then further
      * delayed on every sufficient mouse movement.
@@ -242,6 +331,15 @@
         }
 
         /**
+         * Updates delay that should be used when scheduling clicks. The delay will be used only for
+         * clicks scheduled after this point (pending click tasks are not affected).
+         * @param delay New delay value.
+         */
+        public void updateDelay(int delay) {
+            mDelay = delay;
+        }
+
+        /**
          * Updates the time at which click sequence should occur.
          *
          * @param delay Delay (from now) after which click should occur.
diff --git a/services/accessibility/java/com/android/server/accessibility/TouchExplorer.java b/services/accessibility/java/com/android/server/accessibility/TouchExplorer.java
index 85730cd..c3f5c43 100644
--- a/services/accessibility/java/com/android/server/accessibility/TouchExplorer.java
+++ b/services/accessibility/java/com/android/server/accessibility/TouchExplorer.java
@@ -29,6 +29,7 @@
 import android.os.Handler;
 import android.os.SystemClock;
 import android.util.Slog;
+import android.view.GestureDetector;
 import android.view.InputDevice;
 import android.view.KeyEvent;
 import android.view.MotionEvent;
@@ -114,9 +115,6 @@
     // Timeout within which we try to detect a tap.
     private final int mTapTimeout;
 
-    // Timeout within which we try to detect a double tap.
-    private final int mDoubleTapTimeout;
-
     // Slop between the down and up tap to be a tap.
     private final int mTouchSlop;
 
@@ -230,7 +228,6 @@
         mInjectedPointerTracker = new InjectedPointerTracker();
         mTapTimeout = ViewConfiguration.getTapTimeout();
         mDetermineUserIntentTimeout = ViewConfiguration.getDoubleTapTimeout();
-        mDoubleTapTimeout = ViewConfiguration.getDoubleTapTimeout();
         mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
         mDoubleTapSlop = ViewConfiguration.get(context).getScaledDoubleTapSlop();
         mHandler = new Handler(context.getMainLooper());
@@ -248,7 +245,7 @@
         mSendTouchInteractionEndDelayed = new SendAccessibilityEventDelayed(
                 AccessibilityEvent.TYPE_TOUCH_INTERACTION_END,
                 mDetermineUserIntentTimeout);
-        mDoubleTapDetector = new DoubleTapDetector();
+        mDoubleTapDetector = new DoubleTapDetector(mContext);
         final float density = context.getResources().getDisplayMetrics().density;
         mScaledMinPointerDistanceToUseMiddleLocation =
             (int) (MIN_POINTER_DISTANCE_TO_USE_MIDDLE_LOCATION_DIP * density);
@@ -1109,66 +1106,63 @@
         }
     }
 
-    private class DoubleTapDetector {
-        private MotionEvent mDownEvent;
-        private MotionEvent mFirstTapEvent;
+    private class DoubleTapDetector extends GestureDetector.SimpleOnGestureListener {
+        private final GestureDetector mGestureDetector;
+        private boolean mFirstTapDetected;
+        private boolean mDoubleTapDetected;
 
-        public void onMotionEvent(MotionEvent event, int policyFlags) {
-            final int actionIndex = event.getActionIndex();
-            final int action = event.getActionMasked();
-            switch (action) {
-                case MotionEvent.ACTION_DOWN:
-                case MotionEvent.ACTION_POINTER_DOWN: {
-                    if (mFirstTapEvent != null
-                            && !GestureUtils.isSamePointerContext(mFirstTapEvent, event)) {
-                        clear();
-                    }
-                    mDownEvent = MotionEvent.obtain(event);
-                } break;
-                case MotionEvent.ACTION_UP:
-                case MotionEvent.ACTION_POINTER_UP: {
-                    if (mDownEvent == null) {
-                        return;
-                    }
-                    if (!GestureUtils.isSamePointerContext(mDownEvent, event)) {
-                        clear();
-                        return;
-                    }
-                    if (GestureUtils.isTap(mDownEvent, event, mTapTimeout, mTouchSlop,
-                            actionIndex)) {
-                        if (mFirstTapEvent == null || GestureUtils.isTimedOut(mFirstTapEvent,
-                                event, mDoubleTapTimeout)) {
-                            mFirstTapEvent = MotionEvent.obtain(event);
-                            mDownEvent.recycle();
-                            mDownEvent = null;
-                            return;
-                        }
-                        if (GestureUtils.isMultiTap(mFirstTapEvent, event, mDoubleTapTimeout,
-                                mDoubleTapSlop, actionIndex)) {
-                            onDoubleTap(event, policyFlags);
-                            mFirstTapEvent.recycle();
-                            mFirstTapEvent = null;
-                            mDownEvent.recycle();
-                            mDownEvent = null;
-                            return;
-                        }
-                        mFirstTapEvent.recycle();
-                        mFirstTapEvent = null;
-                    } else {
-                        if (mFirstTapEvent != null) {
-                            mFirstTapEvent.recycle();
-                            mFirstTapEvent = null;
-                        }
-                    }
-                    mDownEvent.recycle();
-                    mDownEvent = null;
-                } break;
-            }
+        DoubleTapDetector(Context context) {
+            mGestureDetector = new GestureDetector(context, this);
+            mGestureDetector.setOnDoubleTapListener(this);
         }
 
-        public void onDoubleTap(MotionEvent secondTapUp, int policyFlags) {
+        public void onMotionEvent(MotionEvent event, int policyFlags) {
+            switch (event.getActionMasked()) {
+                case MotionEvent.ACTION_DOWN:
+                    mDoubleTapDetected = false;
+                    break;
+
+                case MotionEvent.ACTION_UP:
+                    maybeFinishDoubleTap(event, policyFlags);
+                    break;
+            }
+            mGestureDetector.onTouchEvent(event);
+        }
+
+        @Override
+        public boolean onDown(MotionEvent event) {
+            return true;
+        }
+
+        @Override
+        public boolean onSingleTapUp(MotionEvent event) {
+            mFirstTapDetected = true;
+            return false;
+        }
+
+        @Override
+        public boolean onSingleTapConfirmed(MotionEvent event) {
+            clear();
+            return false;
+        }
+
+        @Override
+        public boolean onDoubleTap(MotionEvent event) {
+            // The processing of the double tap is deferred until the finger is
+            // lifted, so that we can detect a long press on the second tap.
+            mDoubleTapDetected = true;
+            return true;
+        }
+
+        private void maybeFinishDoubleTap(MotionEvent event, int policyFlags) {
+            if (!mDoubleTapDetected) {
+                return;
+            }
+
+            clear();
+
             // This should never be called when more than two pointers are down.
-            if (secondTapUp.getPointerCount() > 2) {
+            if (event.getPointerCount() > 2) {
                 return;
             }
 
@@ -1184,8 +1178,8 @@
                 mSendTouchInteractionEndDelayed.forceSendAndRemove();
             }
 
-            final int pointerId = secondTapUp.getPointerId(secondTapUp.getActionIndex());
-            final int pointerIndex = secondTapUp.findPointerIndex(pointerId);
+            final int pointerId = event.getPointerId(event.getActionIndex());
+            final int pointerIndex = event.findPointerIndex(pointerId);
 
             Point clickLocation = mTempPoint;
             final int result = computeClickLocation(clickLocation);
@@ -1196,34 +1190,28 @@
             // Do the click.
             PointerProperties[] properties = new PointerProperties[1];
             properties[0] = new PointerProperties();
-            secondTapUp.getPointerProperties(pointerIndex, properties[0]);
+            event.getPointerProperties(pointerIndex, properties[0]);
             PointerCoords[] coords = new PointerCoords[1];
             coords[0] = new PointerCoords();
             coords[0].x = clickLocation.x;
             coords[0].y = clickLocation.y;
-            MotionEvent event = MotionEvent.obtain(secondTapUp.getDownTime(),
-                    secondTapUp.getEventTime(), MotionEvent.ACTION_DOWN, 1, properties,
-                    coords, 0, 0, 1.0f, 1.0f, secondTapUp.getDeviceId(), 0,
-                    secondTapUp.getSource(), secondTapUp.getFlags());
+            MotionEvent click_event = MotionEvent.obtain(event.getDownTime(),
+                    event.getEventTime(), MotionEvent.ACTION_DOWN, 1, properties,
+                    coords, 0, 0, 1.0f, 1.0f, event.getDeviceId(), 0,
+                    event.getSource(), event.getFlags());
             final boolean targetAccessibilityFocus = (result == CLICK_LOCATION_ACCESSIBILITY_FOCUS);
-            sendActionDownAndUp(event, policyFlags, targetAccessibilityFocus);
-            event.recycle();
+            sendActionDownAndUp(click_event, policyFlags, targetAccessibilityFocus);
+            click_event.recycle();
+            return;
         }
 
         public void clear() {
-            if (mDownEvent != null) {
-                mDownEvent.recycle();
-                mDownEvent = null;
-            }
-            if (mFirstTapEvent != null) {
-                mFirstTapEvent.recycle();
-                mFirstTapEvent = null;
-            }
+            mFirstTapDetected = false;
+            mDoubleTapDetected = false;
         }
 
         public boolean firstTapDetected() {
-            return mFirstTapEvent != null
-                && SystemClock.uptimeMillis() - mFirstTapEvent.getEventTime() < mDoubleTapTimeout;
+            return mFirstTapDetected;
         }
     }
 
diff --git a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
index fa87270..02b5b8a 100644
--- a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
+++ b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
@@ -363,7 +363,8 @@
 
                     // ... and see if these are hosts we've been awaiting.
                     // NOTE: We are backing up and restoring only the owner.
-                    if (newPackageAdded && userId == UserHandle.USER_OWNER) {
+                    // TODO: http://b/22388012
+                    if (newPackageAdded && userId == UserHandle.USER_SYSTEM) {
                         final int uid = getUidForPackage(pkgName, userId);
                         if (uid >= 0 ) {
                             resolveHostUidLocked(pkgName, uid);
@@ -1870,19 +1871,28 @@
         return false;
     }
 
-    private void deleteProviderLocked(Provider provider) {
-        int N = provider.widgets.size();
+    // Remove widgets for provider that are hosted in userId.
+    private void deleteWidgetsLocked(Provider provider, int userId) {
+        final int N = provider.widgets.size();
         for (int i = N - 1; i >= 0; i--) {
-            Widget widget = provider.widgets.remove(i);
-            // Call back with empty RemoteViews
-            updateAppWidgetInstanceLocked(widget, null, false);
-            // clear out references to this appWidgetId
-            widget.host.widgets.remove(widget);
-            removeWidgetLocked(widget);
-            widget.provider = null;
-            pruneHostLocked(widget.host);
-            widget.host = null;
+            Widget widget = provider.widgets.get(i);
+            if (userId == UserHandle.USER_ALL
+                    || userId == widget.host.getUserId()) {
+                provider.widgets.remove(i);
+                // Call back with empty RemoteViews
+                updateAppWidgetInstanceLocked(widget, null, false);
+                // clear out references to this appWidgetId
+                widget.host.widgets.remove(widget);
+                removeWidgetLocked(widget);
+                widget.provider = null;
+                pruneHostLocked(widget.host);
+                widget.host = null;
+            }
         }
+    }
+
+    private void deleteProviderLocked(Provider provider) {
+        deleteWidgetsLocked(provider, UserHandle.USER_ALL);
         mProviders.remove(provider);
 
         // no need to send the DISABLE broadcast, since the receiver is gone anyway
@@ -2729,7 +2739,7 @@
             Host host = lookupHostLocked(oldHostId);
             if (host != null) {
                 final int uid = getUidForPackage(NEW_KEYGUARD_HOST_PACKAGE,
-                        UserHandle.USER_OWNER);
+                        UserHandle.USER_SYSTEM);
                 if (uid >= 0) {
                     host.id = new HostId(uid, KEYGUARD_HOST_ID, NEW_KEYGUARD_HOST_PACKAGE);
                 }
@@ -2750,7 +2760,7 @@
     private static AtomicFile getSavedStateFile(int userId) {
         File dir = Environment.getUserSystemDirectory(userId);
         File settingsFile = getStateFile(userId);
-        if (!settingsFile.exists() && userId == UserHandle.USER_OWNER) {
+        if (!settingsFile.exists() && userId == UserHandle.USER_SYSTEM) {
             if (!dir.exists()) {
                 dir.mkdirs();
             }
@@ -2930,6 +2940,19 @@
         return providersUpdated;
     }
 
+    // Remove widgets for provider in userId that are hosted in parentUserId
+    private void removeWidgetsForPackageLocked(String pkgName, int userId, int parentUserId) {
+        final int N = mProviders.size();
+        for (int i = 0; i < N; ++i) {
+            Provider provider = mProviders.get(i);
+            if (pkgName.equals(provider.info.provider.getPackageName())
+                    && provider.getUserId() == userId
+                    && provider.widgets.size() > 0) {
+                deleteWidgetsLocked(provider, parentUserId);
+            }
+        }
+    }
+
     private boolean removeProvidersForPackageLocked(String pkgName, int userId) {
         boolean removed = false;
 
@@ -3041,14 +3064,14 @@
                             userId, null);
                 }
 
-                // Some packages are no longer whitelisted.
+                // Remove widgets from hosts in parent user for packages not in the whitelist
                 final int removedCount = previousPackages.size();
                 for (int i = 0; i < removedCount; ++i) {
-                    providersChanged |= removeProvidersForPackageLocked(
-                            previousPackages.valueAt(i), userId);
+                    removeWidgetsForPackageLocked(previousPackages.valueAt(i),
+                            userId, parentId);
                 }
 
-                if (providersChanged) {
+                if (providersChanged || removedCount > 0) {
                     saveGroupStateAsync(userId);
                     scheduleNotifyGroupHostsForProvidersChangedLocked(userId);
                 }
diff --git a/services/core/java/com/android/server/BluetoothManagerService.java b/services/core/java/com/android/server/BluetoothManagerService.java
index d5c4a41..fd67b41 100644
--- a/services/core/java/com/android/server/BluetoothManagerService.java
+++ b/services/core/java/com/android/server/BluetoothManagerService.java
@@ -271,7 +271,7 @@
         int sysUiUid = -1;
         try {
             sysUiUid = mContext.getPackageManager().getPackageUid("com.android.systemui",
-                    UserHandle.USER_OWNER);
+                    UserHandle.USER_SYSTEM);
         } catch (PackageManager.NameNotFoundException e) {
             // Some platforms, such as wearables do not have a system ui.
             Log.w(TAG, "Unable to resolve SystemUI's UID.", e);
diff --git a/services/core/java/com/android/server/DeviceIdleController.java b/services/core/java/com/android/server/DeviceIdleController.java
index 46fd28a..ebcdf7c 100644
--- a/services/core/java/com/android/server/DeviceIdleController.java
+++ b/services/core/java/com/android/server/DeviceIdleController.java
@@ -31,6 +31,8 @@
 import android.database.ContentObserver;
 import android.hardware.Sensor;
 import android.hardware.SensorManager;
+import android.hardware.SensorEvent;
+import android.hardware.SensorEventListener;
 import android.hardware.TriggerEvent;
 import android.hardware.TriggerEventListener;
 import android.hardware.display.DisplayManager;
@@ -111,7 +113,7 @@
     private INetworkPolicyManager mNetworkPolicyManager;
     private DisplayManager mDisplayManager;
     private SensorManager mSensorManager;
-    private Sensor mSigMotionSensor;
+    private Sensor mMotionSensor;
     private LocationManager mLocationManager;
     private LocationRequest mLocationRequest;
     private PendingIntent mSensingAlarmIntent;
@@ -123,7 +125,6 @@
     private boolean mForceIdle;
     private boolean mScreenOn;
     private boolean mCharging;
-    private boolean mSigMotionActive;
     private boolean mSensing;
     private boolean mNotMoving;
     private boolean mLocating;
@@ -268,13 +269,57 @@
         }
     };
 
-    private final TriggerEventListener mSigMotionListener = new TriggerEventListener() {
-        @Override public void onTrigger(TriggerEvent event) {
+    private final class MotionListener extends TriggerEventListener
+            implements SensorEventListener {
+
+        boolean active = false;
+
+        @Override
+        public void onTrigger(TriggerEvent event) {
             synchronized (DeviceIdleController.this) {
-                significantMotionLocked();
+                active = false;
+                motionLocked();
             }
         }
-    };
+
+        @Override
+        public void onSensorChanged(SensorEvent event) {
+            synchronized (DeviceIdleController.this) {
+                mSensorManager.unregisterListener(this, mMotionSensor);
+                active = false;
+                motionLocked();
+            }
+        }
+
+        @Override
+        public void onAccuracyChanged(Sensor sensor, int accuracy) {}
+
+        public boolean registerLocked() {
+            boolean success = false;
+            if (mMotionSensor.getReportingMode() == Sensor.REPORTING_MODE_ONE_SHOT) {
+                success = mSensorManager.requestTriggerSensor(mMotionListener, mMotionSensor);
+            } else {
+                success = mSensorManager.registerListener(
+                        mMotionListener, mMotionSensor, SensorManager.SENSOR_DELAY_NORMAL);
+            }
+            if (success) {
+                active = true;
+            } else {
+                Slog.e(TAG, "Unable to register for " + mMotionSensor);
+            }
+            return success;
+        }
+
+        public void unregisterLocked() {
+            if (mMotionSensor.getReportingMode() == Sensor.REPORTING_MODE_ONE_SHOT) {
+                mSensorManager.cancelTriggerSensor(mMotionListener, mMotionSensor);
+            } else {
+                mSensorManager.unregisterListener(mMotionListener);
+            }
+            active = false;
+        }
+    }
+    private final MotionListener mMotionListener = new MotionListener();
 
     private final LocationListener mGenericLocationListener = new LocationListener() {
         @Override
@@ -349,7 +394,7 @@
          * This is the time, after becoming inactive, at which we start looking at the
          * motion sensor to determine if the device is being left alone.  We don't do this
          * immediately after going inactive just because we don't want to be continually running
-         * the significant motion sensor whenever the screen is off.
+         * the motion sensor whenever the screen is off.
          * @see Settings.Global#DEVICE_IDLE_CONSTANTS
          * @see #KEY_INACTIVE_TIMEOUT
          */
@@ -392,7 +437,7 @@
 
         /**
          * This is the time, after the inactive timeout elapses, that we will wait looking
-         * for significant motion until we truly consider the device to be idle.
+         * for motion until we truly consider the device to be idle.
          * @see Settings.Global#DEVICE_IDLE_CONSTANTS
          * @see #KEY_IDLE_AFTER_INACTIVE_TIMEOUT
          */
@@ -886,18 +931,19 @@
                 int sigMotionSensorId = getContext().getResources().getInteger(
                         com.android.internal.R.integer.config_autoPowerModeAnyMotionSensor);
                 if (sigMotionSensorId > 0) {
-                    mSigMotionSensor = mSensorManager.getDefaultSensor(sigMotionSensorId, true);
+                    mMotionSensor = mSensorManager.getDefaultSensor(sigMotionSensorId, true);
                 }
-                if (mSigMotionSensor == null && getContext().getResources().getBoolean(
+                if (mMotionSensor == null && getContext().getResources().getBoolean(
                         com.android.internal.R.bool.config_autoPowerModePreferWristTilt)) {
-                    mSigMotionSensor = mSensorManager.getDefaultSensor(
-                            Sensor.TYPE_WRIST_TILT_GESTURE);
+                    mMotionSensor = mSensorManager.getDefaultSensor(
+                            Sensor.TYPE_WRIST_TILT_GESTURE, true);
                 }
-                if (mSigMotionSensor == null) {
+                if (mMotionSensor == null) {
                     // As a last ditch, fall back to SMD.
-                    mSigMotionSensor = mSensorManager.getDefaultSensor(
-                            Sensor.TYPE_SIGNIFICANT_MOTION);
+                    mMotionSensor = mSensorManager.getDefaultSensor(
+                            Sensor.TYPE_SIGNIFICANT_MOTION, true);
                 }
+
                 if (getContext().getResources().getBoolean(
                         com.android.internal.R.bool.config_autoPowerModePrefetchLocation)) {
                     mLocationManager = (LocationManager) getContext().getSystemService(
@@ -1242,7 +1288,7 @@
         cancelAlarmLocked();
         cancelSensingAlarmLocked();
         cancelLocatingLocked();
-        stopMonitoringSignificantMotion();
+        stopMonitoringMotionLocked();
         mAnyMotionDetector.stop();
     }
 
@@ -1271,8 +1317,8 @@
         switch (mState) {
             case STATE_INACTIVE:
                 // We have now been inactive long enough, it is time to start looking
-                // for significant motion and sleep some more while doing so.
-                startMonitoringSignificantMotion();
+                // for motion and sleep some more while doing so.
+                startMonitoringMotionLocked();
                 scheduleAlarmLocked(mConstants.IDLE_AFTER_INACTIVE_TIMEOUT, false);
                 // Reset the upcoming idle delays.
                 mNextIdlePendingDelay = mConstants.IDLE_PENDING_TIMEOUT;
@@ -1353,17 +1399,16 @@
         }
     }
 
-    void significantMotionLocked() {
-        if (DEBUG) Slog.d(TAG, "significantMotionLocked()");
-        // When the sensor goes off, its trigger is automatically removed.
-        mSigMotionActive = false;
+    void motionLocked() {
+        if (DEBUG) Slog.d(TAG, "motionLocked()");
+        // The motion sensor will have been disabled at this point
         handleMotionDetectedLocked(mConstants.MOTION_INACTIVE_TIMEOUT, "motion");
     }
 
     void handleMotionDetectedLocked(long timeout, String type) {
         // The device is not yet active, so we want to go back to the pending idle
-        // state to wait again for no motion.  Note that we only monitor for significant
-        // motion after moving out of the inactive state, so no need to worry about that.
+        // state to wait again for no motion.  Note that we only monitor for motion
+        // after moving out of the inactive state, so no need to worry about that.
         if (mState != STATE_ACTIVE) {
             scheduleReportActiveLocked(type, Process.myUid());
             mState = STATE_ACTIVE;
@@ -1405,19 +1450,17 @@
         }
     }
 
-    void startMonitoringSignificantMotion() {
-        if (DEBUG) Slog.d(TAG, "startMonitoringSignificantMotion()");
-        if (mSigMotionSensor != null && !mSigMotionActive) {
-            mSensorManager.requestTriggerSensor(mSigMotionListener, mSigMotionSensor);
-            mSigMotionActive = true;
+    void startMonitoringMotionLocked() {
+        if (DEBUG) Slog.d(TAG, "startMonitoringMotionLocked()");
+        if (mMotionSensor != null && !mMotionListener.active) {
+            mMotionListener.registerLocked();
         }
     }
 
-    void stopMonitoringSignificantMotion() {
-        if (DEBUG) Slog.d(TAG, "stopMonitoringSignificantMotion()");
-        if (mSigMotionActive) {
-            mSensorManager.cancelTriggerSensor(mSigMotionListener, mSigMotionSensor);
-            mSigMotionActive = false;
+    void stopMonitoringMotionLocked() {
+        if (DEBUG) Slog.d(TAG, "stopMonitoringMotionLocked()");
+        if (mMotionSensor != null && mMotionListener.active) {
+            mMotionListener.unregisterLocked();
         }
     }
 
@@ -1446,7 +1489,7 @@
 
     void scheduleAlarmLocked(long delay, boolean idleUntil) {
         if (DEBUG) Slog.d(TAG, "scheduleAlarmLocked(" + delay + ", " + idleUntil + ")");
-        if (mSigMotionSensor == null) {
+        if (mMotionSensor == null) {
             // If there is no motion sensor on this device, then we won't schedule
             // alarms, because we can't determine if the device is not moving.  This effectively
             // turns off normal execution of device idling, although it is still possible to
@@ -1523,13 +1566,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() {
@@ -1689,7 +1732,7 @@
         }
 
         if (args != null) {
-            int userId = UserHandle.USER_OWNER;
+            int userId = UserHandle.USER_SYSTEM;
             for (int i=0; i<args.length; i++) {
                 String arg = args[i];
                 if ("-h".equals(arg)) {
@@ -1929,11 +1972,11 @@
 
             pw.print("  mEnabled="); pw.println(mEnabled);
             pw.print("  mForceIdle="); pw.println(mForceIdle);
-            pw.print("  mSigMotionSensor="); pw.println(mSigMotionSensor);
+            pw.print("  mMotionSensor="); pw.println(mMotionSensor);
             pw.print("  mCurDisplay="); pw.println(mCurDisplay);
             pw.print("  mScreenOn="); pw.println(mScreenOn);
             pw.print("  mCharging="); pw.println(mCharging);
-            pw.print("  mSigMotionActive="); pw.println(mSigMotionActive);
+            pw.print("  mMotionActive="); pw.println(mMotionListener.active);
             pw.print("  mSensing="); pw.print(mSensing); pw.print(" mNotMoving=");
                     pw.println(mNotMoving);
             pw.print("  mLocating="); pw.print(mLocating); pw.print(" mHasGps=");
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/EventLogTags.logtags b/services/core/java/com/android/server/EventLogTags.logtags
index b826cfd..9bf2aaa 100644
--- a/services/core/java/com/android/server/EventLogTags.logtags
+++ b/services/core/java/com/android/server/EventLogTags.logtags
@@ -29,6 +29,9 @@
 # This is logged when the partial wake lock (keeping the device awake
 # regardless of whether the screen is off) is acquired or released.
 2729 power_partial_wake_state (releasedorAcquired|1|5),(tag|3)
+# The device is being asked to go into a soft sleep (typically by the ungaze gesture).
+# It logs the time remaining before the device would've normally gone to sleep without the request.
+2731 power_soft_sleep_requested (savedwaketimems|2)
 
 #
 # Leave IDs through 2739 for more power logs (2730 used by battery_discharge above)
diff --git a/services/core/java/com/android/server/GestureLauncherService.java b/services/core/java/com/android/server/GestureLauncherService.java
index 69f0cef..f245985 100644
--- a/services/core/java/com/android/server/GestureLauncherService.java
+++ b/services/core/java/com/android/server/GestureLauncherService.java
@@ -17,6 +17,7 @@
 package com.android.server;
 
 import android.app.ActivityManager;
+import android.app.StatusBarManager;
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
@@ -101,8 +102,7 @@
      * Whether camera double tap power button gesture is currently enabled;
      */
     private boolean mCameraDoubleTapPowerEnabled;
-    private long mLastPowerDownWhileNonInteractive = 0;
-
+    private long mLastPowerDown;
 
     public GestureLauncherService(Context context) {
         super(context);
@@ -251,29 +251,34 @@
 
     public boolean interceptPowerKeyDown(KeyEvent event, boolean interactive) {
         boolean launched = false;
+        boolean intercept = false;
+        long doubleTapInterval;
         synchronized (this) {
-            if (!mCameraDoubleTapPowerEnabled) {
-                mLastPowerDownWhileNonInteractive = 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 = interactive;
             }
-            mLastPowerDownWhileNonInteractive = interactive ? 0 : event.getEventTime();
+            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);
+                    StatusBarManager.CAMERA_LAUNCH_SOURCE_POWER_DOUBLE_TAP);
+            if (launched) {
+                MetricsLogger.action(mContext, MetricsLogger.ACTION_DOUBLE_TAP_POWER_CAMERA_GESTURE,
+                        (int) doubleTapInterval);
+            }
         }
-        return launched;
+        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, int source) {
         boolean userSetupComplete = Settings.Secure.getInt(mContext.getContentResolver(),
                 Settings.Secure.USER_SETUP_COMPLETE, 0) != 0;
         if (!userSetupComplete) {
@@ -292,8 +297,7 @@
         }
         StatusBarManagerInternal service = LocalServices.getService(
                 StatusBarManagerInternal.class);
-        service.onCameraLaunchGestureDetected();
-        MetricsLogger.action(mContext, logCategory);
+        service.onCameraLaunchGestureDetected(source);
         return true;
     }
 
@@ -333,7 +337,8 @@
                             "values=[%.4f, %.4f, %.4f].", values[0], values[1], values[2]));
                 }
                 if (handleCameraLaunchGesture(true /* useWakelock */,
-                        MetricsLogger.ACTION_WIGGLE_CAMERA_GESTURE)) {
+                        StatusBarManager.CAMERA_LAUNCH_SOURCE_WIGGLE)) {
+                    MetricsLogger.action(mContext, MetricsLogger.ACTION_WIGGLE_CAMERA_GESTURE);
                     trackCameraLaunchEvent(event);
                 }
                 return;
diff --git a/services/core/java/com/android/server/InputMethodManagerService.java b/services/core/java/com/android/server/InputMethodManagerService.java
index b418aba..9dad7a1 100644
--- a/services/core/java/com/android/server/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/InputMethodManagerService.java
@@ -541,7 +541,8 @@
             prevSubtypes.addAll(entry.getValue());
         }
 
-        final String mergedImesAndSubtypesString = buildInputMethodsAndSubtypesString(prevMap);
+        final String mergedImesAndSubtypesString =
+                InputMethodUtils.buildInputMethodsAndSubtypesString(prevMap);
         if (DEBUG_RESTORE) {
             Slog.i(TAG, "Merged IME string:");
             Slog.i(TAG, "     " + mergedImesAndSubtypesString);
@@ -550,23 +551,6 @@
                 Settings.Secure.ENABLED_INPUT_METHODS, mergedImesAndSubtypesString);
     }
 
-    // TODO: Move this method to InputMethodUtils with adding unit tests.
-    static String buildInputMethodsAndSubtypesString(ArrayMap<String, ArraySet<String>> map) {
-        // we want to use the canonical InputMethodSettings implementation,
-        // so we convert data structures first.
-        List<Pair<String, ArrayList<String>>> imeMap = new ArrayList<>(4);
-        for (ArrayMap.Entry<String, ArraySet<String>> entry : map.entrySet()) {
-            final String imeName = entry.getKey();
-            final ArraySet<String> subtypeSet = entry.getValue();
-            final ArrayList<String> subtypes = new ArrayList<>(2);
-            if (subtypeSet != null) {
-                subtypes.addAll(subtypeSet);
-            }
-            imeMap.add(new Pair<>(imeName, subtypes));
-        }
-        return InputMethodSettings.buildInputMethodsSettingString(imeMap);
-    }
-
     class MyPackageMonitor extends PackageMonitor {
         private boolean isChangingPackagesOfCurrentUser() {
             final int userId = getChangingUserId();
@@ -3504,7 +3488,7 @@
                 throw new NullPointerException("methodMap is null");
             }
             mMethodMap = methodMap;
-            final File systemDir = userId == UserHandle.USER_OWNER
+            final File systemDir = userId == UserHandle.USER_SYSTEM
                     ? new File(Environment.getDataDirectory(), SYSTEM_PATH)
                     : Environment.getUserSystemDirectory(userId);
             final File inputMethodDir = new File(systemDir, INPUT_METHOD_PATH);
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/LockSettingsService.java b/services/core/java/com/android/server/LockSettingsService.java
index c7e0c98..da81528 100644
--- a/services/core/java/com/android/server/LockSettingsService.java
+++ b/services/core/java/com/android/server/LockSettingsService.java
@@ -145,7 +145,8 @@
         } catch (RemoteException e) {
             Slog.e(TAG, "Failure retrieving IGateKeeperService", e);
         }
-        mStorage.prefetchUser(UserHandle.USER_OWNER);
+        // TODO: maybe skip this for split system user mode.
+        mStorage.prefetchUser(UserHandle.USER_SYSTEM);
     }
 
     private void migrateOldData() {
diff --git a/services/core/java/com/android/server/LockSettingsStrongAuth.java b/services/core/java/com/android/server/LockSettingsStrongAuth.java
index c023f4a..0e4d5a7 100644
--- a/services/core/java/com/android/server/LockSettingsStrongAuth.java
+++ b/services/core/java/com/android/server/LockSettingsStrongAuth.java
@@ -132,7 +132,7 @@
     }
 
     public void requireStrongAuth(int strongAuthReason, int userId) {
-        if (userId == UserHandle.USER_ALL || userId >= UserHandle.USER_OWNER) {
+        if (userId == UserHandle.USER_ALL || userId >= UserHandle.USER_SYSTEM) {
             mHandler.obtainMessage(MSG_REQUIRE_STRONG_AUTH, strongAuthReason,
                     userId).sendToTarget();
         } else {
diff --git a/services/core/java/com/android/server/MmsServiceBroker.java b/services/core/java/com/android/server/MmsServiceBroker.java
index e0352e0..33f9234 100644
--- a/services/core/java/com/android/server/MmsServiceBroker.java
+++ b/services/core/java/com/android/server/MmsServiceBroker.java
@@ -500,7 +500,7 @@
         private Uri adjustUriForUserAndGrantPermission(Uri contentUri, String action,
                 int permission) {
             final int callingUserId = UserHandle.getCallingUserId();
-            if (callingUserId != UserHandle.USER_OWNER) {
+            if (callingUserId != UserHandle.USER_SYSTEM) {
                 contentUri = ContentProvider.maybeAddUserId(contentUri, callingUserId);
             }
             long token = Binder.clearCallingIdentity();
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/TextServicesManagerService.java b/services/core/java/com/android/server/TextServicesManagerService.java
index aace66c..fc3a322 100644
--- a/services/core/java/com/android/server/TextServicesManagerService.java
+++ b/services/core/java/com/android/server/TextServicesManagerService.java
@@ -99,7 +99,7 @@
         broadcastFilter.addAction(Intent.ACTION_USER_REMOVED);
         mContext.registerReceiver(new TextServicesBroadcastReceiver(), broadcastFilter);
 
-        int userId = UserHandle.USER_OWNER;
+        int userId = UserHandle.USER_SYSTEM;
         try {
             ActivityManagerNative.getDefault().registerUserSwitchObserver(
                     new IUserSwitchObserver.Stub() {
diff --git a/services/core/java/com/android/server/ThermalObserver.java b/services/core/java/com/android/server/ThermalObserver.java
new file mode 100644
index 0000000..aee28fb
--- /dev/null
+++ b/services/core/java/com/android/server/ThermalObserver.java
@@ -0,0 +1,146 @@
+/*
+ * 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.server;
+
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.os.Binder;
+import android.os.Handler;
+import android.os.Message;
+import android.os.PowerManager;
+import android.os.UEventObserver;
+import android.os.UserHandle;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+
+/**
+ * ThermalObserver for monitoring temperature changes.
+ */
+public class ThermalObserver extends SystemService {
+    private static final String TAG = "ThermalObserver";
+
+    private static final String CALLSTATE_UEVENT_MATCH =
+            "DEVPATH=/devices/virtual/switch/thermalstate";
+
+    private static final int MSG_THERMAL_STATE_CHANGED = 0;
+
+    private static final int SWITCH_STATE_NORMAL = 0;
+    private static final int SWITCH_STATE_WARNING = 1;
+    private static final int SWITCH_STATE_EXCEEDED = 2;
+
+    private final PowerManager mPowerManager;
+    private final PowerManager.WakeLock mWakeLock;
+
+    private final Object mLock = new Object();
+    private Integer mLastState;
+
+    private final UEventObserver mThermalWarningObserver = new UEventObserver() {
+        @Override
+        public void onUEvent(UEventObserver.UEvent event) {
+            updateLocked(Integer.parseInt(event.get("SWITCH_STATE")));
+        }
+    };
+
+    private final Handler mHandler = new Handler(true /*async*/) {
+        @Override
+        public void handleMessage(Message msg) {
+            switch (msg.what) {
+                case MSG_THERMAL_STATE_CHANGED:
+                    handleThermalStateChange(msg.arg1);
+                    mWakeLock.release();
+                    break;
+            }
+        }
+    };
+
+    public ThermalObserver(Context context) {
+        super(context);
+        mPowerManager = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
+        mWakeLock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG);
+
+        mThermalWarningObserver.startObserving(CALLSTATE_UEVENT_MATCH);
+    }
+
+    private void updateLocked(int state) {
+        Message message = new Message();
+        message.what = MSG_THERMAL_STATE_CHANGED;
+        message.arg1 = state;
+
+        mWakeLock.acquire();
+        mHandler.sendMessage(message);
+    }
+
+    private void handleThermalStateChange(int state) {
+        synchronized (mLock) {
+            mLastState = state;
+            Intent intent = new Intent(Intent.ACTION_THERMAL_EVENT);
+            intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
+
+            final int thermalState;
+
+            switch (state) {
+                case SWITCH_STATE_WARNING:
+                    thermalState = Intent.EXTRA_THERMAL_STATE_WARNING;
+                    break;
+                case SWITCH_STATE_EXCEEDED:
+                    thermalState = Intent.EXTRA_THERMAL_STATE_EXCEEDED;
+                    break;
+                case SWITCH_STATE_NORMAL:
+                default:
+                    thermalState = Intent.EXTRA_THERMAL_STATE_NORMAL;
+                    break;
+            }
+
+            intent.putExtra(Intent.EXTRA_THERMAL_STATE, thermalState);
+
+            getContext().sendBroadcastAsUser(intent, UserHandle.ALL);
+        }
+    }
+
+    @Override
+    public void onStart() {
+        publishBinderService(TAG, new BinderService());
+    }
+
+    private final class BinderService extends Binder {
+        @Override
+        protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+            if (getContext().checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
+                    != PackageManager.PERMISSION_GRANTED) {
+                pw.println("Permission Denial: can't dump thermal observer service from from pid="
+                        + Binder.getCallingPid()
+                        + ", uid=" + Binder.getCallingUid());
+                return;
+            }
+
+            final long ident = Binder.clearCallingIdentity();
+            try {
+                synchronized (mLock) {
+                    if (args == null || args.length == 0 || "-a".equals(args[0])) {
+                        pw.println("Current Thermal Observer Service state:");
+                        pw.println("  last state change: "
+                                + (mLastState != null ? mLastState : "none"));
+                    }
+                }
+            } finally {
+                Binder.restoreCallingIdentity(ident);
+            }
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/VibratorService.java b/services/core/java/com/android/server/VibratorService.java
index 30f4dce..c228422 100644
--- a/services/core/java/com/android/server/VibratorService.java
+++ b/services/core/java/com/android/server/VibratorService.java
@@ -59,6 +59,7 @@
         implements InputManager.InputDeviceListener {
     private static final String TAG = "VibratorService";
     private static final boolean DEBUG = false;
+    private static final String SYSTEM_UI_PACKAGE = "com.android.systemui";
 
     private final LinkedList<Vibration> mVibrations;
     private final LinkedList<VibrationInfo> mPreviousVibrations;
@@ -147,7 +148,8 @@
         }
 
         public boolean isSystemHapticFeedback() {
-            return (mUid == Process.SYSTEM_UID || mUid == 0) && mRepeat < 0;
+            return (mUid == Process.SYSTEM_UID || mUid == 0 || SYSTEM_UI_PACKAGE.equals(mOpPkg))
+                    && mRepeat < 0;
         }
     }
 
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index b21e902..bd10c63 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;
@@ -321,6 +322,9 @@
     // How long we wait for a launched process to attach to the activity manager
     // before we decide it's never going to come up for real.
     static final int PROC_START_TIMEOUT = 10*1000;
+    // How long we wait for an attached process to publish its content providers
+    // before we decide it must be hung.
+    static final int CONTENT_PROVIDER_PUBLISH_TIMEOUT = 10*1000;
 
     // How long we wait for a launched process to attach to the activity manager
     // before we decide it's never going to come up for real, when the process was
@@ -990,6 +994,8 @@
      */
     int mConfigurationSeq = 0;
 
+    boolean mSuppressResizeConfigChanges = false;
+
     /**
      * Hardware-reported OpenGLES version.
      */
@@ -1378,6 +1384,7 @@
     static final int REPORT_USER_SWITCH_COMPLETE_MSG = 56;
     static final int SHUTDOWN_UI_AUTOMATION_CONNECTION_MSG = 57;
     static final int APP_BOOST_DEACTIVATE_MSG = 58;
+    static final int CONTENT_PROVIDER_PUBLISH_TIMEOUT_MSG = 59;
 
     static final int FIRST_ACTIVITY_STACK_MSG = 100;
     static final int FIRST_BROADCAST_QUEUE_MSG = 200;
@@ -1714,6 +1721,12 @@
                     processStartTimedOutLocked(app);
                 }
             } break;
+            case CONTENT_PROVIDER_PUBLISH_TIMEOUT_MSG: {
+                ProcessRecord app = (ProcessRecord)msg.obj;
+                synchronized (ActivityManagerService.this) {
+                    processContentProviderPublishTimedOutLocked(app);
+                }
+            } break;
             case DO_PENDING_ACTIVITY_LAUNCHES_MSG: {
                 synchronized (ActivityManagerService.this) {
                     mStackSupervisor.doPendingActivityLaunchesLocked(true);
@@ -2010,7 +2023,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 {
@@ -2368,8 +2381,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",
@@ -4616,7 +4629,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);
         }
     }
 
@@ -5636,7 +5649,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.
@@ -5654,7 +5667,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;
@@ -5962,6 +5975,11 @@
         return needRestart;
     }
 
+    private final void processContentProviderPublishTimedOutLocked(ProcessRecord app) {
+        cleanupAppInLaunchingProvidersLocked(app, true);
+        removeProcessLocked(app, false, true, "timeout publishing content providers");
+    }
+
     private final void processStartTimedOutLocked(ProcessRecord app) {
         final int pid = app.pid;
         boolean gone = false;
@@ -5988,7 +6006,7 @@
                 mBatteryStatsService.removeIsolatedUid(app.uid, app.info.uid);
             }
             // Take care of any launching providers waiting for this process.
-            checkAppInLaunchingProvidersLocked(app, true);
+            cleanupAppInLaunchingProvidersLocked(app, true);
             // Take care of any services that are waiting for the process.
             mServices.processStartTimedOutLocked(app);
             app.kill("start timeout", true);
@@ -6084,6 +6102,12 @@
         boolean normalMode = mProcessesReady || isAllowedWhileBooting(app.info);
         List<ProviderInfo> providers = normalMode ? generateApplicationProvidersLocked(app) : null;
 
+        if (providers != null && checkAppInLaunchingProvidersLocked(app)) {
+            Message msg = mHandler.obtainMessage(CONTENT_PROVIDER_PUBLISH_TIMEOUT_MSG);
+            msg.obj = app;
+            mHandler.sendMessageDelayed(msg, CONTENT_PROVIDER_PUBLISH_TIMEOUT);
+        }
+
         if (!normalMode) {
             Slog.i(TAG, "Launching preboot mode app: " + app);
         }
@@ -8643,14 +8667,14 @@
             }
             if (task.mResizeable != resizeable) {
                 task.mResizeable = resizeable;
-                mStackSupervisor.ensureActivitiesVisibleLocked(null, 0);
+                mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
                 mStackSupervisor.resumeTopActivitiesLocked();
             }
         }
     }
 
     @Override
-    public void resizeTask(int taskId, Rect bounds, boolean resizedByUser) {
+    public void resizeTask(int taskId, Rect bounds, int resizeMode) {
         enforceCallingPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS,
                 "resizeTask()");
         long ident = Binder.clearCallingIdentity();
@@ -8661,7 +8685,7 @@
                     Slog.w(TAG, "resizeTask: taskId=" + taskId + " not found");
                     return;
                 }
-                mStackSupervisor.resizeTaskLocked(task, bounds, resizedByUser);
+                mStackSupervisor.resizeTaskLocked(task, bounds, resizeMode);
             }
         } finally {
             Binder.restoreCallingIdentity(ident);
@@ -9087,7 +9111,7 @@
         long ident = Binder.clearCallingIdentity();
         try {
             synchronized (this) {
-                mStackSupervisor.resizeStackLocked(stackId, bounds);
+                mStackSupervisor.resizeStackLocked(stackId, bounds, !PRESERVE_WINDOWS);
             }
         } finally {
             Binder.restoreCallingIdentity(ident);
@@ -9370,7 +9394,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,
@@ -9592,7 +9616,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;
@@ -9620,14 +9644,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;
@@ -9720,7 +9744,6 @@
                 Binder.restoreCallingIdentity(origId);
             }
 
-            boolean singleton;
             if (!providerRunning) {
                 try {
                     checkTime(startTime, "getContentProviderImpl: before resolveContentProvider");
@@ -9737,11 +9760,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");
@@ -10049,7 +10072,7 @@
             final long origId = Binder.clearCallingIdentity();
 
             final int N = providers.size();
-            for (int i=0; i<N; i++) {
+            for (int i = 0; i < N; i++) {
                 ContentProviderHolder src = providers.get(i);
                 if (src == null || src.info == null || src.provider == null) {
                     continue;
@@ -10064,15 +10087,20 @@
                         mProviderMap.putProviderByName(names[j], dst);
                     }
 
-                    int NL = mLaunchingProviders.size();
+                    int launchingCount = mLaunchingProviders.size();
                     int j;
-                    for (j=0; j<NL; j++) {
+                    boolean wasInLaunchingProviders = false;
+                    for (j = 0; j < launchingCount; j++) {
                         if (mLaunchingProviders.get(j) == dst) {
                             mLaunchingProviders.remove(j);
+                            wasInLaunchingProviders = true;
                             j--;
-                            NL--;
+                            launchingCount--;
                         }
                     }
+                    if (wasInLaunchingProviders) {
+                        mHandler.removeMessages(CONTENT_PROVIDER_PUBLISH_TIMEOUT_MSG, r);
+                    }
                     synchronized (dst) {
                         dst.provider = src.provider;
                         dst.proc = r;
@@ -10339,7 +10367,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;
         }
@@ -11162,7 +11190,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;
@@ -11190,7 +11218,7 @@
                 if (translucentChanged) {
                     r.task.stack.convertActivityToTranslucent(r);
                 }
-                mStackSupervisor.ensureActivitiesVisibleLocked(null, 0);
+                mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
                 mWindowManager.setAppFullscreen(token, false);
                 return translucentChanged;
             }
@@ -11796,12 +11824,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) {
@@ -11815,22 +11843,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);
             }
         }
 
@@ -11838,9 +11862,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;
         }
@@ -11891,7 +11914,7 @@
                         writeLastDonePreBootReceivers(doneReceivers);
                         systemReady(goingCallback);
                     }
-                }, doneReceivers, UserHandle.USER_OWNER);
+                }, doneReceivers);
 
                 if (mWaitingUpdate) {
                     return;
@@ -15743,7 +15766,7 @@
         app.pubProviders.clear();
 
         // Take care of any launching providers waiting for this process.
-        if (checkAppInLaunchingProvidersLocked(app, false)) {
+        if (cleanupAppInLaunchingProvidersLocked(app, false)) {
             restart = true;
         }
 
@@ -15865,7 +15888,17 @@
         return false;
     }
 
-    boolean checkAppInLaunchingProvidersLocked(ProcessRecord app, boolean alwaysBad) {
+    boolean checkAppInLaunchingProvidersLocked(ProcessRecord app) {
+        for (int i = mLaunchingProviders.size() - 1; i >= 0; i--) {
+            ContentProviderRecord cpr = mLaunchingProviders.get(i);
+            if (cpr.launchingApp == app) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    boolean cleanupAppInLaunchingProvidersLocked(ProcessRecord app, boolean alwaysBad) {
         // Look through the content providers we are waiting to have launched,
         // and if any run in this process then either schedule a restart of
         // the process or kill the client waiting for it if this process has
@@ -17484,6 +17517,15 @@
     }
 
     @Override
+    public void suppressResizeConfigChanges(boolean suppress) throws RemoteException {
+        enforceCallingPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS,
+                "suppressResizeConfigChanges()");
+        synchronized (this) {
+            mSuppressResizeConfigChanges = suppress;
+        }
+    }
+
+    @Override
     public void updatePersistentConfiguration(Configuration values) {
         enforceCallingPermission(android.Manifest.permission.CHANGE_CONFIGURATION,
                 "updateConfiguration()");
@@ -17663,7 +17705,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);
             }
         }
 
@@ -20134,7 +20177,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,
@@ -20372,7 +20415,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;
@@ -20457,8 +20500,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;
                 }
@@ -20481,8 +20528,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 3351226..a796ea7 100644
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -17,8 +17,11 @@
 package com.android.server.am;
 
 import static android.app.ActivityManager.DOCKED_STACK_ID;
+import static android.app.ActivityManager.FIRST_STATIC_STACK_ID;
 import static android.app.ActivityManager.FREEFORM_WORKSPACE_STACK_ID;
+import static android.app.ActivityManager.FULLSCREEN_WORKSPACE_STACK_ID;
 import static android.app.ActivityManager.HOME_STACK_ID;
+import static android.app.ActivityManager.LAST_STATIC_STACK_ID;
 import static android.content.pm.ActivityInfo.FLAG_SHOW_FOR_ALL_USERS;
 
 import static com.android.server.am.ActivityManagerDebugConfig.*;
@@ -27,10 +30,11 @@
 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;
-import android.view.IApplicationToken;
+
 import com.android.internal.app.IVoiceInteractor;
 import com.android.internal.content.ReferrerIntent;
 import com.android.internal.os.BatteryStatsImpl;
@@ -525,21 +529,15 @@
      * */
     void moveToFront(String reason, TaskRecord task) {
         if (isAttached()) {
-            final boolean homeStack = isHomeStack()
-                    || (mActivityContainer.mParentActivity != null
-                        && mActivityContainer.mParentActivity.isHomeActivity());
-            ActivityStack lastFocusStack = null;
-            if (!homeStack) {
-                // Need to move this stack to the front before calling
-                // {@link ActivityStackSupervisor#moveHomeStack} below.
-                lastFocusStack = mStacks.get(mStacks.size() - 1);
-                mStacks.remove(this);
-                mStacks.add(this);
-            }
-            // TODO(multi-display): Focus stack currently adjusted in call to move home stack.
-            // Needs to also work if focus is moving to the non-home display.
+            final ActivityStack lastFocusStack = mStacks.get(mStacks.size() - 1);
+            // Need to move this stack to the front before calling
+            // {@link ActivityStackSupervisor#setFocusStack} below.
+            mStacks.remove(this);
+            mStacks.add(this);
+
+            // TODO(multi-display): Needs to also work if focus is moving to the non-home display.
             if (isOnHomeDisplay()) {
-                mStackSupervisor.moveHomeStack(homeStack, reason, lastFocusStack);
+                mStackSupervisor.setFocusStack(reason, lastFocusStack);
             }
             if (task != null) {
                 insertTaskAtTop(task, null);
@@ -812,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.
@@ -1278,28 +1276,27 @@
         return false;
     }
 
-    /** Return true if this stack is hidden by the presence of a docked stack. */
-    private boolean isHiddenByDockedStack() {
-        final ActivityStack dockedStack = mStackSupervisor.getStack(DOCKED_STACK_ID);
-        if (dockedStack != null) {
-            final int dockedStackIndex = mStacks.indexOf(dockedStack);
-            final int stackIndex = mStacks.indexOf(this);
-            if (dockedStackIndex > stackIndex) {
-                // Fullscreen stacks or stacks with fullscreen task below the docked stack are not
-                // visible. We do this so we don't have the 2 stacks and their tasks overlap.
-                if (mFullscreen) {
-                    return true;
-                }
+    private boolean hasTranslucentActivity(ActivityStack stack) {
+        final ArrayList<TaskRecord> tasks = stack.getAllTasks();
+        for (int taskNdx = tasks.size() - 1; taskNdx >= 0; --taskNdx) {
+            final TaskRecord task = tasks.get(taskNdx);
+            final ArrayList<ActivityRecord> activities = task.mActivities;
+            for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) {
+                final ActivityRecord r = activities.get(activityNdx);
 
-                // We need to also check the tasks in the stack because they can be fullscreen
-                // even though their stack isn't due to their root activity not been resizeable
-                // (i.e. doesn't support multi-window mode).
-                if (hasFullscreenTask()) {
-                    return true;
+                // Conditions for an activity to obscure the stack we're
+                // examining:
+                // 1. Not Finishing AND Visible AND:
+                // 2. Either:
+                // - Full Screen Activity OR
+                // - On top of Home and our stack is NOT home
+                if (!r.finishing && r.visible && (r.fullscreen ||
+                        (!isHomeStack() && r.frontOfTask && task.isOverHomeStack()))) {
+                    return false;
                 }
             }
         }
-        return false;
+        return true;
     }
 
     /** Returns true if the stack is considered visible. */
@@ -1320,54 +1317,64 @@
             return false;
         }
 
-        if (isHiddenByDockedStack()) {
+        final ActivityStack focusedStack = mStackSupervisor.getFocusedStack();
+        final int focusedStackId = focusedStack.mStackId;
+
+        if (mStackId == DOCKED_STACK_ID) {
+            // Docked stack is always visible, except in the case where the home activity
+            // is the top running activity in the focused home stack.
+            if (focusedStackId != HOME_STACK_ID) {
+                return true;
+            }
+            ActivityRecord topHomeActivity = focusedStack.topRunningActivityLocked(null);
+            return topHomeActivity == null || !topHomeActivity.isHomeActivity();
+        }
+
+        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 (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) {
+            // Visibility of any static stack should have been determined by the conditions above.
             return false;
         }
 
-        /**
-         * Start at the task above this one and go up, looking for a visible
-         * fullscreen activity, or a translucent activity that requested the
-         * wallpaper to be shown behind it.
-         */
         for (int i = stackIndex + 1; i < mStacks.size(); i++) {
             final ActivityStack stack = mStacks.get(i);
-            final ArrayList<TaskRecord> tasks = stack.getAllTasks();
 
             if (!stack.mFullscreen && !stack.hasFullscreenTask()) {
                 continue;
             }
 
             if (stack.mStackId == FREEFORM_WORKSPACE_STACK_ID
-                    || stack.mStackId == HOME_STACK_ID) {
-                // The freeform and home stacks can't have any other stack visible behind them
-                // when they are fullscreen since they act as base/cut-off points for visibility.
-                // NOTE: we don't cut-off at the FULLSCREEN_WORKSPACE_STACK_ID because the home
-                // stack sometimes needs to be visible behind it when it is displaying a dialog
-                // activity. We let it fall through to the logic below to determine visibility.
+                    || stack.mStackId == HOME_STACK_ID
+                    || stack.mStackId == FULLSCREEN_WORKSPACE_STACK_ID) {
+                // These stacks can't have any dynamic stacks visible behind them.
                 return false;
             }
 
-            for (int taskNdx = tasks.size() - 1; taskNdx >= 0; --taskNdx) {
-                final TaskRecord task = tasks.get(taskNdx);
-                // task above isn't fullscreen, so, we assume we're still visible.
-                if (!task.mFullscreen) {
-                    continue;
-                }
-                final ArrayList<ActivityRecord> activities = task.mActivities;
-                for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) {
-                    final ActivityRecord r = activities.get(activityNdx);
-
-                    // Conditions for an activity to obscure the stack we're
-                    // examining:
-                    // 1. Not Finishing AND Visible AND:
-                    // 2. Either:
-                    // - Full Screen Activity OR
-                    // - On top of Home and our stack is NOT home
-                    if (!r.finishing && r.visible && (r.fullscreen ||
-                            (!isHomeStack() && r.frontOfTask && task.isOverHomeStack()))) {
-                        return false;
-                    }
-                }
+            if (!hasTranslucentActivity(stack)) {
+                return false;
             }
         }
 
@@ -1378,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;
@@ -1427,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) {
@@ -1949,7 +1957,6 @@
                             ? AppTransition.TRANSIT_ACTIVITY_CLOSE
                             : AppTransition.TRANSIT_TASK_CLOSE, false);
                 }
-                mWindowManager.setAppWillBeHidden(prev.appToken);
                 mWindowManager.setAppVisibility(prev.appToken, false);
             } else {
                 if (DEBUG_TRANSITION) Slog.v(TAG_TRANSITION,
@@ -1965,10 +1972,6 @@
                                     : AppTransition.TRANSIT_TASK_OPEN, false);
                 }
             }
-            if (false) {
-                mWindowManager.setAppWillBeHidden(prev.appToken);
-                mWindowManager.setAppVisibility(prev.appToken, false);
-            }
         } else {
             if (DEBUG_TRANSITION) Slog.v(TAG_TRANSITION, "Prepare open transition: no previous");
             if (mNoAnimActivities.contains(next)) {
@@ -2341,7 +2344,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
@@ -4023,6 +4026,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) {
@@ -4168,8 +4176,12 @@
                 | ActivityInfo.CONFIG_SMALLEST_SCREEN_SIZE | ActivityInfo.CONFIG_ORIENTATION)) == 0;
     }
 
-    private boolean relaunchActivityLocked(ActivityRecord r, int changes, boolean andResume,
-            boolean preserveWindow) {
+    private void relaunchActivityLocked(
+            ActivityRecord r, int changes, boolean andResume, boolean preserveWindow) {
+        if (mService.mSuppressResizeConfigChanges && preserveWindow) {
+            return;
+        }
+
         List<ResultInfo> results = null;
         List<ReferrerIntent> newIntents = null;
         if (andResume) {
@@ -4209,8 +4221,6 @@
             mHandler.removeMessages(PAUSE_TIMEOUT_MSG, r);
             r.state = ActivityState.PAUSED;
         }
-
-        return true;
     }
 
     boolean willActivityBeVisibleLocked(IBinder token) {
@@ -4535,18 +4545,17 @@
 
         if (mTaskHistory.isEmpty()) {
             if (DEBUG_STACK) Slog.i(TAG_STACK, "removeTask: removing stack=" + this);
-            final boolean notHomeStack = !isHomeStack();
             if (isOnHomeDisplay()) {
                 String myReason = reason + " leftTaskHistoryEmpty";
                 if (mFullscreen || !adjustFocusToNextVisibleStackLocked(null, myReason)) {
-                    mStackSupervisor.moveHomeStack(notHomeStack, myReason);
+                    mStackSupervisor.moveHomeStackToFront(myReason);
                 }
             }
             if (mStacks != null) {
                 mStacks.remove(this);
                 mStacks.add(0, this);
             }
-            if (notHomeStack) {
+            if (!isHomeStack()) {
                 mActivityContainer.onTaskListEmptyLocked();
             }
         }
@@ -4563,6 +4572,8 @@
         addTask(task, toTop, false);
         if (mTaskPositioner != null) {
             mTaskPositioner.updateDefaultBounds(task, mTaskHistory, info.initialLayout);
+        } else if (mBounds != null && task.mResizeable) {
+            task.updateOverrideConfiguration(mBounds);
         }
         return task;
     }
@@ -4605,7 +4616,7 @@
                 r.task.taskId, mStackId, r.info.screenOrientation, r.fullscreen,
                 (r.info.flags & ActivityInfo.FLAG_SHOW_FOR_ALL_USERS) != 0, r.userId,
                 r.info.configChanges, task.voiceSession != null, r.mLaunchTaskBehind,
-                bounds, task.mOverrideConfig);
+                bounds, task.mOverrideConfig, !r.isHomeActivity());
         r.taskConfigOverride = task.mOverrideConfig;
     }
 
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index d3cea8d..ed92579 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -24,6 +24,7 @@
 import static android.content.Intent.FLAG_ACTIVITY_TASK_ON_HOME;
 import static android.content.pm.ActivityInfo.FLAG_SHOW_FOR_ALL_USERS;
 import static android.content.pm.PackageManager.PERMISSION_GRANTED;
+import static android.os.Trace.TRACE_TAG_ACTIVITY_MANAGER;
 import static com.android.server.am.ActivityManagerDebugConfig.*;
 import static com.android.server.am.ActivityManagerService.FIRST_SUPERVISOR_STACK_MSG;
 import static com.android.server.am.ActivityRecord.HOME_ACTIVITY_TYPE;
@@ -87,6 +88,7 @@
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.os.SystemClock;
+import android.os.Trace;
 import android.os.TransactionTooLargeException;
 import android.os.UserHandle;
 import android.os.WorkSource;
@@ -175,6 +177,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 +194,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);
@@ -464,35 +470,22 @@
         return stack == mFocusedStack;
     }
 
-    void moveHomeStack(boolean toFront, String reason) {
-        moveHomeStack(toFront, reason, null);
-    }
-
-    void moveHomeStack(boolean toFront, String reason, ActivityStack lastFocusedStack) {
+    void setFocusStack(String reason, ActivityStack lastFocusedStack) {
         ArrayList<ActivityStack> stacks = mHomeStack.mStacks;
         final int topNdx = stacks.size() - 1;
         if (topNdx <= 0) {
             return;
         }
 
-        // The home stack should either be at the top or bottom of the stack list.
-        if ((toFront && (stacks.get(topNdx) != mHomeStack))
-                || (!toFront && (stacks.get(0) != mHomeStack))) {
-            if (DEBUG_STACK) Slog.d(TAG_STACK, "moveHomeTask: topStack old="
-                    + ((lastFocusedStack != null) ? lastFocusedStack : stacks.get(topNdx))
-                    + " new=" + mFocusedStack);
-            stacks.remove(mHomeStack);
-            stacks.add(toFront ? topNdx : 0, mHomeStack);
-        }
-
+        final ActivityStack topStack = stacks.get(topNdx);
+        mFocusedStack = topStack;
         if (lastFocusedStack != null) {
             mLastFocusedStack = lastFocusedStack;
         }
-        mFocusedStack = stacks.get(topNdx);
 
-        EventLog.writeEvent(EventLogTags.AM_HOME_STACK_MOVED,
-                mCurrentUser, toFront ? 1 : 0, stacks.get(topNdx).getStackId(),
-                mFocusedStack == null ? -1 : mFocusedStack.getStackId(), reason);
+        EventLogTags.writeAmFocusedStack(
+                mCurrentUser, mFocusedStack == null ? -1 : mFocusedStack.getStackId(),
+                mLastFocusedStack == null ? -1 : mLastFocusedStack.getStackId(), reason);
 
         if (mService.mBooting || !mService.mBooted) {
             final ActivityRecord r = topRunningActivityLocked();
@@ -502,6 +495,10 @@
         }
     }
 
+    void moveHomeStackToFront(String reason) {
+        mHomeStack.moveToFront(reason);
+    }
+
     /** Returns true if the focus activity was adjusted to the home stack top activity. */
     boolean moveHomeStackTaskToTop(int homeStackTaskType, String reason) {
         if (homeStackTaskType == RECENTS_ACTIVITY_TYPE) {
@@ -660,7 +657,7 @@
             }
         }
         if (!didSomething) {
-            ensureActivitiesVisibleLocked(null, 0);
+            ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
         }
         return didSomething;
     }
@@ -1353,8 +1350,7 @@
 
         r.launchFailed = false;
         if (stack.updateLRUListLocked(r)) {
-            Slog.w(TAG, "Activity " + r
-                  + " being launched, but already in LRU list");
+            Slog.w(TAG, "Activity " + r + " being launched, but already in LRU list");
         }
 
         if (andResume) {
@@ -1803,8 +1799,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;
@@ -1823,7 +1822,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);
@@ -2629,7 +2628,7 @@
                 }
                 mLaunchingActivity.release();
             }
-            ensureActivitiesVisibleLocked(null, 0);
+            ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
         }
 
         // Atomically retrieve all of the other things to do.
@@ -2859,7 +2858,8 @@
             if (opts.hasBounds()) {
                 Rect bounds = opts.getBounds();
                 task.updateOverrideConfiguration(bounds);
-                mWindowManager.resizeTask(task.taskId, bounds, task.mOverrideConfig, false);
+                mWindowManager.resizeTask(task.taskId, bounds, task.mOverrideConfig,
+                        false /*relayout*/, false /*forced*/);
                 stackId = task.getLaunchStackId();
             }
         }
@@ -2962,39 +2962,39 @@
         }
     }
 
-    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.");
             return;
         }
 
+        Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "am.resizeStack_" + stackId);
+
         ActivityRecord r = stack.topRunningActivityLocked(null);
-        final boolean resizeTasks = r != null && r.task.mResizeable;
 
         mTmpBounds.clear();
         mTmpConfigs.clear();
-        if (resizeTasks) {
-            ArrayList<TaskRecord> tasks = stack.getAllTasks();
-            for (int i = tasks.size() - 1; i >= 0; i--) {
-                TaskRecord task = tasks.get(i);
+        ArrayList<TaskRecord> tasks = stack.getAllTasks();
+        for (int i = tasks.size() - 1; i >= 0; i--) {
+            TaskRecord task = tasks.get(i);
+            if (task.mResizeable) {
                 if (stack.mStackId == FREEFORM_WORKSPACE_STACK_ID) {
-                    // For freeform stack we don't adjust the size of the tasks to match that of
-                    // the stack, but we do try to make sure the tasks are still contained with the
-                    // bounds of the stack.
+                    // For freeform stack we don't adjust the size of the tasks to match that
+                    // of the stack, but we do try to make sure the tasks are still contained
+                    // with the bounds of the stack.
                     tempRect2.set(task.mBounds);
                     fitWithinBounds(tempRect2, bounds);
                     task.updateOverrideConfiguration(tempRect2);
                 } else {
                     task.updateOverrideConfiguration(bounds);
                 }
-
-                mTmpConfigs.put(task.taskId, task.mOverrideConfig);
-                mTmpBounds.put(task.taskId, task.mBounds);
             }
+
+            mTmpConfigs.put(task.taskId, task.mOverrideConfig);
+            mTmpBounds.put(task.taskId, task.mBounds);
         }
-        stack.mFullscreen = mWindowManager.resizeStack(stackId, bounds, resizeTasks, mTmpConfigs,
-                mTmpBounds);
+        stack.mFullscreen = mWindowManager.resizeStack(stackId, bounds, mTmpConfigs, mTmpBounds);
         if (stack.mStackId == DOCKED_STACK_ID) {
             // Dock stack funness...Yay!
             if (stack.mFullscreen) {
@@ -3003,11 +3003,10 @@
                 // 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);
                     }
                 }
 
-                final ArrayList<TaskRecord> tasks = stack.getAllTasks();
                 final int count = tasks.size();
                 for (int i = 0; i < count; i++) {
                     moveTaskToStackLocked(tasks.get(i).taskId,
@@ -3035,33 +3034,40 @@
                             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);
             }
         }
+
+        Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
     }
 
-    void resizeTaskLocked(TaskRecord task, Rect bounds, boolean resizedByUser) {
+    void resizeTaskLocked(TaskRecord task, Rect bounds, int resizeMode) {
         if (!task.mResizeable) {
             Slog.w(TAG, "resizeTask: task " + task + " not resizeable.");
             return;
         }
 
-        if (task.mBounds != null && task.mBounds.equals(bounds)) {
+        // If this is a forced resize, let it go through even if the bounds is not changing,
+        // as we might need a relayout due to surface size change (to/from fullscreen).
+        final boolean forced = (resizeMode == RESIZE_MODE_FORCED);
+        if (task.mBounds != null && task.mBounds.equals(bounds) && !forced) {
             // Nothing to do here...
             return;
         }
@@ -3078,6 +3084,8 @@
             return;
         }
 
+        Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "am.resizeTask_" + task.taskId);
+
         // The stack of a task is determined by its size (fullscreen vs non-fullscreen).
         // Place the task in the right stack if it isn't there already based on the requested
         // bounds.
@@ -3102,26 +3110,19 @@
             ActivityRecord r = task.topRunningActivityLocked(null);
             if (r != null) {
                 final ActivityStack stack = task.stack;
+                final boolean resizedByUser = resizeMode == RESIZE_MODE_USER;
                 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) {
-                        // We are about to relaunch the activity because its configuration changed
-                        // due to being maximized, i.e. size change. The activity will first
-                        // remove the old window and then add a new one. This call will tell window
-                        // manager about this, so it can preserve the old window until the new
-                        // one is drawn. This prevents having a gap between the removal and
-                        // addition, in which no window is visible. We also want the entrace of the
-                        // new window to be properly animated.
-                        mWindowManager.setReplacingWindow(r.appToken, true /* animate */);
-                    }
                 }
             }
         }
-        mWindowManager.resizeTask(task.taskId, bounds, task.mOverrideConfig, kept);
+        mWindowManager.resizeTask(task.taskId, bounds, task.mOverrideConfig, kept, forced);
+
+        Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
     }
 
     ActivityStack createStackOnDisplay(int stackId, int displayId, boolean onTop) {
@@ -3208,7 +3209,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);
@@ -3238,22 +3245,32 @@
             return;
         }
         final String reason = "moveTaskToStack";
+        if (stackId == DOCKED_STACK_ID || stackId == FULLSCREEN_WORKSPACE_STACK_ID) {
+            // We are about to relaunch the activity because its configuration changed due to
+            // being maximized, i.e. size change. The activity will first remove the old window
+            // and then add a new one. This call will tell window manager about this, so it can
+            // preserve the old window until the new one is drawn. This prevents having a gap
+            // between the removal and addition, in which no window is visible. We also want the
+            // entrace of the new window to be properly animated.
+            ActivityRecord r = task.getTopActivity();
+            mWindowManager.setReplacingWindow(r.appToken, true /* animate */);
+        }
         final ActivityStack stack =
                 moveTaskToStackUncheckedLocked(task, stackId, toTop, forceFocus, reason);
 
         // Make sure the task has the appropriate bounds/size for the stack it is in.
         if (stackId == FULLSCREEN_WORKSPACE_STACK_ID && task.mBounds != null) {
-            resizeTaskLocked(task, stack.mBounds, false);
+            resizeTaskLocked(task, stack.mBounds, RESIZE_MODE_SYSTEM);
         } else if (stackId == FREEFORM_WORKSPACE_STACK_ID
                 && task.mBounds == null && task.mLastNonFullscreenBounds != null) {
-            resizeTaskLocked(task, task.mLastNonFullscreenBounds, false);
+            resizeTaskLocked(task, task.mLastNonFullscreenBounds, RESIZE_MODE_SYSTEM);
         } else if (stackId == DOCKED_STACK_ID) {
-            resizeTaskLocked(task, stack.mBounds, false);
+            resizeTaskLocked(task, stack.mBounds, RESIZE_MODE_SYSTEM);
         }
 
         // 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();
     }
 
@@ -3273,7 +3290,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();
     }
 
@@ -3448,7 +3465,7 @@
             mService.updateUsageStats(r, true);
         }
         if (allResumedActivitiesComplete()) {
-            ensureActivitiesVisibleLocked(null, 0);
+            ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
             mWindowManager.executeAppTransition();
             return true;
         }
@@ -3534,14 +3551,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);
             }
         }
     }
@@ -3648,11 +3666,7 @@
         }
         final boolean homeInFront = stack.isHomeStack();
         if (stack.isOnHomeDisplay()) {
-            moveHomeStack(homeInFront, "switchUserOnHomeDisplay");
-            TaskRecord task = stack.topTask();
-            if (task != null) {
-                mWindowManager.moveTaskToTop(task.taskId);
-            }
+            stack.moveToFront("switchUserOnHomeDisplay");
         } else {
             // Stack was moved to another display while user was swapped out.
             resumeHomeStackTask(HOME_ACTIVITY_TYPE, null, "switchUserOnOtherDisplay");
diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java
index 335288d..62768c3 100644
--- a/services/core/java/com/android/server/am/BatteryStatsService.java
+++ b/services/core/java/com/android/server/am/BatteryStatsService.java
@@ -171,12 +171,11 @@
     
     public void publish(Context context) {
         mContext = context;
-        ServiceManager.addService(BatteryStats.SERVICE_NAME, asBinder());
-        mStats.setNumSpeedSteps(new PowerProfile(mContext).getNumSpeedSteps());
         mStats.setRadioScanningTimeout(mContext.getResources().getInteger(
                 com.android.internal.R.integer.config_radioScanningTimeout)
                 * 1000L);
         mStats.setPowerProfile(new PowerProfile(context));
+        ServiceManager.addService(BatteryStats.SERVICE_NAME, asBinder());
     }
 
     /**
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/am/EventLogTags.logtags b/services/core/java/com/android/server/am/EventLogTags.logtags
index 9a645df..78b5f33 100644
--- a/services/core/java/com/android/server/am/EventLogTags.logtags
+++ b/services/core/java/com/android/server/am/EventLogTags.logtags
@@ -93,8 +93,8 @@
 # Activity focused
 30043 am_focused_activity (User|1|5),(Component Name|3)
 
-# Home Stack brought to front or rear
-30044 am_home_stack_moved (User|1|5),(To Front|1|5),(Top Stack Id|1|5),(Focused Stack Id|1|5),(Reason|3)
+# Stack focus
+30044 am_focused_stack (User|1|5),(Focused Stack Id|1|5),(Last Focused Stack Id|1|5),(Reason|3)
 
 # Running pre boot receiver
 30045 am_pre_boot (User|1|5),(Package|3)
diff --git a/services/core/java/com/android/server/am/ProviderMap.java b/services/core/java/com/android/server/am/ProviderMap.java
index ed8b1dd..878c0e7 100644
--- a/services/core/java/com/android/server/am/ProviderMap.java
+++ b/services/core/java/com/android/server/am/ProviderMap.java
@@ -211,7 +211,7 @@
             boolean doit, boolean evenPersistent, int userId,
             ArrayList<ContentProviderRecord> result) {
         boolean didSomething = false;
-        if (userId == UserHandle.USER_ALL || userId == UserHandle.USER_OWNER) {
+        if (userId == UserHandle.USER_ALL || userId == UserHandle.USER_SYSTEM) {
             didSomething = collectPackageProvidersLocked(packageName, filterByClasses,
                     doit, evenPersistent, mSingletonByClass, result);
         }
diff --git a/services/core/java/com/android/server/am/TaskRecord.java b/services/core/java/com/android/server/am/TaskRecord.java
index 8f10f08..43f5baa 100644
--- a/services/core/java/com/android/server/am/TaskRecord.java
+++ b/services/core/java/com/android/server/am/TaskRecord.java
@@ -1235,7 +1235,7 @@
         if (stack == null
                 || stack.mStackId == HOME_STACK_ID
                 || stack.mStackId == FULLSCREEN_WORKSPACE_STACK_ID) {
-            return null;
+            return (mResizeable && stack != null) ? stack.mBounds : null;
         } else if (stack.mStackId == DOCKED_STACK_ID) {
             return stack.mBounds;
         }
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 1475f2f..e49a7e4 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -1560,7 +1560,7 @@
         } finally {
             Binder.restoreCallingIdentity(ident);
         }
-        return UserHandle.USER_OWNER;
+        return UserHandle.USER_SYSTEM;
     }
 
     // UI update and Broadcast Intent
@@ -2524,11 +2524,14 @@
     }
 
     /** @see AudioManager#setBluetoothScoOn(boolean) */
-    public void setBluetoothScoOn(boolean on){
+    public void setBluetoothScoOn(boolean on) {
         if (!checkAudioSettingsPermission("setBluetoothScoOn()")) {
             return;
         }
+        setBluetoothScoOnInt(on);
+    }
 
+    public void setBluetoothScoOnInt(boolean on) {
         if (on) {
             mForcedUseForComm = AudioSystem.FORCE_BT_SCO;
         } else if (mForcedUseForComm == AudioSystem.FORCE_BT_SCO) {
@@ -2889,6 +2892,8 @@
             mScoAudioState = SCO_STATE_INACTIVE;
             broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
         }
+        AudioSystem.setParameters("A2dpSuspended=false");
+        setBluetoothScoOnInt(false);
     }
 
     private void broadcastScoConnectionState(int state) {
diff --git a/services/core/java/com/android/server/display/DisplayAdapter.java b/services/core/java/com/android/server/display/DisplayAdapter.java
index 6ba25a5..701b9f1 100644
--- a/services/core/java/com/android/server/display/DisplayAdapter.java
+++ b/services/core/java/com/android/server/display/DisplayAdapter.java
@@ -49,6 +49,13 @@
      */
     private static final AtomicInteger NEXT_DISPLAY_MODE_ID = new AtomicInteger(1);  // 0 = no mode.
 
+    /**
+     * Used to generate globally unique color transform ids.
+     *
+     * Valid IDs start at 1 with 0 as the sentinel value for the default mode.
+     */
+    private static final AtomicInteger NEXT_COLOR_TRANSFORM_ID = new AtomicInteger(1);
+
     // Called with SyncRoot lock held.
     public DisplayAdapter(DisplayManagerService.SyncRoot syncRoot,
             Context context, Handler handler, Listener listener, String name) {
@@ -134,6 +141,11 @@
                 NEXT_DISPLAY_MODE_ID.getAndIncrement(), width, height, refreshRate);
     }
 
+    public static Display.ColorTransform createColorTransform(int colorTransform) {
+        return new Display.ColorTransform(
+                NEXT_COLOR_TRANSFORM_ID.getAndIncrement(), colorTransform);
+    }
+
     public interface Listener {
         public void onDisplayDeviceEvent(DisplayDevice device, int event);
         public void onTraversalRequested();
diff --git a/services/core/java/com/android/server/display/DisplayDevice.java b/services/core/java/com/android/server/display/DisplayDevice.java
index 93bda46..7af0bdb 100644
--- a/services/core/java/com/android/server/display/DisplayDevice.java
+++ b/services/core/java/com/android/server/display/DisplayDevice.java
@@ -135,7 +135,7 @@
     /**
      * Sets the mode, if supported.
      */
-    public void requestModeInTransactionLocked(int id) {
+    public void requestColorTransformAndModeInTransactionLocked(int colorTransformId, int modeId) {
     }
 
     /**
diff --git a/services/core/java/com/android/server/display/DisplayDeviceInfo.java b/services/core/java/com/android/server/display/DisplayDeviceInfo.java
index 97ada15..55ba302 100644
--- a/services/core/java/com/android/server/display/DisplayDeviceInfo.java
+++ b/services/core/java/com/android/server/display/DisplayDeviceInfo.java
@@ -155,6 +155,15 @@
      */
     public Display.Mode[] supportedModes = Display.Mode.EMPTY_ARRAY;
 
+    /** The active color transform of the display */
+    public int colorTransformId;
+
+    /** The default color transform of the display */
+    public int defaultColorTransformId;
+
+    /** The supported color transforms of the display */
+    public Display.ColorTransform[] supportedColorTransforms = Display.ColorTransform.EMPTY_ARRAY;
+
     /**
      * The nominal apparent density of the display in DPI used for layout calculations.
      * This density is sensitive to the viewing distance.  A big TV and a tablet may have
@@ -276,6 +285,9 @@
                 || modeId != other.modeId
                 || defaultModeId != other.defaultModeId
                 || !Arrays.equals(supportedModes, other.supportedModes)
+                || colorTransformId != other.colorTransformId
+                || defaultColorTransformId != other.defaultColorTransformId
+                || !Arrays.equals(supportedColorTransforms, other.supportedColorTransforms)
                 || densityDpi != other.densityDpi
                 || xDpi != other.xDpi
                 || yDpi != other.yDpi
@@ -306,6 +318,9 @@
         modeId = other.modeId;
         defaultModeId = other.defaultModeId;
         supportedModes = other.supportedModes;
+        colorTransformId = other.colorTransformId;
+        defaultColorTransformId = other.defaultColorTransformId;
+        supportedColorTransforms = other.supportedColorTransforms;
         densityDpi = other.densityDpi;
         xDpi = other.xDpi;
         yDpi = other.yDpi;
@@ -331,6 +346,9 @@
         sb.append(", modeId ").append(modeId);
         sb.append(", defaultModeId ").append(defaultModeId);
         sb.append(", supportedModes ").append(Arrays.toString(supportedModes));
+        sb.append(", colorTransformId ").append(colorTransformId);
+        sb.append(", defaultColorTransformId ").append(defaultColorTransformId);
+        sb.append(", supportedColorTransforms ").append(Arrays.toString(supportedColorTransforms));
         sb.append(", density ").append(densityDpi);
         sb.append(", ").append(xDpi).append(" x ").append(yDpi).append(" dpi");
         sb.append(", appVsyncOff ").append(appVsyncOffsetNanos);
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index b2ab797..6a6570b 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -540,6 +540,17 @@
         }
     }
 
+    private void requestColorTransformInternal(int displayId, int colorTransformId) {
+        synchronized (mSyncRoot) {
+            LogicalDisplay display = mLogicalDisplays.get(displayId);
+            if (display != null &&
+                    display.getRequestedColorTransformIdLocked() != colorTransformId) {
+                display.setRequestedColorTransformIdLocked(colorTransformId);
+                scheduleTraversalLocked(false);
+            }
+        }
+    }
+
     private int createVirtualDisplayInternal(IVirtualDisplayCallback callback,
             IMediaProjection projection, int callingUid, String packageName,
             String name, int width, int height, int densityDpi, Surface surface, int flags) {
@@ -1340,6 +1351,19 @@
         }
 
         @Override // Binder call
+        public void requestColorTransform(int displayId, int colorTransformId) {
+            mContext.enforceCallingOrSelfPermission(
+                    Manifest.permission.CONFIGURE_DISPLAY_COLOR_TRANSFORM,
+                    "Permission required to change the display color transform");
+            final long token = Binder.clearCallingIdentity();
+            try {
+                requestColorTransformInternal(displayId, colorTransformId);
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
+        }
+
+        @Override // Binder call
         public int createVirtualDisplay(IVirtualDisplayCallback callback,
                 IMediaProjection projection, String packageName, String name,
                 int width, int height, int densityDpi, Surface surface, int flags) {
diff --git a/services/core/java/com/android/server/display/LocalDisplayAdapter.java b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
index 517a825..088d96e 100644
--- a/services/core/java/com/android/server/display/LocalDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
@@ -31,6 +31,7 @@
 import android.os.Trace;
 import android.util.Slog;
 import android.util.SparseArray;
+import android.util.SparseBooleanArray;
 import android.view.Display;
 import android.view.DisplayEventReceiver;
 import android.view.Surface;
@@ -38,6 +39,7 @@
 
 import java.io.PrintWriter;
 import java.util.ArrayList;
+import java.util.Arrays;
 
 /**
  * A display adapter for the local displays managed by Surface Flinger.
@@ -143,14 +145,22 @@
         private final int mBuiltInDisplayId;
         private final Light mBacklight;
         private final SparseArray<DisplayModeRecord> mSupportedModes = new SparseArray<>();
+        private final SparseArray<Display.ColorTransform> mSupportedColorTransforms =
+                new SparseArray<>();
 
         private DisplayDeviceInfo mInfo;
         private boolean mHavePendingChanges;
         private int mState = Display.STATE_UNKNOWN;
         private int mBrightness = PowerManager.BRIGHTNESS_DEFAULT;
+        private int mActivePhysIndex;
         private int mDefaultModeId;
         private int mActiveModeId;
         private boolean mActiveModeInvalid;
+        private int mDefaultColorTransformId;
+        private int mActiveColorTransformId;
+        private boolean mActiveColorTransformInvalid;
+
+        private  SurfaceControl.PhysicalDisplayInfo mDisplayInfos[];
 
         public LocalDisplayDevice(IBinder displayToken, int builtInDisplayId,
                 SurfaceControl.PhysicalDisplayInfo[] physicalDisplayInfos, int activeDisplayInfo) {
@@ -167,22 +177,73 @@
 
         public boolean updatePhysicalDisplayInfoLocked(
                 SurfaceControl.PhysicalDisplayInfo[] physicalDisplayInfos, int activeDisplayInfo) {
-            // Build an updated list of all existing modes.
-            boolean modesAdded = false;
-            DisplayModeRecord activeRecord = null;
-            ArrayList<DisplayModeRecord> records = new ArrayList<DisplayModeRecord>();
+            mDisplayInfos = Arrays.copyOf(physicalDisplayInfos, physicalDisplayInfos.length);
+            mActivePhysIndex = activeDisplayInfo;
+            ArrayList<Display.ColorTransform> colorTransforms = new ArrayList<>();
+
+            // Build an updated list of all existing color transforms.
+            boolean colorTransformsAdded = false;
+            Display.ColorTransform activeColorTransform = null;
             for (int i = 0; i < physicalDisplayInfos.length; i++) {
                 SurfaceControl.PhysicalDisplayInfo info = physicalDisplayInfos[i];
+                // First check to see if we've already added this color transform
+                boolean existingMode = false;
+                for (int j = 0; j < colorTransforms.size(); j++) {
+                    if (colorTransforms.get(j).getColorTransform() == info.colorTransform) {
+                        existingMode = true;
+                        break;
+                    }
+                }
+                if (existingMode) {
+                    continue;
+                }
+                Display.ColorTransform colorTransform = findColorTransform(info);
+                if (colorTransform == null) {
+                    colorTransform = createColorTransform(info.colorTransform);
+                    colorTransformsAdded = true;
+                }
+                colorTransforms.add(colorTransform);
+                if (i == activeDisplayInfo) {
+                    activeColorTransform = colorTransform;
+                }
+            }
+
+            // Build an updated list of all existing modes.
+            ArrayList<DisplayModeRecord> records = new ArrayList<DisplayModeRecord>();
+            boolean modesAdded = false;
+            for (int i = 0; i < physicalDisplayInfos.length; i++) {
+                SurfaceControl.PhysicalDisplayInfo info = physicalDisplayInfos[i];
+                // First, check to see if we've already added a matching mode. Since not all
+                // configuration options are exposed via Display.Mode, it's possible that we have
+                // multiple PhysicalDisplayInfos that would generate the same Display.Mode.
+                boolean existingMode = false;
+                for (int j = 0; j < records.size(); j++) {
+                    if (records.get(j).hasMatchingMode(info)) {
+                        existingMode = true;
+                        break;
+                    }
+                }
+                if (existingMode) {
+                    continue;
+                }
+                // If we haven't already added a mode for this configuration to the new set of
+                // supported modes then check to see if we have one in the prior set of supported
+                // modes to reuse.
                 DisplayModeRecord record = findDisplayModeRecord(info);
-                if (record != null) {
-                    record.mPhysIndex = i;
-                } else {
-                    record = new DisplayModeRecord(info, i);
+                if (record == null) {
+                    record = new DisplayModeRecord(info);
                     modesAdded = true;
                 }
                 records.add(record);
-                if (i == activeDisplayInfo) {
+            }
+
+            // Get the currently active mode
+            DisplayModeRecord activeRecord = null;
+            for (int i = 0; i < records.size(); i++) {
+                DisplayModeRecord record = records.get(i);
+                if (record.hasMatchingMode(physicalDisplayInfos[activeDisplayInfo])){
                     activeRecord = record;
+                    break;
                 }
             }
             // Check whether surface flinger spontaneously changed modes out from under us. Schedule
@@ -192,25 +253,48 @@
                 mActiveModeInvalid = true;
                 sendTraversalRequestLocked();
             }
-            // If no modes were added and we have the same number of modes as before, then nothing
-            // actually changed except possibly the physical index (which we only care about when
-            // setting the mode) so we're done.
-            if (records.size() == mSupportedModes.size() && !modesAdded) {
+            // Check whether surface flinger spontaneously changed color transforms out from under
+            // us.
+            if (mActiveColorTransformId != 0
+                    && mActiveColorTransformId != activeColorTransform.getId()) {
+                mActiveColorTransformInvalid = true;
+                sendTraversalRequestLocked();
+            }
+
+            boolean colorTransformsChanged =
+                    colorTransforms.size() != mSupportedColorTransforms.size()
+                    || colorTransformsAdded;
+            boolean recordsChanged = records.size() != mSupportedModes.size() || modesAdded;
+            // If neither the records nor the supported color transforms have changed then we're
+            // done here.
+            if (!recordsChanged && !colorTransformsChanged) {
                 return false;
             }
             // Update the index of modes.
             mHavePendingChanges = true;
+
             mSupportedModes.clear();
             for (DisplayModeRecord record : records) {
                 mSupportedModes.put(record.mMode.getModeId(), record);
             }
-            // Update the default mode if needed.
-            if (mSupportedModes.indexOfKey(mDefaultModeId) < 0) {
+            mSupportedColorTransforms.clear();
+            for (Display.ColorTransform colorTransform : colorTransforms) {
+                mSupportedColorTransforms.put(colorTransform.getId(), colorTransform);
+            }
+
+            // Update the default mode and color transform if needed. This needs to be done in
+            // tandem so we always have a default state to fall back to.
+            if (findDisplayInfoIndexLocked(mDefaultColorTransformId, mDefaultModeId) < 0) {
                 if (mDefaultModeId != 0) {
-                    Slog.w(TAG, "Default display mode no longer available, using currently active"
-                            + " mode as default.");
+                    Slog.w(TAG, "Default display mode no longer available, using currently"
+                            + " active mode as default.");
                 }
                 mDefaultModeId = activeRecord.mMode.getModeId();
+                if (mDefaultColorTransformId != 0) {
+                    Slog.w(TAG, "Default color transform no longer available, using currently"
+                            + " active color transform as default");
+                }
+                mDefaultColorTransformId = activeColorTransform.getId();
             }
             // Determine whether the active mode is still there.
             if (mSupportedModes.indexOfKey(mActiveModeId) < 0) {
@@ -221,6 +305,16 @@
                 mActiveModeId = mDefaultModeId;
                 mActiveModeInvalid = true;
             }
+
+            // Determine whether the active color transform is still there.
+            if (mSupportedColorTransforms.indexOfKey(mActiveColorTransformId) < 0) {
+                if (mActiveColorTransformId != 0) {
+                    Slog.w(TAG, "Active color transform no longer available, reverting"
+                            + " to default transform.");
+                }
+                mActiveColorTransformId = mDefaultColorTransformId;
+                mActiveColorTransformInvalid = true;
+            }
             // Schedule traversals so that we apply pending changes.
             sendTraversalRequestLocked();
             return true;
@@ -229,13 +323,23 @@
         private DisplayModeRecord findDisplayModeRecord(SurfaceControl.PhysicalDisplayInfo info) {
             for (int i = 0; i < mSupportedModes.size(); i++) {
                 DisplayModeRecord record = mSupportedModes.valueAt(i);
-                if (record.mPhys.equals(info)) {
+                if (record.hasMatchingMode(info)) {
                     return record;
                 }
             }
             return null;
         }
 
+        private Display.ColorTransform findColorTransform(SurfaceControl.PhysicalDisplayInfo info) {
+            for (int i = 0; i < mSupportedColorTransforms.size(); i++) {
+                Display.ColorTransform transform = mSupportedColorTransforms.valueAt(i);
+                if (transform.getColorTransform() == info.colorTransform) {
+                    return transform;
+                }
+            }
+            return null;
+        }
+
         @Override
         public void applyPendingDisplayDeviceInfoChangesLocked() {
             if (mHavePendingChanges) {
@@ -247,7 +351,7 @@
         @Override
         public DisplayDeviceInfo getDisplayDeviceInfoLocked() {
             if (mInfo == null) {
-                SurfaceControl.PhysicalDisplayInfo phys = mSupportedModes.get(mActiveModeId).mPhys;
+                SurfaceControl.PhysicalDisplayInfo phys = mDisplayInfos[mActivePhysIndex];
                 mInfo = new DisplayDeviceInfo();
                 mInfo.width = phys.width;
                 mInfo.height = phys.height;
@@ -258,6 +362,13 @@
                     DisplayModeRecord record = mSupportedModes.valueAt(i);
                     mInfo.supportedModes[i] = record.mMode;
                 }
+                mInfo.colorTransformId = mActiveColorTransformId;
+                mInfo.defaultColorTransformId = mDefaultColorTransformId;
+                mInfo.supportedColorTransforms =
+                        new Display.ColorTransform[mSupportedColorTransforms.size()];
+                for (int i = 0; i < mSupportedColorTransforms.size(); i++) {
+                    mInfo.supportedColorTransforms[i] = mSupportedColorTransforms.valueAt(i);
+                }
                 mInfo.appVsyncOffsetNanos = phys.appVsyncOffsetNanos;
                 mInfo.presentationDeadlineNanos = phys.presentationDeadlineNanos;
                 mInfo.state = mState;
@@ -402,7 +513,8 @@
         }
 
         @Override
-        public void requestModeInTransactionLocked(int modeId) {
+        public void requestColorTransformAndModeInTransactionLocked(
+                int colorTransformId, int modeId) {
             if (modeId == 0) {
                 modeId = mDefaultModeId;
             } else if (mSupportedModes.indexOfKey(modeId) < 0) {
@@ -410,13 +522,37 @@
                         + " reverting to default display mode.");
                 modeId = mDefaultModeId;
             }
-            if (mActiveModeId == modeId && !mActiveModeInvalid) {
+
+            if (colorTransformId == 0) {
+                colorTransformId = mDefaultColorTransformId;
+            } else if (mSupportedColorTransforms.indexOfKey(colorTransformId) < 0) {
+                Slog.w(TAG, "Requested color transform " + colorTransformId + " is not supported"
+                        + " by this display, reverting to the default color transform");
+                colorTransformId = mDefaultColorTransformId;
+            }
+            int physIndex = findDisplayInfoIndexLocked(colorTransformId, modeId);
+            if (physIndex < 0) {
+                Slog.w(TAG, "Requested color transform, mode ID pair (" + colorTransformId + ", "
+                        + modeId + ") not available, trying color transform with default mode ID");
+                modeId = mDefaultModeId;
+                physIndex = findDisplayInfoIndexLocked(colorTransformId, modeId);
+                if (physIndex < 0) {
+                    Slog.w(TAG, "Requested color transform with default mode ID still not"
+                            + " available, falling back to default color transform with default"
+                            + " mode.");
+                    colorTransformId = mDefaultColorTransformId;
+                    physIndex = findDisplayInfoIndexLocked(colorTransformId, modeId);
+                }
+            }
+            if (mActivePhysIndex == physIndex) {
                 return;
             }
-            DisplayModeRecord record = mSupportedModes.get(modeId);
-            SurfaceControl.setActiveConfig(getDisplayTokenLocked(), record.mPhysIndex);
+            SurfaceControl.setActiveConfig(getDisplayTokenLocked(), physIndex);
+            mActivePhysIndex = physIndex;
             mActiveModeId = modeId;
             mActiveModeInvalid = false;
+            mActiveColorTransformId = colorTransformId;
+            mActiveColorTransformInvalid = false;
             updateDeviceInfoLocked();
         }
 
@@ -424,10 +560,43 @@
         public void dumpLocked(PrintWriter pw) {
             super.dumpLocked(pw);
             pw.println("mBuiltInDisplayId=" + mBuiltInDisplayId);
+            pw.println("mActivePhysIndex=" + mActivePhysIndex);
             pw.println("mActiveModeId=" + mActiveModeId);
+            pw.println("mActiveColorTransformId=" + mActiveColorTransformId);
             pw.println("mState=" + Display.stateToString(mState));
             pw.println("mBrightness=" + mBrightness);
             pw.println("mBacklight=" + mBacklight);
+            pw.println("mDisplayInfos=");
+            for (int i = 0; i < mDisplayInfos.length; i++) {
+                pw.println("  " + mDisplayInfos[i]);
+            }
+            pw.println("mSupportedModes=");
+            for (int i = 0; i < mSupportedModes.size(); i++) {
+                pw.println("  " + mSupportedModes.valueAt(i));
+            }
+            pw.println("mSupportedColorTransforms=[");
+            for (int i = 0; i < mSupportedColorTransforms.size(); i++) {
+                if (i != 0) {
+                    pw.print(", ");
+                }
+                pw.print(mSupportedColorTransforms.valueAt(i));
+            }
+            pw.println("]");
+        }
+
+        private int findDisplayInfoIndexLocked(int colorTransformId, int modeId) {
+            DisplayModeRecord record = mSupportedModes.get(modeId);
+            Display.ColorTransform transform = mSupportedColorTransforms.get(colorTransformId);
+            if (record != null && transform != null) {
+                for (int i = 0; i < mDisplayInfos.length; i++) {
+                    SurfaceControl.PhysicalDisplayInfo info = mDisplayInfos[i];
+                    if (info.colorTransform == transform.getColorTransform()
+                            && record.hasMatchingMode(info)){
+                        return i;
+                    }
+                }
+            }
+            return -1;
         }
 
         private void updateDeviceInfoLocked() {
@@ -441,13 +610,28 @@
      */
     private static final class DisplayModeRecord {
         public final Display.Mode mMode;
-        public final SurfaceControl.PhysicalDisplayInfo mPhys;
-        public int mPhysIndex;
 
-        public DisplayModeRecord(SurfaceControl.PhysicalDisplayInfo phys, int physIndex) {
+        public DisplayModeRecord(SurfaceControl.PhysicalDisplayInfo phys) {
             mMode = createMode(phys.width, phys.height, phys.refreshRate);
-            mPhys = phys;
-            mPhysIndex = physIndex;
+        }
+
+        /**
+         * Returns whether the mode generated by the given PhysicalDisplayInfo matches the mode
+         * contained by the record modulo mode ID.
+         *
+         * Note that this doesn't necessarily mean the the PhysicalDisplayInfos are identical, just
+         * that they generate identical modes.
+         */
+        public boolean hasMatchingMode(SurfaceControl.PhysicalDisplayInfo info) {
+            int modeRefreshRate = Float.floatToIntBits(mMode.getRefreshRate());
+            int displayInfoRefreshRate = Float.floatToIntBits(info.refreshRate);
+            return mMode.getPhysicalWidth() == info.width
+                    && mMode.getPhysicalHeight() == info.height
+                    && modeRefreshRate == displayInfoRefreshRate;
+        }
+
+        public String toString() {
+            return "DisplayModeRecord{mMode=" + mMode + "}";
         }
     }
 
diff --git a/services/core/java/com/android/server/display/LogicalDisplay.java b/services/core/java/com/android/server/display/LogicalDisplay.java
index 6efc99a..6dae397 100644
--- a/services/core/java/com/android/server/display/LogicalDisplay.java
+++ b/services/core/java/com/android/server/display/LogicalDisplay.java
@@ -74,6 +74,7 @@
     private boolean mHasContent;
 
     private int mRequestedModeId;
+    private int mRequestedColorTransformId;
 
     // The display offsets to apply to the display projection.
     private int mDisplayOffsetX;
@@ -235,6 +236,11 @@
             mBaseDisplayInfo.defaultModeId = deviceInfo.defaultModeId;
             mBaseDisplayInfo.supportedModes = Arrays.copyOf(
                     deviceInfo.supportedModes, deviceInfo.supportedModes.length);
+            mBaseDisplayInfo.colorTransformId = deviceInfo.colorTransformId;
+            mBaseDisplayInfo.defaultColorTransformId = deviceInfo.defaultColorTransformId;
+            mBaseDisplayInfo.supportedColorTransforms = Arrays.copyOf(
+                    deviceInfo.supportedColorTransforms,
+                    deviceInfo.supportedColorTransforms.length);
             mBaseDisplayInfo.logicalDensityDpi = deviceInfo.densityDpi;
             mBaseDisplayInfo.physicalXDpi = deviceInfo.xDpi;
             mBaseDisplayInfo.physicalYDpi = deviceInfo.yDpi;
@@ -275,11 +281,12 @@
         // Set the layer stack.
         device.setLayerStackInTransactionLocked(isBlanked ? BLANK_LAYER_STACK : mLayerStack);
 
-        // Set the mode.
+        // Set the color transform and mode.
         if (device == mPrimaryDisplayDevice) {
-            device.requestModeInTransactionLocked(mRequestedModeId);
+            device.requestColorTransformAndModeInTransactionLocked(
+                    mRequestedColorTransformId, mRequestedModeId);
         } else {
-            device.requestModeInTransactionLocked(0);  // Revert to default.
+            device.requestColorTransformAndModeInTransactionLocked(0, 0);  // Revert to default.
         }
 
         // Only grab the display info now as it may have been changed based on the requests above.
@@ -383,6 +390,18 @@
     }
 
     /**
+     * Requests the given color transform.
+     */
+    public void setRequestedColorTransformIdLocked(int colorTransformId) {
+        mRequestedColorTransformId = colorTransformId;
+    }
+
+    /** Returns the pending requested color transform. */
+    public int getRequestedColorTransformIdLocked() {
+        return mRequestedColorTransformId;
+    }
+
+    /**
      * Gets the burn-in offset in X.
      */
     public int getDisplayOffsetXLocked() {
@@ -409,6 +428,7 @@
         pw.println("mLayerStack=" + mLayerStack);
         pw.println("mHasContent=" + mHasContent);
         pw.println("mRequestedMode=" + mRequestedModeId);
+        pw.println("mRequestedColorTransformId=" + mRequestedColorTransformId);
         pw.println("mDisplayOffset=(" + mDisplayOffsetX + ", " + mDisplayOffsetY + ")");
         pw.println("mPrimaryDisplayDevice=" + (mPrimaryDisplayDevice != null ?
                 mPrimaryDisplayDevice.getNameLocked() : "null"));
diff --git a/services/core/java/com/android/server/display/OverlayDisplayAdapter.java b/services/core/java/com/android/server/display/OverlayDisplayAdapter.java
index 0bddff0..cf6264a 100644
--- a/services/core/java/com/android/server/display/OverlayDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/OverlayDisplayAdapter.java
@@ -310,7 +310,7 @@
         }
 
         @Override
-        public void requestModeInTransactionLocked(int id) {
+        public void requestColorTransformAndModeInTransactionLocked(int color, int id) {
             int index = -1;
             if (id == 0) {
                 // Use the default.
diff --git a/services/core/java/com/android/server/firewall/SenderPackageFilter.java b/services/core/java/com/android/server/firewall/SenderPackageFilter.java
index ec9b5de..dc3edd9 100644
--- a/services/core/java/com/android/server/firewall/SenderPackageFilter.java
+++ b/services/core/java/com/android/server/firewall/SenderPackageFilter.java
@@ -44,7 +44,9 @@
 
         int packageUid = -1;
         try {
-            packageUid = pm.getPackageUid(mPackageName, UserHandle.USER_OWNER);
+            // USER_SYSTEM here is not important. Only app id is used and getPackageUid() will
+            // return a uid whether the app is installed for a user or not.
+            packageUid = pm.getPackageUid(mPackageName, UserHandle.USER_SYSTEM);
         } catch (RemoteException ex) {
             // handled below
         }
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/pm/Installer.java b/services/core/java/com/android/server/pm/Installer.java
index b59b4b2..d55697b 100644
--- a/services/core/java/com/android/server/pm/Installer.java
+++ b/services/core/java/com/android/server/pm/Installer.java
@@ -31,6 +31,19 @@
 public final class Installer extends SystemService {
     private static final String TAG = "Installer";
 
+    /* ***************************************************************************
+     * IMPORTANT: These values are passed to native code. Keep them in sync with
+     * frameworks/native/cmds/installd/installd.h
+     * **************************************************************************/
+    /** Application should be visible to everyone */
+    public static final int DEXOPT_PUBLIC       = 1 << 1;
+    /** Application wants to run in VM safe mode */
+    public static final int DEXOPT_SAFEMODE     = 1 << 2;
+    /** Application wants to allow debugging of its code */
+    public static final int DEXOPT_DEBUGGABLE   = 1 << 3;
+    /** The system boot has finished */
+    public static final int DEXOPT_BOOTCOMPLETE = 1 << 4;
+
     private final InstallerConnection mInstaller;
 
     public Installer(Context context) {
@@ -75,26 +88,24 @@
         return mInstaller.execute(builder.toString());
     }
 
-    public int dexopt(String apkPath, int uid, boolean isPublic,
-            String instructionSet, int dexoptNeeded) {
+    public int dexopt(String apkPath, int uid, String instructionSet,
+            int dexoptNeeded, int dexFlags) {
         if (!isValidInstructionSet(instructionSet)) {
             Slog.e(TAG, "Invalid instruction set: " + instructionSet);
             return -1;
         }
 
-        return mInstaller.dexopt(apkPath, uid, isPublic, instructionSet, dexoptNeeded);
+        return mInstaller.dexopt(apkPath, uid, instructionSet, dexoptNeeded, dexFlags);
     }
 
-    public int dexopt(String apkPath, int uid, boolean isPublic, String pkgName,
-            String instructionSet, int dexoptNeeded, boolean vmSafeMode,
-            boolean debuggable, @Nullable String outputPath) {
+    public int dexopt(String apkPath, int uid, String pkgName, String instructionSet,
+            int dexoptNeeded, @Nullable String outputPath, int dexFlags) {
         if (!isValidInstructionSet(instructionSet)) {
             Slog.e(TAG, "Invalid instruction set: " + instructionSet);
             return -1;
         }
-        return mInstaller.dexopt(apkPath, uid, isPublic, pkgName,
-                instructionSet, dexoptNeeded, vmSafeMode,
-                debuggable, outputPath);
+        return mInstaller.dexopt(apkPath, uid, pkgName, instructionSet, dexoptNeeded,
+                outputPath, dexFlags);
     }
 
     public int idmap(String targetApkPath, String overlayApkPath, int uid) {
diff --git a/services/core/java/com/android/server/pm/PackageDexOptimizer.java b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
index 7024ec8..8be670e 100644
--- a/services/core/java/com/android/server/pm/PackageDexOptimizer.java
+++ b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
@@ -35,6 +35,10 @@
 
 import dalvik.system.DexFile;
 
+import static com.android.server.pm.Installer.DEXOPT_BOOTCOMPLETE;
+import static com.android.server.pm.Installer.DEXOPT_DEBUGGABLE;
+import static com.android.server.pm.Installer.DEXOPT_PUBLIC;
+import static com.android.server.pm.Installer.DEXOPT_SAFEMODE;
 import static com.android.server.pm.InstructionSets.getAppDexInstructionSets;
 import static com.android.server.pm.InstructionSets.getDexCodeInstructionSets;
 
@@ -71,7 +75,7 @@
      * {@link PackageManagerService#mInstallLock}.
      */
     int performDexOpt(PackageParser.Package pkg, String[] instructionSets,
-            boolean forceDex, boolean defer, boolean inclDependencies) {
+            boolean forceDex, boolean defer, boolean inclDependencies, boolean bootComplete) {
         ArraySet<String> done;
         if (inclDependencies && (pkg.usesLibraries != null || pkg.usesOptionalLibraries != null)) {
             done = new ArraySet<String>();
@@ -86,7 +90,7 @@
                 mDexoptWakeLock.acquire();
             }
             try {
-                return performDexOptLI(pkg, instructionSets, forceDex, defer, done);
+                return performDexOptLI(pkg, instructionSets, forceDex, defer, bootComplete, done);
             } finally {
                 if (useLock) {
                     mDexoptWakeLock.release();
@@ -96,18 +100,19 @@
     }
 
     private int performDexOptLI(PackageParser.Package pkg, String[] targetInstructionSets,
-            boolean forceDex, boolean defer, ArraySet<String> done) {
+            boolean forceDex, boolean defer, boolean bootComplete, ArraySet<String> done) {
         final String[] instructionSets = targetInstructionSets != null ?
                 targetInstructionSets : getAppDexInstructionSets(pkg.applicationInfo);
 
         if (done != null) {
             done.add(pkg.packageName);
             if (pkg.usesLibraries != null) {
-                performDexOptLibsLI(pkg.usesLibraries, instructionSets, forceDex, defer, done);
+                performDexOptLibsLI(pkg.usesLibraries, instructionSets, forceDex, defer,
+                        bootComplete, done);
             }
             if (pkg.usesOptionalLibraries != null) {
                 performDexOptLibsLI(pkg.usesOptionalLibraries, instructionSets, forceDex, defer,
-                        done);
+                        bootComplete, done);
             }
         }
 
@@ -174,11 +179,15 @@
                     Log.i(TAG, "Running dexopt (" + dexoptType + ") on: " + path + " pkg="
                             + pkg.applicationInfo.packageName + " isa=" + dexCodeInstructionSet
                             + " vmSafeMode=" + vmSafeMode + " debuggable=" + debuggable
-                            + " oatDir = " + oatDir);
+                            + " oatDir = " + oatDir + " bootComplete=" + bootComplete);
                     final int sharedGid = UserHandle.getSharedAppGid(pkg.applicationInfo.uid);
+                    final int dexFlags =
+                            (!pkg.isForwardLocked() ? DEXOPT_PUBLIC : 0)
+                            | (vmSafeMode ? DEXOPT_SAFEMODE : 0)
+                            | (debuggable ? DEXOPT_DEBUGGABLE : 0)
+                            | (bootComplete ? DEXOPT_BOOTCOMPLETE : 0);
                     final int ret = mPackageManagerService.mInstaller.dexopt(path, sharedGid,
-                            !pkg.isForwardLocked(), pkg.packageName, dexCodeInstructionSet,
-                            dexoptNeeded, vmSafeMode, debuggable, oatDir);
+                            pkg.packageName, dexCodeInstructionSet, dexoptNeeded, oatDir, dexFlags);
 
                     // Dex2oat might fail due to compiler / verifier errors. We soldier on
                     // regardless, and attempt to interpret the app as a safety net.
@@ -217,8 +226,7 @@
     @Nullable
     private String createOatDirIfSupported(PackageParser.Package pkg, String dexInstructionSet)
             throws IOException {
-        if ((pkg.isSystemApp() && !pkg.isUpdatedSystemApp()) || pkg.isForwardLocked()
-                || pkg.applicationInfo.isExternalAsec()) {
+        if (!pkg.canHaveOatDir()) {
             return null;
         }
         File codePath = new File(pkg.codePath);
@@ -236,12 +244,12 @@
     }
 
     private void performDexOptLibsLI(ArrayList<String> libs, String[] instructionSets,
-            boolean forceDex, boolean defer, ArraySet<String> done) {
+            boolean forceDex, boolean defer, boolean bootComplete, ArraySet<String> done) {
         for (String libName : libs) {
             PackageParser.Package libPkg = mPackageManagerService.findSharedNonSystemLibrary(
                     libName);
             if (libPkg != null && !done.contains(libName)) {
-                performDexOptLI(libPkg, instructionSets, forceDex, defer, done);
+                performDexOptLI(libPkg, instructionSets, forceDex, defer, bootComplete, done);
             }
         }
     }
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index d409325..7a445a6 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -75,6 +75,7 @@
 import static com.android.internal.content.NativeLibraryHelper.LIB64_DIR_NAME;
 import static com.android.internal.content.NativeLibraryHelper.LIB_DIR_NAME;
 import static com.android.internal.util.ArrayUtils.appendInt;
+import static com.android.server.pm.Installer.DEXOPT_PUBLIC;
 import static com.android.server.pm.InstructionSets.getAppDexInstructionSets;
 import static com.android.server.pm.InstructionSets.getDexCodeInstructionSet;
 import static com.android.server.pm.InstructionSets.getDexCodeInstructionSets;
@@ -2015,7 +2016,8 @@
                             int dexoptNeeded = DexFile.getDexOptNeeded(lib, null, dexCodeInstructionSet, false);
                             if (dexoptNeeded != DexFile.NO_DEXOPT_NEEDED) {
                                 alreadyDexOpted.add(lib);
-                                mInstaller.dexopt(lib, Process.SYSTEM_UID, true, dexCodeInstructionSet, dexoptNeeded);
+                                mInstaller.dexopt(lib, Process.SYSTEM_UID, dexCodeInstructionSet,
+                                        dexoptNeeded, DEXOPT_PUBLIC /*dexFlags*/);
                             }
                         } catch (FileNotFoundException e) {
                             Slog.w(TAG, "Library not found: " + lib);
@@ -2063,7 +2065,8 @@
                         try {
                             int dexoptNeeded = DexFile.getDexOptNeeded(path, null, dexCodeInstructionSet, false);
                             if (dexoptNeeded != DexFile.NO_DEXOPT_NEEDED) {
-                                mInstaller.dexopt(path, Process.SYSTEM_UID, true, dexCodeInstructionSet, dexoptNeeded);
+                                mInstaller.dexopt(path, Process.SYSTEM_UID, dexCodeInstructionSet,
+                                        dexoptNeeded, DEXOPT_PUBLIC /*dexFlags*/);
                             }
                         } catch (FileNotFoundException e) {
                             Slog.w(TAG, "Jar not found: " + path);
@@ -2292,7 +2295,8 @@
                 // the rest of the commands above) because there's precious little we
                 // can do about it. A settings error is reported, though.
                 adjustCpuAbisForSharedUserLPw(setting.packages, null /* scanned package */,
-                        false /* force dexopt */, false /* defer dexopt */);
+                        false /* force dexopt */, false /* defer dexopt */,
+                        false /* boot complete */);
             }
 
             // Now that we know all the packages we are keeping,
@@ -6155,41 +6159,6 @@
                     it.remove();
                 }
             }
-            // Give priority to system apps.
-            for (Iterator<PackageParser.Package> it = pkgs.iterator(); it.hasNext();) {
-                PackageParser.Package pkg = it.next();
-                if (isSystemApp(pkg) && !pkg.isUpdatedSystemApp()) {
-                    if (DEBUG_DEXOPT) {
-                        Log.i(TAG, "Adding system app " + sortedPkgs.size() + ": " + pkg.packageName);
-                    }
-                    sortedPkgs.add(pkg);
-                    it.remove();
-                }
-            }
-            // Give priority to updated system apps.
-            for (Iterator<PackageParser.Package> it = pkgs.iterator(); it.hasNext();) {
-                PackageParser.Package pkg = it.next();
-                if (pkg.isUpdatedSystemApp()) {
-                    if (DEBUG_DEXOPT) {
-                        Log.i(TAG, "Adding updated system app " + sortedPkgs.size() + ": " + pkg.packageName);
-                    }
-                    sortedPkgs.add(pkg);
-                    it.remove();
-                }
-            }
-            // Give priority to apps that listen for boot complete.
-            intent = new Intent(Intent.ACTION_BOOT_COMPLETED);
-            pkgNames = getPackageNamesForIntent(intent, UserHandle.USER_SYSTEM);
-            for (Iterator<PackageParser.Package> it = pkgs.iterator(); it.hasNext();) {
-                PackageParser.Package pkg = it.next();
-                if (pkgNames.contains(pkg.packageName)) {
-                    if (DEBUG_DEXOPT) {
-                        Log.i(TAG, "Adding boot app " + sortedPkgs.size() + ": " + pkg.packageName);
-                    }
-                    sortedPkgs.add(pkg);
-                    it.remove();
-                }
-            }
             // Filter out packages that aren't recently used.
             filterRecentlyUsedApps(pkgs);
             // Add all remaining apps.
@@ -6281,7 +6250,8 @@
         PackageParser.Package p = pkg;
         synchronized (mInstallLock) {
             mPackageDexOptimizer.performDexOpt(p, null /* instruction sets */,
-                    false /* force dex */, false /* defer */, true /* include dependencies */);
+                    false /* force dex */, false /* defer */, true /* include dependencies */,
+                    false /* boot complete */);
         }
     }
 
@@ -6340,7 +6310,8 @@
             synchronized (mInstallLock) {
                 final String[] instructionSets = new String[] { targetInstructionSet };
                 int result = mPackageDexOptimizer.performDexOpt(p, instructionSets,
-                        false /* forceDex */, false /* defer */, true /* inclDependencies */);
+                        false /* forceDex */, false /* defer */, true /* inclDependencies */,
+                        true /* boot complete */);
                 return result == PackageDexOptimizer.DEX_OPT_PERFORMED;
             }
         } finally {
@@ -6390,7 +6361,8 @@
             Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "dexopt");
 
             final int res = mPackageDexOptimizer.performDexOpt(pkg, instructionSets,
-                    true /*forceDex*/, false /* defer */, true /* inclDependencies */);
+                    true /*forceDex*/, false /* defer */, true /* inclDependencies */,
+                    true /* boot complete */);
 
             Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
             if (res != PackageDexOptimizer.DEX_OPT_PERFORMED) {
@@ -7199,14 +7171,15 @@
             // we can avoid redundant dexopts, and also to make sure we've got the
             // code and package path correct.
             adjustCpuAbisForSharedUserLPw(pkgSetting.sharedUser.packages,
-                    pkg, forceDex, (scanFlags & SCAN_DEFER_DEX) != 0);
+                    pkg, forceDex, (scanFlags & SCAN_DEFER_DEX) != 0, true /* boot complete */);
         }
 
         if ((scanFlags & SCAN_NO_DEX) == 0) {
             Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "dexopt");
 
             int result = mPackageDexOptimizer.performDexOpt(pkg, null /* instruction sets */,
-                    forceDex, (scanFlags & SCAN_DEFER_DEX) != 0, false /* inclDependencies */);
+                    forceDex, (scanFlags & SCAN_DEFER_DEX) != 0, false /* inclDependencies */,
+                    (scanFlags & SCAN_BOOTING) == 0);
 
             Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
             if (result == PackageDexOptimizer.DEX_OPT_FAILED) {
@@ -7286,7 +7259,8 @@
                         PackageParser.Package clientPkg = clientLibPkgs.get(i);
                         int result = mPackageDexOptimizer.performDexOpt(clientPkg,
                                 null /* instruction sets */, forceDex,
-                                (scanFlags & SCAN_DEFER_DEX) != 0, false);
+                                (scanFlags & SCAN_DEFER_DEX) != 0, false,
+                                (scanFlags & SCAN_BOOTING) == 0);
                         if (result == PackageDexOptimizer.DEX_OPT_FAILED) {
                             throw new PackageManagerException(INSTALL_FAILED_DEXOPT,
                                     "scanPackageLI failed to dexopt clientLibPkgs");
@@ -7842,7 +7816,8 @@
      * adds unnecessary complexity.
      */
     private void adjustCpuAbisForSharedUserLPw(Set<PackageSetting> packagesForUser,
-            PackageParser.Package scannedPackage, boolean forceDexOpt, boolean deferDexOpt) {
+            PackageParser.Package scannedPackage, boolean forceDexOpt, boolean deferDexOpt,
+            boolean bootComplete) {
         String requiredInstructionSet = null;
         if (scannedPackage != null && scannedPackage.applicationInfo.primaryCpuAbi != null) {
             requiredInstructionSet = VMRuntime.getInstructionSet(
@@ -7908,7 +7883,8 @@
                         Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "dexopt");
 
                         int result = mPackageDexOptimizer.performDexOpt(ps.pkg,
-                                null /* instruction sets */, forceDexOpt, deferDexOpt, true);
+                                null /* instruction sets */, forceDexOpt, deferDexOpt, true,
+                                bootComplete);
 
                         Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
                         if (result == PackageDexOptimizer.DEX_OPT_FAILED) {
@@ -12380,6 +12356,7 @@
 
         // Retrieve PackageSettings and parse package
         final int parseFlags = mDefParseFlags | PackageParser.PARSE_CHATTY
+                | PackageParser.PARSE_ENFORCE_CODE
                 | (forwardLocked ? PackageParser.PARSE_FORWARD_LOCK : 0)
                 | (onExternal ? PackageParser.PARSE_EXTERNAL_STORAGE : 0);
         PackageParser pp = new PackageParser();
@@ -12597,8 +12574,8 @@
 
             int result = mPackageDexOptimizer
                     .performDexOpt(pkg, null /* instruction sets */, false /* forceDex */,
-                            false /* defer */, false /* inclDependencies */);
-
+                            false /* defer */, false /* inclDependencies */,
+                            true /*bootComplete*/);
             Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
             if (result == PackageDexOptimizer.DEX_OPT_FAILED) {
                 res.setError(INSTALL_FAILED_DEXOPT, "Dexopt failed for " + pkg.codePath);
@@ -13851,7 +13828,21 @@
         // TODO(multiArch): Extend getSizeInfo to look at *all* instruction sets, not
         // just the primary.
         String[] dexCodeInstructionSets = getDexCodeInstructionSets(getAppDexInstructionSets(ps));
-        int res = mInstaller.getSizeInfo(p.volumeUuid, packageName, userHandle, p.baseCodePath,
+
+        String apkPath;
+        File packageDir = new File(p.codePath);
+
+        if (packageDir.isDirectory() && p.canHaveOatDir()) {
+            apkPath = packageDir.getAbsolutePath();
+            // If libDirRoot is inside a package dir, set it to null to avoid it being counted twice
+            if (libDirRoot != null && libDirRoot.startsWith(apkPath)) {
+                libDirRoot = null;
+            }
+        } else {
+            apkPath = p.baseCodePath;
+        }
+
+        int res = mInstaller.getSizeInfo(p.volumeUuid, packageName, userHandle, apkPath,
                 libDirRoot, publicSrcDir, asecPath, dexCodeInstructionSets, pStats);
         if (res < 0) {
             return false;
@@ -15887,14 +15878,28 @@
         }
     }
 
-    private void loadPrivatePackages(VolumeInfo vol) {
+    private void loadPrivatePackages(final VolumeInfo vol) {
+        mHandler.post(new Runnable() {
+            @Override
+            public void run() {
+                loadPrivatePackagesInner(vol);
+            }
+        });
+    }
+
+    private void loadPrivatePackagesInner(VolumeInfo vol) {
         final ArrayList<ApplicationInfo> loaded = new ArrayList<>();
         final int parseFlags = mDefParseFlags | PackageParser.PARSE_EXTERNAL_STORAGE;
-        synchronized (mInstallLock) {
+
+        final VersionInfo ver;
+        final List<PackageSetting> packages;
         synchronized (mPackages) {
-            final VersionInfo ver = mSettings.findOrCreateVersion(vol.fsUuid);
-            final List<PackageSetting> packages = mSettings.getVolumePackagesLPr(vol.fsUuid);
-            for (PackageSetting ps : packages) {
+            ver = mSettings.findOrCreateVersion(vol.fsUuid);
+            packages = mSettings.getVolumePackagesLPr(vol.fsUuid);
+        }
+
+        for (PackageSetting ps : packages) {
+            synchronized (mInstallLock) {
                 final PackageParser.Package pkg;
                 try {
                     pkg = scanPackageTracedLI(ps.codePath, parseFlags, SCAN_INITIAL, 0L, null);
@@ -15907,7 +15912,9 @@
                     deleteCodeCacheDirsLI(ps.volumeUuid, ps.name);
                 }
             }
+        }
 
+        synchronized (mPackages) {
             int updateFlags = UPDATE_PERMISSIONS_ALL;
             if (ver.sdkVersion != mSdkVersion) {
                 logCriticalInfo(Log.INFO, "Platform changed from " + ver.sdkVersion + " to "
@@ -15921,13 +15928,21 @@
 
             mSettings.writeLPr();
         }
-        }
 
         if (DEBUG_INSTALL) Slog.d(TAG, "Loaded packages " + loaded);
         sendResourcesChangedBroadcast(true, false, loaded, null);
     }
 
-    private void unloadPrivatePackages(VolumeInfo vol) {
+    private void unloadPrivatePackages(final VolumeInfo vol) {
+        mHandler.post(new Runnable() {
+            @Override
+            public void run() {
+                unloadPrivatePackagesInner(vol);
+            }
+        });
+    }
+
+    private void unloadPrivatePackagesInner(VolumeInfo vol) {
         final ArrayList<ApplicationInfo> unloaded = new ArrayList<>();
         synchronized (mInstallLock) {
         synchronized (mPackages) {
diff --git a/services/core/java/com/android/server/policy/BarController.java b/services/core/java/com/android/server/policy/BarController.java
index 9095f57..051b7fb 100644
--- a/services/core/java/com/android/server/policy/BarController.java
+++ b/services/core/java/com/android/server/policy/BarController.java
@@ -258,7 +258,7 @@
             vis &= ~View.SYSTEM_UI_FLAG_LOW_PROFILE;  // never show transient bars in low profile
         }
         if ((vis & mTranslucentFlag) != 0 || (oldVis & mTranslucentFlag) != 0 ||
-                ((vis | oldVis) & View.SYSTEM_UI_FLAG_FULLSCREEN) != 0) {
+                ((vis | oldVis) & View.SYSTEM_UI_TRANSPARENT) != 0) {
             mLastTranslucent = SystemClock.uptimeMillis();
         }
         return vis;
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 3053904..dbc3970 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -266,9 +266,9 @@
     WindowManagerFuncs mWindowManagerFuncs;
     WindowManagerInternal mWindowManagerInternal;
     PowerManager mPowerManager;
-    PowerManagerInternal mPowerManagerInternal;
     ActivityManagerInternal mActivityManagerInternal;
     DreamManagerInternal mDreamManagerInternal;
+    PowerManagerInternal mPowerManagerInternal;
     IStatusBarService mStatusBarService;
     boolean mPreloadedRecentApps;
     final Object mServiceAquireLock = new Object();
@@ -1343,8 +1343,8 @@
         mWindowManagerInternal = LocalServices.getService(WindowManagerInternal.class);
         mActivityManagerInternal = LocalServices.getService(ActivityManagerInternal.class);
         mDreamManagerInternal = LocalServices.getService(DreamManagerInternal.class);
-        mAppOpsManager = (AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE);
         mPowerManagerInternal = LocalServices.getService(PowerManagerInternal.class);
+        mAppOpsManager = (AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE);
 
         // Init display burn-in protection
         boolean burnInProtectionEnabled = context.getResources().getBoolean(
@@ -1520,6 +1520,13 @@
                         }
                     }
                     @Override
+                    public void onFling(int duration) {
+                        if (mPowerManagerInternal != null) {
+                            mPowerManagerInternal.powerHint(
+                                    PowerManagerInternal.POWER_HINT_INTERACTION, duration);
+                        }
+                    }
+                    @Override
                     public void onDebug() {
                         // no-op
                     }
@@ -1996,6 +2003,7 @@
             case TYPE_SYSTEM_DIALOG:
             case TYPE_VOLUME_OVERLAY:
             case TYPE_PRIVATE_PRESENTATION:
+            case TYPE_DOCK_DIVIDER:
                 break;
         }
 
@@ -2127,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;
@@ -2264,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.
@@ -3570,7 +3581,6 @@
         final Rect of = mTmpOverscanFrame;
         final Rect vf = mTmpVisibleFrame;
         final Rect dcf = mTmpDecorFrame;
-        final Rect osf = mTmpOutsetFrame;
         pf.left = df.left = of.left = vf.left = mDockLeft;
         pf.top = df.top = of.top = vf.top = mDockTop;
         pf.right = df.right = of.right = vf.right = mDockRight;
@@ -3612,152 +3622,165 @@
             // then take that into account.
             navVisible |= !canHideNavigationBar();
 
-            boolean updateSysUiVisibility = false;
-            if (mNavigationBar != null) {
-                boolean transientNavBarShowing = mNavigationBarController.isTransientShowing();
-                // Force the navigation bar to its appropriate place and
-                // size.  We need to do this directly, instead of relying on
-                // it to bubble up from the nav bar, because this needs to
-                // change atomically with screen rotations.
-                mNavigationBarOnBottom = (!mNavigationBarCanMove || displayWidth < displayHeight);
-                if (mNavigationBarOnBottom) {
-                    // It's a system nav bar or a portrait screen; nav bar goes on bottom.
-                    int top = displayHeight - overscanBottom
-                            - mNavigationBarHeightForRotation[displayRotation];
-                    mTmpNavigationFrame.set(0, top, displayWidth, displayHeight - overscanBottom);
-                    mStableBottom = mStableFullscreenBottom = mTmpNavigationFrame.top;
-                    if (transientNavBarShowing) {
-                        mNavigationBarController.setBarShowingLw(true);
-                    } else if (navVisible) {
-                        mNavigationBarController.setBarShowingLw(true);
-                        mDockBottom = mTmpNavigationFrame.top;
-                        mRestrictedScreenHeight = mDockBottom - mRestrictedScreenTop;
-                        mRestrictedOverscanScreenHeight = mDockBottom - mRestrictedOverscanScreenTop;
-                    } else {
-                        // We currently want to hide the navigation UI.
-                        mNavigationBarController.setBarShowingLw(false);
-                    }
-                    if (navVisible && !navTranslucent && !navAllowedHidden
-                            && !mNavigationBar.isAnimatingLw()
-                            && !mNavigationBarController.wasRecentlyTranslucent()) {
-                        // If the opaque nav bar is currently requested to be visible,
-                        // and not in the process of animating on or off, then
-                        // we can tell the app that it is covered by it.
-                        mSystemBottom = mTmpNavigationFrame.top;
-                    }
-                } else {
-                    // Landscape screen; nav bar goes to the right.
-                    int left = displayWidth - overscanRight
-                            - mNavigationBarWidthForRotation[displayRotation];
-                    mTmpNavigationFrame.set(left, 0, displayWidth - overscanRight, displayHeight);
-                    mStableRight = mStableFullscreenRight = mTmpNavigationFrame.left;
-                    if (transientNavBarShowing) {
-                        mNavigationBarController.setBarShowingLw(true);
-                    } else if (navVisible) {
-                        mNavigationBarController.setBarShowingLw(true);
-                        mDockRight = mTmpNavigationFrame.left;
-                        mRestrictedScreenWidth = mDockRight - mRestrictedScreenLeft;
-                        mRestrictedOverscanScreenWidth = mDockRight - mRestrictedOverscanScreenLeft;
-                    } else {
-                        // We currently want to hide the navigation UI.
-                        mNavigationBarController.setBarShowingLw(false);
-                    }
-                    if (navVisible && !navTranslucent && !navAllowedHidden
-                            && !mNavigationBar.isAnimatingLw()
-                            && !mNavigationBarController.wasRecentlyTranslucent()) {
-                        // If the nav bar is currently requested to be visible,
-                        // and not in the process of animating on or off, then
-                        // we can tell the app that it is covered by it.
-                        mSystemRight = mTmpNavigationFrame.left;
-                    }
-                }
-                // Make sure the content and current rectangles are updated to
-                // account for the restrictions from the navigation bar.
-                mContentTop = mVoiceContentTop = mCurTop = mDockTop;
-                mContentBottom = mVoiceContentBottom = mCurBottom = mDockBottom;
-                mContentLeft = mVoiceContentLeft = mCurLeft = mDockLeft;
-                mContentRight = mVoiceContentRight = mCurRight = mDockRight;
-                mStatusBarLayer = mNavigationBar.getSurfaceLayer();
-                // And compute the final frame.
-                mNavigationBar.computeFrameLw(mTmpNavigationFrame, mTmpNavigationFrame,
-                        mTmpNavigationFrame, mTmpNavigationFrame, mTmpNavigationFrame, dcf,
-                        mTmpNavigationFrame, mTmpNavigationFrame);
-                if (DEBUG_LAYOUT) Slog.i(TAG, "mNavigationBar frame: " + mTmpNavigationFrame);
-                if (mNavigationBarController.checkHiddenLw()) {
-                    updateSysUiVisibility = true;
-                }
-            }
+            boolean updateSysUiVisibility = layoutNavigationBar(displayWidth, displayHeight,
+                    displayRotation, overscanRight, overscanBottom, dcf, navVisible, navTranslucent,
+                    navAllowedHidden);
             if (DEBUG_LAYOUT) Slog.i(TAG, String.format("mDock rect: (%d,%d - %d,%d)",
                     mDockLeft, mDockTop, mDockRight, mDockBottom));
-
-            // decide where the status bar goes ahead of time
-            if (mStatusBar != null) {
-                // apply any navigation bar insets
-                pf.left = df.left = of.left = mUnrestrictedScreenLeft;
-                pf.top = df.top = of.top = mUnrestrictedScreenTop;
-                pf.right = df.right = of.right = mUnrestrictedScreenWidth + mUnrestrictedScreenLeft;
-                pf.bottom = df.bottom = of.bottom = mUnrestrictedScreenHeight
-                        + mUnrestrictedScreenTop;
-                vf.left = mStableLeft;
-                vf.top = mStableTop;
-                vf.right = mStableRight;
-                vf.bottom = mStableBottom;
-
-                mStatusBarLayer = mStatusBar.getSurfaceLayer();
-
-                // Let the status bar determine its size.
-                mStatusBar.computeFrameLw(pf /* parentFrame */, df /* displayFrame */,
-                        vf /* overlayFrame */, vf /* contentFrame */, vf /* visibleFrame */,
-                        dcf /* decorFrame */, vf /* stableFrame */, vf /* outsetFrame */);
-
-                // For layout, the status bar is always at the top with our fixed height.
-                mStableTop = mUnrestrictedScreenTop + mStatusBarHeight;
-
-                boolean statusBarTransient = (sysui & View.STATUS_BAR_TRANSIENT) != 0;
-                boolean statusBarTranslucent = (sysui
-                        & (View.STATUS_BAR_TRANSLUCENT | View.SYSTEM_UI_TRANSPARENT)) != 0;
-                if (!isKeyguardShowing) {
-                    statusBarTranslucent &= areTranslucentBarsAllowed();
-                }
-
-                // If the status bar is hidden, we don't want to cause
-                // windows behind it to scroll.
-                if (mStatusBar.isVisibleLw() && !statusBarTransient) {
-                    // Status bar may go away, so the screen area it occupies
-                    // is available to apps but just covering them when the
-                    // status bar is visible.
-                    mDockTop = mUnrestrictedScreenTop + mStatusBarHeight;
-
-                    mContentTop = mVoiceContentTop = mCurTop = mDockTop;
-                    mContentBottom = mVoiceContentBottom = mCurBottom = mDockBottom;
-                    mContentLeft = mVoiceContentLeft = mCurLeft = mDockLeft;
-                    mContentRight = mVoiceContentRight = mCurRight = mDockRight;
-
-                    if (DEBUG_LAYOUT) Slog.v(TAG, "Status bar: " +
-                        String.format(
-                            "dock=[%d,%d][%d,%d] content=[%d,%d][%d,%d] cur=[%d,%d][%d,%d]",
-                            mDockLeft, mDockTop, mDockRight, mDockBottom,
-                            mContentLeft, mContentTop, mContentRight, mContentBottom,
-                            mCurLeft, mCurTop, mCurRight, mCurBottom));
-                }
-                if (mStatusBar.isVisibleLw() && !mStatusBar.isAnimatingLw()
-                        && !statusBarTransient && !statusBarTranslucent
-                        && !mStatusBarController.wasRecentlyTranslucent()) {
-                    // If the opaque status bar is currently requested to be visible,
-                    // and not in the process of animating on or off, then
-                    // we can tell the app that it is covered by it.
-                    mSystemTop = mUnrestrictedScreenTop + mStatusBarHeight;
-                }
-                if (mStatusBarController.checkHiddenLw()) {
-                    updateSysUiVisibility = true;
-                }
-            }
+            updateSysUiVisibility |= layoutStatusBar(pf, df, of, vf, dcf, sysui, isKeyguardShowing);
             if (updateSysUiVisibility) {
                 updateSystemUiVisibilityLw();
             }
         }
     }
 
+    private boolean layoutStatusBar(Rect pf, Rect df, Rect of, Rect vf, Rect dcf, int sysui,
+            boolean isKeyguardShowing) {
+        // decide where the status bar goes ahead of time
+        if (mStatusBar != null) {
+            // apply any navigation bar insets
+            pf.left = df.left = of.left = mUnrestrictedScreenLeft;
+            pf.top = df.top = of.top = mUnrestrictedScreenTop;
+            pf.right = df.right = of.right = mUnrestrictedScreenWidth + mUnrestrictedScreenLeft;
+            pf.bottom = df.bottom = of.bottom = mUnrestrictedScreenHeight
+                    + mUnrestrictedScreenTop;
+            vf.left = mStableLeft;
+            vf.top = mStableTop;
+            vf.right = mStableRight;
+            vf.bottom = mStableBottom;
+
+            mStatusBarLayer = mStatusBar.getSurfaceLayer();
+
+            // Let the status bar determine its size.
+            mStatusBar.computeFrameLw(pf /* parentFrame */, df /* displayFrame */,
+                    vf /* overlayFrame */, vf /* contentFrame */, vf /* visibleFrame */,
+                    dcf /* decorFrame */, vf /* stableFrame */, vf /* outsetFrame */);
+
+            // For layout, the status bar is always at the top with our fixed height.
+            mStableTop = mUnrestrictedScreenTop + mStatusBarHeight;
+
+            boolean statusBarTransient = (sysui & View.STATUS_BAR_TRANSIENT) != 0;
+            boolean statusBarTranslucent = (sysui
+                    & (View.STATUS_BAR_TRANSLUCENT | View.SYSTEM_UI_TRANSPARENT)) != 0;
+            if (!isKeyguardShowing) {
+                statusBarTranslucent &= areTranslucentBarsAllowed();
+            }
+
+            // If the status bar is hidden, we don't want to cause
+            // windows behind it to scroll.
+            if (mStatusBar.isVisibleLw() && !statusBarTransient) {
+                // Status bar may go away, so the screen area it occupies
+                // is available to apps but just covering them when the
+                // status bar is visible.
+                mDockTop = mUnrestrictedScreenTop + mStatusBarHeight;
+
+                mContentTop = mVoiceContentTop = mCurTop = mDockTop;
+                mContentBottom = mVoiceContentBottom = mCurBottom = mDockBottom;
+                mContentLeft = mVoiceContentLeft = mCurLeft = mDockLeft;
+                mContentRight = mVoiceContentRight = mCurRight = mDockRight;
+
+                if (DEBUG_LAYOUT) Slog.v(TAG, "Status bar: " +
+                        String.format(
+                                "dock=[%d,%d][%d,%d] content=[%d,%d][%d,%d] cur=[%d,%d][%d,%d]",
+                                mDockLeft, mDockTop, mDockRight, mDockBottom,
+                                mContentLeft, mContentTop, mContentRight, mContentBottom,
+                                mCurLeft, mCurTop, mCurRight, mCurBottom));
+            }
+            if (mStatusBar.isVisibleLw() && !mStatusBar.isAnimatingLw()
+                    && !statusBarTransient && !statusBarTranslucent
+                    && !mStatusBarController.wasRecentlyTranslucent()) {
+                // If the opaque status bar is currently requested to be visible,
+                // and not in the process of animating on or off, then
+                // we can tell the app that it is covered by it.
+                mSystemTop = mUnrestrictedScreenTop + mStatusBarHeight;
+            }
+            if (mStatusBarController.checkHiddenLw()) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    private boolean layoutNavigationBar(int displayWidth, int displayHeight, int displayRotation,
+            int overscanRight, int overscanBottom, Rect dcf, boolean navVisible,
+            boolean navTranslucent, boolean navAllowedHidden) {
+        if (mNavigationBar != null) {
+            boolean transientNavBarShowing = mNavigationBarController.isTransientShowing();
+            // Force the navigation bar to its appropriate place and
+            // size.  We need to do this directly, instead of relying on
+            // it to bubble up from the nav bar, because this needs to
+            // change atomically with screen rotations.
+            mNavigationBarOnBottom = (!mNavigationBarCanMove || displayWidth < displayHeight);
+            if (mNavigationBarOnBottom) {
+                // It's a system nav bar or a portrait screen; nav bar goes on bottom.
+                int top = displayHeight - overscanBottom
+                        - mNavigationBarHeightForRotation[displayRotation];
+                mTmpNavigationFrame.set(0, top, displayWidth, displayHeight - overscanBottom);
+                mStableBottom = mStableFullscreenBottom = mTmpNavigationFrame.top;
+                if (transientNavBarShowing) {
+                    mNavigationBarController.setBarShowingLw(true);
+                } else if (navVisible) {
+                    mNavigationBarController.setBarShowingLw(true);
+                    mDockBottom = mTmpNavigationFrame.top;
+                    mRestrictedScreenHeight = mDockBottom - mRestrictedScreenTop;
+                    mRestrictedOverscanScreenHeight = mDockBottom - mRestrictedOverscanScreenTop;
+                } else {
+                    // We currently want to hide the navigation UI.
+                    mNavigationBarController.setBarShowingLw(false);
+                }
+                if (navVisible && !navTranslucent && !navAllowedHidden
+                        && !mNavigationBar.isAnimatingLw()
+                        && !mNavigationBarController.wasRecentlyTranslucent()) {
+                    // If the opaque nav bar is currently requested to be visible,
+                    // and not in the process of animating on or off, then
+                    // we can tell the app that it is covered by it.
+                    mSystemBottom = mTmpNavigationFrame.top;
+                }
+            } else {
+                // Landscape screen; nav bar goes to the right.
+                int left = displayWidth - overscanRight
+                        - mNavigationBarWidthForRotation[displayRotation];
+                mTmpNavigationFrame.set(left, 0, displayWidth - overscanRight, displayHeight);
+                mStableRight = mStableFullscreenRight = mTmpNavigationFrame.left;
+                if (transientNavBarShowing) {
+                    mNavigationBarController.setBarShowingLw(true);
+                } else if (navVisible) {
+                    mNavigationBarController.setBarShowingLw(true);
+                    mDockRight = mTmpNavigationFrame.left;
+                    mRestrictedScreenWidth = mDockRight - mRestrictedScreenLeft;
+                    mRestrictedOverscanScreenWidth = mDockRight - mRestrictedOverscanScreenLeft;
+                } else {
+                    // We currently want to hide the navigation UI.
+                    mNavigationBarController.setBarShowingLw(false);
+                }
+                if (navVisible && !navTranslucent && !navAllowedHidden
+                        && !mNavigationBar.isAnimatingLw()
+                        && !mNavigationBarController.wasRecentlyTranslucent()) {
+                    // If the nav bar is currently requested to be visible,
+                    // and not in the process of animating on or off, then
+                    // we can tell the app that it is covered by it.
+                    mSystemRight = mTmpNavigationFrame.left;
+                }
+            }
+            // Make sure the content and current rectangles are updated to
+            // account for the restrictions from the navigation bar.
+            mContentTop = mVoiceContentTop = mCurTop = mDockTop;
+            mContentBottom = mVoiceContentBottom = mCurBottom = mDockBottom;
+            mContentLeft = mVoiceContentLeft = mCurLeft = mDockLeft;
+            mContentRight = mVoiceContentRight = mCurRight = mDockRight;
+            mStatusBarLayer = mNavigationBar.getSurfaceLayer();
+            // And compute the final frame.
+            mNavigationBar.computeFrameLw(mTmpNavigationFrame, mTmpNavigationFrame,
+                    mTmpNavigationFrame, mTmpNavigationFrame, mTmpNavigationFrame, dcf,
+                    mTmpNavigationFrame, mTmpNavigationFrame);
+            if (DEBUG_LAYOUT) Slog.i(TAG, "mNavigationBar frame: " + mTmpNavigationFrame);
+            if (mNavigationBarController.checkHiddenLw()) {
+                return true;
+            }
+        }
+        return false;
+    }
+
     /** {@inheritDoc} */
     @Override
     public int getSystemDecorLayerLw() {
@@ -4409,6 +4432,7 @@
         if (attrs.type == TYPE_STATUS_BAR) {
             if ((attrs.privateFlags & PRIVATE_FLAG_KEYGUARD) != 0) {
                 mForceStatusBarFromKeyguard = true;
+                mShowingLockscreen = true;
             }
             if ((attrs.privateFlags & PRIVATE_FLAG_FORCE_STATUS_BAR_VISIBLE_TRANSPARENT) != 0) {
                 mForceStatusBarTransparent = true;
@@ -4429,9 +4453,6 @@
                     mForceStatusBar = true;
                 }
             }
-            if ((attrs.privateFlags & PRIVATE_FLAG_KEYGUARD) != 0) {
-                mShowingLockscreen = true;
-            }
             if (attrs.type == TYPE_DREAM) {
                 // If the lockscreen was showing when the dream started then wait
                 // for the dream to draw before hiding the lockscreen.
@@ -6114,6 +6135,7 @@
             mKeyguardDelegate.bindService(mContext);
             mKeyguardDelegate.onBootCompleted();
         }
+        mSystemGestures.systemReady();
     }
 
     /** {@inheritDoc} */
diff --git a/services/core/java/com/android/server/policy/SystemGesturesPointerEventListener.java b/services/core/java/com/android/server/policy/SystemGesturesPointerEventListener.java
index 97831d1..4ae3154 100644
--- a/services/core/java/com/android/server/policy/SystemGesturesPointerEventListener.java
+++ b/services/core/java/com/android/server/policy/SystemGesturesPointerEventListener.java
@@ -17,9 +17,14 @@
 package com.android.server.policy;
 
 import android.content.Context;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.SystemClock;
 import android.util.Slog;
+import android.view.GestureDetector;
 import android.view.MotionEvent;
 import android.view.WindowManagerPolicy.PointerEventListener;
+import android.widget.OverScroller;
 
 /*
  * Listens for system-wide input gestures, firing callbacks when detected.
@@ -31,12 +36,14 @@
     private static final long SWIPE_TIMEOUT_MS = 500;
     private static final int MAX_TRACKED_POINTERS = 32;  // max per input system
     private static final int UNTRACKED_POINTER = -1;
+    private static final int MAX_FLING_TIME_MILLIS = 5000;
 
     private static final int SWIPE_NONE = 0;
     private static final int SWIPE_FROM_TOP = 1;
     private static final int SWIPE_FROM_BOTTOM = 2;
     private static final int SWIPE_FROM_RIGHT = 3;
 
+    private final Context mContext;
     private final int mSwipeStartThreshold;
     private final int mSwipeDistanceThreshold;
     private final Callbacks mCallbacks;
@@ -45,14 +52,19 @@
     private final float[] mDownY = new float[MAX_TRACKED_POINTERS];
     private final long[] mDownTime = new long[MAX_TRACKED_POINTERS];
 
+    private GestureDetector mGestureDetector;
+    private OverScroller mOverscroller;
+
     int screenHeight;
     int screenWidth;
     private int mDownPointers;
     private boolean mSwipeFireable;
     private boolean mDebugFireable;
     private boolean mMouseHoveringAtEdge;
+    private long mLastFlingTime;
 
     public SystemGesturesPointerEventListener(Context context, Callbacks callbacks) {
+        mContext = context;
         mCallbacks = checkNull("callbacks", callbacks);
         mSwipeStartThreshold = checkNull("context", context).getResources()
                 .getDimensionPixelSize(com.android.internal.R.dimen.status_bar_height);
@@ -68,8 +80,17 @@
         return arg;
     }
 
+    public void systemReady() {
+        Handler h = new Handler(Looper.myLooper());
+        mGestureDetector = new GestureDetector(mContext, new FlingGestureDetector(), h);
+        mOverscroller = new OverScroller(mContext);
+    }
+
     @Override
     public void onPointerEvent(MotionEvent event) {
+        if (mGestureDetector != null) {
+            mGestureDetector.onTouchEvent(event);
+        }
         switch (event.getActionMasked()) {
             case MotionEvent.ACTION_DOWN:
                 mSwipeFireable = true;
@@ -211,10 +232,40 @@
         return SWIPE_NONE;
     }
 
+    private final class FlingGestureDetector extends GestureDetector.SimpleOnGestureListener {
+        @Override
+        public boolean onSingleTapUp(MotionEvent e) {
+            if (!mOverscroller.isFinished()) {
+                mOverscroller.forceFinished(true);
+            }
+            return true;
+        }
+        @Override
+        public boolean onFling(MotionEvent down, MotionEvent up,
+                float velocityX, float velocityY) {
+            mOverscroller.computeScrollOffset();
+            long now = SystemClock.uptimeMillis();
+
+            if (mLastFlingTime != 0 && now > mLastFlingTime + MAX_FLING_TIME_MILLIS) {
+                mOverscroller.forceFinished(true);
+            }
+            mOverscroller.fling(0, 0, (int)velocityX, (int)velocityY,
+                    Integer.MIN_VALUE, Integer.MAX_VALUE, Integer.MIN_VALUE, Integer.MAX_VALUE);
+            int duration = mOverscroller.getDuration();
+            if (duration > MAX_FLING_TIME_MILLIS) {
+                duration = MAX_FLING_TIME_MILLIS;
+            }
+            mLastFlingTime = now;
+            mCallbacks.onFling(duration);
+            return true;
+        }
+    }
+
     interface Callbacks {
         void onSwipeFromTop();
         void onSwipeFromBottom();
         void onSwipeFromRight();
+        void onFling(int durationMs);
         void onDown();
         void onUpOrCancel();
         void onMouseHoverAtTop();
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/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java
index 4e702aa..a0a31c0 100644
--- a/services/core/java/com/android/server/power/PowerManagerService.java
+++ b/services/core/java/com/android/server/power/PowerManagerService.java
@@ -76,6 +76,7 @@
 
 import libcore.util.Objects;
 
+import static android.os.PowerManagerInternal.POWER_HINT_INTERACTION;
 import static android.os.PowerManagerInternal.WAKEFULNESS_ASLEEP;
 import static android.os.PowerManagerInternal.WAKEFULNESS_AWAKE;
 import static android.os.PowerManagerInternal.WAKEFULNESS_DREAMING;
@@ -150,7 +151,6 @@
     private static final int SCREEN_BRIGHTNESS_BOOST_TIMEOUT = 5 * 1000;
 
     // Power hints defined in hardware/libhardware/include/hardware/power.h.
-    private static final int POWER_HINT_INTERACTION = 2;
     private static final int POWER_HINT_LOW_POWER = 5;
 
     // Power features defined in hardware/libhardware/include/hardware/power.h.
@@ -1577,6 +1577,13 @@
                 }
 
                 if (mUserActivitySummary != USER_ACTIVITY_SCREEN_DREAM && userInactiveOverride) {
+                    if ((mUserActivitySummary &
+                            (USER_ACTIVITY_SCREEN_BRIGHT | USER_ACTIVITY_SCREEN_DIM)) != 0) {
+                      // Device is being kept awake by recent user activity
+                      long savedWakeTimeMs = now - nextTimeout;
+                      EventLog.writeEvent(
+                              EventLogTags.POWER_SOFT_SLEEP_REQUESTED, savedWakeTimeMs);
+                    }
                     mUserActivitySummary = USER_ACTIVITY_SCREEN_DREAM;
                     nextTimeout = -1;
                 }
@@ -3570,5 +3577,10 @@
         public void uidGone(int uid) {
             uidGoneInternal(uid);
         }
+
+        @Override
+        public void powerHint(int hintId, int data) {
+            powerHintInternal(hintId, data);
+        }
     }
 }
diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java b/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java
index 5d01931..25d646d 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java
@@ -28,5 +28,5 @@
     void showScreenPinningRequest();
     void showAssistDisclosure();
     void startAssist(Bundle args);
-    void onCameraLaunchGestureDetected();
+    void onCameraLaunchGestureDetected(int source);
 }
diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
index 87dc6c4..19b03d5 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
@@ -178,10 +178,10 @@
         }
 
         @Override
-        public void onCameraLaunchGestureDetected() {
+        public void onCameraLaunchGestureDetected(int source) {
             if (mBar != null) {
                 try {
-                    mBar.onCameraLaunchGestureDetected();
+                    mBar.onCameraLaunchGestureDetected(source);
                 } catch (RemoteException e) {
                 }
             }
@@ -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/AppTransition.java b/services/core/java/com/android/server/wm/AppTransition.java
index 192b168..a64cda6 100644
--- a/services/core/java/com/android/server/wm/AppTransition.java
+++ b/services/core/java/com/android/server/wm/AppTransition.java
@@ -1003,17 +1003,33 @@
         return prepareThumbnailAnimation(a, appWidth, appHeight, transit);
     }
 
-    private Animation createRelaunchAnimation(int appWidth, int appHeight) {
+    private Animation createRelaunchAnimation(int appWidth, int appHeight,
+            Rect containingFrame) {
         getDefaultNextAppTransitionStartRect(mTmpFromClipRect);
         final int left = mTmpFromClipRect.left;
         final int top = mTmpFromClipRect.top;
         mTmpFromClipRect.offset(-left, -top);
         mTmpToClipRect.set(0, 0, appWidth, appHeight);
         AnimationSet set = new AnimationSet(true);
-        ClipRectAnimation clip = new ClipRectAnimation(mTmpFromClipRect, mTmpToClipRect);
-        TranslateAnimation translate = new TranslateAnimation(left, 0, top, 0);
-        clip.setInterpolator(mDecelerateInterpolator);
-        set.addAnimation(clip);
+        float fromWidth = mTmpFromClipRect.width();
+        float toWidth = mTmpToClipRect.width();
+        float fromHeight = mTmpFromClipRect.height();
+        float toHeight = mTmpToClipRect.height();
+        if (fromWidth <= toWidth && fromHeight <= toHeight) {
+            // The final window is larger in both dimensions than current window (e.g. we are
+            // maximizing), so we can simply unclip the new window and there will be no disappearing
+            // frame.
+            set.addAnimation(new ClipRectAnimation(mTmpFromClipRect, mTmpToClipRect));
+        } else {
+            // The disappearing window has one larger dimension. We need to apply scaling, so the
+            // first frame of the entry animation matches the old window.
+            set.addAnimation(new ScaleAnimation(fromWidth / toWidth, 1, fromHeight / toHeight, 1));
+        }
+
+        // We might not be going exactly full screen, but instead be aligned under the status bar.
+        // We need to take this into account when creating the translate animation.
+        TranslateAnimation translate = new TranslateAnimation(left - containingFrame.left,
+                0, top - containingFrame.top, 0);
         set.addAnimation(translate);
         set.setDuration(DEFAULT_APP_TRANSITION_DURATION);
         return set;
@@ -1056,7 +1072,12 @@
                     + " anim=" + a + " transit=" + appTransitionToString(transit)
                     + " isEntrance=" + enter + " Callers=" + Debug.getCallers(3));
         } else if (transit == TRANSIT_ACTIVITY_RELAUNCH) {
-            a = createRelaunchAnimation(appWidth, appHeight);
+            a = createRelaunchAnimation(appWidth, appHeight, containingFrame);
+            if (DEBUG_APP_TRANSITIONS || DEBUG_ANIM) Slog.v(TAG,
+                    "applyAnimation:"
+                    + " anim=" + a + " nextAppTransition=" + mNextAppTransition
+                    + " transit=" + appTransitionToString(transit)
+                    + " Callers=" + Debug.getCallers(3));
         } else if (mNextAppTransitionType == NEXT_TRANSIT_TYPE_CUSTOM) {
             a = loadAnimationRes(mNextAppTransitionPackage, enter ?
                     mNextAppTransitionEnter : mNextAppTransitionExit);
@@ -1077,6 +1098,7 @@
             if (DEBUG_APP_TRANSITIONS || DEBUG_ANIM) Slog.v(TAG,
                     "applyAnimation:"
                             + " anim=" + a + " nextAppTransition=ANIM_CLIP_REVEAL"
+                            + " transit=" + appTransitionToString(transit)
                             + " Callers=" + Debug.getCallers(3));
         } else if (mNextAppTransitionType == NEXT_TRANSIT_TYPE_SCALE_UP) {
             a = createScaleUpAnimationLocked(transit, enter, appWidth, appHeight);
diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java
index 4f62909..5a1ed58 100644
--- a/services/core/java/com/android/server/wm/AppWindowToken.java
+++ b/services/core/java/com/android/server/wm/AppWindowToken.java
@@ -74,10 +74,6 @@
     // case do not clear allDrawn until the animation completes.
     boolean deferClearAllDrawn;
 
-    // Is this token going to be hidden in a little while?  If so, it
-    // won't be taken into account for setting the screen orientation.
-    boolean willBeHidden;
-
     // Is this window's surface needed?  This is almost like hidden, except
     // it will sometimes be true a little earlier: when the token has
     // been shown, but is still waiting for its app transition to execute
@@ -112,6 +108,9 @@
     boolean mLaunchTaskBehind;
     boolean mEnteringAnimation;
 
+    // True if the windows associated with this token should be cropped to their stack bounds.
+    boolean mCropWindowsToStack;
+
     // This application will have its window replaced due to relaunch. This allows window manager
     // to differentiate between simple removal of a window and replacement. In the latter case it
     // will preserve the old window until the new one is drawn.
@@ -318,7 +317,6 @@
                 pw.print(" requestedOrientation="); pw.println(requestedOrientation);
         pw.print(prefix); pw.print("hiddenRequested="); pw.print(hiddenRequested);
                 pw.print(" clientHidden="); pw.print(clientHidden);
-                pw.print(" willBeHidden="); pw.print(willBeHidden);
                 pw.print(" reportedDrawn="); pw.print(reportedDrawn);
                 pw.print(" reportedVisible="); pw.println(reportedVisible);
         if (paused) {
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index bfeddb3..ede377d 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -16,12 +16,14 @@
 
 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;
 import static com.android.server.wm.WindowManagerService.TAG;
+import static com.android.server.wm.WindowState.RESIZE_HANDLE_WIDTH_IN_DP;
+import static com.android.server.wm.WindowState.BOUNDS_FOR_TOUCH;
 
-import android.content.res.Configuration;
 import android.graphics.Rect;
 import android.graphics.Region;
 import android.util.DisplayMetrics;
@@ -72,6 +74,7 @@
     boolean mDisplayScalingDisabled;
     private final DisplayInfo mDisplayInfo = new DisplayInfo();
     private final Display mDisplay;
+    private final DisplayMetrics mDisplayMetrics = new DisplayMetrics();
 
     Rect mBaseDisplayRect = new Rect();
     Rect mContentRect = new Rect();
@@ -82,11 +85,11 @@
     final boolean isDefaultDisplay;
 
     /** Window tokens that are in the process of exiting, but still on screen for animations. */
-    final ArrayList<WindowToken> mExitingTokens = new ArrayList<WindowToken>();
+    final ArrayList<WindowToken> mExitingTokens = new ArrayList<>();
 
     /** Array containing all TaskStacks on this display.  Array
      * is stored in display order with the current bottom stack at 0. */
-    private final ArrayList<TaskStack> mStacks = new ArrayList<TaskStack>();
+    private final ArrayList<TaskStack> mStacks = new ArrayList<>();
 
     /** A special TaskStack with id==HOME_STACK_ID that moves to the bottom whenever any TaskStack
      * (except a future lockscreen TaskStack) moves to the top. */
@@ -99,7 +102,8 @@
     Region mTouchExcludeRegion = new Region();
 
     /** Save allocating when calculating rects */
-    Rect mTmpRect = new Rect();
+    private Rect mTmpRect = new Rect();
+    private Rect mTmpRect2 = new Rect();
 
     /** For gathering Task objects in order. */
     final ArrayList<Task> mTmpTaskHistory = new ArrayList<Task>();
@@ -109,6 +113,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.
@@ -117,8 +123,10 @@
         mDisplay = display;
         mDisplayId = display.getDisplayId();
         display.getDisplayInfo(mDisplayInfo);
+        display.getMetrics(mDisplayMetrics);
         isDefaultDisplay = mDisplayId == Display.DEFAULT_DISPLAY;
         mService = service;
+        mDividerControllerLocked = new DockedStackDividerController(service.mContext, this);
     }
 
     int getDisplayId() {
@@ -137,6 +145,10 @@
         return mDisplayInfo;
     }
 
+    DisplayMetrics getDisplayMetrics() {
+        return mDisplayMetrics;
+    }
+
     /**
      * Returns true if the specified UID has access to this display.
      */
@@ -174,6 +186,7 @@
 
     void updateDisplayInfo() {
         mDisplay.getDisplayInfo(mDisplayInfo);
+        mDisplay.getMetrics(mDisplayMetrics);
         for (int i = mStacks.size() - 1; i >= 0; --i) {
             mStacks.get(i).updateDisplayInfo(null);
         }
@@ -243,11 +256,11 @@
     }
 
     /**
-     * Find the id of the task whose outside touch area (for resizing) (x, y)
-     * falls within. Returns -1 if the touch doesn't fall into a resizing area.
+     * Find the window whose outside touch area (for resizing) (x, y) falls within.
+     * Returns null if the touch doesn't fall into a resizing area.
      */
-    int taskIdForControlPoint(int x, int y) {
-        final int delta = calculatePixelFromDp(WindowState.RESIZE_HANDLE_WIDTH_IN_DP);
+    WindowState findWindowForControlPoint(int x, int y) {
+        final int delta = mService.dipToPixel(RESIZE_HANDLE_WIDTH_IN_DP, mDisplayMetrics);
         for (int stackNdx = mStacks.size() - 1; stackNdx >= 0; --stackNdx) {
             TaskStack stack = mStacks.get(stackNdx);
             if (!stack.allowTaskResize()) {
@@ -257,38 +270,40 @@
             for (int taskNdx = tasks.size() - 1; taskNdx >= 0; --taskNdx) {
                 final Task task = tasks.get(taskNdx);
                 if (task.isFullscreen()) {
-                    return -1;
+                    return null;
                 }
-                task.getBounds(mTmpRect);
-                mTmpRect.inset(-delta, -delta);
-                if (mTmpRect.contains(x, y)) {
-                    mTmpRect.inset(delta, delta);
-                    if (!mTmpRect.contains(x, y)) {
-                        return task.mTaskId;
+
+                // We need to use the visible frame on the window for any touch-related
+                // tests. Can't use the task's bounds because the original task bounds
+                // might be adjusted to fit the content frame. (One example is when the
+                // task is put to top-left quadrant, the actual visible frame would not
+                // start at (0,0) after it's adjusted for the status bar.)
+                WindowState win = task.getTopAppMainWindow();
+                if (win != null) {
+                    win.getVisibleBounds(mTmpRect, !BOUNDS_FOR_TOUCH);
+                    mTmpRect.inset(-delta, -delta);
+                    if (mTmpRect.contains(x, y)) {
+                        mTmpRect.inset(delta, delta);
+                        if (!mTmpRect.contains(x, y)) {
+                            return win;
+                        }
+                        // User touched inside the task. No need to look further,
+                        // focus transfer will be handled in ACTION_UP.
+                        return null;
                     }
-                    // User touched inside the task. No need to look further,
-                    // focus transfer will be handled in ACTION_UP.
-                    return -1;
                 }
             }
         }
-        return -1;
-    }
-
-    private int calculatePixelFromDp(int dp) {
-        final Configuration serviceConfig = mService.mCurConfiguration;
-        // TODO(multidisplay): Update Dp to that of display stack is on.
-        final float density = serviceConfig.densityDpi * DisplayMetrics.DENSITY_DEFAULT_SCALE;
-        return (int)(dp * density);
+        return null;
     }
 
     void setTouchExcludeRegion(Task focusedTask) {
         mTouchExcludeRegion.set(mBaseDisplayRect);
         WindowList windows = getWindowList();
-        final int delta = calculatePixelFromDp(WindowState.RESIZE_HANDLE_WIDTH_IN_DP);
+        final int delta = mService.dipToPixel(RESIZE_HANDLE_WIDTH_IN_DP, mDisplayMetrics);
         for (int i = windows.size() - 1; i >= 0; --i) {
             final WindowState win = windows.get(i);
-            final Task task = win.mAppToken != null ? win.getTask() : null;
+            final Task task = win.getTask();
             if (win.isVisibleLw() && task != null) {
                 /**
                  * Exclusion region is the region that TapDetector doesn't care about.
@@ -442,6 +457,35 @@
         }
     }
 
+    void rotateBounds(int oldRotation, int newRotation, Rect bounds) {
+        final int rotationDelta = DisplayContent.deltaRotation(oldRotation, newRotation);
+        getLogicalDisplayRect(mTmpRect);
+        switch (rotationDelta) {
+            case Surface.ROTATION_0:
+                mTmpRect2.set(bounds);
+                break;
+            case Surface.ROTATION_90:
+                mTmpRect2.top = mTmpRect.bottom - bounds.right;
+                mTmpRect2.left = bounds.top;
+                mTmpRect2.right = mTmpRect2.left + bounds.height();
+                mTmpRect2.bottom = mTmpRect2.top + bounds.width();
+                break;
+            case Surface.ROTATION_180:
+                mTmpRect2.top = mTmpRect.bottom - bounds.bottom;
+                mTmpRect2.left = mTmpRect.right - bounds.right;
+                mTmpRect2.right = mTmpRect2.left + bounds.width();
+                mTmpRect2.bottom = mTmpRect2.top + bounds.height();
+                break;
+            case Surface.ROTATION_270:
+                mTmpRect2.top = bounds.left;
+                mTmpRect2.left = mTmpRect.right - bounds.bottom;
+                mTmpRect2.right = mTmpRect2.left + bounds.height();
+                mTmpRect2.bottom = mTmpRect2.top + bounds.width();
+                break;
+        }
+        bounds.set(mTmpRect2);
+    }
+
     static int deltaRotation(int oldRotation, int newRotation) {
         int delta = newRotation - oldRotation;
         if (delta < 0) delta += 4;
@@ -522,4 +566,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/DragState.java b/services/core/java/com/android/server/wm/DragState.java
index 8f77176..3521682 100644
--- a/services/core/java/com/android/server/wm/DragState.java
+++ b/services/core/java/com/android/server/wm/DragState.java
@@ -27,6 +27,7 @@
 import android.graphics.Point;
 import android.graphics.Rect;
 import android.graphics.Region;
+import android.os.Binder;
 import android.os.IBinder;
 import android.os.Message;
 import android.os.Process;
@@ -34,6 +35,7 @@
 import android.util.Slog;
 import android.view.Display;
 import android.view.DragEvent;
+import android.view.DropPermissionHolder;
 import android.view.InputChannel;
 import android.view.SurfaceControl;
 import android.view.View;
@@ -50,6 +52,7 @@
     SurfaceControl mSurfaceControl;
     int mFlags;
     IBinder mLocalWin;
+    int mUid;
     ClipData mData;
     ClipDescription mDataDescription;
     boolean mDragResult;
@@ -74,6 +77,7 @@
         mSurfaceControl = surface;
         mFlags = flags;
         mLocalWin = localWin;
+        mUid = Binder.getCallingUid();
         mNotifiedWindows = new ArrayList<WindowState>();
     }
 
@@ -225,7 +229,7 @@
 
         if (mDragInProgress && newWin.isPotentialDragTarget()) {
             DragEvent event = obtainDragEvent(newWin, DragEvent.ACTION_DRAG_STARTED,
-                    touchX, touchY, null, desc, null, false);
+                    touchX, touchY, null, desc, null, null, false);
             try {
                 newWin.mClient.dispatchDragEvent(event);
                 // track each window that we've notified that the drag is starting
@@ -265,7 +269,7 @@
             Slog.d(WindowManagerService.TAG, "broadcasting DRAG_ENDED");
         }
         DragEvent evt = DragEvent.obtain(DragEvent.ACTION_DRAG_ENDED,
-                0, 0, null, null, null, mDragResult);
+                0, 0, null, null, null, null, mDragResult);
         for (WindowState ws: mNotifiedWindows) {
             try {
                 ws.mClient.dispatchDragEvent(evt);
@@ -331,7 +335,7 @@
                 }
                 // force DRAG_EXITED_EVENT if appropriate
                 DragEvent evt = obtainDragEvent(mTargetWindow, DragEvent.ACTION_DRAG_EXITED,
-                        x, y, null, null, null, false);
+                        x, y, null, null, null, null, false);
                 mTargetWindow.mClient.dispatchDragEvent(evt);
                 if (myPid != mTargetWindow.mSession.mPid) {
                     evt.recycle();
@@ -342,7 +346,7 @@
                     Slog.d(WindowManagerService.TAG, "sending DRAG_LOCATION to " + touchedWin);
                 }
                 DragEvent evt = obtainDragEvent(touchedWin, DragEvent.ACTION_DRAG_LOCATION,
-                        x, y, null, null, null, false);
+                        x, y, null, null, null, null, false);
                 touchedWin.mClient.dispatchDragEvent(evt);
                 if (myPid != touchedWin.mSession.mPid) {
                     evt.recycle();
@@ -354,11 +358,15 @@
         mTargetWindow = touchedWin;
     }
 
+    WindowState getDropTargetWinLw(float x, float y) {
+        return getTouchedWinAtPointLw(x, y);
+    }
+
     // Tell the drop target about the data.  Returns 'true' if we can immediately
     // dispatch the global drag-ended message, 'false' if we need to wait for a
     // result from the recipient.
-    boolean notifyDropLw(float x, float y) {
-        WindowState touchedWin = getTouchedWinAtPointLw(x, y);
+    boolean notifyDropLw(WindowState touchedWin, DropPermissionHolder dropPermissionHolder,
+            float x, float y) {
         if (touchedWin == null) {
             // "drop" outside a valid window -- no recipient to apply a
             // timeout to, and we can send the drag-ended message immediately.
@@ -372,7 +380,7 @@
         final int myPid = Process.myPid();
         final IBinder token = touchedWin.mClient.asBinder();
         DragEvent evt = obtainDragEvent(touchedWin, DragEvent.ACTION_DROP, x, y,
-                null, null, mData, false);
+                null, null, mData, dropPermissionHolder, false);
         try {
             touchedWin.mClient.dispatchDragEvent(evt);
 
@@ -415,7 +423,7 @@
                 continue;
             }
 
-            child.getTaskBounds(mTmpRect, !BOUNDS_FOR_TOUCH);
+            child.getVisibleBounds(mTmpRect, !BOUNDS_FOR_TOUCH);
             if (!mTmpRect.contains(x, y)) {
                 // outside of this window's activity stack == don't tell about drags
                 continue;
@@ -438,13 +446,16 @@
 
     private static DragEvent obtainDragEvent(WindowState win, int action,
             float x, float y, Object localState,
-            ClipDescription description, ClipData data, boolean result) {
+            ClipDescription description, ClipData data,
+            DropPermissionHolder dropPermissionHolder,
+            boolean result) {
         float winX = x - win.mFrame.left;
         float winY = y - win.mFrame.top;
         if (win.mEnforceSizeCompat) {
             winX *= win.mGlobalScale;
             winY *= win.mGlobalScale;
         }
-        return DragEvent.obtain(action, winX, winY, localState, description, data, result);
+        return DragEvent.obtain(action, winX, winY, localState, description, data,
+                dropPermissionHolder, result);
     }
 }
\ No newline at end of file
diff --git a/services/core/java/com/android/server/wm/InputMonitor.java b/services/core/java/com/android/server/wm/InputMonitor.java
index 9adcafc..4244205 100644
--- a/services/core/java/com/android/server/wm/InputMonitor.java
+++ b/services/core/java/com/android/server/wm/InputMonitor.java
@@ -178,7 +178,7 @@
         if (modal && child.mAppToken != null) {
             // Limit the outer touch to the activity stack region.
             flags |= WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
-            child.getTaskBounds(mTmpRect, BOUNDS_FOR_TOUCH);
+            child.getVisibleBounds(mTmpRect, BOUNDS_FOR_TOUCH);
             inputWindowHandle.touchableRegion.set(mTmpRect);
         } else {
             // Not modal or full screen modal
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index cc9efdb..d1111f7 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;
@@ -72,6 +73,9 @@
     // For handling display rotations.
     private Rect mTmpRect2 = new Rect();
 
+    // Whether the task is currently being drag-resized
+    private boolean mDragResizing;
+
     // The particular window with FLAG_DIM_BEHIND set. If null, hide mDimLayer.
     WindowStateAnimator mDimWinAnimator;
     // Used to support {@link android.view.WindowManager.LayoutParams#FLAG_DIM_BEHIND}
@@ -227,10 +231,32 @@
         return boundsChange;
     }
 
+    boolean resizeLocked(Rect bounds, Configuration configuration, boolean forced) {
+        int boundsChanged = setBounds(bounds, configuration);
+        if (forced) {
+            boundsChanged |= BOUNDS_CHANGE_SIZE;
+        }
+        if (boundsChanged == BOUNDS_CHANGE_NONE) {
+            return false;
+        }
+        if ((boundsChanged & BOUNDS_CHANGE_SIZE) == BOUNDS_CHANGE_SIZE) {
+            resizeWindows();
+        }
+        return true;
+    }
+
     void getBounds(Rect out) {
         out.set(mBounds);
     }
 
+    void setDragResizing(boolean dragResizing) {
+        mDragResizing = dragResizing;
+    }
+
+    boolean isDragResizing() {
+        return mDragResizing;
+    }
+
     void updateDisplayInfo(final DisplayContent displayContent) {
         if (displayContent == null) {
             return;
@@ -246,31 +272,8 @@
 
         // Device rotation changed. We don't want the task to move around on the screen when
         // this happens, so update the task bounds so it stays in the same place.
-        final int rotationDelta = DisplayContent.deltaRotation(mRotation, newRotation);
-        displayContent.getLogicalDisplayRect(mTmpRect);
-        switch (rotationDelta) {
-            case Surface.ROTATION_0:
-                mTmpRect2.set(mBounds);
-                break;
-            case Surface.ROTATION_90:
-                mTmpRect2.top = mTmpRect.bottom - mBounds.right;
-                mTmpRect2.left = mBounds.top;
-                mTmpRect2.right = mTmpRect2.left + mBounds.height();
-                mTmpRect2.bottom = mTmpRect2.top + mBounds.width();
-                break;
-            case Surface.ROTATION_180:
-                mTmpRect2.top = mTmpRect.bottom - mBounds.bottom;
-                mTmpRect2.left = mTmpRect.right - mBounds.right;
-                mTmpRect2.right = mTmpRect2.left + mBounds.width();
-                mTmpRect2.bottom = mTmpRect2.top + mBounds.height();
-                break;
-            case Surface.ROTATION_270:
-                mTmpRect2.top = mBounds.left;
-                mTmpRect2.left = mTmpRect.right - mBounds.bottom;
-                mTmpRect2.right = mTmpRect2.left + mBounds.height();
-                mTmpRect2.bottom = mTmpRect2.top + mBounds.width();
-                break;
-        }
+        mTmpRect2.set(mBounds);
+        displayContent.rotateBounds(mRotation, newRotation, mTmpRect2);
         setBounds(mTmpRect2, mOverrideConfig);
     }
 
@@ -430,6 +433,15 @@
         return mStack != null && mStack.mStackId == FREEFORM_WORKSPACE_STACK_ID;
     }
 
+    boolean inDockedWorkspace() {
+        return mStack != null && mStack.mStackId == DOCKED_STACK_ID;
+    }
+
+    WindowState getTopAppMainWindow() {
+        final int tokensCount = mAppTokens.size();
+        return tokensCount > 0 ? mAppTokens.get(tokensCount - 1).findMainWindow() : null;
+    }
+
     @Override
     public boolean isFullscreen() {
         return mFullscreen;
diff --git a/services/core/java/com/android/server/wm/TaskPositioner.java b/services/core/java/com/android/server/wm/TaskPositioner.java
index 8d377fd..df2e5e8 100644
--- a/services/core/java/com/android/server/wm/TaskPositioner.java
+++ b/services/core/java/com/android/server/wm/TaskPositioner.java
@@ -19,9 +19,14 @@
 import static android.app.ActivityManager.DOCKED_STACK_CREATE_MODE_BOTTOM_OR_RIGHT;
 import static android.app.ActivityManager.DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT;
 import static android.app.ActivityManager.FREEFORM_WORKSPACE_STACK_ID;
+import static android.app.ActivityManager.RESIZE_MODE_FORCED;
+import static android.app.ActivityManager.RESIZE_MODE_USER;
 import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
+import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
 import static com.android.server.wm.WindowManagerService.DEBUG_TASK_POSITIONING;
 import static com.android.server.wm.WindowManagerService.SHOW_TRANSACTIONS;
+import static com.android.server.wm.WindowState.MINIMUM_VISIBLE_HEIGHT_IN_DP;
+import static com.android.server.wm.WindowState.MINIMUM_VISIBLE_WIDTH_IN_DP;
 
 import android.annotation.IntDef;
 import android.graphics.Point;
@@ -29,15 +34,16 @@
 import android.os.Looper;
 import android.os.Process;
 import android.os.RemoteException;
+import android.os.Trace;
 import android.util.DisplayMetrics;
 import android.util.Slog;
-import android.util.TypedValue;
+import android.view.Choreographer;
 import android.view.Display;
 import android.view.DisplayInfo;
 import android.view.InputChannel;
 import android.view.InputDevice;
 import android.view.InputEvent;
-import android.view.InputEventReceiver;
+import android.view.BatchedInputEventReceiver;
 import android.view.MotionEvent;
 import android.view.SurfaceControl;
 import android.view.WindowManager;
@@ -82,9 +88,10 @@
     private int mCurrentDimSide;
     private Rect mTmpRect = new Rect();
     private int mSideMargin;
+    private int mMinVisibleWidth;
+    private int mMinVisibleHeight;
 
-    private int mTaskId;
-    private TaskStack mStack;
+    private Task mTask;
     private boolean mResizing;
     private final Rect mWindowOriginalBounds = new Rect();
     private final Rect mWindowDragBounds = new Rect();
@@ -92,15 +99,17 @@
     private float mStartDragY;
     @CtrlType
     private int mCtrlType = CTRL_NONE;
+    private boolean mDragEnded = false;
 
     InputChannel mServerChannel;
     InputChannel mClientChannel;
     InputApplicationHandle mDragApplicationHandle;
     InputWindowHandle mDragWindowHandle;
 
-    private final class WindowPositionerEventReceiver extends InputEventReceiver {
-        public WindowPositionerEventReceiver(InputChannel inputChannel, Looper looper) {
-            super(inputChannel, looper);
+    private final class WindowPositionerEventReceiver extends BatchedInputEventReceiver {
+        public WindowPositionerEventReceiver(
+                InputChannel inputChannel, Looper looper, Choreographer choreographer) {
+            super(inputChannel, looper, choreographer);
         }
 
         @Override
@@ -113,7 +122,14 @@
             boolean handled = false;
 
             try {
-                boolean endDrag = false;
+                if (mDragEnded) {
+                    // The drag has ended but the clean-up message has not been processed by
+                    // window manager. Drop events that occur after this until window manager
+                    // has a chance to clean-up the input handle.
+                    handled = true;
+                    return;
+                }
+
                 final float newX = motionEvent.getRawX();
                 final float newY = motionEvent.getRawY();
 
@@ -129,45 +145,55 @@
                             Slog.w(TAG, "ACTION_MOVE @ {" + newX + ", " + newY + "}");
                         }
                         synchronized (mService.mWindowMap) {
-                            notifyMoveLocked(newX, newY);
+                            mDragEnded = notifyMoveLocked(newX, newY);
                         }
+                        Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "wm.TaskPositioner.resizeTask");
                         try {
                             mService.mActivityManager.resizeTask(
-                                    mTaskId, mWindowDragBounds, true /* resizedByUser */);
+                                    mTask.mTaskId, mWindowDragBounds, RESIZE_MODE_USER);
                         } catch(RemoteException e) {}
+                        Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
                     } break;
 
                     case MotionEvent.ACTION_UP: {
                         if (DEBUG_TASK_POSITIONING) {
                             Slog.w(TAG, "ACTION_UP @ {" + newX + ", " + newY + "}");
                         }
-                        endDrag = true;
+                        mDragEnded = true;
                     } break;
 
                     case MotionEvent.ACTION_CANCEL: {
                         if (DEBUG_TASK_POSITIONING) {
                             Slog.w(TAG, "ACTION_CANCEL @ {" + newX + ", " + newY + "}");
                         }
-                        endDrag = true;
+                        mDragEnded = true;
                     } break;
                 }
 
-                if (endDrag) {
-                    mResizing = false;
+                if (mDragEnded) {
+                    synchronized (mService.mWindowMap) {
+                        endDragLocked();
+                    }
                     try {
-                        mService.mActivityManager.resizeTask(
-                                mTaskId, mWindowDragBounds, true /* resizedByUser */);
+                        if (mResizing) {
+                            // We were using fullscreen surface during resizing. Request
+                            // resizeTask() one last time to restore surface to window size.
+                            mService.mActivityManager.resizeTask(
+                                    mTask.mTaskId, mWindowDragBounds, RESIZE_MODE_FORCED);
+                        }
+
+                        if (mCurrentDimSide != CTRL_NONE) {
+                            final int createMode = mCurrentDimSide == CTRL_LEFT
+                                    ? DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT
+                                    : DOCKED_STACK_CREATE_MODE_BOTTOM_OR_RIGHT;
+                            mService.mActivityManager.moveTaskToDockedStack(
+                                    mTask.mTaskId, createMode, true /*toTop*/);
+                        }
                     } catch(RemoteException e) {}
+
                     // Post back to WM to handle clean-ups. We still need the input
                     // event handler for the last finishInputEvent()!
                     mService.mH.sendEmptyMessage(H.FINISH_TASK_POSITIONING);
-                    if (mCurrentDimSide != CTRL_NONE) {
-                        final int createMode = mCurrentDimSide == CTRL_LEFT
-                                ? DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT
-                                : DOCKED_STACK_CREATE_MODE_BOTTOM_OR_RIGHT;
-                        mService.mActivityManager.moveTaskToDockedStack(
-                                mTaskId, createMode, true /*toTop*/);
-                    }
                 }
                 handled = true;
             } catch (Exception e) {
@@ -202,8 +228,8 @@
         mClientChannel = channels[1];
         mService.mInputManager.registerInputChannel(mServerChannel, null);
 
-        mInputEventReceiver = new WindowPositionerEventReceiver(mClientChannel,
-                mService.mH.getLooper());
+        mInputEventReceiver = new WindowPositionerEventReceiver(
+                mClientChannel, mService.mH.getLooper(), mService.mChoreographer);
 
         mDragApplicationHandle = new InputApplicationHandle(null);
         mDragApplicationHandle.name = TAG;
@@ -247,7 +273,11 @@
         mService.pauseRotationLocked();
 
         mDimLayer = new DimLayer(mService, this, mDisplay.getDisplayId());
-        mSideMargin = (int)dipToPx(SIDE_MARGIN_DIP);
+        mSideMargin = mService.dipToPixel(SIDE_MARGIN_DIP, mDisplayMetrics);
+        mMinVisibleWidth = mService.dipToPixel(MINIMUM_VISIBLE_WIDTH_IN_DP, mDisplayMetrics);
+        mMinVisibleHeight = mService.dipToPixel(MINIMUM_VISIBLE_HEIGHT_IN_DP, mDisplayMetrics);
+
+        mDragEnded = false;
     }
 
     void unregister() {
@@ -278,6 +308,7 @@
             mDimLayer = null;
         }
         mCurrentDimSide = CTRL_NONE;
+        mDragEnded = true;
 
         // Resume rotations after a drag.
         if (WindowManagerService.DEBUG_ORIENTATION) {
@@ -286,10 +317,6 @@
         mService.resumeRotationLocked();
     }
 
-    boolean isTaskResizing(final Task task) {
-        return mResizing && task != null && mTaskId == task.mTaskId;
-    }
-
     void startDragLocked(WindowState win, boolean resize, float startX, float startY) {
         if (DEBUG_TASK_POSITIONING) {
             Slog.d(TAG, "startDragLocked: win=" + win + ", resize=" + resize
@@ -313,16 +340,20 @@
             mResizing = true;
         }
 
-        final Task task = win.getTask();
-        mTaskId = task.mTaskId;
-        mStack = task.mStack;
+        mTask = win.getTask();
         mStartDragX = startX;
         mStartDragY = startY;
 
-        mService.getTaskBounds(mTaskId, mWindowOriginalBounds);
+        mService.getTaskBounds(mTask.mTaskId, mWindowOriginalBounds);
     }
 
-    private void notifyMoveLocked(float x, float y) {
+    private void endDragLocked() {
+        mResizing = false;
+        mTask.setDragResizing(false);
+    }
+
+    /** Returns true if the move operation should be ended. */
+    private boolean notifyMoveLocked(float x, float y) {
         if (DEBUG_TASK_POSITIONING) {
             Slog.d(TAG, "notifyMoveLw: {" + x + "," + y + "}");
         }
@@ -331,33 +362,38 @@
             // This is a resizing operation.
             final int deltaX = Math.round(x - mStartDragX);
             final int deltaY = Math.round(y - mStartDragY);
-            // TODO: fix the min sizes when we have mininum width/height support,
-            //       use hard-coded min sizes for now.
-            final int minSizeX = (int)(dipToPx(96));
-            final int minSizeY = (int)(dipToPx(64));
             int left = mWindowOriginalBounds.left;
             int top = mWindowOriginalBounds.top;
             int right = mWindowOriginalBounds.right;
             int bottom = mWindowOriginalBounds.bottom;
             if ((mCtrlType & CTRL_LEFT) != 0) {
-                left = Math.min(left + deltaX, right - minSizeX);
+                left = Math.min(left + deltaX, right - mMinVisibleWidth);
             }
             if ((mCtrlType & CTRL_TOP) != 0) {
-                top = Math.min(top + deltaY, bottom - minSizeY);
+                top = Math.min(top + deltaY, bottom - mMinVisibleHeight);
             }
             if ((mCtrlType & CTRL_RIGHT) != 0) {
-                right = Math.max(left + minSizeX, right + deltaX);
+                right = Math.max(left + mMinVisibleWidth, right + deltaX);
             }
             if ((mCtrlType & CTRL_BOTTOM) != 0) {
-                bottom = Math.max(top + minSizeY, bottom + deltaY);
+                bottom = Math.max(top + mMinVisibleHeight, bottom + deltaY);
             }
             mWindowDragBounds.set(left, top, right, bottom);
-        } else {
-            // This is a moving operation.
-            mWindowDragBounds.set(mWindowOriginalBounds);
-            mWindowDragBounds.offset(Math.round(x - mStartDragX), Math.round(y - mStartDragY));
-            updateDimLayerVisibility((int) x);
+            mTask.setDragResizing(true);
+            return false;
         }
+
+        // This is a moving operation.
+        mTask.mStack.getBounds(mTmpRect);
+        mTmpRect.inset(mMinVisibleWidth, mMinVisibleHeight);
+        if (!mTmpRect.contains((int) x, (int) y)) {
+            // We end the moving operation if position is outside the stack bounds.
+            return true;
+        }
+        mWindowDragBounds.set(mWindowOriginalBounds);
+        mWindowDragBounds.offset(Math.round(x - mStartDragX), Math.round(y - mStartDragY));
+        updateDimLayerVisibility((int) x);
+        return false;
     }
 
     private void updateDimLayerVisibility(int x) {
@@ -387,13 +423,13 @@
      * shouldn't be shown.
      */
     private int getDimSide(int x) {
-        if (mStack.mStackId != FREEFORM_WORKSPACE_STACK_ID
-                || !mStack.isFullscreen()
+        if (mTask.mStack.mStackId != FREEFORM_WORKSPACE_STACK_ID
+                || !mTask.mStack.isFullscreen()
                 || mService.mCurConfiguration.orientation != ORIENTATION_LANDSCAPE) {
             return CTRL_NONE;
         }
 
-        mStack.getBounds(mTmpRect);
+        mTask.mStack.getBounds(mTmpRect);
         if (x - mSideMargin <= mTmpRect.left) {
             return CTRL_LEFT;
         }
@@ -405,7 +441,7 @@
     }
 
     private void showDimLayer() {
-        mStack.getBounds(mTmpRect);
+        mTask.mStack.getBounds(mTmpRect);
         if (mCurrentDimSide == CTRL_LEFT) {
             mTmpRect.right = mTmpRect.centerX();
         } else if (mCurrentDimSide == CTRL_RIGHT) {
@@ -423,7 +459,7 @@
 
     @Override /** {@link DimLayer.DimLayerUser} */
     public DisplayInfo getDisplayInfo() {
-        return mStack.getDisplayInfo();
+        return mTask.mStack.getDisplayInfo();
     }
 
     private int getDragLayerLocked() {
@@ -431,8 +467,4 @@
                 * WindowManagerService.TYPE_LAYER_MULTIPLIER
                 + WindowManagerService.TYPE_LAYER_OFFSET;
     }
-
-    private float dipToPx(float dip) {
-        return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dip, mDisplayMetrics);
-    }
 }
diff --git a/services/core/java/com/android/server/wm/TaskStack.java b/services/core/java/com/android/server/wm/TaskStack.java
index 1e6fab6..3357a9c 100644
--- a/services/core/java/com/android/server/wm/TaskStack.java
+++ b/services/core/java/com/android/server/wm/TaskStack.java
@@ -20,6 +20,7 @@
 import static com.android.server.wm.WindowManagerService.DEBUG_TASK_MOVEMENT;
 import static com.android.server.wm.WindowManagerService.TAG;
 
+import android.annotation.IntDef;
 import android.content.res.Configuration;
 import android.graphics.Rect;
 import android.os.Debug;
@@ -29,9 +30,12 @@
 import android.util.SparseArray;
 import android.view.DisplayInfo;
 
+import android.view.Surface;
 import com.android.server.EventLogTags;
 
 import java.io.PrintWriter;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 import java.util.ArrayList;
 
 public class TaskStack implements DimLayer.DimLayerUser {
@@ -54,6 +58,7 @@
 
     /** For comparison with DisplayContent bounds. */
     private Rect mTmpRect = new Rect();
+    private Rect TmpRect2 = new Rect();
 
     /** Content limits relative to the DisplayContent this sits in. */
     private Rect mBounds = new Rect();
@@ -61,6 +66,9 @@
     /** Whether mBounds is fullscreen */
     private boolean mFullscreen = true;
 
+    // Device rotation as of the last time {@link #mBounds} was set.
+    int mRotation;
+
     /** Support for non-zero {@link android.view.animation.Animation#getBackgroundColor()} */
     DimLayer mAnimationBackgroundSurface;
 
@@ -73,6 +81,20 @@
     /** Detach this stack from its display when animation completes. */
     boolean mDeferDetach;
 
+    static final int DOCKED_INVALID = -1;
+    static final int DOCKED_LEFT = 1;
+    static final int DOCKED_TOP = 2;
+    static final int DOCKED_RIGHT = 3;
+    static final int DOCKED_BOTTOM = 4;
+    @IntDef({
+            DOCKED_INVALID,
+            DOCKED_LEFT,
+            DOCKED_TOP,
+            DOCKED_RIGHT,
+            DOCKED_BOTTOM})
+    @Retention(RetentionPolicy.SOURCE)
+    @interface DockSide {}
+
     TaskStack(WindowManagerService service, int stackId) {
         mService = service;
         mStackId = stackId;
@@ -101,30 +123,22 @@
     /**
      * Set the bounds of the stack and its containing tasks.
      * @param stackBounds New stack bounds. Passing in null sets the bounds to fullscreen.
-     * @param resizeTasks If true, the tasks within the stack will also be resized.
      * @param configs Configuration for individual tasks, keyed by task id.
      * @param taskBounds Bounds for individual tasks, keyed by task id.
      * @return True if the stack bounds was changed.
      * */
-    boolean setBounds(Rect stackBounds, boolean resizeTasks, SparseArray<Configuration> configs,
-            SparseArray<Rect> taskBounds) {
+    boolean setBounds(
+            Rect stackBounds, SparseArray<Configuration> configs, SparseArray<Rect> taskBounds) {
         if (!setBounds(stackBounds)) {
             return false;
         }
 
-        if (!resizeTasks) {
-            return true;
-        }
-
         // Update bounds of containing tasks.
         for (int taskNdx = mTasks.size() - 1; taskNdx >= 0; --taskNdx) {
             final Task task = mTasks.get(taskNdx);
             Configuration config = configs.get(task.mTaskId);
             if (config != null) {
                 Rect bounds = taskBounds.get(task.mTaskId);
-                if (bounds == null) {
-                    bounds = stackBounds;
-                }
                 task.setBounds(bounds, config);
             } else {
                 Slog.wtf(TAG, "No config for task: " + task + ", is there a mismatch with AM?");
@@ -135,8 +149,10 @@
 
     private boolean setBounds(Rect bounds) {
         boolean oldFullscreen = mFullscreen;
+        int rotation = Surface.ROTATION_0;
         if (mDisplayContent != null) {
             mDisplayContent.getLogicalDisplayRect(mTmpRect);
+            rotation = mDisplayContent.getDisplayInfo().rotation;
             if (bounds == null) {
                 bounds = mTmpRect;
                 mFullscreen = true;
@@ -154,12 +170,13 @@
             // Can't set to fullscreen if we don't have a display to get bounds from...
             return false;
         }
-        if (mBounds.equals(bounds) && oldFullscreen == mFullscreen) {
+        if (mBounds.equals(bounds) && oldFullscreen == mFullscreen && mRotation == rotation) {
             return false;
         }
 
         mAnimationBackgroundSurface.setBounds(bounds);
         mBounds.set(bounds);
+        mRotation = rotation;
         return true;
     }
 
@@ -171,8 +188,13 @@
         if (mDisplayContent != null) {
             if (bounds != null) {
                 setBounds(bounds);
+            } else if (mFullscreen) {
+                setBounds(null);
             } else {
-                setBounds(mFullscreen ? null : mBounds);
+                TmpRect2.set(mBounds);
+                mDisplayContent.rotateBounds(
+                        mRotation, mDisplayContent.getDisplayInfo().rotation, TmpRect2);
+                setBounds(TmpRect2);
             }
             for (int taskNdx = mTasks.size() - 1; taskNdx >= 0; --taskNdx) {
                 mTasks.get(taskNdx).updateDisplayInfo(mDisplayContent);
@@ -330,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);
@@ -349,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;
             }
         }
     }
@@ -382,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();
@@ -412,8 +438,7 @@
                 for (int winNdx = appWindows.size() - 1; winNdx >= 0; --winNdx) {
                     // We are in the middle of changing the state of displays/stacks/tasks. We need
                     // to finish that, before we let layout interfere with it.
-                    mService.removeWindowInnerLocked(appWindows.get(winNdx),
-                            false /* performLayout */);
+                    mService.removeWindowLocked(appWindows.get(winNdx));
                     doAnotherLayoutPass = true;
                 }
             }
@@ -508,4 +533,36 @@
     public String toString() {
         return "{stackId=" + mStackId + " tasks=" + mTasks + "}";
     }
+
+    /**
+     * For docked workspace provides information which side of the screen was the dock anchored.
+     */
+    @DockSide
+    int getDockSide() {
+        if (mStackId != DOCKED_STACK_ID) {
+            return DOCKED_INVALID;
+        }
+        if (mDisplayContent == null) {
+            return DOCKED_INVALID;
+        }
+        mDisplayContent.getLogicalDisplayRect(mTmpRect);
+        final int orientation = mService.mCurConfiguration.orientation;
+        if (orientation == Configuration.ORIENTATION_PORTRAIT) {
+            // Portrait mode, docked either at the top or the 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 (mBounds.left - mTmpRect.left < mTmpRect.right - mBounds.right) {
+                return DOCKED_LEFT;
+            } else {
+                return DOCKED_RIGHT;
+            }
+        } else {
+            return DOCKED_INVALID;
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 386a28f..76e7c3c 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;
@@ -107,6 +109,7 @@
 import android.view.Choreographer;
 import android.view.Display;
 import android.view.DisplayInfo;
+import android.view.DropPermissionHolder;
 import android.view.Gravity;
 import android.view.IApplicationToken;
 import android.view.IInputFilter;
@@ -626,6 +629,13 @@
 
     private WindowContentFrameStats mTempWindowRenderStats;
 
+    private static final int DRAG_FLAGS_URI_ACCESS = View.DRAG_FLAG_GLOBAL_URI_READ |
+            View.DRAG_FLAG_GLOBAL_URI_WRITE;
+
+    private static final int DRAG_FLAGS_URI_PERMISSIONS = DRAG_FLAGS_URI_ACCESS |
+            View.DRAG_FLAG_GLOBAL_PERSISTABLE_URI_PERMISSION |
+            View.DRAG_FLAG_GLOBAL_PREFIX_URI_PERMISSION;
+
     final class DragInputEventReceiver extends InputEventReceiver {
         // Set, if stylus button was down at the start of the drag.
         private boolean mStylusButtonDownAtStart;
@@ -671,7 +681,7 @@
                             if (DEBUG_DRAG) Slog.d(TAG, "Button no longer pressed; dropping at "
                                     + newX + "," + newY);
                             synchronized (mWindowMap) {
-                                endDrag = mDragState.notifyDropLw(newX, newY);
+                                endDrag = completeDrop(newX, newY);
                             }
                         } else {
                             synchronized (mWindowMap) {
@@ -685,7 +695,7 @@
                         if (DEBUG_DRAG) Slog.d(TAG, "Got UP on move channel; dropping at "
                                 + newX + "," + newY);
                         synchronized (mWindowMap) {
-                            endDrag = mDragState.notifyDropLw(newX, newY);
+                            endDrag = completeDrop(newX, newY);
                         }
                     } break;
 
@@ -715,6 +725,25 @@
         }
     }
 
+    private boolean completeDrop(float x, float y) {
+        WindowState dropTargetWin = mDragState.getDropTargetWinLw(x, y);
+
+        DropPermissionHolder dropPermissionHolder = null;
+        if ((mDragState.mFlags & View.DRAG_FLAG_GLOBAL) != 0 &&
+                (mDragState.mFlags & DRAG_FLAGS_URI_ACCESS) != 0) {
+            dropPermissionHolder = new DropPermissionHolder(
+                    mDragState.mData,
+                    mActivityManager,
+                    mDragState.mUid,
+                    dropTargetWin.getOwningPackage(),
+                    mDragState.mFlags & DRAG_FLAGS_URI_PERMISSIONS,
+                    UserHandle.getUserId(mDragState.mUid),
+                    UserHandle.getUserId(dropTargetWin.getOwningUid()));
+        }
+
+        return mDragState.notifyDropLw(dropTargetWin, dropPermissionHolder, x, y);
+    }
+
     /**
      * Whether the UI is currently running in touch mode (not showing
      * navigational focus because the user is directly pressing the screen).
@@ -894,6 +923,7 @@
         mAllowTheaterModeWakeFromLayout = context.getResources().getBoolean(
                 com.android.internal.R.bool.config_allowTheaterModeWakeFromWindowLayout);
 
+
         LocalServices.addService(WindowManagerInternal.class, new LocalService());
         initPolicy();
 
@@ -1827,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
@@ -2171,7 +2206,7 @@
         removeWindowInnerLocked(win, true);
     }
 
-    void removeWindowInnerLocked(WindowState win, boolean performLayout) {
+    private void removeWindowInnerLocked(WindowState win, boolean performLayout) {
         if (win.mRemoved) {
             // Nothing to do.
             return;
@@ -2412,6 +2447,7 @@
         boolean inTouchMode;
         boolean configChanged;
         boolean surfaceChanged = false;
+        boolean dragResizing = false;
         boolean animating;
         boolean hasStatusBarPermission =
                 mContext.checkCallingOrSelfPermission(android.Manifest.permission.STATUS_BAR)
@@ -2560,6 +2596,23 @@
                         surfaceChanged = true;
                     }
                 }
+
+                // If we're starting a drag-resize, we'll be changing the surface size as well as
+                // notifying the client to render to with an offset from the surface's top-left.
+                // Do a screen freeze, and keep the old surface until the the first frame drawn to
+                // the new surface comes back, so that we avoid a flash due to mismatching surface
+                // setups on window manager side and client side.
+                if (win.isDragResizeChanged()) {
+                    win.setDragResizing();
+                    if (win.mHasSurface) {
+                        winAnimator.mDestroyPendingSurfaceUponRedraw = true;
+                        winAnimator.mSurfaceDestroyDeferred = true;
+                        winAnimator.destroySurfaceLocked();
+                        startFreezingDisplayLocked(false, 0, 0);
+                        toBeDisplayed = true;
+                    }
+                }
+                dragResizing = win.isDragResizing();
                 try {
                     if (!win.mHasSurface) {
                         surfaceChanged = true;
@@ -2726,7 +2779,8 @@
 
         return (inTouchMode ? WindowManagerGlobal.RELAYOUT_RES_IN_TOUCH_MODE : 0)
                 | (toBeDisplayed ? WindowManagerGlobal.RELAYOUT_RES_FIRST_TIME : 0)
-                | (surfaceChanged ? WindowManagerGlobal.RELAYOUT_RES_SURFACE_CHANGED : 0);
+                | (surfaceChanged ? WindowManagerGlobal.RELAYOUT_RES_SURFACE_CHANGED : 0)
+                | (dragResizing ? WindowManagerGlobal.RELAYOUT_RES_DRAG_RESIZING : 0);
     }
 
     public void performDeferredDestroyWindow(Session session, IWindow client) {
@@ -3058,7 +3112,7 @@
     public void addAppToken(int addPos, IApplicationToken token, int taskId, int stackId,
             int requestedOrientation, boolean fullscreen, boolean showForAllUsers, int userId,
             int configChanges, boolean voiceInteraction, boolean launchTaskBehind,
-            Rect taskBounds, Configuration config) {
+            Rect taskBounds, Configuration config, boolean cropWindowsToStack) {
         if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS,
                 "addAppToken()")) {
             throw new SecurityException("Requires MANAGE_APP_TOKENS permission");
@@ -3092,6 +3146,7 @@
             atoken.layoutConfigChanges = (configChanges &
                     (ActivityInfo.CONFIG_SCREEN_SIZE | ActivityInfo.CONFIG_ORIENTATION)) != 0;
             atoken.mLaunchTaskBehind = launchTaskBehind;
+            atoken.mCropWindowsToStack = cropWindowsToStack;
             if (DEBUG_TOKEN_MOVEMENT || DEBUG_ADD_REMOVE) Slog.v(TAG, "addAppToken: " + atoken
                     + " to stack=" + stackId + " task=" + taskId + " at " + addPos);
 
@@ -3213,8 +3268,7 @@
 
                 // if we're about to tear down this window and not seek for
                 // the behind activity, don't use it for orientation
-                if (!findingBehind
-                        && (!atoken.hidden && atoken.hiddenRequested)) {
+                if (!findingBehind && !atoken.hidden && atoken.hiddenRequested) {
                     if (DEBUG_ORIENTATION) Slog.v(TAG, "Skipping " + atoken
                             + " -- going to hide");
                     continue;
@@ -3235,7 +3289,7 @@
                 }
 
                 // We ignore any hidden applications on the top.
-                if (atoken.hiddenRequested || atoken.willBeHidden) {
+                if (atoken.hiddenRequested) {
                     if (DEBUG_ORIENTATION) Slog.v(TAG, "Skipping " + atoken
                             + " -- hidden on top");
                     continue;
@@ -3628,12 +3682,8 @@
                     // pretend like we didn't see that.
                     return;
                 }
-                final boolean windowIsTranslucentDefined = ent.array.hasValue(
-                        com.android.internal.R.styleable.Window_windowIsTranslucent);
                 final boolean windowIsTranslucent = ent.array.getBoolean(
                         com.android.internal.R.styleable.Window_windowIsTranslucent, false);
-                final boolean windowSwipeToDismiss = ent.array.getBoolean(
-                        com.android.internal.R.styleable.Window_windowSwipeToDismiss, false);
                 final boolean windowIsFloating = ent.array.getBoolean(
                         com.android.internal.R.styleable.Window_windowIsFloating, false);
                 final boolean windowShowWallpaper = ent.array.getBoolean(
@@ -3643,7 +3693,7 @@
                 if (DEBUG_STARTING_WINDOW) Slog.v(TAG, "Translucent=" + windowIsTranslucent
                         + " Floating=" + windowIsFloating
                         + " ShowWallpaper=" + windowShowWallpaper);
-                if (windowIsTranslucent || (!windowIsTranslucentDefined && windowSwipeToDismiss)) {
+                if (windowIsTranslucent) {
                     return;
                 }
                 if (windowIsFloating || windowDisableStarting) {
@@ -3735,7 +3785,6 @@
             if (!ttoken.hidden) {
                 wtoken.hidden = false;
                 wtoken.hiddenRequested = false;
-                wtoken.willBeHidden = false;
             }
             if (wtoken.clientHidden != ttoken.clientHidden) {
                 wtoken.clientHidden = ttoken.clientHidden;
@@ -3791,25 +3840,6 @@
         }
     }
 
-    @Override
-    public void setAppWillBeHidden(IBinder token) {
-        if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS,
-                "setAppWillBeHidden()")) {
-            throw new SecurityException("Requires MANAGE_APP_TOKENS permission");
-        }
-
-        AppWindowToken wtoken;
-
-        synchronized(mWindowMap) {
-            wtoken = findAppWindowToken(token);
-            if (wtoken == null) {
-                Slog.w(TAG, "Attempted to set will be hidden of non-existing app token: " + token);
-                return;
-            }
-            wtoken.willBeHidden = true;
-        }
-    }
-
     public void setAppFullscreen(IBinder token, boolean toOpaque) {
         synchronized (mWindowMap) {
             AppWindowToken atoken = findAppWindowToken(token);
@@ -3846,7 +3876,6 @@
             wtoken.sendAppVisibilityToClients();
         }
 
-        wtoken.willBeHidden = false;
         // Allow for state changes and animation to be applied if:
         // * token is transitioning visibility state
         // * or the token was marked as hidden and is exiting before we had a chance to play the
@@ -4478,7 +4507,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) {
@@ -4501,6 +4533,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) {
@@ -4589,12 +4626,11 @@
      * Re-sizes a stack and its containing tasks.
      * @param stackId Id of stack to resize.
      * @param bounds New stack bounds. Passing in null sets the bounds to fullscreen.
-     * @param resizeTasks If true, the tasks within the stack will also be resized.
      * @param configs Configurations for tasks in the resized stack, keyed by task id.
      * @param taskBounds Bounds for tasks in the resized stack, keyed by task id.
      * @return True if the stack is now fullscreen.
      * */
-    public boolean resizeStack(int stackId, Rect bounds, boolean resizeTasks,
+    public boolean resizeStack(int stackId, Rect bounds,
             SparseArray<Configuration> configs, SparseArray<Rect> taskBounds) {
         synchronized (mWindowMap) {
             final TaskStack stack = mStackIdToStack.get(stackId);
@@ -4602,7 +4638,7 @@
                 throw new IllegalArgumentException("resizeStack: stackId " + stackId
                         + " not found.");
             }
-            if (stack.setBounds(bounds, resizeTasks, configs, taskBounds)) {
+            if (stack.setBounds(bounds, configs, taskBounds)) {
                 stack.resizeWindows();
                 stack.getDisplayContent().layoutNeeded = true;
                 mWindowPlacerLocked.performSurfacePlacement();
@@ -4639,23 +4675,18 @@
      * Returns a {@link Configuration} object that contains configurations settings
      * that should be overridden due to the operation.
      */
-    public void resizeTask(int taskId, Rect bounds, Configuration configuration, boolean relayout) {
+    public void resizeTask(int taskId, Rect bounds, Configuration configuration,
+            boolean relayout, boolean forced) {
         synchronized (mWindowMap) {
             Task task = mTaskIdToTask.get(taskId);
             if (task == null) {
                 throw new IllegalArgumentException("resizeTask: taskId " + taskId
                         + " not found.");
             }
-            final int boundsChanged = task.setBounds(bounds, configuration);
-            if (boundsChanged != Task.BOUNDS_CHANGE_NONE) {
-                if ((boundsChanged & Task.BOUNDS_CHANGE_SIZE) == Task.BOUNDS_CHANGE_SIZE) {
-                    task.resizeWindows();
-                }
 
-                if (relayout) {
-                    task.getDisplayContent().layoutNeeded = true;
-                    mWindowPlacerLocked.performSurfacePlacement();
-                }
+            if (task.resizeLocked(bounds, configuration, forced) && relayout) {
+                task.getDisplayContent().layoutNeeded = true;
+                mWindowPlacerLocked.performSurfacePlacement();
             }
         }
     }
@@ -5634,7 +5665,7 @@
                         int right = wf.right - cr.right;
                         int bottom = wf.bottom - cr.bottom;
                         frame.union(left, top, right, bottom);
-                        ws.getTaskBounds(stackBounds, !BOUNDS_FOR_TOUCH);
+                        ws.getVisibleBounds(stackBounds, !BOUNDS_FOR_TOUCH);
                         if (!frame.intersect(stackBounds)) {
                             // Set frame empty if there's no intersection.
                             frame.setEmpty();
@@ -6832,37 +6863,29 @@
     }
 
     boolean startMovingTask(IWindow window, float startX, float startY) {
-        WindowState callingWin = null;
+        WindowState win = null;
         synchronized (mWindowMap) {
-            callingWin = windowForClientLocked(null, window, false);
-            if (!startPositioningLocked(callingWin, false /*resize*/, startX, startY)) {
+            win = windowForClientLocked(null, window, false);
+            if (!startPositioningLocked(win, false /*resize*/, startX, startY)) {
                 return false;
             }
         }
         try {
-            mActivityManager.setFocusedTask(callingWin.getTask().mTaskId);
+            mActivityManager.setFocusedTask(win.getTask().mTaskId);
         } catch(RemoteException e) {}
         return true;
     }
 
     private void startResizingTask(DisplayContent displayContent, int startX, int startY) {
-        int taskId = -1;
-        AppWindowToken atoken = null;
+        WindowState win = null;
         synchronized (mWindowMap) {
-            taskId = displayContent.taskIdForControlPoint(startX, startY);
-            Task task = mTaskIdToTask.get(taskId);
-            if (task == null || task.mAppTokens == null) {
-                return;
-            }
-            AppTokenList tokens = task.mAppTokens;
-            atoken = tokens.get(tokens.size() - 1);
-            WindowState win = atoken.findMainWindow();
+            win = displayContent.findWindowForControlPoint(startX, startY);
             if (!startPositioningLocked(win, true /*resize*/, startX, startY)) {
                 return;
             }
         }
         try {
-            mActivityManager.setFocusedTask(taskId);
+            mActivityManager.setFocusedTask(win.getTask().mTaskId);
         } catch(RemoteException e) {}
     }
 
@@ -7183,6 +7206,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) {
@@ -7722,6 +7747,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");
@@ -8263,7 +8295,7 @@
                 final int numTokens = tokens.size();
                 for (int tokenNdx = 0; tokenNdx < numTokens; ++tokenNdx) {
                     final AppWindowToken wtoken = tokens.get(tokenNdx);
-                    if (wtoken.mIsExiting) {
+                    if (wtoken.mIsExiting && !wtoken.mReplacingWindow) {
                         continue;
                     }
                     i = reAddAppWindowsLocked(displayContent, i, wtoken);
@@ -8438,15 +8470,18 @@
                 Slog.v(TAG, "Win " + w + " config changed: "
                         + mCurConfiguration);
             }
+            final boolean dragResizingChanged = w.isDragResizeChanged();
             if (localLOGV) Slog.v(TAG, "Resizing " + w
                     + ": configChanged=" + configChanged
+                    + " dragResizingChanged=" + dragResizingChanged
                     + " last=" + w.mLastFrame + " frame=" + w.mFrame);
             w.mLastFrame.set(w.mFrame);
             if (w.mContentInsetsChanged
                     || w.mVisibleInsetsChanged
                     || winAnimator.mSurfaceResized
                     || w.mOutsetsChanged
-                    || configChanged) {
+                    || configChanged
+                    || dragResizingChanged) {
                 if (DEBUG_RESIZE || DEBUG_ORIENTATION) {
                     Slog.v(TAG, "Resize reasons for w=" + w + ": "
                             + " contentInsetsChanged=" + w.mContentInsetsChanged
@@ -8458,7 +8493,8 @@
                             + " outsetsChanged=" + w.mOutsetsChanged
                             + " " + w.mOutsets.toShortString()
                             + " surfaceResized=" + winAnimator.mSurfaceResized
-                            + " configChanged=" + configChanged);
+                            + " configChanged=" + configChanged
+                            + " dragResizingChanged=" + dragResizingChanged);
                 }
 
                 w.mLastOverscanInsets.set(w.mOverscanInsets);
@@ -8467,12 +8503,12 @@
                 w.mLastStableInsets.set(w.mStableInsets);
                 w.mLastOutsets.set(w.mOutsets);
                 makeWindowFreezingScreenIfNeededLocked(w);
-                // If the orientation is changing, then we need to
-                // hold off on unfreezing the display until this
-                // window has been redrawn; to do that, we need
-                // to go through the process of getting informed
-                // by the application when it has finished drawing.
-                if (w.mOrientationChanging) {
+                // If the orientation is changing, or we're starting or ending
+                // a drag resizing action, then we need to hold off on unfreezing
+                // the display until this window has been redrawn; to do that,
+                // we need to go through the process of getting informed by the
+                // application when it has finished drawing.
+                if (w.mOrientationChanging || dragResizingChanged) {
                     if (DEBUG_SURFACE_TRACE || DEBUG_ANIM || DEBUG_ORIENTATION) Slog.v(TAG,
                             "Orientation start waiting for draw mDrawState=DRAW_PENDING in "
                             + w + ", surface " + winAnimator.mSurfaceControl);
@@ -9851,6 +9887,12 @@
         return mWindowMap;
     }
 
+    /**
+     * Hint to a token that its activity will relaunch, which will trigger removal and addition of
+     * a window.
+     * @param token Application token for which the activity will be relaunched.
+     * @param animate Whether to animate the addition of the new window.
+     */
     public void setReplacingWindow(IBinder token, boolean animate) {
         synchronized (mWindowMap) {
             AppWindowToken appWindowToken = findAppWindowToken(token);
@@ -9863,6 +9905,10 @@
         }
     }
 
+    static int dipToPixel(int dip, DisplayMetrics displayMetrics) {
+        return (int)TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dip, displayMetrics);
+    }
+
     private final class LocalService extends WindowManagerInternal {
         @Override
         public void requestTraversalFromDisplayManager() {
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index e467b19..37deffe 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -16,7 +16,7 @@
 
 package com.android.server.wm;
 
-import static android.app.ActivityManager.FREEFORM_WORKSPACE_STACK_ID;
+import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
 import static android.view.WindowManager.LayoutParams.FIRST_SUB_WINDOW;
 import static android.view.WindowManager.LayoutParams.FLAG_DIM_BEHIND;
 import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED;
@@ -24,6 +24,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;
@@ -38,6 +39,7 @@
 import android.os.PowerManager;
 import android.os.RemoteCallbackList;
 import android.os.SystemClock;
+import android.os.Trace;
 import android.os.WorkSource;
 import android.util.DisplayMetrics;
 import android.util.TimeUtils;
@@ -81,12 +83,14 @@
     static final String TAG = "WindowState";
 
     // The minimal size of a window within the usable area of the freeform stack.
-    private static final int MINIMUM_VISIBLE_WIDTH_IN_DP = 48;
-    private static final int MINIMUM_VISIBLE_HEIGHT_IN_DP = 32;
+    // TODO(multi-window): fix the min sizes when we have mininum width/height support,
+    //                     use hard-coded min sizes for now.
+    static final int MINIMUM_VISIBLE_WIDTH_IN_DP = 48;
+    static final int MINIMUM_VISIBLE_HEIGHT_IN_DP = 32;
 
     // The thickness of a window resize handle outside the window bounds on the free form workspace
     // to capture touch events in that area.
-    static final int RESIZE_HANDLE_WIDTH_IN_DP = 10;
+    static final int RESIZE_HANDLE_WIDTH_IN_DP = 30;
 
     static final boolean BOUNDS_FOR_TOUCH = true;
 
@@ -126,6 +130,8 @@
     boolean mAppFreezing;
     boolean mAttachedHidden;    // is our parent window hidden?
     boolean mWallpaperVisible;  // for wallpaper, what was last vis report?
+    boolean mDragResizing;
+    boolean mDragResizeChanging;
 
     RemoteCallbackList<IWindowFocusObserver> mFocusCallbacks;
 
@@ -379,6 +385,8 @@
      */
     PowerManager.WakeLock mDrawLock;
 
+    final private Rect mTmpRect = new Rect();
+
     WindowState(WindowManagerService service, Session s, IWindow c, WindowToken token,
            WindowState attachedWindow, int appOp, int seq, WindowManager.LayoutParams a,
            int viewVisibility, final DisplayContent displayContent) {
@@ -555,7 +563,7 @@
         }
         mHaveFrame = true;
 
-        final Task task = mAppToken != null ? getTask() : null;
+        final Task task = getTask();
         final boolean nonFullscreenTask = task != null && !task.isFullscreen();
         final boolean freeformWorkspace = task != null && task.inFreeformWorkspace();
         if (nonFullscreenTask) {
@@ -689,8 +697,11 @@
             // into a usable area..
             final int height = Math.min(mFrame.height(), mContentFrame.height());
             final int width = Math.min(mContentFrame.width(), mFrame.width());
-            final int minVisibleHeight = calculatePixelFromDp(MINIMUM_VISIBLE_HEIGHT_IN_DP);
-            final int minVisibleWidth = calculatePixelFromDp(MINIMUM_VISIBLE_WIDTH_IN_DP);
+            final DisplayMetrics displayMetrics = getDisplayContent().getDisplayMetrics();
+            final int minVisibleHeight =
+                    mService.dipToPixel(MINIMUM_VISIBLE_HEIGHT_IN_DP, displayMetrics);
+            final int minVisibleWidth =
+                    mService.dipToPixel(MINIMUM_VISIBLE_WIDTH_IN_DP, displayMetrics);
             final int top = Math.max(mContentFrame.top,
                     Math.min(mFrame.top, mContentFrame.bottom - minVisibleHeight));
             final int left = Math.max(mContentFrame.left + minVisibleWidth - width,
@@ -699,6 +710,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),
@@ -911,12 +924,7 @@
     }
 
     Task getTask() {
-        AppWindowToken wtoken = mAppToken == null ? mService.mFocusedApp : mAppToken;
-        if (wtoken == null) {
-            return null;
-        }
-        final Task task = wtoken.mTask;
-        return task;
+        return mAppToken != null ? mAppToken.mTask : null;
     }
 
     TaskStack getStack() {
@@ -926,28 +934,45 @@
                 return task.mStack;
             }
         }
-        return mDisplayContent.getHomeStack();
+        return null;
     }
 
     /**
-     * Retrieves the bounds for a task.
+     * Retrieves the visible bounds of the window.
      * @param bounds The rect which gets the bounds.
      * @param forTouch Pass in BOUNDS_FOR_TOUCH to get touch related bounds, otherwise visible
      *        bounds will be returned.
      */
-    void getTaskBounds(Rect bounds, boolean forTouch) {
-        final Task task = getTask();
-        if (task != null) {
-            task.getBounds(bounds);
-            if (forTouch == BOUNDS_FOR_TOUCH) {
-                if (task.inFreeformWorkspace()) {
-                    final int delta = calculatePixelFromDp(RESIZE_HANDLE_WIDTH_IN_DP);
-                    bounds.inset(-delta, -delta);
-                }
+    void getVisibleBounds(Rect bounds, boolean forTouch) {
+        boolean intersectWithStackBounds = mAppToken != null && mAppToken.mCropWindowsToStack;
+        bounds.setEmpty();
+        mTmpRect.setEmpty();
+        if (intersectWithStackBounds) {
+            final TaskStack stack = getStack();
+            if (stack != null) {
+                stack.getBounds(mTmpRect);
+            } else {
+                intersectWithStackBounds = false;
+            }
+        }
+
+        bounds.set(mVisibleFrame);
+        if (intersectWithStackBounds) {
+            bounds.intersect(mTmpRect);
+        }
+
+        if (bounds.isEmpty()) {
+            bounds.set(mFrame);
+            if (intersectWithStackBounds) {
+                bounds.intersect(mTmpRect);
             }
             return;
         }
-        bounds.set(mFrame);
+        if (forTouch && inFreeformWorkspace()) {
+            final DisplayMetrics displayMetrics = getDisplayContent().getDisplayMetrics();
+            final int delta = mService.dipToPixel(RESIZE_HANDLE_WIDTH_IN_DP, displayMetrics);
+            bounds.inset(-delta, -delta);
+        }
     }
 
     public long getInputDispatchingTimeoutNanos() {
@@ -1442,10 +1467,7 @@
                 // We want the tag name to be somewhat stable so that it is easier to correlate
                 // in wake lock statistics.  So in particular, we don't want to include the
                 // window's hash code as in toString().
-                CharSequence tag = mAttrs.getTitle();
-                if (tag == null) {
-                    tag = mAttrs.packageName;
-                }
+                final CharSequence tag = getWindowTag();
                 mDrawLock = mService.mPowerManager.newWakeLock(
                         PowerManager.DRAW_WAKE_LOCK, "Window:" + tag);
                 mDrawLock.setReferenceCounted(false);
@@ -1582,6 +1604,7 @@
     }
 
     void reportResized() {
+        Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "wm.reportResized_" + getWindowTag());
         try {
             if (DEBUG_RESIZE || DEBUG_ORIENTATION) Slog.v(TAG, "Reporting new frame to " + this
                     + ": " + mCompatFrame);
@@ -1647,6 +1670,7 @@
             mService.mPendingRemove.add(this);
             mService.mWindowPlacerLocked.requestTraversal();
         }
+        Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
     }
 
     public void registerFocusObserver(IWindowFocusObserver observer) {
@@ -1673,20 +1697,22 @@
     }
 
     boolean inFreeformWorkspace() {
-        final Task task = mAppToken != null ? getTask() : null;
+        final Task task = getTask();
         return task != null && task.inFreeformWorkspace();
     }
 
-    boolean isDragResizing() {
-        final Task task = mAppToken != null ? getTask() : null;
-        return mService.mTaskPositioner != null && mService.mTaskPositioner.isTaskResizing(task);
+    boolean isDragResizeChanged() {
+        final Task task = getTask();
+        return task != null && mDragResizing != task.isDragResizing();
     }
 
-    private int calculatePixelFromDp(int dp) {
-        final Configuration serviceConfig = mService.mCurConfiguration;
-        // TODO(multidisplay): Update Dp to that of display stack is on.
-        final float density = serviceConfig.densityDpi * DisplayMetrics.DENSITY_DEFAULT_SCALE;
-        return (int)(dp * density);
+    void setDragResizing() {
+        final Task task = getTask();
+        mDragResizing = task != null && task.isDragResizing();
+    }
+
+    boolean isDragResizing() {
+        return mDragResizing;
     }
 
     void dump(PrintWriter pw, String prefix, boolean dumpAll) {
@@ -1876,15 +1902,20 @@
 
     String makeInputChannelName() {
         return Integer.toHexString(System.identityHashCode(this))
-            + " " + mAttrs.getTitle();
+            + " " + getWindowTag();
+    }
+
+    private CharSequence getWindowTag() {
+        CharSequence tag = mAttrs.getTitle();
+        if (tag == null || tag.length() <= 0) {
+            tag = mAttrs.packageName;
+        }
+        return tag;
     }
 
     @Override
     public String toString() {
-        CharSequence title = mAttrs.getTitle();
-        if (title == null || title.length() <= 0) {
-            title = mAttrs.packageName;
-        }
+        final CharSequence title = getWindowTag();
         if (mStringNameCache == null || mLastTitle != title || mWasExiting != mExiting) {
             mLastTitle = title;
             mWasExiting = mExiting;
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index dfc9784..0b9f2c6 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -107,6 +107,7 @@
      */
     boolean mSurfaceDestroyDeferred;
 
+    boolean mDestroyPendingSurfaceUponRedraw;
     float mShownAlpha = 0;
     float mAlpha = 0;
     float mLastAlpha = 0;
@@ -546,9 +547,15 @@
             Slog.i(TAG, "commitFinishDrawingLocked: mDrawState=READY_TO_SHOW " + mSurfaceControl);
         }
         mDrawState = READY_TO_SHOW;
+        boolean result = false;
         final AppWindowToken atoken = mWin.mAppToken;
         if (atoken == null || atoken.allDrawn || mWin.mAttrs.type == TYPE_APPLICATION_STARTING) {
-            return performShowLocked();
+            result = performShowLocked();
+        }
+        if (mDestroyPendingSurfaceUponRedraw) {
+            mDestroyPendingSurfaceUponRedraw = false;
+            destroyDeferredSurfaceLocked();
+            mService.stopFreezingDisplayLocked();
         }
         return false;
     }
@@ -796,6 +803,9 @@
                 flags |= SurfaceControl.SECURE;
             }
 
+            float left = w.mFrame.left + w.mXOffset;
+            float top = w.mFrame.top + w.mYOffset;
+
             int width;
             int height;
             if ((attrs.flags & LayoutParams.FLAG_SCALED) != 0) {
@@ -809,6 +819,8 @@
                 // buffer drops due to size mismatch.
                 final DisplayInfo displayInfo = w.getDisplayInfo();
                 if (displayInfo != null && w.isDragResizing()) {
+                    left = 0;
+                    top = 0;
                     width = displayInfo.logicalWidth;
                     height = displayInfo.logicalHeight;
                 } else {
@@ -826,9 +838,6 @@
                 height = 1;
             }
 
-            float left = w.mFrame.left + w.mXOffset;
-            float top = w.mFrame.top + w.mYOffset;
-
             // Adjust for surface insets.
             width += attrs.surfaceInsets.left + attrs.surfaceInsets.right;
             height += attrs.surfaceInsets.top + attrs.surfaceInsets.bottom;
@@ -1078,6 +1087,8 @@
                 mAnimator.getScreenRotationAnimationLocked(displayId);
         final boolean screenAnimation =
                 screenRotationAnimation != null && screenRotationAnimation.isAnimating();
+
+        mHasClipRect = false;
         if (selfTransformation || attachedTransformation != null
                 || appTransformation != null || screenAnimation) {
             // cache often used attributes locally
@@ -1156,7 +1167,6 @@
             // transforming since it is more important to have that
             // animation be smooth.
             mShownAlpha = mAlpha;
-            mHasClipRect = false;
             if (!mService.mLimitedAlphaCompositing
                     || (!PixelFormat.formatHasAlpha(mWin.mAttrs.format)
                     || (mWin.isIdentityMatrix(mDsDx, mDtDx, mDsDy, mDtDy)
@@ -1202,6 +1212,13 @@
             return;
         } else if (mIsWallpaper && mService.mWindowPlacerLocked.mWallpaperActionPending) {
             return;
+        } else if (mWin.isDragResizeChanged()) {
+            // This window is awaiting a relayout because user just started (or ended)
+            // drag-resizing. The shown frame (which affects surface size and pos)
+            // should not be updated until we get next finished draw with the new surface.
+            // Otherwise one or two frames rendered with old settings would be displayed
+            // with new geometry.
+            return;
         }
 
         if (WindowManagerService.localLOGV) Slog.v(
@@ -1352,7 +1369,12 @@
         // so we need to translate to match the actual surface coordinates.
         clipRect.offset(attrs.surfaceInsets.left, attrs.surfaceInsets.top);
 
-        adjustCropToStackBounds(w, clipRect);
+        // We don't want to clip to stack bounds windows that are currently doing entrance
+        // animation. This is necessary for docking operation, otherwise the window will be
+        // suddenly cut off.
+        if (!mAnimator.mAnimating) {
+            adjustCropToStackBounds(w, clipRect);
+        }
 
         if (!clipRect.equals(mLastClipRect)) {
             mLastClipRect.set(clipRect);
@@ -1377,7 +1399,8 @@
     }
 
     private void adjustCropToStackBounds(WindowState w, Rect clipRect) {
-        if (w.getAttrs().type == LayoutParams.TYPE_BASE_APPLICATION) {
+        final AppWindowToken appToken = w.mAppToken;
+        if (appToken != null && appToken.mCropWindowsToStack) {
             TaskStack stack = w.getTask().mStack;
             stack.getBounds(mTmpStackBounds);
             final int surfaceX = (int) mSurfaceX;
@@ -1398,6 +1421,9 @@
     void setSurfaceBoundariesLocked(final boolean recoveringMemory) {
         final WindowState w = mWin;
 
+        float left = w.mShownFrame.left;
+        float top = w.mShownFrame.top;
+
         int width;
         int height;
         if ((w.mAttrs.flags & LayoutParams.FLAG_SCALED) != 0) {
@@ -1411,6 +1437,8 @@
             // buffer drops due to size mismatch.
             final DisplayInfo displayInfo = w.getDisplayInfo();
             if (displayInfo != null && w.isDragResizing()) {
+                left = 0;
+                top = 0;
                 width = displayInfo.logicalWidth;
                 height = displayInfo.logicalHeight;
             } else {
@@ -1428,9 +1456,6 @@
             height = 1;
         }
 
-        float left = w.mShownFrame.left;
-        float top = w.mShownFrame.top;
-
         // Adjust for surface insets.
         final LayoutParams attrs = w.getAttrs();
         final int displayId = w.getDisplayId();
diff --git a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
index 86da94f..d86a8af 100644
--- a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
+++ b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
@@ -47,6 +47,7 @@
 import android.os.SystemClock;
 import android.os.Trace;
 import android.provider.Settings;
+import android.util.ArraySet;
 import android.util.Slog;
 import android.view.Display;
 import android.view.DisplayInfo;
@@ -625,8 +626,11 @@
 
             for (int i = windows.size() - 1; i >= 0; i--) {
                 WindowState w = windows.get(i);
-                final Task task = w.getTask();
-                if (task == null && w.getAttrs().type != TYPE_PRIVATE_PRESENTATION) {
+                Task task = w.getTask();
+                if (task == null && w.getDisplayContent().getHomeStack() == null
+                        && w.getAttrs().type != TYPE_PRIVATE_PRESENTATION) {
+                    // TODO: Understand what the use case is here and see if the conditions can be
+                    // simplified.
                     continue;
                 }
 
@@ -1225,11 +1229,15 @@
         final WindowState oldWallpaper =
                 mWallpaperControllerLocked.isWallpaperTargetAnimating()
                         ? null : wallpaperTarget;
+        final ArraySet<AppWindowToken> openingApps = mService.mOpeningApps;
+        final ArraySet<AppWindowToken> closingApps = mService.mClosingApps;
         if (DEBUG_APP_TRANSITIONS) Slog.v(TAG,
                 "New wallpaper target=" + wallpaperTarget
                         + ", oldWallpaper=" + oldWallpaper
                         + ", lower target=" + lowerWallpaperTarget
-                        + ", upper target=" + upperWallpaperTarget);
+                        + ", upper target=" + upperWallpaperTarget
+                        + ", openingApps=" + openingApps
+                        + ", closingApps=" + closingApps);
         mService.mAnimateWallpaperWithTarget = false;
         if (closingAppHasWallpaper && openingAppHasWallpaper) {
             if (DEBUG_APP_TRANSITIONS)
@@ -1248,15 +1256,16 @@
             }
             if (DEBUG_APP_TRANSITIONS) Slog.v(TAG,
                     "New transit: " + AppTransition.appTransitionToString(transit));
-        } else if ((oldWallpaper != null) && !mService.mOpeningApps.isEmpty()
-                && !mService.mOpeningApps.contains(oldWallpaper.mAppToken)) {
-            // We are transitioning from an activity with
-            // a wallpaper to one without.
+        } else if (oldWallpaper != null && !mService.mOpeningApps.isEmpty()
+                && !openingApps.contains(oldWallpaper.mAppToken)
+                && closingApps.contains(oldWallpaper.mAppToken)) {
+            // We are transitioning from an activity with a wallpaper to one without.
             transit = AppTransition.TRANSIT_WALLPAPER_CLOSE;
             if (DEBUG_APP_TRANSITIONS) Slog.v(TAG,
                     "New transit away from wallpaper: "
                     + AppTransition.appTransitionToString(transit));
-        } else if (wallpaperTarget != null && wallpaperTarget.isVisibleLw()) {
+        } else if (wallpaperTarget != null && wallpaperTarget.isVisibleLw() &&
+                openingApps.contains(wallpaperTarget.mAppToken)) {
             // We are transitioning from an activity without
             // a wallpaper to now showing the wallpaper
             transit = AppTransition.TRANSIT_WALLPAPER_OPEN;
diff --git a/services/core/jni/com_android_server_AlarmManagerService.cpp b/services/core/jni/com_android_server_AlarmManagerService.cpp
index 3fd0f84..5cbb277 100644
--- a/services/core/jni/com_android_server_AlarmManagerService.cpp
+++ b/services/core/jni/com_android_server_AlarmManagerService.cpp
@@ -460,7 +460,7 @@
     return result;
 }
 
-static JNINativeMethod sMethods[] = {
+static const JNINativeMethod sMethods[] = {
      /* name, signature, funcPtr */
     {"init", "()J", (void*)android_server_AlarmManagerService_init},
     {"close", "(J)V", (void*)android_server_AlarmManagerService_close},
diff --git a/services/core/jni/com_android_server_AssetAtlasService.cpp b/services/core/jni/com_android_server_AssetAtlasService.cpp
index 8f4fb51..ed79ceb 100644
--- a/services/core/jni/com_android_server_AssetAtlasService.cpp
+++ b/services/core/jni/com_android_server_AssetAtlasService.cpp
@@ -204,7 +204,7 @@
 
 const char* const kClassPathName = "com/android/server/AssetAtlasService";
 
-static JNINativeMethod gMethods[] = {
+static const JNINativeMethod gMethods[] = {
     { "nUploadAtlas", "(Landroid/view/GraphicBuffer;Landroid/graphics/Bitmap;)Z",
             (void*) com_android_server_AssetAtlasService_upload },
 };
diff --git a/services/core/jni/com_android_server_ConsumerIrService.cpp b/services/core/jni/com_android_server_ConsumerIrService.cpp
index f5121cd..7104870 100644
--- a/services/core/jni/com_android_server_ConsumerIrService.cpp
+++ b/services/core/jni/com_android_server_ConsumerIrService.cpp
@@ -100,7 +100,7 @@
     return freqsOut.getJavaArray();
 }
 
-static JNINativeMethod method_table[] = {
+static const JNINativeMethod method_table[] = {
     { "halOpen", "()J", (void *)halOpen },
     { "halTransmit", "(JI[I)I", (void *)halTransmit },
     { "halGetCarrierFrequencies", "(J)[I", (void *)halGetCarrierFrequencies},
diff --git a/services/core/jni/com_android_server_PersistentDataBlockService.cpp b/services/core/jni/com_android_server_PersistentDataBlockService.cpp
index 4ccfa56..06de592 100644
--- a/services/core/jni/com_android_server_PersistentDataBlockService.cpp
+++ b/services/core/jni/com_android_server_PersistentDataBlockService.cpp
@@ -97,7 +97,7 @@
         return wipe_block_device(fd);
     }
 
-    static JNINativeMethod sMethods[] = {
+    static const JNINativeMethod sMethods[] = {
          /* name, signature, funcPtr */
         {"nativeGetBlockDeviceSize", "(Ljava/lang/String;)J", (void*)com_android_server_PersistentDataBlockService_getBlockDeviceSize},
         {"nativeWipe", "(Ljava/lang/String;)I", (void*)com_android_server_PersistentDataBlockService_wipe},
diff --git a/services/core/jni/com_android_server_SerialService.cpp b/services/core/jni/com_android_server_SerialService.cpp
index d48d159..1bd7a59 100644
--- a/services/core/jni/com_android_server_SerialService.cpp
+++ b/services/core/jni/com_android_server_SerialService.cpp
@@ -55,7 +55,7 @@
 }
 
 
-static JNINativeMethod method_table[] = {
+static const JNINativeMethod method_table[] = {
     { "native_open",                "(Ljava/lang/String;)Landroid/os/ParcelFileDescriptor;",
                                     (void*)android_server_SerialService_open },
 };
diff --git a/services/core/jni/com_android_server_SystemServer.cpp b/services/core/jni/com_android_server_SystemServer.cpp
index 64514a9..c7d6b95 100644
--- a/services/core/jni/com_android_server_SystemServer.cpp
+++ b/services/core/jni/com_android_server_SystemServer.cpp
@@ -37,7 +37,7 @@
 /*
  * JNI registration.
  */
-static JNINativeMethod gMethods[] = {
+static const JNINativeMethod gMethods[] = {
     /* name, signature, funcPtr */
     { "startSensorService", "()V", (void*) android_server_SystemServer_startSensorService },
 };
diff --git a/services/core/jni/com_android_server_UsbDeviceManager.cpp b/services/core/jni/com_android_server_UsbDeviceManager.cpp
index a1bff9d..3733a55 100644
--- a/services/core/jni/com_android_server_UsbDeviceManager.cpp
+++ b/services/core/jni/com_android_server_UsbDeviceManager.cpp
@@ -118,7 +118,7 @@
     return result;
 }
 
-static JNINativeMethod method_table[] = {
+static const JNINativeMethod method_table[] = {
     { "nativeGetAccessoryStrings",  "()[Ljava/lang/String;",
                                     (void*)android_server_UsbDeviceManager_getAccessoryStrings },
     { "nativeOpenAccessory",        "()Landroid/os/ParcelFileDescriptor;",
diff --git a/services/core/jni/com_android_server_UsbHostManager.cpp b/services/core/jni/com_android_server_UsbHostManager.cpp
index d8c172f..795f6aa 100644
--- a/services/core/jni/com_android_server_UsbHostManager.cpp
+++ b/services/core/jni/com_android_server_UsbHostManager.cpp
@@ -186,7 +186,7 @@
         gParcelFileDescriptorOffsets.mConstructor, fileDescriptor);
 }
 
-static JNINativeMethod method_table[] = {
+static const JNINativeMethod method_table[] = {
     { "monitorUsbHostBus", "()V", (void*)android_server_UsbHostManager_monitorUsbHostBus },
     { "nativeOpenDevice",  "(Ljava/lang/String;)Landroid/os/ParcelFileDescriptor;",
                                   (void*)android_server_UsbHostManager_openDevice },
diff --git a/services/core/jni/com_android_server_VibratorService.cpp b/services/core/jni/com_android_server_VibratorService.cpp
index fb1166b..64278ed 100644
--- a/services/core/jni/com_android_server_VibratorService.cpp
+++ b/services/core/jni/com_android_server_VibratorService.cpp
@@ -46,7 +46,7 @@
     vibrator_off();
 }
 
-static JNINativeMethod method_table[] = {
+static const JNINativeMethod method_table[] = {
     { "vibratorExists", "()Z", (void*)vibratorExists },
     { "vibratorOn", "(J)V", (void*)vibratorOn },
     { "vibratorOff", "()V", (void*)vibratorOff }
diff --git a/services/core/jni/com_android_server_am_ActivityManagerService.cpp b/services/core/jni/com_android_server_am_ActivityManagerService.cpp
index 52217b9..50e4502 100644
--- a/services/core/jni/com_android_server_am_ActivityManagerService.cpp
+++ b/services/core/jni/com_android_server_am_ActivityManagerService.cpp
@@ -110,7 +110,6 @@
             return 0;
         }
         char buf[17];
-        char *curBuf = buf;
         while (fgets(buf, 16, boost_cpuset_file)) {
             //ALOGE("Appending FD %s to fg", buf);
             int i = 0;
diff --git a/services/core/jni/com_android_server_am_BatteryStatsService.cpp b/services/core/jni/com_android_server_am_BatteryStatsService.cpp
index dfc5ef6..5c43659 100644
--- a/services/core/jni/com_android_server_am_BatteryStatsService.cpp
+++ b/services/core/jni/com_android_server_am_BatteryStatsService.cpp
@@ -170,7 +170,7 @@
     return mergedreasonpos - mergedreason;
 }
 
-static JNINativeMethod method_table[] = {
+static const JNINativeMethod method_table[] = {
     { "nativeWaitWakeup", "(Ljava/nio/ByteBuffer;)I", (void*)nativeWaitWakeup },
 };
 
diff --git a/services/core/jni/com_android_server_connectivity_Vpn.cpp b/services/core/jni/com_android_server_connectivity_Vpn.cpp
index 7faeb49..2d0dfd2 100644
--- a/services/core/jni/com_android_server_connectivity_Vpn.cpp
+++ b/services/core/jni/com_android_server_connectivity_Vpn.cpp
@@ -350,7 +350,7 @@
 
 //------------------------------------------------------------------------------
 
-static JNINativeMethod gMethods[] = {
+static const JNINativeMethod gMethods[] = {
     {"jniCreate", "(I)I", (void *)create},
     {"jniGetName", "(I)Ljava/lang/String;", (void *)getName},
     {"jniSetAddresses", "(Ljava/lang/String;Ljava/lang/String;)I", (void *)setAddresses},
diff --git a/services/core/jni/com_android_server_hdmi_HdmiCecController.cpp b/services/core/jni/com_android_server_hdmi_HdmiCecController.cpp
index f2d0f06..b72cf4d 100644
--- a/services/core/jni/com_android_server_hdmi_HdmiCecController.cpp
+++ b/services/core/jni/com_android_server_hdmi_HdmiCecController.cpp
@@ -384,7 +384,7 @@
     return controller->isConnected(port) ? JNI_TRUE : JNI_FALSE ;
 }
 
-static JNINativeMethod sMethods[] = {
+static const JNINativeMethod sMethods[] = {
     /* name, signature, funcPtr */
     { "nativeInit",
       "(Lcom/android/server/hdmi/HdmiCecController;Landroid/os/MessageQueue;)J",
diff --git a/services/core/jni/com_android_server_input_InputApplicationHandle.cpp b/services/core/jni/com_android_server_input_InputApplicationHandle.cpp
index 11388d8..bdc109d 100644
--- a/services/core/jni/com_android_server_input_InputApplicationHandle.cpp
+++ b/services/core/jni/com_android_server_input_InputApplicationHandle.cpp
@@ -120,7 +120,7 @@
 }
 
 
-static JNINativeMethod gInputApplicationHandleMethods[] = {
+static const JNINativeMethod gInputApplicationHandleMethods[] = {
     /* name, signature, funcPtr */
     { "nativeDispose", "()V",
             (void*) android_server_InputApplicationHandle_nativeDispose },
diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp
index e29d0a9..1d4f047 100644
--- a/services/core/jni/com_android_server_input_InputManagerService.cpp
+++ b/services/core/jni/com_android_server_input_InputManagerService.cpp
@@ -1369,7 +1369,7 @@
 
 // ----------------------------------------------------------------------------
 
-static JNINativeMethod gInputManagerMethods[] = {
+static const JNINativeMethod gInputManagerMethods[] = {
     /* name, signature, funcPtr */
     { "nativeInit",
             "(Lcom/android/server/input/InputManagerService;Landroid/content/Context;Landroid/os/MessageQueue;)J",
diff --git a/services/core/jni/com_android_server_input_InputWindowHandle.cpp b/services/core/jni/com_android_server_input_InputWindowHandle.cpp
index 01c51cf..92ef7f1 100644
--- a/services/core/jni/com_android_server_input_InputWindowHandle.cpp
+++ b/services/core/jni/com_android_server_input_InputWindowHandle.cpp
@@ -210,7 +210,7 @@
 }
 
 
-static JNINativeMethod gInputWindowHandleMethods[] = {
+static const JNINativeMethod gInputWindowHandleMethods[] = {
     /* name, signature, funcPtr */
     { "nativeDispose", "()V",
             (void*) android_server_InputWindowHandle_nativeDispose },
diff --git a/services/core/jni/com_android_server_lights_LightsService.cpp b/services/core/jni/com_android_server_lights_LightsService.cpp
index b2b2783..3f074f5 100644
--- a/services/core/jni/com_android_server_lights_LightsService.cpp
+++ b/services/core/jni/com_android_server_lights_LightsService.cpp
@@ -126,7 +126,7 @@
     }
 }
 
-static JNINativeMethod method_table[] = {
+static const JNINativeMethod method_table[] = {
     { "init_native", "()J", (void*)init_native },
     { "finalize_native", "(J)V", (void*)finalize_native },
     { "setLight_native", "(JIIIIII)V", (void*)setLight_native },
diff --git a/services/core/jni/com_android_server_location_FlpHardwareProvider.cpp b/services/core/jni/com_android_server_location_FlpHardwareProvider.cpp
index c0a0c9c..774577d 100644
--- a/services/core/jni/com_android_server_location_FlpHardwareProvider.cpp
+++ b/services/core/jni/com_android_server_location_FlpHardwareProvider.cpp
@@ -1023,7 +1023,7 @@
   env->ReleaseIntArrayElements(geofenceIdsArray, geofenceIds, 0 /*mode*/);
 }
 
-static JNINativeMethod sMethods[] = {
+static const JNINativeMethod sMethods[] = {
   //{"name", "signature", functionPointer }
   {"nativeClassInit", "()V", reinterpret_cast<void*>(ClassInit)},
   {"nativeInit", "()V", reinterpret_cast<void*>(Init)},
diff --git a/services/core/jni/com_android_server_location_GpsLocationProvider.cpp b/services/core/jni/com_android_server_location_GpsLocationProvider.cpp
index 5c27b1f..b8d4196 100644
--- a/services/core/jni/com_android_server_location_GpsLocationProvider.cpp
+++ b/services/core/jni/com_android_server_location_GpsLocationProvider.cpp
@@ -1434,7 +1434,7 @@
     env->ReleaseStringUTFChars(config_content, data);
 }
 
-static JNINativeMethod sMethods[] = {
+static const JNINativeMethod sMethods[] = {
      /* name, signature, funcPtr */
     {"class_init_native", "()V", (void *)android_location_GpsLocationProvider_class_init_native},
     {"native_is_supported", "()Z", (void*)android_location_GpsLocationProvider_is_supported},
diff --git a/services/core/jni/com_android_server_power_PowerManagerService.cpp b/services/core/jni/com_android_server_power_PowerManagerService.cpp
index 1662755..2fdb8e2 100644
--- a/services/core/jni/com_android_server_power_PowerManagerService.cpp
+++ b/services/core/jni/com_android_server_power_PowerManagerService.cpp
@@ -166,7 +166,7 @@
 
 // ----------------------------------------------------------------------------
 
-static JNINativeMethod gPowerManagerServiceMethods[] = {
+static const JNINativeMethod gPowerManagerServiceMethods[] = {
     /* name, signature, funcPtr */
     { "nativeInit", "()V",
             (void*) nativeInit },
diff --git a/services/core/jni/com_android_server_tv_TvInputHal.cpp b/services/core/jni/com_android_server_tv_TvInputHal.cpp
index 507bc9c..89b2a47 100644
--- a/services/core/jni/com_android_server_tv_TvInputHal.cpp
+++ b/services/core/jni/com_android_server_tv_TvInputHal.cpp
@@ -662,7 +662,7 @@
     delete tvInputHal;
 }
 
-static JNINativeMethod gTvInputHalMethods[] = {
+static const JNINativeMethod gTvInputHalMethods[] = {
     /* name, signature, funcPtr */
     { "nativeOpen", "(Landroid/os/MessageQueue;)J",
             (void*) nativeOpen },
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 67e5703..2dd7cde 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -29,6 +29,7 @@
 import android.Manifest.permission;
 import android.accessibilityservice.AccessibilityServiceInfo;
 import android.accounts.AccountManager;
+import android.annotation.NonNull;
 import android.app.Activity;
 import android.app.ActivityManagerNative;
 import android.app.AlarmManager;
@@ -54,6 +55,7 @@
 import android.content.pm.ActivityInfo;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.IPackageManager;
+import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.pm.ResolveInfo;
@@ -73,6 +75,7 @@
 import android.os.FileUtils;
 import android.os.Handler;
 import android.os.IBinder;
+import android.os.Looper;
 import android.os.PersistableBundle;
 import android.os.PowerManager;
 import android.os.PowerManagerInternal;
@@ -111,6 +114,7 @@
 import android.view.inputmethod.InputMethodManager;
 
 import com.android.internal.R;
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.statusbar.IStatusBarService;
 import com.android.internal.util.FastXmlSerializer;
 import com.android.internal.util.JournaledFile;
@@ -266,17 +270,12 @@
             | DevicePolicyManager.KEYGUARD_DISABLE_UNREDACTED_NOTIFICATIONS;
 
     final Context mContext;
+    final Injector mInjector;
+    final IPackageManager mIPackageManager;
     final UserManager mUserManager;
-    final PowerManager.WakeLock mWakeLock;
 
     final LocalService mLocalService;
 
-    final PowerManager mPowerManager;
-    final PowerManagerInternal mPowerManagerInternal;
-
-    IWindowManager mIWindowManager;
-    NotificationManager mNotificationManager;
-
     // Stores and loads state on device and profile owners.
     private final Owners mOwners;
 
@@ -346,7 +345,7 @@
 
     final SparseArray<DevicePolicyData> mUserData = new SparseArray<>();
 
-    Handler mHandler = new Handler();
+    final Handler mHandler;
 
     BroadcastReceiver mReceiver = new BroadcastReceiver() {
         @Override
@@ -992,7 +991,6 @@
         boolean removed = false;
         if (DBG) Slog.d(LOG_TAG, "Handling package changes for user " + userHandle);
         DevicePolicyData policy = getUserData(userHandle);
-        IPackageManager pm = AppGlobals.getPackageManager();
         synchronized (this) {
             for (int i = policy.mAdminList.size() - 1; i >= 0; i--) {
                 ActiveAdmin aa = policy.mAdminList.get(i);
@@ -1001,9 +999,9 @@
                     // then check if the package and receiver still exist.
                     final String adminPackage = aa.info.getPackageName();
                     if (packageName == null || packageName.equals(adminPackage)) {
-                        if (pm.getPackageInfo(adminPackage, 0, userHandle) == null
-                                || pm.getReceiverInfo(aa.info.getComponent(), 0, userHandle)
-                                    == null) {
+                        if (mIPackageManager.getPackageInfo(adminPackage, 0, userHandle) == null
+                                || mIPackageManager.getReceiverInfo(
+                                    aa.info.getComponent(), 0, userHandle) == null) {
                             removed = true;
                             policy.mAdminList.remove(i);
                             policy.mAdminMap.remove(aa.info.getComponent());
@@ -1024,7 +1022,7 @@
                     || packageName.equals(policy.mDelegatedCertInstallerPackage))) {
                 try {
                     // Check if delegated cert installer package is removed.
-                    if (pm.getPackageInfo(
+                    if (mIPackageManager.getPackageInfo(
                             policy.mDelegatedCertInstallerPackage, 0, userHandle) == null) {
                         policy.mDelegatedCertInstallerPackage = null;
                         saveSettingsLocked(policy.mUserHandle);
@@ -1037,18 +1035,145 @@
     }
 
     /**
+     * Unit test will subclass it to inject mocks.
+     */
+    @VisibleForTesting
+    static class Injector {
+
+        private final Context mContext;
+
+        Injector(Context context) {
+            mContext = context;
+        }
+
+        Owners newOwners() {
+            return new Owners(mContext);
+        }
+
+        UserManager getUserManager() {
+            return UserManager.get(mContext);
+        }
+
+        NotificationManager getNotificationManager() {
+            return mContext.getSystemService(NotificationManager.class);
+        }
+
+        PowerManagerInternal getPowerManagerInternal() {
+            return LocalServices.getService(PowerManagerInternal.class);
+        }
+
+        IWindowManager getIWindowManager() {
+            return IWindowManager.Stub
+                    .asInterface(ServiceManager.getService(Context.WINDOW_SERVICE));
+        }
+
+        IActivityManager getIActivityManager() {
+            return ActivityManagerNative.getDefault();
+        }
+
+        IPackageManager getIPackageManager() {
+            return AppGlobals.getPackageManager();
+        }
+
+        IBackupManager getIBackupManager() {
+            return IBackupManager.Stub.asInterface(
+                    ServiceManager.getService(Context.BACKUP_SERVICE));
+        }
+
+        IAudioService getIAudioService() {
+            return IAudioService.Stub.asInterface(ServiceManager.getService(Context.AUDIO_SERVICE));
+        }
+
+        LockPatternUtils newLockPatternUtils() {
+            return new LockPatternUtils(mContext);
+        }
+
+        Looper getMyLooper() {
+            return Looper.myLooper();
+        }
+
+        long binderClearCallingIdentity() {
+            return Binder.clearCallingIdentity();
+        }
+
+        void binderRestoreCallingIdentity(long token) {
+            Binder.restoreCallingIdentity(token);
+        }
+
+        int binderGetCallingUid() {
+            return Binder.getCallingUid();
+        }
+
+        int binderGetCallingPid() {
+            return Binder.getCallingPid();
+        }
+
+        UserHandle binderGetCallingUserHandle() {
+            return Binder.getCallingUserHandle();
+        }
+
+        boolean binderIsCallingUidMyUid() {
+            return getCallingUid() == Process.myUid();
+        }
+
+        File environmentGetUserSystemDirectory(int userId) {
+            return Environment.getUserSystemDirectory(userId);
+        }
+
+        void powerManagerGoToSleep(long time, int reason, int flags) {
+            mContext.getSystemService(PowerManager.class).goToSleep(time, reason, flags);
+        }
+
+        boolean systemPropertiesGetBoolean(String key, boolean def) {
+            return SystemProperties.getBoolean(key, def);
+        }
+
+        long systemPropertiesGetLong(String key, long def) {
+            return SystemProperties.getLong(key, def);
+        }
+
+        String systemPropertiesGet(String key, String def) {
+            return SystemProperties.get(key, def);
+        }
+
+        String systemPropertiesGet(String key) {
+            return SystemProperties.get(key);
+        }
+
+        void systemPropertiesSet(String key, String value) {
+            SystemProperties.set(key, value);
+        }
+
+        boolean userManagerIsSplitSystemUser() {
+            return UserManager.isSplitSystemUser();
+        }
+
+        String getDevicePolicyFilePathForSystemUser() {
+            return "/data/system/";
+        }
+    }
+
+    /**
      * Instantiates the service.
      */
     public DevicePolicyManagerService(Context context) {
-        mContext = context;
-        mOwners = new Owners(mContext);
-        mUserManager = UserManager.get(mContext);
-        mHasFeature = context.getPackageManager().hasSystemFeature(
-                PackageManager.FEATURE_DEVICE_ADMIN);
-        mPowerManager = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
-        mPowerManagerInternal = LocalServices.getService(PowerManagerInternal.class);
-        mWakeLock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "DPM");
+        this(new Injector(context));
+    }
+
+    @VisibleForTesting
+    DevicePolicyManagerService(Injector injector) {
+        mInjector = injector;
+        mContext = Preconditions.checkNotNull(injector.mContext);
+        mHandler = new Handler(Preconditions.checkNotNull(injector.getMyLooper()));
+        mOwners = Preconditions.checkNotNull(injector.newOwners());
+
+        mUserManager = Preconditions.checkNotNull(injector.getUserManager());
+        mIPackageManager = Preconditions.checkNotNull(injector.getIPackageManager());
+
         mLocalService = new LocalService();
+
+        mHasFeature = mContext.getPackageManager()
+                .hasSystemFeature(PackageManager.FEATURE_DEVICE_ADMIN);
         if (!mHasFeature) {
             // Skip the rest of the initialization
             return;
@@ -1060,17 +1185,17 @@
         filter.addAction(Intent.ACTION_USER_STARTED);
         filter.addAction(KeyChain.ACTION_STORAGE_CHANGED);
         filter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
-        context.registerReceiverAsUser(mReceiver, UserHandle.ALL, filter, null, mHandler);
+        mContext.registerReceiverAsUser(mReceiver, UserHandle.ALL, filter, null, mHandler);
         filter = new IntentFilter();
         filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
         filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
         filter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
         filter.addAction(Intent.ACTION_PACKAGE_ADDED);
         filter.addDataScheme("package");
-        context.registerReceiverAsUser(mReceiver, UserHandle.ALL, filter, null, mHandler);
+        mContext.registerReceiverAsUser(mReceiver, UserHandle.ALL, filter, null, mHandler);
         filter = new IntentFilter();
         filter.addAction(Intent.ACTION_MANAGED_PROFILE_ADDED);
-        context.registerReceiverAsUser(mReceiver, UserHandle.ALL, filter, null, mHandler);
+        mContext.registerReceiverAsUser(mReceiver, UserHandle.ALL, filter, null, mHandler);
 
         LocalServices.addService(DevicePolicyManagerInternal.class, mLocalService);
     }
@@ -1080,6 +1205,7 @@
      * @param userHandle the user for whom to load the policy data
      * @return
      */
+    @NonNull
     DevicePolicyData getUserData(int userHandle) {
         synchronized (this) {
             DevicePolicyData policy = mUserData.get(userHandle);
@@ -1103,17 +1229,17 @@
      * @return
      */
     DevicePolicyData getUserDataUnchecked(int userHandle) {
-        long ident = Binder.clearCallingIdentity();
+        long ident = mInjector.binderClearCallingIdentity();
         try {
             return getUserData(userHandle);
         } finally {
-            Binder.restoreCallingIdentity(ident);
+            mInjector.binderRestoreCallingIdentity(ident);
         }
     }
 
     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;
             }
@@ -1124,7 +1250,7 @@
             if (policy != null) {
                 mUserData.remove(userHandle);
             }
-            File policyFile = new File(Environment.getUserSystemDirectory(userHandle),
+            File policyFile = new File(mInjector.environmentGetUserSystemDirectory(userHandle),
                     DEVICE_POLICIES_XML);
             policyFile.delete();
             Slog.i(LOG_TAG, "Removed device policy file " + policyFile.getAbsolutePath());
@@ -1164,7 +1290,7 @@
             alarmTime = now + alarmInterval;
         }
 
-        long token = Binder.clearCallingIdentity();
+        long token = mInjector.binderClearCallingIdentity();
         try {
             AlarmManager am = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
             PendingIntent pi = PendingIntent.getBroadcastAsUser(context, REQUEST_EXPIRE_PASSWORD,
@@ -1176,26 +1302,10 @@
                 am.set(AlarmManager.RTC, alarmTime, pi);
             }
         } finally {
-            Binder.restoreCallingIdentity(token);
+            mInjector.binderRestoreCallingIdentity(token);
         }
     }
 
-    private IWindowManager getWindowManager() {
-        if (mIWindowManager == null) {
-            IBinder b = ServiceManager.getService(Context.WINDOW_SERVICE);
-            mIWindowManager = IWindowManager.Stub.asInterface(b);
-        }
-        return mIWindowManager;
-    }
-
-    private NotificationManager getNotificationManager() {
-        if (mNotificationManager == null) {
-            mNotificationManager =
-                    (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE);
-        }
-        return mNotificationManager;
-    }
-
     ActiveAdmin getActiveAdminUncheckedLocked(ComponentName who, int userHandle) {
         ActiveAdmin admin = getUserData(userHandle).mAdminMap.get(who);
         if (admin != null
@@ -1208,7 +1318,7 @@
 
     ActiveAdmin getActiveAdminForCallerLocked(ComponentName who, int reqPolicy)
             throws SecurityException {
-        final int callingUid = Binder.getCallingUid();
+        final int callingUid = mInjector.binderGetCallingUid();
 
         ActiveAdmin result = getActiveAdminWithPolicyForUidLocked(who, reqPolicy, callingUid);
         if (result != null) {
@@ -1232,7 +1342,7 @@
                     + admin.info.getTagForPolicy(reqPolicy));
         } else {
             throw new SecurityException("No active admin owned by uid "
-                    + Binder.getCallingUid() + " for policy #" + reqPolicy);
+                    + mInjector.binderGetCallingUid() + " for policy #" + reqPolicy);
         }
     }
 
@@ -1248,7 +1358,7 @@
             }
             if (admin.getUid() != uid) {
                 throw new SecurityException("Admin " + who + " is not owned by uid "
-                        + Binder.getCallingUid());
+                        + mInjector.binderGetCallingUid());
             }
             if (isActiveAdminWithPolicyForUserLocked(admin, reqPolicy, userId)) {
                 return admin;
@@ -1275,12 +1385,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;
             }
@@ -1410,11 +1520,11 @@
         }
     }
 
-    private static JournaledFile makeJournaledFile(int userHandle) {
-        final String base = userHandle == 0
-                ? "/data/system/" + DEVICE_POLICIES_XML
-                : new File(Environment.getUserSystemDirectory(userHandle), DEVICE_POLICIES_XML)
-                        .getAbsolutePath();
+    private JournaledFile makeJournaledFile(int userHandle) {
+        final String base = userHandle == UserHandle.USER_SYSTEM
+                ? mInjector.getDevicePolicyFilePathForSystemUser() + DEVICE_POLICIES_XML
+                : new File(mInjector.environmentGetUserSystemDirectory(userHandle),
+                        DEVICE_POLICIES_XML).getAbsolutePath();
         return new JournaledFile(new File(base), new File(base + ".tmp"));
     }
 
@@ -1513,6 +1623,7 @@
             journal.commit();
             sendChangedNotification(userHandle);
         } catch (IOException e) {
+            Slog.w(LOG_TAG, "failed writing file", e);
             try {
                 if (stream != null) {
                     stream.close();
@@ -1527,11 +1638,11 @@
     private void sendChangedNotification(int userHandle) {
         Intent intent = new Intent(DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED);
         intent.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
-        long ident = Binder.clearCallingIdentity();
+        long ident = mInjector.binderClearCallingIdentity();
         try {
             mContext.sendBroadcastAsUser(intent, new UserHandle(userHandle));
         } finally {
-            Binder.restoreCallingIdentity(ident);
+            mInjector.binderRestoreCallingIdentity(ident);
         }
     }
 
@@ -1663,9 +1774,9 @@
         // sufficiently what is currently set.  Note that this is only
         // a sanity check in case the two get out of sync; this should
         // never normally happen.
-        final long identity = Binder.clearCallingIdentity();
+        final long identity = mInjector.binderClearCallingIdentity();
         try {
-            LockPatternUtils utils = new LockPatternUtils(mContext);
+            LockPatternUtils utils = mInjector.newLockPatternUtils();
             if (utils.getActivePasswordQuality(userHandle) < policy.mActivePasswordQuality) {
                 Slog.w(LOG_TAG, "Active password quality 0x"
                         + Integer.toHexString(policy.mActivePasswordQuality)
@@ -1681,7 +1792,7 @@
                 policy.mActivePasswordNonLetter = 0;
             }
         } finally {
-            Binder.restoreCallingIdentity(identity);
+            mInjector.binderRestoreCallingIdentity(identity);
         }
 
         validatePasswordOwnerLocked(policy);
@@ -1695,26 +1806,26 @@
     }
 
     private void updateLockTaskPackagesLocked(List<String> packages, int userId) {
-        IActivityManager am = ActivityManagerNative.getDefault();
-        long ident = Binder.clearCallingIdentity();
+        long ident = mInjector.binderClearCallingIdentity();
         try {
-            am.updateLockTaskPackages(userId, packages.toArray(new String[packages.size()]));
+            mInjector.getIActivityManager()
+                    .updateLockTaskPackages(userId, packages.toArray(new String[packages.size()]));
         } catch (RemoteException e) {
             // Not gonna happen.
         } finally {
-            Binder.restoreCallingIdentity(ident);
+            mInjector.binderRestoreCallingIdentity(ident);
         }
     }
 
     private void updateDeviceOwnerLocked() {
-        IActivityManager am = ActivityManagerNative.getDefault();
-        long ident = Binder.clearCallingIdentity();
+        long ident = mInjector.binderClearCallingIdentity();
         try {
-            am.updateDeviceOwner(getDeviceOwner());
+            mInjector.getIActivityManager()
+                    .updateDeviceOwner(getDeviceOwner());
         } catch (RemoteException e) {
             // Not gonna happen.
         } finally {
-            Binder.restoreCallingIdentity(ident);
+            mInjector.binderRestoreCallingIdentity(ident);
         }
     }
 
@@ -1759,17 +1870,17 @@
         // Ensure the status of the camera is synced down to the system. Interested native services
         // should monitor this value and act accordingly.
         String cameraPropertyForUser = SYSTEM_PROP_DISABLE_CAMERA_PREFIX + policy.mUserHandle;
-        boolean systemState = SystemProperties.getBoolean(cameraPropertyForUser, false);
+        boolean systemState = mInjector.systemPropertiesGetBoolean(cameraPropertyForUser, false);
         boolean cameraDisabled = getCameraDisabled(null, policy.mUserHandle);
         if (cameraDisabled != systemState) {
-            long token = Binder.clearCallingIdentity();
+            long token = mInjector.binderClearCallingIdentity();
             try {
                 String value = cameraDisabled ? "1" : "0";
                 if (DBG) Slog.v(LOG_TAG, "Change in camera state ["
                         + cameraPropertyForUser + "] = " + value);
-                SystemProperties.set(cameraPropertyForUser, value);
+                mInjector.systemPropertiesSet(cameraPropertyForUser, value);
             } finally {
-                Binder.restoreCallingIdentity(token);
+                mInjector.binderRestoreCallingIdentity(token);
             }
         }
     }
@@ -1789,7 +1900,7 @@
     }
 
     private void onLockSettingsReady() {
-        getUserData(UserHandle.USER_OWNER);
+        getUserData(UserHandle.USER_SYSTEM);
         loadOwners();
         cleanUpOldUsers();
         // Register an observer for watching for user setup complete.
@@ -1809,14 +1920,13 @@
 
     private void ensureDeviceOwnerUserStarted() {
         if (mOwners.hasDeviceOwner()) {
-            final IActivityManager am = ActivityManagerNative.getDefault();
             final int userId = mOwners.getDeviceOwnerUserId();
             if (VERBOSE_LOG) {
                 Log.v(LOG_TAG, "Starting non-system DO user: " + userId);
             }
             if (userId != UserHandle.USER_SYSTEM) {
                 try {
-                    am.startUserInBackground(userId);
+                    mInjector.getIActivityManager().startUserInBackground(userId);
 
                     // STOPSHIP Prevent the DO user from being killed.
 
@@ -1917,7 +2027,7 @@
                 Log.e(LOG_TAG, "Could not connect to KeyChain service", e);
             }
             if (!hasCert) {
-                getNotificationManager().cancelAsUser(
+                mInjector.getNotificationManager().cancelAsUser(
                         null, MONITORING_CERT_NOTIFICATION_ID, userHandle);
                 return;
             }
@@ -1962,7 +2072,7 @@
                         com.android.internal.R.color.system_notification_accent_color))
                 .build();
 
-            getNotificationManager().notifyAsUser(
+            mInjector.getNotificationManager().notifyAsUser(
                     null, MONITORING_CERT_NOTIFICATION_ID, noti, userHandle);
         }
     }
@@ -1991,7 +2101,7 @@
             throw new IllegalArgumentException("Bad admin: " + adminReceiver);
         }
         synchronized (this) {
-            long ident = Binder.clearCallingIdentity();
+            long ident = mInjector.binderClearCallingIdentity();
             try {
                 if (!refreshing
                         && getActiveAdminUncheckedLocked(adminReceiver, userHandle) != null) {
@@ -2018,7 +2128,7 @@
                 sendAdminCommandLocked(newAdmin, DeviceAdminReceiver.ACTION_DEVICE_ADMIN_ENABLED,
                         onEnableData, null);
             } finally {
-                Binder.restoreCallingIdentity(ident);
+                mInjector.binderRestoreCallingIdentity(ident);
             }
         }
     }
@@ -2112,7 +2222,7 @@
             if (admin == null) {
                 return;
             }
-            if (admin.getUid() != Binder.getCallingUid()) {
+            if (admin.getUid() != mInjector.binderGetCallingUid()) {
                 // Active device owners must remain active admins.
                 if (isDeviceOwner(adminReceiver.getPackageName())) {
                     return;
@@ -2120,11 +2230,11 @@
                 mContext.enforceCallingOrSelfPermission(
                         android.Manifest.permission.MANAGE_DEVICE_ADMINS, null);
             }
-            long ident = Binder.clearCallingIdentity();
+            long ident = mInjector.binderClearCallingIdentity();
             try {
                 removeActiveAdminLocked(adminReceiver, userHandle);
             } finally {
-                Binder.restoreCallingIdentity(ident);
+                mInjector.binderRestoreCallingIdentity(ident);
             }
         }
     }
@@ -2394,7 +2504,7 @@
                     || activeAdmin.crossProfileWidgetProviders.isEmpty()) {
                 return null;
             }
-            if (Binder.getCallingUid() == Process.myUid()) {
+            if (mInjector.binderIsCallingUidMyUid()) {
                 return new ArrayList<>(activeAdmin.crossProfileWidgetProviders);
             } else {
                 return activeAdmin.crossProfileWidgetProviders;
@@ -2959,7 +3069,7 @@
             }
         }
 
-        int callingUid = Binder.getCallingUid();
+        int callingUid = mInjector.binderGetCallingUid();
         DevicePolicyData policy = getUserData(userHandle);
         if (policy.mPasswordOwner >= 0 && policy.mPasswordOwner != callingUid) {
             Slog.w(LOG_TAG, "resetPassword: already set by another uid and not entered by user");
@@ -2975,7 +3085,7 @@
 
         // Don't do this with the lock held, because it is going to call
         // back in to the service.
-        long ident = Binder.clearCallingIdentity();
+        long ident = mInjector.binderClearCallingIdentity();
         try {
             LockPatternUtils utils = new LockPatternUtils(mContext);
             if (!TextUtils.isEmpty(password)) {
@@ -2996,7 +3106,7 @@
                 }
             }
         } finally {
-            Binder.restoreCallingIdentity(ident);
+            mInjector.binderRestoreCallingIdentity(ident);
         }
 
         return true;
@@ -3004,10 +3114,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);
             }
         }
     }
@@ -3017,7 +3127,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;
         }
     }
@@ -3046,7 +3156,7 @@
             return;
         }
 
-        long ident = Binder.clearCallingIdentity();
+        long ident = mInjector.binderClearCallingIdentity();
         try {
             if (timeMs <= 0) {
                 timeMs = Integer.MAX_VALUE;
@@ -3058,9 +3168,11 @@
             }
 
             policy.mLastMaximumTimeToLock = timeMs;
-            mPowerManagerInternal.setMaximumScreenOffTimeoutFromDeviceAdmin((int)timeMs);
+            // TODO It can overflow.  Cap it.
+            mInjector.getPowerManagerInternal()
+                    .setMaximumScreenOffTimeoutFromDeviceAdmin((int)timeMs);
         } finally {
-            Binder.restoreCallingIdentity(ident);
+            mInjector.binderRestoreCallingIdentity(ident);
         }
     }
 
@@ -3112,26 +3224,21 @@
     }
 
     private void lockNowUnchecked() {
-        long ident = Binder.clearCallingIdentity();
+        long ident = mInjector.binderClearCallingIdentity();
         try {
             // Power off the display
-            mPowerManager.goToSleep(SystemClock.uptimeMillis(),
+            mInjector.powerManagerGoToSleep(SystemClock.uptimeMillis(),
                     PowerManager.GO_TO_SLEEP_REASON_DEVICE_ADMIN, 0);
             // Ensure the device is locked
             new LockPatternUtils(mContext).requireStrongAuth(
                     STRONG_AUTH_REQUIRED_AFTER_DPM_LOCK_NOW, UserHandle.USER_ALL);
-            getWindowManager().lockNow(null);
+            mInjector.getIWindowManager().lockNow(null);
         } catch (RemoteException e) {
         } finally {
-            Binder.restoreCallingIdentity(ident);
+            mInjector.binderRestoreCallingIdentity(ident);
         }
     }
 
-    private boolean isExtStorageEncrypted() {
-        String state = SystemProperties.get("vold.decrypt");
-        return !"".equals(state);
-    }
-
     @Override
     public void enforceCanManageCaCerts(ComponentName who) {
         if (who == null) {
@@ -3146,7 +3253,7 @@
     }
 
     private boolean isCallerDelegatedCertInstaller() {
-        final int callingUid = Binder.getCallingUid();
+        final int callingUid = mInjector.binderGetCallingUid();
         final int userHandle = UserHandle.getUserId(callingUid);
         synchronized (this) {
             final DevicePolicyData policy = getUserData(userHandle);
@@ -3181,7 +3288,7 @@
         }
 
         final UserHandle userHandle = new UserHandle(UserHandle.getCallingUserId());
-        final long id = Binder.clearCallingIdentity();
+        final long id = mInjector.binderClearCallingIdentity();
         try {
             final KeyChainConnection keyChainConnection = KeyChain.bindAsUser(mContext, userHandle);
             try {
@@ -3196,7 +3303,7 @@
             Log.w(LOG_TAG, "installCaCertsToKeyChain(): ", e1);
             Thread.currentThread().interrupt();
         } finally {
-            Binder.restoreCallingIdentity(id);
+            mInjector.binderRestoreCallingIdentity(id);
         }
         return false;
     }
@@ -3212,7 +3319,7 @@
         enforceCanManageCaCerts(admin);
 
         final UserHandle userHandle = new UserHandle(UserHandle.getCallingUserId());
-        final long id = Binder.clearCallingIdentity();
+        final long id = mInjector.binderClearCallingIdentity();
         try {
             final KeyChainConnection keyChainConnection = KeyChain.bindAsUser(mContext, userHandle);
             try {
@@ -3228,7 +3335,7 @@
             Log.w(LOG_TAG, "CaCertUninstaller: ", ie);
             Thread.currentThread().interrupt();
         } finally {
-            Binder.restoreCallingIdentity(id);
+            mInjector.binderRestoreCallingIdentity(id);
         }
     }
 
@@ -3244,7 +3351,7 @@
             }
         }
         final UserHandle userHandle = new UserHandle(UserHandle.getCallingUserId());
-        final long id = Binder.clearCallingIdentity();
+        final long id = mInjector.binderClearCallingIdentity();
         try {
           final KeyChainConnection keyChainConnection = KeyChain.bindAsUser(mContext, userHandle);
           try {
@@ -3259,7 +3366,7 @@
             Log.w(LOG_TAG, "Interrupted while installing certificate", e);
             Thread.currentThread().interrupt();
         } finally {
-            Binder.restoreCallingIdentity(id);
+            mInjector.binderRestoreCallingIdentity(id);
         }
         return false;
     }
@@ -3268,11 +3375,11 @@
     public void choosePrivateKeyAlias(final int uid, final Uri uri, final String alias,
             final IBinder response) {
         // Caller UID needs to be trusted, so we restrict this method to SYSTEM_UID callers.
-        if (UserHandle.getAppId(Binder.getCallingUid()) != Process.SYSTEM_UID) {
+        if (UserHandle.getAppId(mInjector.binderGetCallingUid()) != Process.SYSTEM_UID) {
             return;
         }
 
-        final UserHandle caller = Binder.getCallingUserHandle();
+        final UserHandle caller = mInjector.binderGetCallingUserHandle();
         // If there is a profile owner, redirect to that; otherwise query the device owner.
         ComponentName aliasChooser = getProfileOwner(caller.getIdentifier());
         if (aliasChooser == null && caller.isOwner()) {
@@ -3293,7 +3400,7 @@
         intent.putExtra(DeviceAdminReceiver.EXTRA_CHOOSE_PRIVATE_KEY_ALIAS, alias);
         intent.putExtra(DeviceAdminReceiver.EXTRA_CHOOSE_PRIVATE_KEY_RESPONSE, response);
 
-        final long id = Binder.clearCallingIdentity();
+        final long id = mInjector.binderClearCallingIdentity();
         try {
             mContext.sendOrderedBroadcastAsUser(intent, caller, null, new BroadcastReceiver() {
                 @Override
@@ -3303,7 +3410,7 @@
                 }
             }, null, Activity.RESULT_OK, null, null);
         } finally {
-            Binder.restoreCallingIdentity(id);
+            mInjector.binderRestoreCallingIdentity(id);
         }
     }
 
@@ -3372,20 +3479,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 = Binder.clearCallingIdentity();
+            long ident = mInjector.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(
@@ -3401,22 +3502,22 @@
                 wipeDeviceOrUserLocked(wipeExtRequested, userHandle,
                         "DevicePolicyManager.wipeData() from " + source);
             } finally {
-                Binder.restoreCallingIdentity(ident);
+                mInjector.binderRestoreCallingIdentity(ident);
             }
         }
     }
 
     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() {
                 @Override
                 public void run() {
                     try {
-                        IActivityManager am = ActivityManagerNative.getDefault();
+                        IActivityManager am = mInjector.getIActivityManager();
                         if (am.getCurrentUser().id == userHandle) {
-                            am.switchUser(UserHandle.USER_OWNER);
+                            am.switchUser(UserHandle.USER_SYSTEM);
                         }
 
                         boolean isManagedProfile = isManagedProfile(userHandle);
@@ -3442,11 +3543,11 @@
                 .setColor(mContext.getColor(R.color.system_notification_accent_color))
                 .setStyle(new Notification.BigTextStyle().bigText(contentText))
                 .build();
-        getNotificationManager().notify(PROFILE_WIPED_NOTIFICATION_ID, notification);
+        mInjector.getNotificationManager().notify(PROFILE_WIPED_NOTIFICATION_ID, notification);
     }
 
     private void clearWipeProfileNotification() {
-        getNotificationManager().cancel(PROFILE_WIPED_NOTIFICATION_ID);
+        mInjector.getNotificationManager().cancel(PROFILE_WIPED_NOTIFICATION_ID);
     }
 
     @Override
@@ -3506,7 +3607,7 @@
                     || p.mActivePasswordNumeric != numbers
                     || p.mActivePasswordSymbols != symbols
                     || p.mActivePasswordNonLetter != nonletter) {
-                long ident = Binder.clearCallingIdentity();
+                long ident = mInjector.binderClearCallingIdentity();
                 try {
                     p.mActivePasswordQuality = quality;
                     p.mActivePasswordLength = length;
@@ -3524,7 +3625,7 @@
                             DeviceAdminReceiver.ACTION_PASSWORD_CHANGED,
                             DeviceAdminInfo.USES_POLICY_LIMIT_PASSWORD, userHandle);
                 } finally {
-                    Binder.restoreCallingIdentity(ident);
+                    mInjector.binderRestoreCallingIdentity(ident);
                 }
             }
         }
@@ -3560,7 +3661,7 @@
         mContext.enforceCallingOrSelfPermission(
                 android.Manifest.permission.BIND_DEVICE_ADMIN, null);
 
-        long ident = Binder.clearCallingIdentity();
+        long ident = mInjector.binderClearCallingIdentity();
         try {
             boolean wipeData = false;
             int identifier = 0;
@@ -3591,7 +3692,7 @@
                         "reportFailedPasswordAttempt()");
             }
         } finally {
-            Binder.restoreCallingIdentity(ident);
+            mInjector.binderRestoreCallingIdentity(ident);
         }
     }
 
@@ -3604,7 +3705,7 @@
         synchronized (this) {
             DevicePolicyData policy = getUserData(userHandle);
             if (policy.mFailedPasswordAttempts != 0 || policy.mPasswordOwner >= 0) {
-                long ident = Binder.clearCallingIdentity();
+                long ident = mInjector.binderClearCallingIdentity();
                 try {
                     policy.mFailedPasswordAttempts = 0;
                     policy.mPasswordOwner = -1;
@@ -3615,7 +3716,7 @@
                                 DeviceAdminInfo.USES_POLICY_WATCH_LOGIN, userHandle);
                     }
                 } finally {
-                    Binder.restoreCallingIdentity(ident);
+                    mInjector.binderRestoreCallingIdentity(ident);
                 }
             }
         }
@@ -3630,8 +3731,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);
 
@@ -3647,8 +3748,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;
@@ -3666,11 +3767,11 @@
 
             // Reset the global proxy accordingly
             // Do this using system permissions, as apps cannot write to secure settings
-            long origId = Binder.clearCallingIdentity();
+            long origId = mInjector.binderClearCallingIdentity();
             try {
                 resetGlobalProxyLocked(policy);
             } finally {
-                Binder.restoreCallingIdentity(origId);
+                mInjector.binderRestoreCallingIdentity(origId);
             }
             return null;
         }
@@ -3683,7 +3784,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();
@@ -3705,13 +3806,13 @@
         synchronized (this) {
             getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER);
         }
-        long token = Binder.clearCallingIdentity();
+        long token = mInjector.binderClearCallingIdentity();
         try {
             ConnectivityManager connectivityManager = (ConnectivityManager)
                     mContext.getSystemService(Context.CONNECTIVITY_SERVICE);
             connectivityManager.setGlobalProxy(proxyInfo);
         } finally {
-            Binder.restoreCallingIdentity(token);
+            mInjector.binderRestoreCallingIdentity(token);
         }
     }
 
@@ -3771,10 +3872,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;
             }
@@ -3793,7 +3893,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();
@@ -3873,15 +3973,15 @@
      * {@link DevicePolicyManager#ENCRYPTION_STATUS_ACTIVE}.
      */
     private int getEncryptionStatus() {
-        String status = SystemProperties.get("ro.crypto.state", "unsupported");
+        String status = mInjector.systemPropertiesGet("ro.crypto.state", "unsupported");
         if ("encrypted".equalsIgnoreCase(status)) {
-            final long token = Binder.clearCallingIdentity();
+            final long token = mInjector.binderClearCallingIdentity();
             try {
                 return LockPatternUtils.isDeviceEncrypted()
                         ? DevicePolicyManager.ENCRYPTION_STATUS_ACTIVE
                         : DevicePolicyManager.ENCRYPTION_STATUS_ACTIVE_DEFAULT_KEY;
             } finally {
-                Binder.restoreCallingIdentity(token);
+                mInjector.binderRestoreCallingIdentity(token);
             }
         } else if ("unencrypted".equalsIgnoreCase(status)) {
             return DevicePolicyManager.ENCRYPTION_STATUS_INACTIVE;
@@ -3946,13 +4046,13 @@
     }
 
     private void updateScreenCaptureDisabledInWindowManager(int userHandle, boolean disabled) {
-        long ident = Binder.clearCallingIdentity();
+        long ident = mInjector.binderClearCallingIdentity();
         try {
-            getWindowManager().setScreenCaptureDisabled(userHandle, disabled);
+            mInjector.getIWindowManager().setScreenCaptureDisabled(userHandle, disabled);
         } catch (RemoteException e) {
             Log.w(LOG_TAG, "Unable to notify WindowManager.", e);
         } finally {
-            Binder.restoreCallingIdentity(ident);
+            mInjector.binderRestoreCallingIdentity(ident);
         }
     }
 
@@ -3977,12 +4077,12 @@
 
         // Turn AUTO_TIME on in settings if it is required
         if (required) {
-            long ident = Binder.clearCallingIdentity();
+            long ident = mInjector.binderClearCallingIdentity();
             try {
                 Settings.Global.putInt(mContext.getContentResolver(),
                         Settings.Global.AUTO_TIME, 1 /* AUTO_TIME on */);
             } finally {
-                Binder.restoreCallingIdentity(ident);
+                mInjector.binderRestoreCallingIdentity(ident);
             }
         }
     }
@@ -4091,7 +4191,7 @@
             return 0;
         }
         enforceCrossUserPermission(userHandle);
-        long ident = Binder.clearCallingIdentity();
+        long ident = mInjector.binderClearCallingIdentity();
         try {
             synchronized (this) {
                 if (who != null) {
@@ -4134,7 +4234,7 @@
                 return which;
             }
         } finally {
-            Binder.restoreCallingIdentity(ident);
+            mInjector.binderRestoreCallingIdentity(ident);
         }
     }
 
@@ -4144,7 +4244,7 @@
             return false;
         }
         if (packageName == null
-                || !Owners.isInstalledForUser(packageName, userId)) {
+                || !isPackageInstalledForUser(packageName, userId)) {
             throw new IllegalArgumentException("Invalid package name " + packageName
                     + " for device owner");
         }
@@ -4152,15 +4252,13 @@
             enforceCanSetDeviceOwner(userId);
 
             // Shutting down backup manager service permanently.
-            long ident = Binder.clearCallingIdentity();
+            long ident = mInjector.binderClearCallingIdentity();
             try {
-                IBackupManager ibm = IBackupManager.Stub.asInterface(
-                        ServiceManager.getService(Context.BACKUP_SERVICE));
-                ibm.setBackupServiceActive(UserHandle.USER_OWNER, false);
+                mInjector.getIBackupManager().setBackupServiceActive(UserHandle.USER_SYSTEM, false);
             } catch (RemoteException e) {
                 throw new IllegalStateException("Failed deactivating backup service.", e);
             } finally {
-                Binder.restoreCallingIdentity(ident);
+                mInjector.binderRestoreCallingIdentity(ident);
             }
 
             mOwners.setDeviceOwner(packageName, ownerName, userId);
@@ -4168,12 +4266,12 @@
             updateDeviceOwnerLocked();
             Intent intent = new Intent(DevicePolicyManager.ACTION_DEVICE_OWNER_CHANGED);
 
-            ident = Binder.clearCallingIdentity();
+            ident = mInjector.binderClearCallingIdentity();
             try {
                 // TODO Send to system too?
                 mContext.sendBroadcastAsUser(intent, new UserHandle(userId));
             } finally {
-                Binder.restoreCallingIdentity(ident);
+                mInjector.binderRestoreCallingIdentity(ident);
             }
             return true;
         }
@@ -4205,13 +4303,16 @@
         if (!mHasFeature) {
             return null;
         }
+        // TODO: Do we really need it?  getDeviceOwner() doesn't require it.
         mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USERS, null);
         synchronized (this) {
             if (!mOwners.hasDeviceOwner()) {
                 return null;
             }
+            // TODO This totally ignores the name passed to setDeviceOwner (change for b/20679292)
+            // Should setDeviceOwner/ProfileOwner still take a name?
             String deviceOwnerPackage = mOwners.getDeviceOwnerPackageName();
-            return getApplicationLabel(deviceOwnerPackage, UserHandle.USER_OWNER);
+            return getApplicationLabel(deviceOwnerPackage, UserHandle.USER_SYSTEM);
         }
     }
 
@@ -4222,7 +4323,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);
@@ -4238,7 +4339,7 @@
         Preconditions.checkNotNull(packageName, "packageName is null");
         try {
             int uid = mContext.getPackageManager().getPackageUid(packageName, 0);
-            if (uid != Binder.getCallingUid()) {
+            if (uid != mInjector.binderGetCallingUid()) {
                 throw new SecurityException("Invalid packageName");
             }
         } catch (NameNotFoundException e) {
@@ -4248,21 +4349,19 @@
             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();
             updateDeviceOwnerLocked();
             // Reactivate backup service.
-            long ident = Binder.clearCallingIdentity();
+            long ident = mInjector.binderClearCallingIdentity();
             try {
-                IBackupManager ibm = IBackupManager.Stub.asInterface(
-                        ServiceManager.getService(Context.BACKUP_SERVICE));
-                ibm.setBackupServiceActive(UserHandle.USER_OWNER, true);
+                mInjector.getIBackupManager().setBackupServiceActive(UserHandle.USER_SYSTEM, true);
             } catch (RemoteException e) {
                 throw new IllegalStateException("Failed reactivating backup service.", e);
             } finally {
-                Binder.restoreCallingIdentity(ident);
+                mInjector.binderRestoreCallingIdentity(ident);
             }
         }
     }
@@ -4274,15 +4373,16 @@
         }
         if (initializer == null ||
                 !mOwners.hasDeviceOwner() ||
-                !Owners.isInstalledForUser(initializer.getPackageName(),
+                !isPackageInstalledForUser(initializer.getPackageName(),
                         mOwners.getDeviceOwnerUserId())) {
             throw new IllegalArgumentException("Invalid component name " + initializer
                     + " for device initializer or no device owner set");
         }
         boolean isInitializerSystemApp;
         try {
-            isInitializerSystemApp = isSystemApp(AppGlobals.getPackageManager(),
-                    initializer.getPackageName(), Binder.getCallingUserHandle().getIdentifier());
+            isInitializerSystemApp = isSystemApp(mIPackageManager,
+                    initializer.getPackageName(),
+                    mInjector.binderGetCallingUserHandle().getIdentifier());
         } catch (RemoteException | IllegalArgumentException e) {
             isInitializerSystemApp = false;
             Slog.e(LOG_TAG, "Fail to check if device initialzer is system app.", e);
@@ -4365,9 +4465,9 @@
 
         ActiveAdmin admin = getActiveAdminUncheckedLocked(who, UserHandle.getCallingUserId());
 
-        if (admin.getUid() != Binder.getCallingUid()) {
+        if (admin.getUid() != mInjector.binderGetCallingUid()) {
             throw new SecurityException("Admin " + who + " is not owned by uid "
-                    + Binder.getCallingUid());
+                    + mInjector.binderGetCallingUid());
         }
 
         if (!isDeviceInitializer(admin.info.getPackageName())
@@ -4376,12 +4476,12 @@
                     "clearDeviceInitializer can only be called by the device initializer/owner");
         }
         synchronized (this) {
-            long ident = Binder.clearCallingIdentity();
+            long ident = mInjector.binderClearCallingIdentity();
             try {
                 mOwners.clearDeviceInitializer();
                 mOwners.writeDeviceOwner();
             } finally {
-                Binder.restoreCallingIdentity(ident);
+                mInjector.binderRestoreCallingIdentity(ident);
             }
         }
     }
@@ -4392,7 +4492,7 @@
             return false;
         }
         if (who == null
-                || !Owners.isInstalledForUser(who.getPackageName(), userHandle)) {
+                || !isPackageInstalledForUser(who.getPackageName(), userHandle)) {
             throw new IllegalArgumentException("Component " + who
                     + " not installed for userId:" + userHandle);
         }
@@ -4409,7 +4509,7 @@
         if (!mHasFeature) {
             return;
         }
-        UserHandle callingUser = Binder.getCallingUserHandle();
+        UserHandle callingUser = mInjector.binderGetCallingUserHandle();
         // Check if this is the profile owner who is calling
         getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
         synchronized (this) {
@@ -4429,15 +4529,15 @@
         policy.mStatusBarDisabled = false;
         saveSettingsLocked(userId);
 
-        final long ident = Binder.clearCallingIdentity();
+        final long ident = mInjector.binderClearCallingIdentity();
         try {
             clearUserRestrictions(userHandle);
-            AppGlobals.getPackageManager().updatePermissionFlagsForAllApps(
+            mIPackageManager.updatePermissionFlagsForAllApps(
                     PackageManager.FLAG_PERMISSION_POLICY_FIXED,
                     0  /* flagValues */, userHandle.getIdentifier());
         } catch (RemoteException re) {
         } finally {
-            Binder.restoreCallingIdentity(ident);
+            mInjector.binderRestoreCallingIdentity(ident);
         }
     }
 
@@ -4445,11 +4545,9 @@
     private void clearUserRestrictions(UserHandle userHandle) {
         Bundle userRestrictions = mUserManager.getUserRestrictions();
         mUserManager.setUserRestrictions(new Bundle(), userHandle);
-        IAudioService iAudioService = IAudioService.Stub.asInterface(
-                ServiceManager.getService(Context.AUDIO_SERVICE));
         if (userRestrictions.getBoolean(UserManager.DISALLOW_ADJUST_VOLUME)) {
             try {
-                iAudioService.setMasterMute(true, 0, mContext.getPackageName(),
+                mInjector.getIAudioService().setMasterMute(true, 0, mContext.getPackageName(),
                         userHandle.getIdentifier());
             } catch (RemoteException e) {
                 // Not much we can do here.
@@ -4457,7 +4555,7 @@
         }
         if (userRestrictions.getBoolean(UserManager.DISALLOW_UNMUTE_MICROPHONE)) {
             try {
-                iAudioService.setMicrophoneMute(true, mContext.getPackageName(),
+                mInjector.getIAudioService().setMicrophoneMute(true, mContext.getPackageName(),
                         userHandle.getIdentifier());
             } catch (RemoteException e) {
                 // Not much we can do here.
@@ -4474,9 +4572,7 @@
         if (!mHasFeature) {
             return true;
         }
-        DevicePolicyData policy = getUserData(userHandle);
-        // If policy is null, return true, else check if the setup has completed.
-        return policy == null || policy.mUserSetupComplete;
+        return getUserData(userHandle).mUserSetupComplete;
     }
 
     @Override
@@ -4497,18 +4593,17 @@
                         "This method can only be called by device initializers");
             }
 
-            long id = Binder.clearCallingIdentity();
+            long id = mInjector.binderClearCallingIdentity();
             try {
                 if (!isDeviceOwner(activeAdmin.info.getPackageName())) {
-                    IPackageManager ipm = AppGlobals.getPackageManager();
-                    ipm.setComponentEnabledSetting(who,
+                    mIPackageManager.setComponentEnabledSetting(who,
                             PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
                             PackageManager.DONT_KILL_APP, userId);
 
                     removeActiveAdmin(who, userId);
                 }
 
-                if (userId == UserHandle.USER_OWNER) {
+                if (userId == UserHandle.USER_SYSTEM) {
                     Settings.Global.putInt(mContext.getContentResolver(),
                             Settings.Global.DEVICE_PROVISIONED, 1);
                 }
@@ -4518,7 +4613,7 @@
                 Log.i(LOG_TAG, "Can't talk to package manager", e);
                 return false;
             } finally {
-                restoreCallingIdentity(id);
+                mInjector.binderRestoreCallingIdentity(id);
             }
             return true;
         }
@@ -4536,7 +4631,7 @@
             getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
             int userId = UserHandle.getCallingUserId();
 
-            long id = Binder.clearCallingIdentity();
+            long id = mInjector.binderClearCallingIdentity();
             try {
                 mUserManager.setUserEnabled(userId);
                 UserInfo parent = mUserManager.getProfileParent(userId);
@@ -4546,7 +4641,7 @@
                         Intent.FLAG_RECEIVER_FOREGROUND);
                 mContext.sendBroadcastAsUser(intent, new UserHandle(parent.id));
             } finally {
-                restoreCallingIdentity(id);
+                mInjector.binderRestoreCallingIdentity(id);
             }
         }
     }
@@ -4558,11 +4653,11 @@
         // Check if this is the profile owner (includes device owner).
         getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
 
-        long id = Binder.clearCallingIdentity();
+        long id = mInjector.binderClearCallingIdentity();
         try {
             mUserManager.setUserName(userId, profileName);
         } finally {
-            restoreCallingIdentity(id);
+            mInjector.binderRestoreCallingIdentity(id);
         }
     }
 
@@ -4612,7 +4707,7 @@
      * Canonical name for a given package.
      */
     private String getApplicationLabel(String packageName, int userHandle) {
-        long token = Binder.clearCallingIdentity();
+        long token = mInjector.binderClearCallingIdentity();
         try {
             final Context userContext;
             try {
@@ -4630,7 +4725,7 @@
             }
             return result != null ? result.toString() : null;
         } finally {
-            Binder.restoreCallingIdentity(token);
+            mInjector.binderRestoreCallingIdentity(token);
         }
     }
 
@@ -4656,7 +4751,7 @@
             throw new IllegalStateException("Trying to set the profile owner, but profile owner "
                     + "is already set.");
         }
-        int callingUid = Binder.getCallingUid();
+        int callingUid = mInjector.binderGetCallingUid();
         if (callingUid == Process.SHELL_UID || callingUid == Process.ROOT_UID) {
             if (hasUserSetupCompleted(userHandle) &&
                     AccountManager.get(mContext).getAccountsAsUser(userHandle).length > 0) {
@@ -4689,13 +4784,13 @@
             throw new IllegalStateException("User not running: " + userId);
         }
 
-        int callingUid = Binder.getCallingUid();
+        int callingUid = mInjector.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
-            if (!UserManager.isSplitSystemUser()) {
+            if (!mInjector.userManagerIsSplitSystemUser()) {
                 if (mUserManager.getUserCount() > 1) {
                     throw new IllegalStateException(
                             "Not allowed to set the device owner because there "
@@ -4714,8 +4809,8 @@
         mContext.enforceCallingOrSelfPermission(
                 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 (!mInjector.userManagerIsSplitSystemUser()) {
+            if (hasUserSetupCompleted(UserHandle.USER_SYSTEM)) {
                 throw new IllegalStateException("Cannot set the device owner if the device is "
                         + "already set-up");
             }
@@ -4726,7 +4821,7 @@
         if (userHandle < 0) {
             throw new IllegalArgumentException("Invalid userId " + userHandle);
         }
-        final int callingUid = Binder.getCallingUid();
+        final int callingUid = mInjector.binderGetCallingUid();
         if (userHandle == UserHandle.getUserId(callingUid)) return;
         if (callingUid != Process.SYSTEM_UID && callingUid != 0) {
             mContext.enforceCallingOrSelfPermission(
@@ -4742,32 +4837,31 @@
     }
 
     private UserInfo getProfileParent(int userHandle) {
-        long ident = Binder.clearCallingIdentity();
+        long ident = mInjector.binderClearCallingIdentity();
         try {
             return mUserManager.getProfileParent(userHandle);
         } finally {
-            Binder.restoreCallingIdentity(ident);
+            mInjector.binderRestoreCallingIdentity(ident);
         }
     }
 
     private boolean isManagedProfile(int userHandle) {
-        long ident = Binder.clearCallingIdentity();
+        long ident = mInjector.binderClearCallingIdentity();
         try {
             return mUserManager.getUserInfo(userHandle).isManagedProfile();
         } finally {
-            Binder.restoreCallingIdentity(ident);
+            mInjector.binderRestoreCallingIdentity(ident);
         }
     }
 
     private void enableIfNecessary(String packageName, int userId) {
         try {
-            IPackageManager ipm = AppGlobals.getPackageManager();
-            ApplicationInfo ai = ipm.getApplicationInfo(packageName,
+            ApplicationInfo ai = mIPackageManager.getApplicationInfo(packageName,
                     PackageManager.GET_DISABLED_UNTIL_USED_COMPONENTS,
                     userId);
             if (ai.enabledSetting
                     == PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED) {
-                ipm.setApplicationEnabledSetting(packageName,
+                mIPackageManager.setApplicationEnabledSetting(packageName,
                         PackageManager.COMPONENT_ENABLED_STATE_DEFAULT,
                         PackageManager.DONT_KILL_APP, userId, "DevicePolicyManager");
             }
@@ -4781,8 +4875,8 @@
                 != PackageManager.PERMISSION_GRANTED) {
 
             pw.println("Permission Denial: can't dump DevicePolicyManagerService from from pid="
-                    + Binder.getCallingPid()
-                    + ", uid=" + Binder.getCallingUid());
+                    + mInjector.binderGetCallingPid()
+                    + ", uid=" + mInjector.binderGetCallingUid());
             return;
         }
 
@@ -4823,14 +4917,13 @@
         synchronized (this) {
             getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
 
-            IPackageManager pm = AppGlobals.getPackageManager();
-            long id = Binder.clearCallingIdentity();
+            long id = mInjector.binderClearCallingIdentity();
             try {
-                pm.addPersistentPreferredActivity(filter, activity, userHandle);
+                mIPackageManager.addPersistentPreferredActivity(filter, activity, userHandle);
             } catch (RemoteException re) {
                 // Shouldn't happen
             } finally {
-                restoreCallingIdentity(id);
+                mInjector.binderRestoreCallingIdentity(id);
             }
         }
     }
@@ -4842,14 +4935,13 @@
         synchronized (this) {
             getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
 
-            IPackageManager pm = AppGlobals.getPackageManager();
-            long id = Binder.clearCallingIdentity();
+            long id = mInjector.binderClearCallingIdentity();
             try {
-                pm.clearPackagePersistentPreferredActivities(packageName, userHandle);
+                mIPackageManager.clearPackagePersistentPreferredActivities(packageName, userHandle);
             } catch (RemoteException re) {
                 // Shouldn't happen
             } finally {
-                restoreCallingIdentity(id);
+                mInjector.binderRestoreCallingIdentity(id);
             }
         }
     }
@@ -4861,11 +4953,11 @@
         synchronized (this) {
             getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
 
-            long id = Binder.clearCallingIdentity();
+            long id = mInjector.binderClearCallingIdentity();
             try {
                 mUserManager.setApplicationRestrictions(packageName, settings, userHandle);
             } finally {
-                restoreCallingIdentity(id);
+                mInjector.binderRestoreCallingIdentity(id);
             }
         }
     }
@@ -4963,7 +5055,7 @@
     @Override
     public ComponentName getRestrictionsProvider(int userHandle) {
         synchronized (this) {
-            if (Binder.getCallingUid() != Process.SYSTEM_UID) {
+            if (mInjector.binderGetCallingUid() != Process.SYSTEM_UID) {
                 throw new SecurityException("Only the system can query the permission provider");
             }
             DevicePolicyData userData = getUserData(userHandle);
@@ -4978,8 +5070,7 @@
         synchronized (this) {
             getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
 
-            IPackageManager pm = AppGlobals.getPackageManager();
-            long id = Binder.clearCallingIdentity();
+            long id = mInjector.binderClearCallingIdentity();
             try {
                 UserInfo parent = mUserManager.getProfileParent(callingUserId);
                 if (parent == null) {
@@ -4988,17 +5079,17 @@
                     return;
                 }
                 if ((flags & DevicePolicyManager.FLAG_PARENT_CAN_ACCESS_MANAGED) != 0) {
-                    pm.addCrossProfileIntentFilter(filter, who.getPackageName(), callingUserId,
-                            parent.id, 0);
+                    mIPackageManager.addCrossProfileIntentFilter(
+                            filter, who.getPackageName(), callingUserId, parent.id, 0);
                 }
                 if ((flags & DevicePolicyManager.FLAG_MANAGED_CAN_ACCESS_PARENT) != 0) {
-                    pm.addCrossProfileIntentFilter(filter, who.getPackageName(),
+                    mIPackageManager.addCrossProfileIntentFilter(filter, who.getPackageName(),
                             parent.id, callingUserId, 0);
                 }
             } catch (RemoteException re) {
                 // Shouldn't happen
             } finally {
-                restoreCallingIdentity(id);
+                mInjector.binderRestoreCallingIdentity(id);
             }
         }
     }
@@ -5009,8 +5100,7 @@
         int callingUserId = UserHandle.getCallingUserId();
         synchronized (this) {
             getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
-            IPackageManager pm = AppGlobals.getPackageManager();
-            long id = Binder.clearCallingIdentity();
+            long id = mInjector.binderClearCallingIdentity();
             try {
                 UserInfo parent = mUserManager.getProfileParent(callingUserId);
                 if (parent == null) {
@@ -5019,15 +5109,16 @@
                     return;
                 }
                 // Removing those that go from the managed profile to the parent.
-                pm.clearCrossProfileIntentFilters(callingUserId, who.getPackageName());
+                mIPackageManager.clearCrossProfileIntentFilters(
+                        callingUserId, who.getPackageName());
                 // And those that go from the parent to the managed profile.
                 // If we want to support multiple managed profiles, we will have to only remove
                 // those that have callingUserId as their target.
-                pm.clearCrossProfileIntentFilters(parent.id, who.getPackageName());
+                mIPackageManager.clearCrossProfileIntentFilters(parent.id, who.getPackageName());
             } catch (RemoteException re) {
                 // Shouldn't happen
             } finally {
-                restoreCallingIdentity(id);
+                mInjector.binderRestoreCallingIdentity(id);
             }
         }
     }
@@ -5039,7 +5130,7 @@
     private boolean checkPackagesInPermittedListOrSystem(List<String> enabledPackages,
             List<String> permittedList) {
         int userIdToCheck = UserHandle.getCallingUserId();
-        long id = Binder.clearCallingIdentity();
+        long id = mInjector.binderClearCallingIdentity();
         try {
             // If we have an enabled packages list for a managed profile the packages
             // we should check are installed for the parent user.
@@ -5048,12 +5139,11 @@
                 userIdToCheck = user.profileGroupId;
             }
 
-            IPackageManager pm = AppGlobals.getPackageManager();
             for (String enabledPackage : enabledPackages) {
                 boolean systemService = false;
                 try {
-                    ApplicationInfo applicationInfo = pm.getApplicationInfo(enabledPackage,
-                            PackageManager.GET_UNINSTALLED_PACKAGES, userIdToCheck);
+                    ApplicationInfo applicationInfo = mIPackageManager.getApplicationInfo(
+                            enabledPackage, PackageManager.GET_UNINSTALLED_PACKAGES, userIdToCheck);
                     systemService = (applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0;
                 } catch (RemoteException e) {
                     Log.i(LOG_TAG, "Can't talk to package managed", e);
@@ -5063,7 +5153,7 @@
                 }
             }
         } finally {
-            restoreCallingIdentity(id);
+            mInjector.binderRestoreCallingIdentity(id);
         }
         return true;
     }
@@ -5088,7 +5178,7 @@
         if (packageList != null) {
             int userId = UserHandle.getCallingUserId();
             List<AccessibilityServiceInfo> enabledServices = null;
-            long id = Binder.clearCallingIdentity();
+            long id = mInjector.binderClearCallingIdentity();
             try {
                 UserInfo user = mUserManager.getUserInfo(userId);
                 if (user.isManagedProfile()) {
@@ -5098,7 +5188,7 @@
                 enabledServices = accessibilityManager.getEnabledAccessibilityServiceList(
                         AccessibilityServiceInfo.FEEDBACK_ALL_MASK);
             } finally {
-                restoreCallingIdentity(id);
+                mInjector.binderRestoreCallingIdentity(id);
             }
 
             if (enabledServices != null) {
@@ -5169,7 +5259,7 @@
 
             // If we have a permitted list add all system accessibility services.
             if (result != null) {
-                long id = Binder.clearCallingIdentity();
+                long id = mInjector.binderClearCallingIdentity();
                 try {
                     UserInfo user = mUserManager.getUserInfo(userId);
                     if (user.isManagedProfile()) {
@@ -5180,7 +5270,6 @@
                     List<AccessibilityServiceInfo> installedServices =
                             accessibilityManager.getInstalledAccessibilityServiceList();
 
-                    IPackageManager pm = AppGlobals.getPackageManager();
                     if (installedServices != null) {
                         for (AccessibilityServiceInfo service : installedServices) {
                             ServiceInfo serviceInfo = service.getResolveInfo().serviceInfo;
@@ -5191,7 +5280,7 @@
                         }
                     }
                 } finally {
-                    restoreCallingIdentity(id);
+                    mInjector.binderRestoreCallingIdentity(id);
                 }
             }
 
@@ -5201,12 +5290,12 @@
 
     private boolean checkCallerIsCurrentUserOrProfile() {
         int callingUserId = UserHandle.getCallingUserId();
-        long token = Binder.clearCallingIdentity();
+        long token = mInjector.binderClearCallingIdentity();
         try {
             UserInfo currentUser;
             UserInfo callingUser = mUserManager.getUserInfo(callingUserId);
             try {
-                currentUser = ActivityManagerNative.getDefault().getCurrentUser();
+                currentUser = mInjector.getIActivityManager().getCurrentUser();
             } catch (RemoteException e) {
                 Slog.e(LOG_TAG, "Failed to talk to activity managed.", e);
                 return false;
@@ -5223,7 +5312,7 @@
                 return false;
             }
         } finally {
-            Binder.restoreCallingIdentity(token);
+            mInjector.binderRestoreCallingIdentity(token);
         }
         return true;
     }
@@ -5289,7 +5378,7 @@
     public List getPermittedInputMethodsForCurrentUser() {
         UserInfo currentUser;
         try {
-            currentUser = ActivityManagerNative.getDefault().getCurrentUser();
+            currentUser = mInjector.getIActivityManager().getCurrentUser();
         } catch (RemoteException e) {
             Slog.e(LOG_TAG, "Failed to make remote calls to get current user", e);
             // Activity managed is dead, just allow all IMEs
@@ -5327,9 +5416,8 @@
                 InputMethodManager inputMethodManager = (InputMethodManager) mContext
                         .getSystemService(Context.INPUT_METHOD_SERVICE);
                 List<InputMethodInfo> imes = inputMethodManager.getInputMethodList();
-                long id = Binder.clearCallingIdentity();
+                long id = mInjector.binderClearCallingIdentity();
                 try {
-                    IPackageManager pm = AppGlobals.getPackageManager();
                     if (imes != null) {
                         for (InputMethodInfo ime : imes) {
                             ServiceInfo serviceInfo = ime.getServiceInfo();
@@ -5340,7 +5428,7 @@
                         }
                     }
                 } finally {
-                    restoreCallingIdentity(id);
+                    mInjector.binderRestoreCallingIdentity(id);
                 }
             }
             return result;
@@ -5353,7 +5441,7 @@
         synchronized (this) {
             getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER);
 
-            long id = Binder.clearCallingIdentity();
+            long id = mInjector.binderClearCallingIdentity();
             try {
                 UserInfo userInfo = mUserManager.createUser(name, 0 /* flags */);
                 if (userInfo != null) {
@@ -5361,7 +5449,7 @@
                 }
                 return null;
             } finally {
-                restoreCallingIdentity(id);
+                mInjector.binderRestoreCallingIdentity(id);
             }
         }
     }
@@ -5373,21 +5461,19 @@
         if (user == null) {
             return null;
         }
-        long id = Binder.clearCallingIdentity();
+        long id = mInjector.binderClearCallingIdentity();
         try {
             String profileOwnerPkg = profileOwnerComponent.getPackageName();
-            final IPackageManager ipm = AppGlobals.getPackageManager();
-            IActivityManager activityManager = ActivityManagerNative.getDefault();
 
             final int userHandle = user.getIdentifier();
             try {
                 // Install the profile owner if not present.
-                if (!ipm.isPackageAvailable(profileOwnerPkg, userHandle)) {
-                    ipm.installExistingPackageAsUser(profileOwnerPkg, userHandle);
+                if (!mIPackageManager.isPackageAvailable(profileOwnerPkg, userHandle)) {
+                    mIPackageManager.installExistingPackageAsUser(profileOwnerPkg, userHandle);
                 }
 
                 // Start user in background.
-                activityManager.startUserInBackground(userHandle);
+                mInjector.getIActivityManager().startUserInBackground(userHandle);
             } catch (RemoteException e) {
                 Slog.e(LOG_TAG, "Failed to make remote calls for configureUser", e);
             }
@@ -5396,7 +5482,7 @@
             setProfileOwner(profileOwnerComponent, ownerName, userHandle);
             return user;
         } finally {
-            restoreCallingIdentity(id);
+            mInjector.binderRestoreCallingIdentity(id);
         }
     }
 
@@ -5406,11 +5492,11 @@
         synchronized (this) {
             getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER);
 
-            long id = Binder.clearCallingIdentity();
+            long id = mInjector.binderClearCallingIdentity();
             try {
                 return mUserManager.removeUser(userHandle.getIdentifier());
             } finally {
-                restoreCallingIdentity(id);
+                mInjector.binderRestoreCallingIdentity(id);
             }
         }
     }
@@ -5421,18 +5507,18 @@
         synchronized (this) {
             getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER);
 
-            long id = Binder.clearCallingIdentity();
+            long id = mInjector.binderClearCallingIdentity();
             try {
-                int userId = UserHandle.USER_OWNER;
+                int userId = UserHandle.USER_SYSTEM;
                 if (userHandle != null) {
                     userId = userHandle.getIdentifier();
                 }
-                return ActivityManagerNative.getDefault().switchUser(userId);
+                return mInjector.getIActivityManager().switchUser(userId);
             } catch (RemoteException e) {
                 Log.e(LOG_TAG, "Couldn't switch user", e);
                 return false;
             } finally {
-                restoreCallingIdentity(id);
+                mInjector.binderRestoreCallingIdentity(id);
             }
         }
     }
@@ -5445,14 +5531,14 @@
         synchronized (this) {
             getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
 
-            long id = Binder.clearCallingIdentity();
+            long id = mInjector.binderClearCallingIdentity();
             try {
                 Bundle bundle = mUserManager.getApplicationRestrictions(packageName, userHandle);
                 // if no restrictions were saved, mUserManager.getApplicationRestrictions
                 // returns null, but DPM method should return an empty Bundle as per JavaDoc
                 return bundle != null ? bundle : Bundle.EMPTY;
             } finally {
-                restoreCallingIdentity(id);
+                mInjector.binderRestoreCallingIdentity(id);
             }
         }
     }
@@ -5460,13 +5546,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);
             }
@@ -5475,24 +5561,16 @@
             }
             boolean alreadyRestricted = mUserManager.hasUserRestriction(key, user);
 
-            IAudioService iAudioService = null;
-            if (UserManager.DISALLOW_UNMUTE_MICROPHONE.equals(key)
-                    || UserManager.DISALLOW_ADJUST_VOLUME.equals(key)) {
-                iAudioService = IAudioService.Stub.asInterface(
-                        ServiceManager.getService(Context.AUDIO_SERVICE));
-            }
-
-            long id = Binder.clearCallingIdentity();
+            long id = mInjector.binderClearCallingIdentity();
             try {
                 if (enabled && !alreadyRestricted) {
                     if (UserManager.DISALLOW_UNMUTE_MICROPHONE.equals(key)) {
-                        iAudioService.setMicrophoneMute(true, mContext.getPackageName(),
-                                userHandle);
+                        mInjector.getIAudioService()
+                                .setMicrophoneMute(true, mContext.getPackageName(), userHandle);
                     } else if (UserManager.DISALLOW_ADJUST_VOLUME.equals(key)) {
-                        iAudioService.setMasterMute(true, 0, mContext.getPackageName(),
-                                userHandle);
-                    }
-                    if (UserManager.DISALLOW_CONFIG_WIFI.equals(key)) {
+                        mInjector.getIAudioService()
+                                .setMasterMute(true, 0, mContext.getPackageName(), userHandle);
+                    } else if (UserManager.DISALLOW_CONFIG_WIFI.equals(key)) {
                         Settings.Secure.putIntForUser(mContext.getContentResolver(),
                                 Settings.Secure.WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON, 0,
                                 userHandle);
@@ -5504,8 +5582,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);
                         }
@@ -5528,8 +5607,8 @@
                         // Send out notifications however as some clients may want to reread the
                         // value which actually changed due to a restriction having been applied.
                         final String property = Settings.Secure.SYS_PROP_SETTING_VERSION;
-                        long version = SystemProperties.getLong(property, 0) + 1;
-                        SystemProperties.set(property, Long.toString(version));
+                        long version = mInjector.systemPropertiesGetLong(property, 0) + 1;
+                        mInjector.systemPropertiesSet(property, Long.toString(version));
 
                         final String name = Settings.Secure.LOCATION_PROVIDERS_ALLOWED;
                         Uri url = Uri.withAppendedPath(Settings.Secure.CONTENT_URI, name);
@@ -5538,17 +5617,17 @@
                 }
                 if (!enabled && alreadyRestricted) {
                     if (UserManager.DISALLOW_UNMUTE_MICROPHONE.equals(key)) {
-                        iAudioService.setMicrophoneMute(false, mContext.getPackageName(),
-                                userHandle);
+                        mInjector.getIAudioService()
+                                .setMicrophoneMute(false, mContext.getPackageName(), userHandle);
                     } else if (UserManager.DISALLOW_ADJUST_VOLUME.equals(key)) {
-                        iAudioService.setMasterMute(false, 0, mContext.getPackageName(),
-                                userHandle);
+                        mInjector.getIAudioService()
+                                .setMasterMute(false, 0, mContext.getPackageName(), userHandle);
                     }
                 }
             } catch (RemoteException re) {
                 Slog.e(LOG_TAG, "Failed to talk to AudioService.", re);
             } finally {
-                restoreCallingIdentity(id);
+                mInjector.binderRestoreCallingIdentity(id);
             }
             sendChangedNotification(userHandle);
         }
@@ -5562,15 +5641,15 @@
         synchronized (this) {
             getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
 
-            long id = Binder.clearCallingIdentity();
+            long id = mInjector.binderClearCallingIdentity();
             try {
-                IPackageManager pm = AppGlobals.getPackageManager();
-                return pm.setApplicationHiddenSettingAsUser(packageName, hidden, callingUserId);
+                return mIPackageManager.setApplicationHiddenSettingAsUser(
+                        packageName, hidden, callingUserId);
             } catch (RemoteException re) {
                 // shouldn't happen
                 Slog.e(LOG_TAG, "Failed to setApplicationHiddenSetting", re);
             } finally {
-                restoreCallingIdentity(id);
+                mInjector.binderRestoreCallingIdentity(id);
             }
             return false;
         }
@@ -5583,15 +5662,15 @@
         synchronized (this) {
             getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
 
-            long id = Binder.clearCallingIdentity();
+            long id = mInjector.binderClearCallingIdentity();
             try {
-                IPackageManager pm = AppGlobals.getPackageManager();
-                return pm.getApplicationHiddenSettingAsUser(packageName, callingUserId);
+                return mIPackageManager.getApplicationHiddenSettingAsUser(
+                        packageName, callingUserId);
             } catch (RemoteException re) {
                 // shouldn't happen
                 Slog.e(LOG_TAG, "Failed to getApplicationHiddenSettingAsUser", re);
             } finally {
-                restoreCallingIdentity(id);
+                mInjector.binderRestoreCallingIdentity(id);
             }
             return false;
         }
@@ -5606,7 +5685,7 @@
             getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
 
             int userId = UserHandle.getCallingUserId();
-            long id = Binder.clearCallingIdentity();
+            long id = mInjector.binderClearCallingIdentity();
 
             try {
                 if (DBG) {
@@ -5622,19 +5701,18 @@
                     primaryUser = um.getUserInfo(userId);
                 }
 
-                IPackageManager pm = AppGlobals.getPackageManager();
-                if (!isSystemApp(pm, packageName, primaryUser.id)) {
+                if (!isSystemApp(mIPackageManager, packageName, primaryUser.id)) {
                     throw new IllegalArgumentException("Only system apps can be enabled this way.");
                 }
 
                 // Install the app.
-                pm.installExistingPackageAsUser(packageName, userId);
+                mIPackageManager.installExistingPackageAsUser(packageName, userId);
 
             } catch (RemoteException re) {
                 // shouldn't happen
                 Slog.wtf(LOG_TAG, "Failed to install " + packageName, re);
             } finally {
-                restoreCallingIdentity(id);
+                mInjector.binderRestoreCallingIdentity(id);
             }
         }
     }
@@ -5648,7 +5726,7 @@
             getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
 
             int userId = UserHandle.getCallingUserId();
-            long id = Binder.clearCallingIdentity();
+            long id = mInjector.binderClearCallingIdentity();
 
             try {
                 UserManager um = UserManager.get(mContext);
@@ -5659,8 +5737,8 @@
                     primaryUser = um.getUserInfo(userId);
                 }
 
-                IPackageManager pm = AppGlobals.getPackageManager();
-                List<ResolveInfo> activitiesToEnable = pm.queryIntentActivities(intent,
+                List<ResolveInfo> activitiesToEnable = mIPackageManager.queryIntentActivities(
+                        intent,
                         intent.resolveTypeIfNeeded(mContext.getContentResolver()),
                         0, // no flags
                         primaryUser.id);
@@ -5671,9 +5749,9 @@
                     for (ResolveInfo info : activitiesToEnable) {
                         if (info.activityInfo != null) {
                             String packageName = info.activityInfo.packageName;
-                            if (isSystemApp(pm, packageName, primaryUser.id)) {
+                            if (isSystemApp(mIPackageManager, packageName, primaryUser.id)) {
                                 numberOfAppsInstalled++;
-                                pm.installExistingPackageAsUser(packageName, userId);
+                                mIPackageManager.installExistingPackageAsUser(packageName, userId);
                             } else {
                                 Slog.d(LOG_TAG, "Not enabling " + packageName + " since is not a"
                                         + " system app");
@@ -5687,7 +5765,7 @@
                 Slog.wtf(LOG_TAG, "Failed to resolve intent for: " + intent);
                 return 0;
             } finally {
-                restoreCallingIdentity(id);
+                mInjector.binderRestoreCallingIdentity(id);
             }
         }
     }
@@ -5753,15 +5831,14 @@
         synchronized (this) {
             getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
 
-            long id = Binder.clearCallingIdentity();
+            long id = mInjector.binderClearCallingIdentity();
             try {
-                IPackageManager pm = AppGlobals.getPackageManager();
-                pm.setBlockUninstallForUser(packageName, uninstallBlocked, userId);
+                mIPackageManager.setBlockUninstallForUser(packageName, uninstallBlocked, userId);
             } catch (RemoteException re) {
                 // Shouldn't happen.
                 Slog.e(LOG_TAG, "Failed to setBlockUninstallForUser", re);
             } finally {
-                restoreCallingIdentity(id);
+                mInjector.binderRestoreCallingIdentity(id);
             }
         }
     }
@@ -5778,15 +5855,14 @@
                 getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
             }
 
-            long id = Binder.clearCallingIdentity();
+            long id = mInjector.binderClearCallingIdentity();
             try {
-                IPackageManager pm = AppGlobals.getPackageManager();
-                return pm.getBlockUninstallForUser(packageName, userId);
+                return mIPackageManager.getBlockUninstallForUser(packageName, userId);
             } catch (RemoteException re) {
                 // Shouldn't happen.
                 Slog.e(LOG_TAG, "Failed to getBlockUninstallForUser", re);
             } finally {
-                restoreCallingIdentity(id);
+                mInjector.binderRestoreCallingIdentity(id);
             }
         }
         return false;
@@ -5838,7 +5914,7 @@
                 actualLookupKey, actualContactId, originalIntent);
         final int callingUserId = UserHandle.getCallingUserId();
 
-        final long ident = Binder.clearCallingIdentity();
+        final long ident = mInjector.binderClearCallingIdentity();
         try {
             synchronized (this) {
                 final int managedUserId = getManagedUserId(callingUserId);
@@ -5856,7 +5932,7 @@
                         mContext, intent, new UserHandle(managedUserId));
             }
         } finally {
-            Binder.restoreCallingIdentity(ident);
+            mInjector.binderRestoreCallingIdentity(ident);
         }
     }
 
@@ -5937,7 +6013,7 @@
         synchronized (this) {
             getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER);
 
-            int userHandle = Binder.getCallingUserHandle().getIdentifier();
+            int userHandle = mInjector.binderGetCallingUserHandle().getIdentifier();
             setLockTaskPackagesLocked(userHandle, new ArrayList<>(Arrays.asList(packages)));
         }
     }
@@ -5959,7 +6035,7 @@
         Preconditions.checkNotNull(who, "ComponentName is null");
         synchronized (this) {
             getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER);
-            int userHandle = Binder.getCallingUserHandle().getIdentifier();
+            int userHandle = mInjector.binderGetCallingUserHandle().getIdentifier();
             final List<String> packages = getLockTaskPackagesLocked(userHandle);
             return packages.toArray(new String[packages.size()]);
         }
@@ -5978,7 +6054,7 @@
     @Override
     public boolean isLockTaskPermitted(String pkg) {
         // Get current user's devicepolicy
-        int uid = Binder.getCallingUid();
+        int uid = mInjector.binderGetCallingUid();
         int userHandle = UserHandle.getUserId(uid);
         DevicePolicyData policy = getUserData(userHandle);
         synchronized (this) {
@@ -5997,7 +6073,7 @@
 
     @Override
     public void notifyLockTaskModeChanged(boolean isEnabled, String pkg, int userHandle) {
-        if (Binder.getCallingUid() != Process.SYSTEM_UID) {
+        if (mInjector.binderGetCallingUid() != Process.SYSTEM_UID) {
             throw new SecurityException("notifyLockTaskModeChanged can only be called by system");
         }
         synchronized (this) {
@@ -6048,11 +6124,11 @@
                 }
             }
 
-            long id = Binder.clearCallingIdentity();
+            long id = mInjector.binderClearCallingIdentity();
             try {
                 Settings.Global.putString(contentResolver, setting, value);
             } finally {
-                restoreCallingIdentity(id);
+                mInjector.binderRestoreCallingIdentity(id);
             }
         }
     }
@@ -6077,11 +6153,11 @@
                         "Permission denial: Profile owners cannot update %1$s", setting));
             }
 
-            long id = Binder.clearCallingIdentity();
+            long id = mInjector.binderClearCallingIdentity();
             try {
                 Settings.Secure.putStringForUser(contentResolver, setting, value, callingUserId);
             } finally {
-                restoreCallingIdentity(id);
+                mInjector.binderRestoreCallingIdentity(id);
             }
         }
     }
@@ -6092,7 +6168,7 @@
         synchronized (this) {
             getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
             int userId = UserHandle.getCallingUserId();
-            long identity = Binder.clearCallingIdentity();
+            long identity = mInjector.binderClearCallingIdentity();
             try {
                 IAudioService iAudioService = IAudioService.Stub.asInterface(
                         ServiceManager.getService(Context.AUDIO_SERVICE));
@@ -6100,7 +6176,7 @@
             } catch (RemoteException re) {
                 Slog.e(LOG_TAG, "Failed to setMasterMute", re);
             } finally {
-                Binder.restoreCallingIdentity(identity);
+                mInjector.binderRestoreCallingIdentity(identity);
             }
         }
     }
@@ -6124,11 +6200,11 @@
             getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
 
             int userId = UserHandle.getCallingUserId();
-            long id = Binder.clearCallingIdentity();
+            long id = mInjector.binderClearCallingIdentity();
             try {
                 mUserManager.setUserIcon(userId, icon);
             } finally {
-                restoreCallingIdentity(id);
+                mInjector.binderRestoreCallingIdentity(id);
             }
         }
     }
@@ -6142,7 +6218,7 @@
         final int userId = UserHandle.getCallingUserId();
         LockPatternUtils utils = new LockPatternUtils(mContext);
 
-        long ident = Binder.clearCallingIdentity();
+        long ident = mInjector.binderClearCallingIdentity();
         try {
             // disallow disabling the keyguard if a password is currently set
             if (disabled && utils.isSecure(userId)) {
@@ -6150,7 +6226,7 @@
             }
             utils.setLockScreenDisabled(disabled, userId);
         } finally {
-            Binder.restoreCallingIdentity(ident);
+            mInjector.binderRestoreCallingIdentity(ident);
         }
         return true;
     }
@@ -6173,7 +6249,7 @@
     }
 
     private boolean setStatusBarDisabledInternal(boolean disabled, int userId) {
-        long ident = Binder.clearCallingIdentity();
+        long ident = mInjector.binderClearCallingIdentity();
         try {
             IStatusBarService statusBarService = IStatusBarService.Stub.asInterface(
                     ServiceManager.checkService(Context.STATUS_BAR_SERVICE));
@@ -6187,7 +6263,7 @@
         } catch (RemoteException e) {
             Slog.e(LOG_TAG, "Failed to disable the status bar", e);
         } finally {
-            Binder.restoreCallingIdentity(ident);
+            mInjector.binderRestoreCallingIdentity(ident);
         }
         return false;
     }
@@ -6394,8 +6470,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;
         }
@@ -6418,7 +6494,7 @@
                 Log.e(LOG_TAG, "Cannot find device owner package", e);
             }
             if (receivers != null) {
-                long ident = Binder.clearCallingIdentity();
+                long ident = mInjector.binderClearCallingIdentity();
                 try {
                     for (int i = 0; i < receivers.length; i++) {
                         if (permission.BIND_DEVICE_ADMIN.equals(receivers[i].permission)) {
@@ -6428,7 +6504,7 @@
                         }
                     }
                 } finally {
-                    Binder.restoreCallingIdentity(ident);
+                    mInjector.binderRestoreCallingIdentity(ident);
                 }
             }
         }
@@ -6459,12 +6535,12 @@
     @Override
     public boolean setPermissionGrantState(ComponentName admin, String packageName,
             String permission, int grantState) throws RemoteException {
-        UserHandle user = Binder.getCallingUserHandle();
+        UserHandle user = mInjector.binderGetCallingUserHandle();
         synchronized (this) {
             getActiveAdminForCallerLocked(admin, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
-            long ident = Binder.clearCallingIdentity();
+            long ident = mInjector.binderClearCallingIdentity();
             try {
-                final ApplicationInfo ai = AppGlobals.getPackageManager()
+                final ApplicationInfo ai = mIPackageManager
                         .getApplicationInfo(packageName, 0, user.getIdentifier());
                 final int targetSdkVersion = ai == null ? 0 : ai.targetSdkVersion;
                 if (targetSdkVersion < android.os.Build.VERSION_CODES.M) {
@@ -6496,7 +6572,7 @@
             } catch (SecurityException se) {
                 return false;
             } finally {
-                Binder.restoreCallingIdentity(ident);
+                mInjector.binderRestoreCallingIdentity(ident);
             }
         }
     }
@@ -6506,12 +6582,12 @@
             String permission) throws RemoteException {
         PackageManager packageManager = mContext.getPackageManager();
 
-        UserHandle user = Binder.getCallingUserHandle();
+        UserHandle user = mInjector.binderGetCallingUserHandle();
         synchronized (this) {
             getActiveAdminForCallerLocked(admin, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
-            long ident = Binder.clearCallingIdentity();
+            long ident = mInjector.binderClearCallingIdentity();
             try {
-                int granted = AppGlobals.getPackageManager().checkPermission(permission,
+                int granted = mIPackageManager.checkPermission(permission,
                         packageName, user.getIdentifier());
                 int permFlags = packageManager.getPermissionFlags(permission, packageName, user);
                 if ((permFlags & PackageManager.FLAG_PERMISSION_POLICY_FIXED)
@@ -6525,8 +6601,17 @@
                             : DevicePolicyManager.PERMISSION_GRANT_STATE_DENIED;
                 }
             } finally {
-                Binder.restoreCallingIdentity(ident);
+                mInjector.binderRestoreCallingIdentity(ident);
             }
         }
     }
+
+    boolean isPackageInstalledForUser(String packageName, int userHandle) {
+        try {
+            PackageInfo pi = mIPackageManager.getPackageInfo(packageName, 0, userHandle);
+            return (pi != null) && (pi.applicationInfo.flags != 0);
+        } catch (RemoteException re) {
+            throw new RuntimeException("Package manager has died", re);
+        }
+    }
 }
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java b/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java
index 87cf28f..a169082 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java
@@ -230,20 +230,6 @@
         return mDeviceOwner != null;
     }
 
-    static boolean isInstalledForUser(String packageName, int userHandle) {
-        try {
-            PackageInfo pi = (AppGlobals.getPackageManager())
-                    .getPackageInfo(packageName, 0, userHandle);
-            if (pi != null && pi.applicationInfo.flags != 0) {
-                return true;
-            }
-        } catch (RemoteException re) {
-            throw new RuntimeException("Package manager has died", re);
-        }
-
-        return false;
-    }
-
     private boolean readLegacyOwnerFile(File file) {
         if (!file.exists()) {
             // Already migrated or the device has no owners.
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 93dc6cb..1ec1a46 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -867,6 +867,11 @@
 
             if (!disableNonCoreServices) {
                 mSystemServiceManager.startService(DockObserver.class);
+
+                if (context.getPackageManager().hasSystemFeature
+                        (PackageManager.FEATURE_WATCH)) {
+                    mSystemServiceManager.startService(ThermalObserver.class);
+                }
             }
 
             traceBeginAndSlog("StartWiredAccessoryManager");
diff --git a/services/midi/java/com/android/server/midi/MidiService.java b/services/midi/java/com/android/server/midi/MidiService.java
index f6de12d..c6d5a7e 100644
--- a/services/midi/java/com/android/server/midi/MidiService.java
+++ b/services/midi/java/com/android/server/midi/MidiService.java
@@ -588,6 +588,8 @@
         Client client = getClient(token);
         if (client == null) return;
         client.addListener(listener);
+        // Let listener know whether any ports are already busy.
+        updateStickyDeviceStatus(client.mUid, listener);
     }
 
     @Override
@@ -597,6 +599,25 @@
         client.removeListener(listener);
     }
 
+    // Inform listener of the status of all known devices.
+    private void updateStickyDeviceStatus(int uid, IMidiDeviceListener listener) {
+        synchronized (mDevicesByInfo) {
+            for (Device device : mDevicesByInfo.values()) {
+                // ignore private devices that our client cannot access
+                if (device.isUidAllowed(uid)) {
+                    try {
+                        MidiDeviceStatus status = device.getDeviceStatus();
+                        if (status != null) {
+                            listener.onDeviceStatusChanged(status);
+                        }
+                    } catch (RemoteException e) {
+                        Log.e(TAG, "remote exception", e);
+                    }
+                }
+            }
+        }
+    }
+
     private static final MidiDeviceInfo[] EMPTY_DEVICE_INFO_ARRAY = new MidiDeviceInfo[0];
 
     public MidiDeviceInfo[] getDevices() {
diff --git a/services/net/java/android/net/dhcp/DhcpClient.java b/services/net/java/android/net/dhcp/DhcpClient.java
index 9ee9cf4..e0d2ac1 100644
--- a/services/net/java/android/net/dhcp/DhcpClient.java
+++ b/services/net/java/android/net/dhcp/DhcpClient.java
@@ -244,8 +244,9 @@
     private PendingIntent createStateMachineCommandIntent(final String cmdName, final int cmd) {
         String action = DhcpClient.class.getName() + "." + mIfaceName + "." + cmdName;
 
-        Intent intent = new Intent(action, null)
-                .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
+        Intent intent = new Intent(action, null).addFlags(
+                Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT |
+                Intent.FLAG_RECEIVER_FOREGROUND);
         // TODO: The intent's package covers the whole of the system server, so it's pretty generic.
         // Consider adding some sort of token as well.
         intent.setPackage(mContext.getPackageName());
diff --git a/core/java/android/net/IpReachabilityMonitor.java b/services/net/java/android/net/ip/IpReachabilityMonitor.java
similarity index 91%
rename from core/java/android/net/IpReachabilityMonitor.java
rename to services/net/java/android/net/ip/IpReachabilityMonitor.java
index 88fb014..3acd565 100644
--- a/core/java/android/net/IpReachabilityMonitor.java
+++ b/services/net/java/android/net/ip/IpReachabilityMonitor.java
@@ -14,10 +14,11 @@
  * limitations under the License.
  */
 
-package android.net;
+package android.net.ip;
 
 import com.android.internal.annotations.GuardedBy;
 
+import android.content.Context;
 import android.net.LinkAddress;
 import android.net.LinkProperties;
 import android.net.LinkProperties.ProvisioningChange;
@@ -31,6 +32,7 @@
 import android.net.netlink.StructNdaCacheInfo;
 import android.net.netlink.StructNdMsg;
 import android.net.netlink.StructNlMsgHdr;
+import android.os.PowerManager;
 import android.os.SystemClock;
 import android.system.ErrnoException;
 import android.system.NetlinkSocketAddress;
@@ -74,6 +76,7 @@
     }
 
     private final Object mLock = new Object();
+    private final PowerManager.WakeLock mWakeLock;
     private final String mInterfaceName;
     private final int mInterfaceIndex;
     private final Callback mCallback;
@@ -136,7 +139,8 @@
         return returnValue;
     }
 
-    public IpReachabilityMonitor(String ifName, Callback callback) throws IllegalArgumentException {
+    public IpReachabilityMonitor(Context context, String ifName, Callback callback)
+                throws IllegalArgumentException {
         mInterfaceName = ifName;
         int ifIndex = -1;
         try {
@@ -145,6 +149,8 @@
         } catch (SocketException | NullPointerException e) {
             throw new IllegalArgumentException("invalid interface '" + ifName + "': ", e);
         }
+        mWakeLock = ((PowerManager) context.getSystemService(Context.POWER_SERVICE)).newWakeLock(
+                PowerManager.PARTIAL_WAKE_LOCK, TAG + "." + mInterfaceName);
         mCallback = callback;
         mNetlinkSocketObserver = new NetlinkSocketObserver();
         mObserverThread = new Thread(mNetlinkSocketObserver);
@@ -291,6 +297,17 @@
         synchronized (mLock) {
             ipProbeList.addAll(mIpWatchList.keySet());
         }
+
+        if (!ipProbeList.isEmpty() && stillRunning()) {
+            // Keep the CPU awake long enough to allow all ARP/ND
+            // probes a reasonable chance at success. See b/23197666.
+            //
+            // The wakelock we use is (by default) refcounted, and this version
+            // of acquire(timeout) queues a release message to keep acquisitions
+            // and releases balanced.
+            mWakeLock.acquire(getProbeWakeLockDuration());
+        }
+
         for (InetAddress target : ipProbeList) {
             if (!stillRunning()) {
                 break;
@@ -299,6 +316,22 @@
         }
     }
 
+    private long getProbeWakeLockDuration() {
+        // Ideally, this would be computed by examining the values of:
+        //
+        //     /proc/sys/net/ipv[46]/neigh/<ifname>/ucast_solicit
+        //
+        // and:
+        //
+        //     /proc/sys/net/ipv[46]/neigh/<ifname>/retrans_time_ms
+        //
+        // For now, just make some assumptions.
+        final long numUnicastProbes = 3;
+        final long retransTimeMs = 1000;
+        final long gracePeriodMs = 500;
+        return (numUnicastProbes * retransTimeMs) + gracePeriodMs;
+    }
+
 
     // TODO: simply the number of objects by making this extend Thread.
     private final class NetlinkSocketObserver implements Runnable {
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/services/tests/servicestests/AndroidManifest.xml b/services/tests/servicestests/AndroidManifest.xml
index 7d1282b..c147bcc 100644
--- a/services/tests/servicestests/AndroidManifest.xml
+++ b/services/tests/servicestests/AndroidManifest.xml
@@ -67,6 +67,33 @@
             </intent-filter>
         </receiver>
 
+        <receiver android:name="com.android.server.devicepolicy.DummyDeviceAdmins$Admin1"
+            android:permission="android.permission.BIND_DEVICE_ADMIN">
+            <meta-data android:name="android.app.device_admin"
+                android:resource="@xml/device_admin_sample" />
+            <intent-filter>
+                <action android:name="android.app.action.DEVICE_ADMIN_ENABLED" />
+            </intent-filter>
+        </receiver>
+
+        <receiver android:name="com.android.server.devicepolicy.DummyDeviceAdmins$Admin2"
+            android:permission="android.permission.BIND_DEVICE_ADMIN">
+            <meta-data android:name="android.app.device_admin"
+                android:resource="@xml/device_admin_sample" />
+            <intent-filter>
+                <action android:name="android.app.action.DEVICE_ADMIN_ENABLED" />
+            </intent-filter>
+        </receiver>
+
+        <receiver android:name="com.android.server.devicepolicy.DummyDeviceAdmins$Admin3"
+            android:permission="android.permission.BIND_DEVICE_ADMIN">
+            <meta-data android:name="android.app.device_admin"
+                android:resource="@xml/device_admin_sample" />
+            <intent-filter>
+                <action android:name="android.app.action.DEVICE_ADMIN_ENABLED" />
+            </intent-filter>
+        </receiver>
+
     </application>
 
     <instrumentation
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/tests/servicestests/src/com/android/server/devicepolicy/ApplicationRestrictionsTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/ApplicationRestrictionsTest.java
deleted file mode 100644
index ca270e7..0000000
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/ApplicationRestrictionsTest.java
+++ /dev/null
@@ -1,135 +0,0 @@
-/*
- * Copyright (C) 2014 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.devicepolicy;
-
-import android.app.admin.DeviceAdminReceiver;
-import android.app.admin.DevicePolicyManager;
-import android.content.BroadcastReceiver;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.os.Bundle;
-import android.os.UserHandle;
-import android.provider.Settings;
-import android.test.AndroidTestCase;
-import android.test.suitebuilder.annotation.SmallTest;
-
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-
-/**
- * Tests for application restrictions persisting via profile owner:
- *   make -j FrameworksServicesTests
- *   runtest --path frameworks/base/services/tests/servicestests/ \
- *       src/com/android/server/devicepolicy/ApplicationRestrictionsTest.java
- */
-public class ApplicationRestrictionsTest extends AndroidTestCase {
-
-    static DevicePolicyManager sDpm;
-    static ComponentName sAdminReceiver;
-    private static final String RESTRICTED_APP = "com.example.restrictedApp";
-    static boolean sAddBack = false;
-
-    public static class AdminReceiver extends DeviceAdminReceiver {
-
-        @Override
-        public void onDisabled(Context context, Intent intent) {
-            if (sAddBack) {
-                sDpm.setActiveAdmin(sAdminReceiver, false);
-                sAddBack = false;
-            }
-
-            super.onDisabled(context, intent);
-        }
-    }
-
-    @Override
-    public void setUp() {
-        final Context context = getContext();
-        sAdminReceiver = new ComponentName(mContext.getPackageName(),
-                AdminReceiver.class.getName());
-        sDpm = (DevicePolicyManager) context.getSystemService(Context.DEVICE_POLICY_SERVICE);
-        Settings.Secure.putInt(context.getContentResolver(),
-                Settings.Secure.USER_SETUP_COMPLETE, 0);
-        sDpm.setProfileOwner(sAdminReceiver, "Test", UserHandle.myUserId());
-        Settings.Secure.putInt(context.getContentResolver(),
-                Settings.Secure.USER_SETUP_COMPLETE, 1);
-        // Remove the admin if already registered. It's async, so add it back
-        // when the admin gets a broadcast. Otherwise add it back right away.
-        if (sDpm.isAdminActive(sAdminReceiver)) {
-            sAddBack = true;
-            sDpm.removeActiveAdmin(sAdminReceiver);
-        } else {
-            sDpm.setActiveAdmin(sAdminReceiver, false);
-        }
-    }
-
-    @Override
-    public void tearDown() {
-        Settings.Secure.putInt(getContext().getContentResolver(),
-                Settings.Secure.USER_SETUP_COMPLETE, 0);
-        sDpm.removeActiveAdmin(sAdminReceiver);
-        Settings.Secure.putInt(getContext().getContentResolver(),
-                Settings.Secure.USER_SETUP_COMPLETE, 1);
-    }
-
-    public void testSettingRestrictions() {
-        Bundle restrictions = new Bundle();
-        restrictions.putString("KEY_STRING", "Foo");
-        assertNotNull(sDpm.getApplicationRestrictions(sAdminReceiver, RESTRICTED_APP));
-        sDpm.setApplicationRestrictions(sAdminReceiver, RESTRICTED_APP, restrictions);
-        Bundle returned = sDpm.getApplicationRestrictions(sAdminReceiver, RESTRICTED_APP);
-        assertNotNull(returned);
-        assertEquals(returned.size(), 1);
-        assertEquals(returned.get("KEY_STRING"), "Foo");
-        sDpm.setApplicationRestrictions(sAdminReceiver, RESTRICTED_APP, new Bundle());
-        returned = sDpm.getApplicationRestrictions(sAdminReceiver, RESTRICTED_APP);
-        assertEquals(returned.size(), 0);
-    }
-
-    public void testRestrictionTypes() {
-        Bundle restrictions = new Bundle();
-        restrictions.putString("KEY_STRING", "Foo");
-        restrictions.putInt("KEY_INT", 7);
-        restrictions.putBoolean("KEY_BOOLEAN", true);
-        restrictions.putBoolean("KEY_BOOLEAN_2", false);
-        restrictions.putString("KEY_STRING_2", "Bar");
-        restrictions.putStringArray("KEY_STR_ARRAY", new String[] { "Foo", "Bar" });
-        sDpm.setApplicationRestrictions(sAdminReceiver, RESTRICTED_APP, restrictions);
-        Bundle returned = sDpm.getApplicationRestrictions(sAdminReceiver, RESTRICTED_APP);
-        assertTrue(returned.getBoolean("KEY_BOOLEAN"));
-        assertFalse(returned.getBoolean("KEY_BOOLEAN_2"));
-        assertFalse(returned.getBoolean("KEY_BOOLEAN_3"));
-        assertEquals(returned.getInt("KEY_INT"), 7);
-        assertTrue(returned.get("KEY_BOOLEAN") instanceof Boolean);
-        assertTrue(returned.get("KEY_INT") instanceof Integer);
-        assertEquals(returned.get("KEY_STRING"), "Foo");
-        assertEquals(returned.get("KEY_STRING_2"), "Bar");
-        assertTrue(returned.getStringArray("KEY_STR_ARRAY") instanceof String[]);
-        sDpm.setApplicationRestrictions(sAdminReceiver, RESTRICTED_APP, new Bundle());
-    }
-
-    public void testTextEscaping() {
-        String fancyText = "<This contains XML/> <JSON> "
-                + "{ \"One\": { \"OneOne\": \"11\", \"OneTwo\": \"12\" }, \"Two\": \"2\" } <JSON/>";
-        Bundle restrictions = new Bundle();
-        restrictions.putString("KEY_FANCY_TEXT", fancyText);
-        sDpm.setApplicationRestrictions(sAdminReceiver, RESTRICTED_APP, restrictions);
-        Bundle returned = sDpm.getApplicationRestrictions(sAdminReceiver, RESTRICTED_APP);
-        assertEquals(returned.getString("KEY_FANCY_TEXT"), fancyText);
-    }
-}
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java
new file mode 100644
index 0000000..c6c7497
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java
@@ -0,0 +1,241 @@
+/*
+ * 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.server.devicepolicy;
+
+import com.android.internal.widget.LockPatternUtils;
+
+import android.app.IActivityManager;
+import android.app.NotificationManager;
+import android.app.backup.IBackupManager;
+import android.content.Context;
+import android.content.pm.IPackageManager;
+import android.media.IAudioService;
+import android.os.Looper;
+import android.os.PowerManagerInternal;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.view.IWindowManager;
+
+import java.io.File;
+
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.when;
+
+/**
+ * Overrides {@link #DevicePolicyManagerService} for dependency injection.
+ */
+public class DevicePolicyManagerServiceTestable extends DevicePolicyManagerService {
+    /**
+     * Overrides {@link #Owners} for dependency injection.
+     */
+    public static class OwnersTestable extends Owners {
+        public static final String LEGACY_FILE = "legacy.xml";
+        public static final String DEVICE_OWNER_FILE = "device_owner2.xml";
+        public static final String PROFILE_OWNER_FILE_BASE = "profile_owner.xml";
+
+        private final File mLegacyFile;
+        private final File mDeviceOwnerFile;
+        private final File mProfileOwnerBase;
+
+        public OwnersTestable(Context context, File dataDir) {
+            super(context);
+            mLegacyFile = new File(dataDir, LEGACY_FILE);
+            mDeviceOwnerFile = new File(dataDir, DEVICE_OWNER_FILE);
+            mProfileOwnerBase = new File(dataDir, PROFILE_OWNER_FILE_BASE);
+        }
+
+        @Override
+        File getLegacyConfigFileWithTestOverride() {
+            return mLegacyFile;
+        }
+
+        @Override
+        File getDeviceOwnerFileWithTestOverride() {
+            return mDeviceOwnerFile;
+        }
+
+        @Override
+        File getProfileOwnerFileWithTestOverride(int userId) {
+            return new File(mDeviceOwnerFile.getAbsoluteFile() + "-" + userId);
+        }
+    }
+
+    public final DpmMockContext context;
+
+    public DevicePolicyManagerServiceTestable(DpmMockContext context, File dataDir) {
+        this(new MockInjector(context, dataDir));
+    }
+
+    private DevicePolicyManagerServiceTestable(MockInjector injector) {
+        super(injector);
+        this.context = injector.context;
+    }
+
+    private static class MockInjector extends Injector {
+
+        public final DpmMockContext context;
+
+        public final File dataDir;
+
+        public final File systemUserDataDir;
+        public final File secondUserDataDir;
+
+        private MockInjector(DpmMockContext context, File dataDir) {
+            super(context);
+            this.context = context;
+            this.dataDir = dataDir;
+
+            systemUserDataDir = new File(dataDir, "user0");
+            DpmTestUtils.clearDir(dataDir);
+
+            secondUserDataDir = new File(dataDir, "user" + DpmMockContext.CALLER_USER_HANDLE);
+            DpmTestUtils.clearDir(secondUserDataDir);
+
+            when(context.environment.getUserSystemDirectory(
+                    eq(DpmMockContext.CALLER_USER_HANDLE))).thenReturn(secondUserDataDir);
+        }
+
+        @Override
+        Owners newOwners() {
+            return new OwnersTestable(context, dataDir);
+        }
+
+        @Override
+        UserManager getUserManager() {
+            return context.userManager;
+        }
+
+        @Override
+        PowerManagerInternal getPowerManagerInternal() {
+            return context.powerManagerInternal;
+        }
+
+        @Override
+        NotificationManager getNotificationManager() {
+            return context.notificationManager;
+        }
+
+        @Override
+        IWindowManager getIWindowManager() {
+            return context.iwindowManager;
+        }
+
+        @Override
+        IActivityManager getIActivityManager() {
+            return context.iactivityManager;
+        }
+
+        @Override
+        IPackageManager getIPackageManager() {
+            return context.ipackageManager;
+        }
+
+        @Override
+        IBackupManager getIBackupManager() {
+            return context.ibackupManager;
+        }
+
+        @Override
+        IAudioService getIAudioService() {
+            return context.iaudioService;
+        }
+
+        @Override
+        Looper getMyLooper() {
+            return Looper.getMainLooper();
+        }
+
+        @Override
+        LockPatternUtils newLockPatternUtils() {
+            return context.lockPatternUtils;
+        }
+
+        @Override
+        String getDevicePolicyFilePathForSystemUser() {
+            return systemUserDataDir.getAbsolutePath();
+        }
+
+        @Override
+        long binderClearCallingIdentity() {
+            return context.binder.clearCallingIdentity();
+        }
+
+        @Override
+        void binderRestoreCallingIdentity(long token) {
+            context.binder.restoreCallingIdentity(token);
+        }
+
+        @Override
+        int binderGetCallingUid() {
+            return context.binder.getCallingUid();
+        }
+
+        @Override
+        int binderGetCallingPid() {
+            return context.binder.getCallingPid();
+        }
+
+        @Override
+        UserHandle binderGetCallingUserHandle() {
+            return context.binder.getCallingUserHandle();
+        }
+
+        @Override
+        boolean binderIsCallingUidMyUid() {
+            return context.binder.isCallerUidMyUid();
+        }
+
+        @Override
+        File environmentGetUserSystemDirectory(int userId) {
+            return context.environment.getUserSystemDirectory(userId);
+        }
+
+        @Override
+        void powerManagerGoToSleep(long time, int reason, int flags) {
+            context.powerManager.goToSleep(time, reason, flags);
+        }
+
+        @Override
+        boolean systemPropertiesGetBoolean(String key, boolean def) {
+            return context.systemProperties.getBoolean(key, def);
+        }
+
+        @Override
+        long systemPropertiesGetLong(String key, long def) {
+            return context.systemProperties.getLong(key, def);
+        }
+
+        @Override
+        String systemPropertiesGet(String key, String def) {
+            return context.systemProperties.get(key, def);
+        }
+
+        @Override
+        String systemPropertiesGet(String key) {
+            return context.systemProperties.get(key);
+        }
+
+        @Override
+        void systemPropertiesSet(String key, String value) {
+            context.systemProperties.set(key, value);
+        }
+
+        @Override
+        boolean userManagerIsSplitSystemUser() {
+            return context.userManagerForMock.isSplitSystemUser();
+        }
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
new file mode 100644
index 0000000..0072f52
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -0,0 +1,592 @@
+/*
+ * 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.server.devicepolicy;
+
+import com.android.server.LocalServices;
+
+import android.Manifest.permission;
+import android.app.Activity;
+import android.app.admin.DeviceAdminReceiver;
+import android.app.admin.DevicePolicyManager;
+import android.app.admin.DevicePolicyManagerInternal;
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.Intent;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.IPackageManager;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.os.Bundle;
+import android.content.pm.PackageInfo;
+import android.content.pm.UserInfo;
+import android.os.UserHandle;
+import android.util.Pair;
+
+import org.mockito.ArgumentCaptor;
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.stubbing.Answer;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.anyString;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Matchers.isNull;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+/**
+ * Tests for DevicePolicyManager( and DevicePolicyManagerService).
+ *
+ m FrameworksServicesTests &&
+ adb install \
+ -r out/target/product/hammerhead/data/app/FrameworksServicesTests/FrameworksServicesTests.apk &&
+ adb shell am instrument -e class com.android.server.devicepolicy.DevicePolicyManagerTest \
+ -w com.android.frameworks.servicestests/android.support.test.runner.AndroidJUnitRunner
+
+ (mmma frameworks/base/services/tests/servicestests/ for non-ninja build)
+ */
+public class DevicePolicyManagerTest extends DpmTestBase {
+
+
+    private DpmMockContext mContext;
+    public DevicePolicyManager dpm;
+    public DevicePolicyManagerServiceTestable dpms;
+    public ComponentName admin1;
+    public ComponentName admin2;
+    public ComponentName admin3;
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+
+        mContext = getContext();
+
+        when(mContext.packageManager.hasSystemFeature(eq(PackageManager.FEATURE_DEVICE_ADMIN)))
+                .thenReturn(true);
+
+        LocalServices.removeServiceForTest(DevicePolicyManagerInternal.class);
+        dpms = new DevicePolicyManagerServiceTestable(mContext, dataDir);
+        dpm = new DevicePolicyManagerTestable(mContext, dpms);
+
+        admin1 = new ComponentName(mRealTestContext, DummyDeviceAdmins.Admin1.class);
+        admin2 = new ComponentName(mRealTestContext, DummyDeviceAdmins.Admin2.class);
+        admin3 = new ComponentName(mRealTestContext, DummyDeviceAdmins.Admin3.class);
+
+        setUpPackageManagerForAdmin(admin1);
+        setUpPackageManagerForAdmin(admin2);
+        setUpPackageManagerForAdmin(admin3);
+
+        setUpApplicationInfo(PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED);
+        setUpPackageInfo();
+        setUpUserManager();
+    }
+
+    /**
+     * Set up a mock result for {@link PackageManager#queryBroadcastReceivers}.  We'll return
+     * the actual ResolveInfo for the admin component, but we need to mock PM so it'll return
+     * it for user {@link DpmMockContext#CALLER_USER_HANDLE}.
+     */
+    private void setUpPackageManagerForAdmin(ComponentName admin) {
+        final Intent resolveIntent = new Intent();
+        resolveIntent.setComponent(admin);
+        final List<ResolveInfo> realResolveInfo =
+                mRealTestContext.getPackageManager().queryBroadcastReceivers(
+                        resolveIntent,
+                        PackageManager.GET_META_DATA);
+        assertNotNull(realResolveInfo);
+        assertEquals(1, realResolveInfo.size());
+
+        // We need to rewrite the UID in the activity info.
+        realResolveInfo.get(0).activityInfo.applicationInfo.uid = DpmMockContext.CALLER_UID;
+
+        doReturn(realResolveInfo).when(mContext.packageManager).queryBroadcastReceivers(
+                MockUtils.checkIntentComponent(admin),
+                eq(PackageManager.GET_META_DATA
+                        | PackageManager.GET_DISABLED_UNTIL_USED_COMPONENTS),
+                eq(DpmMockContext.CALLER_USER_HANDLE)
+        );
+    }
+
+    /**
+     * Set up a mock result for {@link IPackageManager#getApplicationInfo} for user
+     * {@link DpmMockContext#CALLER_USER_HANDLE}.
+     */
+    private void setUpApplicationInfo(int enabledSetting) throws Exception {
+        final ApplicationInfo ai = mRealTestContext.getPackageManager().getApplicationInfo(
+                admin1.getPackageName(),
+                PackageManager.GET_DISABLED_UNTIL_USED_COMPONENTS);
+
+        ai.enabledSetting = enabledSetting;
+
+        doReturn(ai).when(mContext.ipackageManager).getApplicationInfo(
+                eq(admin1.getPackageName()),
+                eq(PackageManager.GET_DISABLED_UNTIL_USED_COMPONENTS),
+                eq(DpmMockContext.CALLER_USER_HANDLE));
+    }
+
+    /**
+     * Set up a mock result for {@link IPackageManager#getPackageInfo(String, int, int)} for user
+     * {@link DpmMockContext#CALLER_USER_HANDLE} as well as the system user.
+     */
+    private void setUpPackageInfo() throws Exception {
+        final PackageInfo pi = mRealTestContext.getPackageManager().getPackageInfo(
+                admin1.getPackageName(), 0);
+        assertTrue(pi.applicationInfo.flags != 0);
+
+        doReturn(pi).when(mContext.ipackageManager).getPackageInfo(
+                eq(admin1.getPackageName()),
+                eq(0),
+                eq(DpmMockContext.CALLER_USER_HANDLE));
+        doReturn(pi).when(mContext.ipackageManager).getPackageInfo(
+                eq(admin1.getPackageName()),
+                eq(0),
+                eq(UserHandle.USER_SYSTEM));
+    }
+
+    private void setUpUserManager() {
+        // Emulate UserManager.set/getApplicationRestriction().
+        final Map<Pair<String, UserHandle>, Bundle> appRestrictions = new HashMap<>();
+
+        // UM.setApplicationRestrictions() will save to appRestrictions.
+        doAnswer(new Answer<Void>() {
+            @Override
+            public Void answer(InvocationOnMock invocation) throws Throwable {
+                String pkg = (String) invocation.getArguments()[0];
+                Bundle bundle = (Bundle) invocation.getArguments()[1];
+                UserHandle user = (UserHandle) invocation.getArguments()[2];
+
+                appRestrictions.put(Pair.create(pkg, user), bundle);
+
+                return null;
+            }
+        }).when(mContext.userManager).setApplicationRestrictions(
+                anyString(), any(Bundle.class), any(UserHandle.class));
+
+        // UM.getApplicationRestrictions() will read from appRestrictions.
+        doAnswer(new Answer<Bundle>() {
+            @Override
+            public Bundle answer(InvocationOnMock invocation) throws Throwable {
+                String pkg = (String) invocation.getArguments()[0];
+                UserHandle user = (UserHandle) invocation.getArguments()[1];
+
+                return appRestrictions.get(Pair.create(pkg, user));
+            }
+        }).when(mContext.userManager).getApplicationRestrictions(
+                anyString(), any(UserHandle.class));
+
+        // System user is always running.
+        when(mContext.userManager.isUserRunning(MockUtils.checkUserHandle(UserHandle.USER_SYSTEM)))
+                .thenReturn(true);
+
+        // Set up (default) UserInfo for CALLER_USER_HANDLE.
+        final UserInfo uh = new UserInfo(DpmMockContext.CALLER_USER_HANDLE,
+                "user" + DpmMockContext.CALLER_USER_HANDLE, 0);
+
+        when(mContext.userManager.getUserInfo(eq(DpmMockContext.CALLER_USER_HANDLE)))
+                .thenReturn(uh);
+    }
+
+    private void setAsProfileOwner(ComponentName admin) {
+        mContext.callerPermissions.add(permission.MANAGE_DEVICE_ADMINS);
+        mContext.callerPermissions.add(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS);
+
+        final UserInfo uh = new UserInfo(DpmMockContext.CALLER_USER_HANDLE, "user", 0);
+
+        // DO needs to be an DA.
+        dpm.setActiveAdmin(admin, /* replace =*/ false);
+
+        // Fire!
+        assertTrue(dpm.setProfileOwner(admin, "owner-name", DpmMockContext.CALLER_USER_HANDLE));
+
+        // Check
+        assertEquals(admin1, dpm.getProfileOwnerAsUser(DpmMockContext.CALLER_USER_HANDLE));
+    }
+
+    public void testHasNoFeature() throws Exception {
+        when(mContext.packageManager.hasSystemFeature(eq(PackageManager.FEATURE_DEVICE_ADMIN)))
+                .thenReturn(false);
+
+        LocalServices.removeServiceForTest(DevicePolicyManagerInternal.class);
+        new DevicePolicyManagerServiceTestable(mContext, dataDir);
+
+        // If the device has no DPMS feature, it shouldn't register the local service.
+        assertNull(LocalServices.getService(DevicePolicyManagerInternal.class));
+    }
+
+    /**
+     * Caller doesn't have proper permissions.
+     */
+    public void testSetActiveAdmin_SecurityException() {
+        // 1. Failure cases.
+
+        // Caller doesn't have MANAGE_DEVICE_ADMINS.
+        try {
+            dpm.setActiveAdmin(admin1, false);
+            fail("Didn't throw SecurityException");
+        } catch (SecurityException expected) {
+        }
+
+        // Caller has MANAGE_DEVICE_ADMINS, but for different user.
+        mContext.callerPermissions.add(android.Manifest.permission.MANAGE_DEVICE_ADMINS);
+        try {
+            dpm.setActiveAdmin(admin1, false, DpmMockContext.CALLER_USER_HANDLE + 1);
+            fail("Didn't throw SecurityException");
+        } catch (SecurityException expected) {
+        }
+    }
+
+    /**
+     * Test for:
+     * {@link DevicePolicyManager#setActiveAdmin}
+     *   with replace=false and replace=true
+     * {@link DevicePolicyManager#isAdminActive}
+     * {@link DevicePolicyManager#isAdminActiveAsUser}
+     * {@link DevicePolicyManager#getActiveAdmins}
+     * {@link DevicePolicyManager#getActiveAdminsAsUser}
+     */
+    public void testSetActiveAdmin() throws Exception {
+        // 1. Make sure the caller has proper permissions.
+        mContext.callerPermissions.add(android.Manifest.permission.MANAGE_DEVICE_ADMINS);
+
+        // 2. Call the API.
+        dpm.setActiveAdmin(admin1, /* replace =*/ false);
+
+        // 3. Verify internal calls.
+
+        // Check if the boradcast is sent.
+        verify(mContext.spiedContext).sendBroadcastAsUser(
+                MockUtils.checkIntentAction(
+                        DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED),
+                MockUtils.checkUserHandle(DpmMockContext.CALLER_USER_HANDLE));
+        verify(mContext.spiedContext).sendBroadcastAsUser(
+                MockUtils.checkIntentAction(
+                        DeviceAdminReceiver.ACTION_DEVICE_ADMIN_ENABLED),
+                MockUtils.checkUserHandle(DpmMockContext.CALLER_USER_HANDLE));
+
+        verify(mContext.ipackageManager, times(1)).setApplicationEnabledSetting(
+                eq(admin1.getPackageName()),
+                eq(PackageManager.COMPONENT_ENABLED_STATE_DEFAULT),
+                eq(PackageManager.DONT_KILL_APP),
+                eq(DpmMockContext.CALLER_USER_HANDLE),
+                anyString());
+
+        // TODO Verify other calls too.
+
+        // Make sure it's active admin1.
+        assertTrue(dpm.isAdminActive(admin1));
+        assertFalse(dpm.isAdminActive(admin2));
+        assertFalse(dpm.isAdminActive(admin3));
+
+        // But not admin1 for a different user.
+
+        // For this to work, caller needs android.permission.INTERACT_ACROSS_USERS_FULL.
+        // (Because we're checking a different user's status from CALLER_USER_HANDLE.)
+        mContext.callerPermissions.add("android.permission.INTERACT_ACROSS_USERS_FULL");
+
+        assertFalse(dpm.isAdminActiveAsUser(admin1, DpmMockContext.CALLER_USER_HANDLE + 1));
+        assertFalse(dpm.isAdminActiveAsUser(admin2, DpmMockContext.CALLER_USER_HANDLE + 1));
+
+        mContext.callerPermissions.remove("android.permission.INTERACT_ACROSS_USERS_FULL");
+
+        // Next, add one more admin.
+        // Before doing so, update the application info, now it's enabled.
+        setUpApplicationInfo(PackageManager.COMPONENT_ENABLED_STATE_ENABLED);
+
+        dpm.setActiveAdmin(admin2, /* replace =*/ false);
+
+        // Now we have two admins.
+        assertTrue(dpm.isAdminActive(admin1));
+        assertTrue(dpm.isAdminActive(admin2));
+        assertFalse(dpm.isAdminActive(admin3));
+
+        // Admin2 was already enabled, so setApplicationEnabledSetting() shouldn't have called
+        // again.  (times(1) because it was previously called for admin1)
+        verify(mContext.ipackageManager, times(1)).setApplicationEnabledSetting(
+                eq(admin1.getPackageName()),
+                eq(PackageManager.COMPONENT_ENABLED_STATE_DEFAULT),
+                eq(PackageManager.DONT_KILL_APP),
+                eq(DpmMockContext.CALLER_USER_HANDLE),
+                anyString());
+
+        // 4. Add the same admin1 again without replace, which should throw.
+        try {
+            dpm.setActiveAdmin(admin1, /* replace =*/ false);
+            fail("Didn't throw");
+        } catch (IllegalArgumentException expected) {
+        }
+
+        // 5. Add the same admin1 again with replace, which should succeed.
+        dpm.setActiveAdmin(admin1, /* replace =*/ true);
+
+        // TODO make sure it's replaced.
+
+        // 6. Test getActiveAdmins()
+        List<ComponentName> admins = dpm.getActiveAdmins();
+        assertEquals(2, admins.size());
+        assertEquals(admin1, admins.get(0));
+        assertEquals(admin2, admins.get(1));
+
+        // Another user has no admins.
+        mContext.callerPermissions.add("android.permission.INTERACT_ACROSS_USERS_FULL");
+
+        assertEquals(0, DpmTestUtils.getListSizeAllowingNull(
+                dpm.getActiveAdminsAsUser(DpmMockContext.CALLER_USER_HANDLE + 1)));
+
+        mContext.callerPermissions.remove("android.permission.INTERACT_ACROSS_USERS_FULL");
+    }
+
+    /**
+     * Test for:
+     * {@link DevicePolicyManager#setActiveAdmin}
+     *   with replace=false
+     */
+    public void testSetActiveAdmin_twiceWithoutReplace() throws Exception {
+        // 1. Make sure the caller has proper permissions.
+        mContext.callerPermissions.add(android.Manifest.permission.MANAGE_DEVICE_ADMINS);
+
+        dpm.setActiveAdmin(admin1, /* replace =*/ false);
+        assertTrue(dpm.isAdminActive(admin1));
+
+        // Add the same admin1 again without replace, which should throw.
+        try {
+            dpm.setActiveAdmin(admin1, /* replace =*/ false);
+            fail("Didn't throw");
+        } catch (IllegalArgumentException expected) {
+        }
+    }
+
+    /**
+     * Test for:
+     * {@link DevicePolicyManager#removeActiveAdmin}
+     */
+    public void testRemoveActiveAdmin_SecurityException() {
+        mContext.callerPermissions.add(android.Manifest.permission.MANAGE_DEVICE_ADMINS);
+
+        // Add admin.
+
+        dpm.setActiveAdmin(admin1, /* replace =*/ false);
+
+        assertTrue(dpm.isAdminActive(admin1));
+
+        assertFalse(dpm.isRemovingAdmin(admin1, DpmMockContext.CALLER_USER_HANDLE));
+
+        // Directly call the DPMS method with a different userid, which should fail.
+        try {
+            dpms.removeActiveAdmin(admin1, DpmMockContext.CALLER_USER_HANDLE + 1);
+            fail("Didn't throw SecurityException");
+        } catch (SecurityException expected) {
+        }
+
+        // Try to remove active admin with a different caller userid should fail too, without
+        // having MANAGE_DEVICE_ADMINS.
+        mContext.callerPermissions.clear();
+
+        mContext.binder.callingUid = 1234567;
+        try {
+            dpm.removeActiveAdmin(admin1);
+            fail("Didn't throw SecurityException");
+        } catch (SecurityException expected) {
+        }
+    }
+
+    /**
+     * Test for:
+     * {@link DevicePolicyManager#removeActiveAdmin}
+     */
+    public void testRemoveActiveAdmin_fromDifferentUserWithMINTERACT_ACROSS_USERS_FULL() {
+        mContext.callerPermissions.add(android.Manifest.permission.MANAGE_DEVICE_ADMINS);
+
+        // Add admin1.
+
+        dpm.setActiveAdmin(admin1, /* replace =*/ false);
+
+        assertTrue(dpm.isAdminActive(admin1));
+        assertFalse(dpm.isRemovingAdmin(admin1, DpmMockContext.CALLER_USER_HANDLE));
+
+        // Different user, but should work, because caller has proper permissions.
+        mContext.callerPermissions.add(permission.INTERACT_ACROSS_USERS_FULL);
+        mContext.binder.callingUid = 1234567;
+        dpm.removeActiveAdmin(admin1);
+
+        assertTrue(dpm.isRemovingAdmin(admin1, DpmMockContext.CALLER_USER_HANDLE));
+
+        // TODO DO Still can't be removed in this case.
+    }
+
+    /**
+     * Test for:
+     * {@link DevicePolicyManager#removeActiveAdmin}
+     */
+    public void testRemoveActiveAdmin_sameUserNoMANAGE_DEVICE_ADMINS() {
+        // Need MANAGE_DEVICE_ADMINS for setActiveAdmin.  We'll remove it later.
+        mContext.callerPermissions.add(android.Manifest.permission.MANAGE_DEVICE_ADMINS);
+
+        // Add admin1.
+
+        dpm.setActiveAdmin(admin1, /* replace =*/ false);
+
+        assertTrue(dpm.isAdminActive(admin1));
+        assertFalse(dpm.isRemovingAdmin(admin1, DpmMockContext.CALLER_USER_HANDLE));
+
+        // Broadcast from saveSettingsLocked().
+        verify(mContext.spiedContext, times(1)).sendBroadcastAsUser(
+                MockUtils.checkIntentAction(
+                        DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED),
+                MockUtils.checkUserHandle(DpmMockContext.CALLER_USER_HANDLE));
+
+        // Remove.  No permissions, but same user, so it'll work.
+        mContext.callerPermissions.clear();
+        dpm.removeActiveAdmin(admin1);
+
+        final ArgumentCaptor<BroadcastReceiver> brCap =
+                ArgumentCaptor.forClass(BroadcastReceiver.class);
+
+        // Is removing now, but not removed yet.
+        assertTrue(dpm.isAdminActive(admin1));
+        assertTrue(dpm.isRemovingAdmin(admin1, DpmMockContext.CALLER_USER_HANDLE));
+
+        verify(mContext.spiedContext).sendOrderedBroadcastAsUser(
+                MockUtils.checkIntentAction(
+                        DeviceAdminReceiver.ACTION_DEVICE_ADMIN_DISABLED),
+                MockUtils.checkUserHandle(DpmMockContext.CALLER_USER_HANDLE),
+                isNull(String.class),
+                brCap.capture(),
+                eq(dpms.mHandler),
+                eq(Activity.RESULT_OK),
+                isNull(String.class),
+                isNull(Bundle.class));
+
+        brCap.getValue().onReceive(mContext, null);
+
+        assertFalse(dpm.isAdminActive(admin1));
+        assertFalse(dpm.isRemovingAdmin(admin1, DpmMockContext.CALLER_USER_HANDLE));
+
+        // Again broadcast from saveSettingsLocked().
+        verify(mContext.spiedContext, times(2)).sendBroadcastAsUser(
+                MockUtils.checkIntentAction(
+                        DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED),
+                MockUtils.checkUserHandle(DpmMockContext.CALLER_USER_HANDLE));
+
+        // TODO Check other internal calls.
+    }
+
+    /**
+     * Test for: {@link DevicePolicyManager#setDeviceOwner} DO on system user installs
+     * successfully.
+     */
+    public void testSetDeviceOwner() throws Exception {
+        mContext.callerPermissions.add(permission.MANAGE_DEVICE_ADMINS);
+        mContext.callerPermissions.add(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS);
+        mContext.callerPermissions.add(permission.INTERACT_ACROSS_USERS_FULL);
+
+        // Call from a process on the system user.
+        mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID;
+
+        // DO needs to be an DA.
+        dpm.setActiveAdmin(admin1, /* replace =*/ false);
+
+        // Fire!
+        assertTrue(dpm.setDeviceOwner(admin1.getPackageName(), "owner-name"));
+
+        // Verify internal calls.
+        verify(mContext.iactivityManager, times(1)).updateDeviceOwner(
+                eq(admin1.getPackageName()));
+
+        // TODO We should check if the caller has called clearCallerIdentity().
+        verify(mContext.ibackupManager, times(1)).setBackupServiceActive(
+                eq(UserHandle.USER_SYSTEM), eq(false));
+
+        verify(mContext.spiedContext, times(1)).sendBroadcastAsUser(
+                MockUtils.checkIntentAction(DevicePolicyManager.ACTION_DEVICE_OWNER_CHANGED),
+                MockUtils.checkUserHandle(UserHandle.USER_SYSTEM));
+
+        assertEquals(admin1.getPackageName(), dpm.getDeviceOwner());
+
+        // TODO Test getDeviceOwnerName() too.  To do so, we need to change
+        // DPMS.getApplicationLabel() because Context.createPackageContextAsUser() is not mockable.
+    }
+
+    /**
+     * Test for: {@link DevicePolicyManager#setDeviceOwner} Package doesn't exist.
+     */
+    public void testSetDeviceOwner_noSuchPackage() {
+        mContext.callerPermissions.add(permission.MANAGE_DEVICE_ADMINS);
+        mContext.callerPermissions.add(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS);
+        mContext.callerPermissions.add(permission.INTERACT_ACROSS_USERS_FULL);
+
+        // Call from a process on the system user.
+        mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID;
+
+        // DO needs to be an DA.
+        dpm.setActiveAdmin(admin1, /* replace =*/ false);
+        try {
+            dpm.setDeviceOwner("a.b.c");
+            fail("Didn't throw IllegalArgumentException");
+        } catch (IllegalArgumentException expected) {
+        }
+    }
+
+    public void testSetDeviceOwner_failures() throws Exception {
+        // TODO Test more failure cases.  Basically test all chacks in enforceCanSetDeviceOwner().
+    }
+
+    public void testSetProfileOwner() throws Exception {
+        setAsProfileOwner(admin1);
+    }
+
+    public void testSetProfileOwner_failures() throws Exception {
+        // TODO Test more failure cases.  Basically test all chacks in enforceCanSetProfileOwner().
+    }
+
+    public void testSetGetApplicationRestriction() {
+        setAsProfileOwner(admin1);
+
+        {
+            Bundle rest = new Bundle();
+            rest.putString("KEY_STRING", "Foo1");
+            dpm.setApplicationRestrictions(admin1, "pkg1", rest);
+        }
+
+        {
+            Bundle rest = new Bundle();
+            rest.putString("KEY_STRING", "Foo2");
+            dpm.setApplicationRestrictions(admin1, "pkg2", rest);
+        }
+
+        {
+            Bundle returned = dpm.getApplicationRestrictions(admin1, "pkg1");
+            assertNotNull(returned);
+            assertEquals(returned.size(), 1);
+            assertEquals(returned.get("KEY_STRING"), "Foo1");
+        }
+
+        {
+            Bundle returned = dpm.getApplicationRestrictions(admin1, "pkg2");
+            assertNotNull(returned);
+            assertEquals(returned.size(), 1);
+            assertEquals(returned.get("KEY_STRING"), "Foo2");
+        }
+
+        dpm.setApplicationRestrictions(admin1, "pkg2", new Bundle());
+        assertEquals(0, dpm.getApplicationRestrictions(admin1, "pkg2").size());
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTestable.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTestable.java
new file mode 100644
index 0000000..325bf9f
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTestable.java
@@ -0,0 +1,36 @@
+/*
+ * 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.server.devicepolicy;
+
+import android.app.admin.DevicePolicyManager;
+
+/**
+ * Overrides {@link #DevicePolicyManager} for dependency injection.
+ */
+public class DevicePolicyManagerTestable extends DevicePolicyManager {
+    public final DevicePolicyManagerServiceTestable dpms;
+
+    public DevicePolicyManagerTestable(DpmMockContext context,
+            DevicePolicyManagerServiceTestable dpms) {
+        super(context, dpms);
+        this.dpms = dpms;
+    }
+
+    @Override
+    public int myUserId() {
+        return DpmMockContext.CALLER_USER_HANDLE;
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java
index c2b8981..f3d9160 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java
@@ -16,31 +16,382 @@
 
 package com.android.server.devicepolicy;
 
+import com.android.internal.widget.LockPatternUtils;
+
+import android.app.IActivityManager;
+import android.app.NotificationManager;
+import android.app.backup.IBackupManager;
+import android.content.BroadcastReceiver;
 import android.content.Context;
-import android.content.ContextWrapper;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.IPackageManager;
+import android.content.pm.PackageManager;
+import android.media.IAudioService;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.PowerManager.WakeLock;
+import android.os.PowerManagerInternal;
+import android.os.UserHandle;
 import android.os.UserManager;
+import android.test.mock.MockContext;
+import android.view.IWindowManager;
+
+import org.junit.Assert;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
 
 import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
 
-public class DpmMockContext extends ContextWrapper {
-    private final UserManager mMockUserManager;
+/**
+ * Context used throughout DPMS tests.
+ */
+public class DpmMockContext extends MockContext {
+    /**
+     * User-id of a non-system user we use throughout unit tests.
+     */
+    public static final int CALLER_USER_HANDLE = 20;
 
+    /**
+     * UID corresponding to {@link #CALLER_USER_HANDLE}.
+     */
+    public static final int CALLER_UID = UserHandle.PER_USER_RANGE * CALLER_USER_HANDLE + 123;
 
-    public DpmMockContext(Context context) {
-        super(context);
-        mMockUserManager = mock(UserManager.class);
+    /**
+     * UID used when a caller is on the system user.
+     */
+    public static final int CALLER_SYSTEM_USER_UID = 123;
+
+    /**
+     * PID of the caller.
+     */
+    public static final int CALLER_PID = 22222;
+
+    /**
+     * UID of the system server.
+     */
+    public static final int SYSTEM_UID = android.os.Process.SYSTEM_UID;
+
+    /**
+     * PID of the system server.
+     */
+    public static final int SYSTEM_PID = 11111;
+
+    public static class MockBinder {
+        public int callingUid = CALLER_UID;
+        public int callingPid = CALLER_PID;
+
+        public long clearCallingIdentity() {
+            final long token = (((long) callingUid) << 32) | (callingPid);
+            callingUid = SYSTEM_UID;
+            callingPid = SYSTEM_PID;
+            return token;
+        }
+
+        public void restoreCallingIdentity(long token) {
+            callingUid = (int) (token >> 32);
+            callingPid = (int) token;
+        }
+
+        public int getCallingUid() {
+            return callingUid;
+        }
+
+        public int getCallingPid() {
+            return callingPid;
+        }
+
+        public UserHandle getCallingUserHandle() {
+            return new UserHandle(UserHandle.getUserId(getCallingUid()));
+        }
+
+        public boolean isCallerUidMyUid() {
+            return callingUid == SYSTEM_UID;
+        }
     }
 
-    public UserManager getMockUserManager() {
-        return mMockUserManager;
+    public static class EnvironmentForMock {
+        public File getUserSystemDirectory(int userId) {
+            return null;
+        }
+    }
+
+    public static class PowerManagerForMock {
+        public WakeLock newWakeLock(int levelAndFlags, String tag) {
+            return null;
+        }
+
+        public void goToSleep(long time, int reason, int flags) {
+        }
+    }
+
+    public static class SystemPropertiesForMock {
+        public boolean getBoolean(String key, boolean def) {
+            return false;
+        }
+
+        public long getLong(String key, long def) {
+            return 0;
+        }
+
+        public String get(String key, String def) {
+            return null;
+        }
+
+        public String get(String key) {
+            return null;
+        }
+
+        public void set(String key, String value) {
+        }
+    }
+
+    public static class UserManagerForMock {
+        public boolean isSplitSystemUser() {
+            return false;
+        }
+    }
+
+    public final Context realTestContext;
+
+    /**
+     * Use this instance to verify unimplemented methods such as {@link #sendBroadcast}.
+     * (Spying on {@code this} instance will confuse mockito somehow and I got weired "wrong number
+     * of arguments" exceptions.)
+     */
+    public final Context spiedContext;
+
+    public final MockBinder binder;
+    public final EnvironmentForMock environment;
+    public final SystemPropertiesForMock systemProperties;
+    public final UserManager userManager;
+    public final UserManagerForMock userManagerForMock;
+    public final PowerManagerForMock powerManager;
+    public final PowerManagerInternal powerManagerInternal;
+    public final NotificationManager notificationManager;
+    public final IWindowManager iwindowManager;
+    public final IActivityManager iactivityManager;
+    public final IPackageManager ipackageManager;
+    public final IBackupManager ibackupManager;
+    public final IAudioService iaudioService;
+    public final LockPatternUtils lockPatternUtils;
+
+    /** Note this is a partial mock, not a real mock. */
+    public final PackageManager packageManager;
+
+    public final List<String> callerPermissions = new ArrayList<>();
+
+    public DpmMockContext(Context context) {
+        realTestContext = context;
+        binder = new MockBinder();
+        environment = mock(EnvironmentForMock.class);
+        systemProperties= mock(SystemPropertiesForMock.class);
+        userManager = mock(UserManager.class);
+        userManagerForMock = mock(UserManagerForMock.class);
+        powerManager = mock(PowerManagerForMock.class);
+        powerManagerInternal = mock(PowerManagerInternal.class);
+        notificationManager = mock(NotificationManager.class);
+        iwindowManager = mock(IWindowManager.class);
+        iactivityManager = mock(IActivityManager.class);
+        ipackageManager = mock(IPackageManager.class);
+        ibackupManager = mock(IBackupManager.class);
+        iaudioService = mock(IAudioService.class);
+        lockPatternUtils = mock(LockPatternUtils.class);
+
+        // Package manager is huge, so we use a partial mock instead.
+        packageManager = spy(context.getPackageManager());
+
+        spiedContext = mock(Context.class);
     }
 
     @Override
     public Object getSystemService(String name) {
         switch (name) {
             case Context.USER_SERVICE:
-                return mMockUserManager;
+                return userManager;
+            case Context.POWER_SERVICE:
+                return powerManager;
         }
-        return super.getSystemService(name);
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public PackageManager getPackageManager() {
+        return packageManager;
+    }
+
+    @Override
+    public void enforceCallingOrSelfPermission(String permission, String message) {
+        if (binder.getCallingUid() == SYSTEM_UID) {
+            return; // Assume system has all permissions.
+        }
+        if (!callerPermissions.contains(permission)) {
+            throw new SecurityException("Caller doesn't have " + permission + " : " + message);
+        }
+    }
+
+    @Override
+    public void sendBroadcast(Intent intent) {
+        spiedContext.sendBroadcast(intent);
+    }
+
+    @Override
+    public void sendBroadcast(Intent intent, String receiverPermission) {
+        spiedContext.sendBroadcast(intent, receiverPermission);
+    }
+
+    @Override
+    public void sendBroadcastMultiplePermissions(Intent intent, String[] receiverPermissions) {
+        spiedContext.sendBroadcastMultiplePermissions(intent, receiverPermissions);
+    }
+
+    @Override
+    public void sendBroadcast(Intent intent, String receiverPermission, Bundle options) {
+        spiedContext.sendBroadcast(intent, receiverPermission, options);
+    }
+
+    @Override
+    public void sendBroadcast(Intent intent, String receiverPermission, int appOp) {
+        spiedContext.sendBroadcast(intent, receiverPermission, appOp);
+    }
+
+    @Override
+    public void sendOrderedBroadcast(Intent intent, String receiverPermission) {
+        spiedContext.sendOrderedBroadcast(intent, receiverPermission);
+    }
+
+    @Override
+    public void sendOrderedBroadcast(Intent intent, String receiverPermission,
+            BroadcastReceiver resultReceiver, Handler scheduler, int initialCode,
+            String initialData, Bundle initialExtras) {
+        spiedContext.sendOrderedBroadcast(intent, receiverPermission, resultReceiver, scheduler,
+                initialCode, initialData, initialExtras);
+    }
+
+    @Override
+    public void sendOrderedBroadcast(Intent intent, String receiverPermission, Bundle options,
+            BroadcastReceiver resultReceiver, Handler scheduler, int initialCode,
+            String initialData, Bundle initialExtras) {
+        spiedContext.sendOrderedBroadcast(intent, receiverPermission, options, resultReceiver,
+                scheduler,
+                initialCode, initialData, initialExtras);
+    }
+
+    @Override
+    public void sendOrderedBroadcast(Intent intent, String receiverPermission, int appOp,
+            BroadcastReceiver resultReceiver, Handler scheduler, int initialCode,
+            String initialData, Bundle initialExtras) {
+        spiedContext.sendOrderedBroadcast(intent, receiverPermission, appOp, resultReceiver,
+                scheduler,
+                initialCode, initialData, initialExtras);
+    }
+
+    @Override
+    public void sendBroadcastAsUser(Intent intent, UserHandle user) {
+        if (binder.callingPid != SYSTEM_PID) {
+            // Unless called as the system process, can only call if the target user is the
+            // calling user.
+            // (The actual check is more complex; we may need to change it later.)
+            Assert.assertEquals(UserHandle.getUserId(binder.getCallingUid()), user.getIdentifier());
+        }
+
+        spiedContext.sendBroadcastAsUser(intent, user);
+    }
+
+    @Override
+    public void sendBroadcastAsUser(Intent intent, UserHandle user, String receiverPermission) {
+        spiedContext.sendBroadcastAsUser(intent, user, receiverPermission);
+    }
+
+    @Override
+    public void sendBroadcastAsUser(Intent intent, UserHandle user, String receiverPermission,
+            int appOp) {
+        spiedContext.sendBroadcastAsUser(intent, user, receiverPermission, appOp);
+    }
+
+    @Override
+    public void sendOrderedBroadcastAsUser(Intent intent, UserHandle user,
+            String receiverPermission, BroadcastReceiver resultReceiver, Handler scheduler,
+            int initialCode, String initialData, Bundle initialExtras) {
+        spiedContext.sendOrderedBroadcastAsUser(intent, user, receiverPermission, resultReceiver,
+                scheduler, initialCode, initialData, initialExtras);
+    }
+
+    @Override
+    public void sendOrderedBroadcastAsUser(Intent intent, UserHandle user,
+            String receiverPermission, int appOp, BroadcastReceiver resultReceiver,
+            Handler scheduler, int initialCode, String initialData, Bundle initialExtras) {
+        spiedContext.sendOrderedBroadcastAsUser(intent, user, receiverPermission, appOp,
+                resultReceiver,
+                scheduler, initialCode, initialData, initialExtras);
+    }
+
+    @Override
+    public void sendOrderedBroadcastAsUser(Intent intent, UserHandle user,
+            String receiverPermission, int appOp, Bundle options, BroadcastReceiver resultReceiver,
+            Handler scheduler, int initialCode, String initialData, Bundle initialExtras) {
+        spiedContext.sendOrderedBroadcastAsUser(intent, user, receiverPermission, appOp, options,
+                resultReceiver, scheduler, initialCode, initialData, initialExtras);
+    }
+
+    @Override
+    public void sendStickyBroadcast(Intent intent) {
+        spiedContext.sendStickyBroadcast(intent);
+    }
+
+    @Override
+    public void sendStickyOrderedBroadcast(Intent intent, BroadcastReceiver resultReceiver,
+            Handler scheduler, int initialCode, String initialData, Bundle initialExtras) {
+        spiedContext.sendStickyOrderedBroadcast(intent, resultReceiver, scheduler, initialCode,
+                initialData, initialExtras);
+    }
+
+    @Override
+    public void removeStickyBroadcast(Intent intent) {
+        spiedContext.removeStickyBroadcast(intent);
+    }
+
+    @Override
+    public void sendStickyBroadcastAsUser(Intent intent, UserHandle user) {
+        spiedContext.sendStickyBroadcastAsUser(intent, user);
+    }
+
+    @Override
+    public void sendStickyOrderedBroadcastAsUser(Intent intent, UserHandle user,
+            BroadcastReceiver resultReceiver, Handler scheduler, int initialCode,
+            String initialData, Bundle initialExtras) {
+        spiedContext.sendStickyOrderedBroadcastAsUser(intent, user, resultReceiver, scheduler, initialCode,
+                initialData, initialExtras);
+    }
+
+    @Override
+    public void removeStickyBroadcastAsUser(Intent intent, UserHandle user) {
+        spiedContext.removeStickyBroadcastAsUser(intent, user);
+    }
+
+    @Override
+    public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter) {
+        return spiedContext.registerReceiver(receiver, filter);
+    }
+
+    @Override
+    public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter,
+            String broadcastPermission, Handler scheduler) {
+        return spiedContext.registerReceiver(receiver, filter, broadcastPermission, scheduler);
+    }
+
+    @Override
+    public Intent registerReceiverAsUser(BroadcastReceiver receiver, UserHandle user,
+            IntentFilter filter, String broadcastPermission, Handler scheduler) {
+        return spiedContext.registerReceiverAsUser(receiver, user, filter, broadcastPermission,
+                scheduler);
+    }
+
+    @Override
+    public void unregisterReceiver(BroadcastReceiver receiver) {
+        spiedContext.unregisterReceiver(receiver);
     }
 }
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DpmTestBase.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DpmTestBase.java
index 445260b..6f9f6ab 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DpmTestBase.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DpmTestBase.java
@@ -19,14 +19,25 @@
 import android.content.Context;
 import android.test.AndroidTestCase;
 
-public class DpmTestBase extends AndroidTestCase {
-    private DpmMockContext mMockContext;
+import java.io.File;
+
+public abstract class DpmTestBase extends AndroidTestCase {
+    public static final String TAG = "DpmTest";
+
+    protected Context mRealTestContext;
+    protected DpmMockContext mMockContext;
+
+    public File dataDir;
 
     @Override
     protected void setUp() throws Exception {
         super.setUp();
 
+        mRealTestContext = super.getContext();
         mMockContext = new DpmMockContext(super.getContext());
+
+        dataDir = new File(mRealTestContext.getCacheDir(), "test-data");
+        DpmTestUtils.clearDir(dataDir);
     }
 
     @Override
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DpmTestUtils.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DpmTestUtils.java
new file mode 100644
index 0000000..44a851a
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DpmTestUtils.java
@@ -0,0 +1,50 @@
+/*
+ * 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.server.devicepolicy;
+
+import android.os.FileUtils;
+import android.util.Log;
+import android.util.Printer;
+
+import org.junit.Assert;
+
+import java.io.File;
+import java.util.List;
+
+public class DpmTestUtils {
+    private DpmTestUtils() {
+    }
+
+    public static void clearDir(File dir) {
+        if (dir.exists()) {
+            Assert.assertTrue("failed to delete dir", FileUtils.deleteContents(dir));
+        }
+        dir.mkdirs();
+        Log.i(DpmTestBase.TAG, "Created " + dir);
+    }
+
+    public static int getListSizeAllowingNull(List<?> list) {
+        return list == null ? 0 : list.size();
+    }
+
+    public static Printer LOG_PRINTER = new Printer() {
+        @Override
+        public void println(String x) {
+            Log.i(DpmTestBase.TAG, x);
+        }
+    };
+}
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DummyDeviceAdmins.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DummyDeviceAdmins.java
new file mode 100644
index 0000000..08293a2
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DummyDeviceAdmins.java
@@ -0,0 +1,27 @@
+/*
+ * 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.server.devicepolicy;
+
+import android.app.admin.DeviceAdminReceiver;
+
+public class DummyDeviceAdmins {
+    public static class Admin1 extends DeviceAdminReceiver {
+    }
+    public static class Admin2 extends DeviceAdminReceiver {
+    }
+    public static class Admin3 extends DeviceAdminReceiver {
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/MockUtils.java b/services/tests/servicestests/src/com/android/server/devicepolicy/MockUtils.java
new file mode 100644
index 0000000..5008fbf
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/MockUtils.java
@@ -0,0 +1,80 @@
+/*
+ * 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.server.devicepolicy;
+
+import com.google.common.base.Objects;
+
+import android.content.ComponentName;
+import android.content.Intent;
+import android.os.UserHandle;
+
+import org.hamcrest.BaseMatcher;
+import org.hamcrest.Description;
+import org.hamcrest.Matcher;
+import org.mockito.Mockito;
+
+public class MockUtils {
+    private MockUtils() {
+    }
+
+    public static UserHandle checkUserHandle(final int userId) {
+        final Matcher<UserHandle> m = new BaseMatcher<UserHandle>() {
+            @Override
+            public boolean matches(Object item) {
+                if (item == null) return false;
+                return Objects.equal(((UserHandle) item).getIdentifier(), userId);
+            }
+
+            @Override
+            public void describeTo(Description description) {
+                description.appendText("UserHandle: user-id= \"" + userId + "\"");
+            }
+        };
+        return Mockito.argThat(m);
+    }
+
+    public static Intent checkIntentComponent(final ComponentName component) {
+        final Matcher<Intent> m = new BaseMatcher<Intent>() {
+            @Override
+            public boolean matches(Object item) {
+                if (item == null) return false;
+                return Objects.equal(((Intent) item).getComponent(), component);
+            }
+
+            @Override
+            public void describeTo(Description description) {
+                description.appendText("Intent: component=\"" + component + "\"");
+            }
+        };
+        return Mockito.argThat(m);
+    }
+
+    public static Intent checkIntentAction(final String action) {
+        final Matcher<Intent> m = new BaseMatcher<Intent>() {
+            @Override
+            public boolean matches(Object item) {
+                if (item == null) return false;
+                return Objects.equal(((Intent) item).getAction(), action);
+            }
+
+            @Override
+            public void describeTo(Description description) {
+                description.appendText("Intent: action=\"" + action + "\"");
+            }
+        };
+        return Mockito.argThat(m);
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/OwnersTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/OwnersTest.java
index 3b88fb1..a07d615 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/OwnersTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/OwnersTest.java
@@ -16,12 +16,11 @@
 
 package com.android.server.devicepolicy;
 
+import com.android.server.devicepolicy.DevicePolicyManagerServiceTestable.OwnersTestable;
+
 import android.content.ComponentName;
-import android.content.Context;
 import android.content.pm.UserInfo;
-import android.os.FileUtils;
 import android.os.UserHandle;
-import android.test.AndroidTestCase;
 import android.util.Log;
 
 import java.io.BufferedReader;
@@ -31,8 +30,6 @@
 import java.io.InputStreamReader;
 import java.util.ArrayList;
 
-import junit.framework.Assert;
-
 import static org.mockito.Mockito.when;
 
 /**
@@ -47,58 +44,11 @@
  (mmma frameworks/base/services/tests/servicestests/ for non-ninja build)
  */
 public class OwnersTest extends DpmTestBase {
-    private static final String TAG = "DeviceOwnerTest";
-
-    private static final String LEGACY_FILE = "legacy.xml";
-    private static final String DEVICE_OWNER_FILE = "device_owner2.xml";
-    private static final String PROFILE_OWNER_FILE_BASE = "profile_owner.xml";
-
-    private File mDataDir;
-
-    private class OwnersSub extends Owners {
-        final File mLegacyFile;
-        final File mDeviceOwnerFile;
-        final File mProfileOwnerBase;
-
-        public OwnersSub() {
-            super(getContext());
-            mLegacyFile = new File(mDataDir, LEGACY_FILE);
-            mDeviceOwnerFile = new File(mDataDir, DEVICE_OWNER_FILE);
-            mProfileOwnerBase = new File(mDataDir, PROFILE_OWNER_FILE_BASE);
-        }
-
-        @Override
-        File getLegacyConfigFileWithTestOverride() {
-            return mLegacyFile;
-        }
-
-        @Override
-        File getDeviceOwnerFileWithTestOverride() {
-            return mDeviceOwnerFile;
-        }
-
-        @Override
-        File getProfileOwnerFileWithTestOverride(int userId) {
-            return new File(mDeviceOwnerFile.getAbsoluteFile() + "-" + userId);
-        }
-    }
-
-    @Override
-    protected void setUp() throws Exception {
-        super.setUp();
-
-        mDataDir = new File(getContext().getCacheDir(), "OwnersTest");
-        if (mDataDir.exists()) {
-            assertTrue("failed to delete dir", FileUtils.deleteContents(mDataDir));
-        }
-        mDataDir.mkdirs();
-        Log.i(TAG, "Created " + mDataDir);
-    }
-
     private String readAsset(String assetPath) throws IOException {
         final StringBuilder sb = new StringBuilder();
         try (BufferedReader br = new BufferedReader(
-                new InputStreamReader((getContext().getResources().getAssets().open(assetPath))))) {
+                new InputStreamReader(
+                        mRealTestContext.getResources().getAssets().open(assetPath)))) {
             String line;
             while ((line = br.readLine()) != null) {
                 sb.append(line);
@@ -126,7 +76,7 @@
             ui.id = userId;
             userInfos.add(ui);
         }
-        when(getContext().getMockUserManager().getUsers()).thenReturn(userInfos);
+        when(getContext().userManager.getUsers()).thenReturn(userInfos);
     }
 
     public void testUpgrade01() throws Exception {
@@ -134,9 +84,10 @@
 
         // First, migrate.
         {
-            final OwnersSub owners = new OwnersSub();
+            final OwnersTestable owners = new OwnersTestable(getContext(), dataDir);
 
-            createLegacyFile(owners.mLegacyFile, readAsset("OwnersTest/test01/input.xml"));
+            createLegacyFile(owners.getLegacyConfigFileWithTestOverride(),
+                    readAsset("OwnersTest/test01/input.xml"));
 
             owners.load();
 
@@ -160,7 +111,7 @@
 
         // Then re-read and check.
         {
-            final OwnersSub owners = new OwnersSub();
+            final OwnersTestable owners = new OwnersTestable(getContext(), dataDir);
             owners.load();
 
             assertFalse(owners.hasDeviceOwner());
@@ -176,9 +127,10 @@
 
         // First, migrate.
         {
-            final OwnersSub owners = new OwnersSub();
+            final OwnersTestable owners = new OwnersTestable(getContext(), dataDir);
 
-            createLegacyFile(owners.mLegacyFile, readAsset("OwnersTest/test02/input.xml"));
+            createLegacyFile(owners.getLegacyConfigFileWithTestOverride(),
+                    readAsset("OwnersTest/test02/input.xml"));
 
             owners.load();
 
@@ -204,7 +156,7 @@
 
         // Then re-read and check.
         {
-            final OwnersSub owners = new OwnersSub();
+            final OwnersTestable owners = new OwnersTestable(getContext(), dataDir);
             owners.load();
 
             assertTrue(owners.hasDeviceOwner());
@@ -223,9 +175,10 @@
 
         // First, migrate.
         {
-            final OwnersSub owners = new OwnersSub();
+            final OwnersTestable owners = new OwnersTestable(getContext(), dataDir);
 
-            createLegacyFile(owners.mLegacyFile, readAsset("OwnersTest/test03/input.xml"));
+            createLegacyFile(owners.getLegacyConfigFileWithTestOverride(),
+                    readAsset("OwnersTest/test03/input.xml"));
 
             owners.load();
 
@@ -259,7 +212,7 @@
 
         // Then re-read and check.
         {
-            final OwnersSub owners = new OwnersSub();
+            final OwnersTestable owners = new OwnersTestable(getContext(), dataDir);
             owners.load();
 
             assertFalse(owners.hasDeviceOwner());
@@ -286,9 +239,10 @@
 
         // First, migrate.
         {
-            final OwnersSub owners = new OwnersSub();
+            final OwnersTestable owners = new OwnersTestable(getContext(), dataDir);
 
-            createLegacyFile(owners.mLegacyFile, readAsset("OwnersTest/test04/input.xml"));
+            createLegacyFile(owners.getLegacyConfigFileWithTestOverride(),
+                    readAsset("OwnersTest/test04/input.xml"));
 
             owners.load();
 
@@ -327,7 +281,7 @@
 
         // Then re-read and check.
         {
-            final OwnersSub owners = new OwnersSub();
+            final OwnersTestable owners = new OwnersTestable(getContext(), dataDir);
             owners.load();
 
             assertTrue(owners.hasDeviceOwner());
@@ -359,9 +313,10 @@
 
         // First, migrate.
         {
-            final OwnersSub owners = new OwnersSub();
+            final OwnersTestable owners = new OwnersTestable(getContext(), dataDir);
 
-            createLegacyFile(owners.mLegacyFile, readAsset("OwnersTest/test05/input.xml"));
+            createLegacyFile(owners.getLegacyConfigFileWithTestOverride(),
+                    readAsset("OwnersTest/test05/input.xml"));
 
             owners.load();
 
@@ -386,7 +341,7 @@
 
         // Then re-read and check.
         {
-            final OwnersSub owners = new OwnersSub();
+            final OwnersTestable owners = new OwnersTestable(getContext(), dataDir);
             owners.load();
 
             assertFalse(owners.hasDeviceOwner());
@@ -405,9 +360,10 @@
 
         // First, migrate.
         {
-            final OwnersSub owners = new OwnersSub();
+            final OwnersTestable owners = new OwnersTestable(getContext(), dataDir);
 
-            createLegacyFile(owners.mLegacyFile, readAsset("OwnersTest/test06/input.xml"));
+            createLegacyFile(owners.getLegacyConfigFileWithTestOverride(),
+                    readAsset("OwnersTest/test06/input.xml"));
 
             owners.load();
 
@@ -431,7 +387,7 @@
 
         // Then re-read and check.
         {
-            final OwnersSub owners = new OwnersSub();
+            final OwnersTestable owners = new OwnersTestable(getContext(), dataDir);
             owners.load();
 
             assertFalse(owners.hasDeviceOwner());
@@ -447,10 +403,11 @@
     public void testRemoveExistingFiles() throws Exception {
         addUsersToUserManager(10, 11, 20, 21);
 
-        final OwnersSub owners = new OwnersSub();
+        final OwnersTestable owners = new OwnersTestable(getContext(), dataDir);
 
         // First, migrate to create new-style config files.
-        createLegacyFile(owners.mLegacyFile, readAsset("OwnersTest/test04/input.xml"));
+        createLegacyFile(owners.getLegacyConfigFileWithTestOverride(),
+                readAsset("OwnersTest/test04/input.xml"));
 
         owners.load();
 
diff --git a/services/usage/java/com/android/server/usage/UnixCalendar.java b/services/usage/java/com/android/server/usage/UnixCalendar.java
index ce06a91..db7b42d 100644
--- a/services/usage/java/com/android/server/usage/UnixCalendar.java
+++ b/services/usage/java/com/android/server/usage/UnixCalendar.java
@@ -15,40 +15,22 @@
  */
 package com.android.server.usage;
 
-import android.app.usage.UsageStatsManager;
-
 /**
  * A handy calendar object that knows nothing of Locale's or TimeZones. This simplifies
  * interval book-keeping. It is *NOT* meant to be used as a user-facing calendar, as it has
  * no concept of Locale or TimeZone.
  */
 public class UnixCalendar {
-    private static final long DAY_IN_MILLIS = 24 * 60 * 60 * 1000;
-    private static final long WEEK_IN_MILLIS = 7 * DAY_IN_MILLIS;
-    private static final long MONTH_IN_MILLIS = 30 * DAY_IN_MILLIS;
-    private static final long YEAR_IN_MILLIS = 365 * DAY_IN_MILLIS;
+    public static final long DAY_IN_MILLIS = 24 * 60 * 60 * 1000;
+    public static final long WEEK_IN_MILLIS = 7 * DAY_IN_MILLIS;
+    public static final long MONTH_IN_MILLIS = 30 * DAY_IN_MILLIS;
+    public static final long YEAR_IN_MILLIS = 365 * DAY_IN_MILLIS;
     private long mTime;
 
     public UnixCalendar(long time) {
         mTime = time;
     }
 
-    public void truncateToDay() {
-        mTime -= mTime % DAY_IN_MILLIS;
-    }
-
-    public void truncateToWeek() {
-        mTime -= mTime % WEEK_IN_MILLIS;
-    }
-
-    public void truncateToMonth() {
-        mTime -= mTime % MONTH_IN_MILLIS;
-    }
-
-    public void truncateToYear() {
-        mTime -= mTime % YEAR_IN_MILLIS;
-    }
-
     public void addDays(int val) {
         mTime += val * DAY_IN_MILLIS;
     }
@@ -72,28 +54,4 @@
     public long getTimeInMillis() {
         return mTime;
     }
-
-    public static void truncateTo(UnixCalendar calendar, int intervalType) {
-        switch (intervalType) {
-            case UsageStatsManager.INTERVAL_YEARLY:
-                calendar.truncateToYear();
-                break;
-
-            case UsageStatsManager.INTERVAL_MONTHLY:
-                calendar.truncateToMonth();
-                break;
-
-            case UsageStatsManager.INTERVAL_WEEKLY:
-                calendar.truncateToWeek();
-                break;
-
-            case UsageStatsManager.INTERVAL_DAILY:
-                calendar.truncateToDay();
-                break;
-
-            default:
-                throw new UnsupportedOperationException("Can't truncate date to interval " +
-                        intervalType);
-        }
-    }
 }
diff --git a/services/usage/java/com/android/server/usage/UsageStatsDatabase.java b/services/usage/java/com/android/server/usage/UsageStatsDatabase.java
index f8ae03f..0ca4bd8 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsDatabase.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsDatabase.java
@@ -350,23 +350,6 @@
     }
 
     /**
-     * Get the time at which the latest stats begin for this interval type.
-     */
-    public long getLatestUsageStatsBeginTime(int intervalType) {
-        synchronized (mLock) {
-            if (intervalType < 0 || intervalType >= mIntervalDirs.length) {
-                throw new IllegalArgumentException("Bad interval type " + intervalType);
-            }
-
-            final int statsFileCount = mSortedStatFiles[intervalType].size();
-            if (statsFileCount > 0) {
-                return mSortedStatFiles[intervalType].keyAt(statsFileCount - 1);
-            }
-            return -1;
-        }
-    }
-
-    /**
      * Figures out what to extract from the given IntervalStats object.
      */
     interface StatCombiner<T> {
diff --git a/services/usage/java/com/android/server/usage/UserUsageStatsService.java b/services/usage/java/com/android/server/usage/UserUsageStatsService.java
index b07b815..5188e5f 100644
--- a/services/usage/java/com/android/server/usage/UserUsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UserUsageStatsService.java
@@ -65,6 +65,11 @@
     private final String mLogPrefix;
     private final int mUserId;
 
+    private static final long[] INTERVAL_LENGTH = new long[] {
+            UnixCalendar.DAY_IN_MILLIS, UnixCalendar.WEEK_IN_MILLIS,
+            UnixCalendar.MONTH_IN_MILLIS, UnixCalendar.YEAR_IN_MILLIS
+    };
+
     interface StatsUpdatedListener {
         void onStatsUpdated();
     }
@@ -104,18 +109,12 @@
 
             // By calling loadActiveStats, we will
             // generate new stats for each bucket.
-            loadActiveStats(currentTimeMillis,/*force=*/ false, /*resetBeginIdleTime=*/ false);
+            loadActiveStats(currentTimeMillis, /*resetBeginIdleTime=*/ false);
         } else {
             // Set up the expiry date to be one day from the latest daily stat.
             // This may actually be today and we will rollover on the first event
             // that is reported.
-            mDailyExpiryDate.setTimeInMillis(
-                    mCurrentStats[UsageStatsManager.INTERVAL_DAILY].beginTime);
-            mDailyExpiryDate.addDays(1);
-            mDailyExpiryDate.truncateToDay();
-            Slog.i(TAG, mLogPrefix + "Rollover scheduled @ " +
-                    sDateFormat.format(mDailyExpiryDate.getTimeInMillis()) +
-                    "(" + mDailyExpiryDate.getTimeInMillis() + ")");
+            updateRolloverDeadline();
         }
 
         // Now close off any events that were open at the time this was saved.
@@ -170,7 +169,7 @@
     void onTimeChanged(long oldTime, long newTime, boolean resetBeginIdleTime) {
         persistActiveStats();
         mDatabase.onTimeChanged(newTime - oldTime);
-        loadActiveStats(newTime, /* force= */ true, resetBeginIdleTime);
+        loadActiveStats(newTime, resetBeginIdleTime);
     }
 
     void reportEvent(UsageEvents.Event event, long deviceUsageTime) {
@@ -237,7 +236,7 @@
             new StatCombiner<UsageStats>() {
                 @Override
                 public void combine(IntervalStats stats, boolean mutable,
-                        List<UsageStats> accResult) {
+                                    List<UsageStats> accResult) {
                     if (!mutable) {
                         accResult.addAll(stats.packageStats.values());
                         return;
@@ -254,7 +253,7 @@
             new StatCombiner<ConfigurationStats>() {
                 @Override
                 public void combine(IntervalStats stats, boolean mutable,
-                        List<ConfigurationStats> accResult) {
+                                    List<ConfigurationStats> accResult) {
                     if (!mutable) {
                         accResult.addAll(stats.configurations.values());
                         return;
@@ -448,7 +447,7 @@
 
         persistActiveStats();
         mDatabase.prune(currentTimeMillis);
-        loadActiveStats(currentTimeMillis, /*force=*/ false, /*resetBeginIdleTime=*/ false);
+        loadActiveStats(currentTimeMillis, /*resetBeginIdleTime=*/ false);
 
         final int continueCount = continuePreviousDay.size();
         for (int i = 0; i < continueCount; i++) {
@@ -474,45 +473,28 @@
         }
     }
 
-    /**
-     * @param force To force all in-memory stats to be reloaded.
-     */
-    private void loadActiveStats(final long currentTimeMillis, boolean force,
-            boolean resetBeginIdleTime) {
-        final UnixCalendar tempCal = mDailyExpiryDate;
+    private void loadActiveStats(final long currentTimeMillis, boolean resetBeginIdleTime) {
         for (int intervalType = 0; intervalType < mCurrentStats.length; intervalType++) {
-            tempCal.setTimeInMillis(currentTimeMillis);
-            UnixCalendar.truncateTo(tempCal, intervalType);
-
-            if (!force && mCurrentStats[intervalType] != null &&
-                    mCurrentStats[intervalType].beginTime == tempCal.getTimeInMillis()) {
-                // These are the same, no need to load them (in memory stats are always newer
-                // than persisted stats).
-                continue;
-            }
-
-            final long lastBeginTime = mDatabase.getLatestUsageStatsBeginTime(intervalType);
-            if (lastBeginTime >= tempCal.getTimeInMillis()) {
+            final IntervalStats stats = mDatabase.getLatestUsageStats(intervalType);
+            if (stats != null && currentTimeMillis - 500 >= stats.endTime &&
+                    currentTimeMillis < stats.beginTime + INTERVAL_LENGTH[intervalType]) {
                 if (DEBUG) {
                     Slog.d(TAG, mLogPrefix + "Loading existing stats @ " +
-                            sDateFormat.format(lastBeginTime) + "(" + lastBeginTime +
+                            sDateFormat.format(stats.beginTime) + "(" + stats.beginTime +
                             ") for interval " + intervalType);
                 }
-                mCurrentStats[intervalType] = mDatabase.getLatestUsageStats(intervalType);
+                mCurrentStats[intervalType] = stats;
             } else {
-                mCurrentStats[intervalType] = null;
-            }
-
-            if (mCurrentStats[intervalType] == null) {
+                // No good fit remains.
                 if (DEBUG) {
                     Slog.d(TAG, "Creating new stats @ " +
-                            sDateFormat.format(tempCal.getTimeInMillis()) + "(" +
-                            tempCal.getTimeInMillis() + ") for interval " + intervalType);
-
+                            sDateFormat.format(currentTimeMillis) + "(" +
+                            currentTimeMillis + ") for interval " + intervalType);
                 }
+
                 mCurrentStats[intervalType] = new IntervalStats();
-                mCurrentStats[intervalType].beginTime = tempCal.getTimeInMillis();
-                mCurrentStats[intervalType].endTime = currentTimeMillis;
+                mCurrentStats[intervalType].beginTime = currentTimeMillis;
+                mCurrentStats[intervalType].endTime = currentTimeMillis + 1;
             }
 
             if (resetBeginIdleTime) {
@@ -522,12 +504,16 @@
             }
         }
         mStatsChanged = false;
-        mDailyExpiryDate.setTimeInMillis(currentTimeMillis);
+        updateRolloverDeadline();
+    }
+
+    private void updateRolloverDeadline() {
+        mDailyExpiryDate.setTimeInMillis(
+                mCurrentStats[UsageStatsManager.INTERVAL_DAILY].beginTime);
         mDailyExpiryDate.addDays(1);
-        mDailyExpiryDate.truncateToDay();
         Slog.i(TAG, mLogPrefix + "Rollover scheduled @ " +
                 sDateFormat.format(mDailyExpiryDate.getTimeInMillis()) + "(" +
-                tempCal.getTimeInMillis() + ")");
+                mDailyExpiryDate.getTimeInMillis() + ")");
     }
 
     //
diff --git a/services/usb/java/com/android/server/usb/UsbDeviceManager.java b/services/usb/java/com/android/server/usb/UsbDeviceManager.java
index 0c3f9da..7f813ec 100644
--- a/services/usb/java/com/android/server/usb/UsbDeviceManager.java
+++ b/services/usb/java/com/android/server/usb/UsbDeviceManager.java
@@ -419,10 +419,9 @@
         private boolean setUsbConfig(String config) {
             if (DEBUG) Slog.d(TAG, "setUsbConfig(" + config + ")");
             // set the new configuration
-            String oldConfig = SystemProperties.get(USB_CONFIG_PROPERTY);
-            if (!config.equals(oldConfig)) {
-                SystemProperties.set(USB_CONFIG_PROPERTY, config);
-            }
+            // we always set it due to b/23631400, where adbd was getting killed
+            // and not restarted due to property timeouts on some devices
+            SystemProperties.set(USB_CONFIG_PROPERTY, config);
             return waitForState(config);
         }
 
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/telecomm/java/android/telecom/Conference.java b/telecomm/java/android/telecom/Conference.java
index 77fdb65..094b3a9 100644
--- a/telecomm/java/android/telecom/Conference.java
+++ b/telecomm/java/android/telecom/Conference.java
@@ -65,6 +65,7 @@
     private final List<Connection> mUnmodifiableConferenceableConnections =
             Collections.unmodifiableList(mConferenceableConnections);
 
+    private String mTelecomCallId;
     private PhoneAccountHandle mPhoneAccount;
     private CallAudioState mCallAudioState;
     private int mState = Connection.STATE_NEW;
@@ -94,6 +95,26 @@
     }
 
     /**
+     * Returns the telecom internal call ID associated with this conference.
+     *
+     * @return The telecom call ID.
+     * @hide
+     */
+    public final String getTelecomCallId() {
+        return mTelecomCallId;
+    }
+
+    /**
+     * Sets the telecom internal call ID associated with this conference.
+     *
+     * @param telecomCallId The telecom call ID.
+     * @hide
+     */
+    public final void setTelecomCallId(String telecomCallId) {
+        mTelecomCallId = telecomCallId;
+    }
+
+    /**
      * Returns the {@link PhoneAccountHandle} the conference call is being placed through.
      *
      * @return A {@code PhoneAccountHandle} object representing the PhoneAccount of the conference.
diff --git a/telecomm/java/android/telecom/Connection.java b/telecomm/java/android/telecom/Connection.java
index 430760a..4115756 100644
--- a/telecomm/java/android/telecom/Connection.java
+++ b/telecomm/java/android/telecom/Connection.java
@@ -1074,6 +1074,8 @@
     private final List<Conferenceable> mUnmodifiableConferenceables =
             Collections.unmodifiableList(mConferenceables);
 
+    // The internal telecom call ID associated with this connection.
+    private String mTelecomCallId;
     private int mState = STATE_NEW;
     private CallAudioState mCallAudioState;
     private Uri mAddress;
@@ -1098,6 +1100,17 @@
     public Connection() {}
 
     /**
+     * Returns the Telecom internal call ID associated with this connection.  Should only be used
+     * for debugging and tracing purposes.
+     *
+     * @return The Telecom call ID.
+     * @hide
+     */
+    public final String getTelecomCallId() {
+        return mTelecomCallId;
+    }
+
+    /**
      * @return The address (e.g., phone number) to which this Connection is currently communicating.
      */
     public final Uri getAddress() {
@@ -1259,6 +1272,17 @@
     }
 
     /**
+     * Sets the telecom call ID associated with this Connection.  The Telecom Call ID should be used
+     * ONLY for debugging purposes.
+     *
+     * @param callId The telecom call ID.
+     * @hide
+     */
+    public void setTelecomCallId(String callId) {
+        mTelecomCallId = callId;
+    }
+
+    /**
      * Inform this Connection that the state of its audio output has been changed externally.
      *
      * @param state The new audio state.
diff --git a/telecomm/java/android/telecom/ConnectionRequest.java b/telecomm/java/android/telecom/ConnectionRequest.java
index 6863214..aba38fe 100644
--- a/telecomm/java/android/telecom/ConnectionRequest.java
+++ b/telecomm/java/android/telecom/ConnectionRequest.java
@@ -32,6 +32,7 @@
     private final Uri mAddress;
     private final Bundle mExtras;
     private final int mVideoState;
+    private final String mTelecomCallId;
 
     /**
      * @param accountHandle The accountHandle which should be used to place the call.
@@ -42,7 +43,7 @@
             PhoneAccountHandle accountHandle,
             Uri handle,
             Bundle extras) {
-        this(accountHandle, handle, extras, VideoProfile.STATE_AUDIO_ONLY);
+        this(accountHandle, handle, extras, VideoProfile.STATE_AUDIO_ONLY, null);
     }
 
     /**
@@ -56,10 +57,28 @@
             Uri handle,
             Bundle extras,
             int videoState) {
+        this(accountHandle, handle, extras, videoState, null);
+    }
+
+    /**
+     * @param accountHandle The accountHandle which should be used to place the call.
+     * @param handle The handle (e.g., phone number) to which the {@link Connection} is to connect.
+     * @param extras Application-specific extra data.
+     * @param videoState Determines the video state for the connection.
+     * @param telecomCallId The telecom call ID.
+     * @hide
+     */
+    public ConnectionRequest(
+            PhoneAccountHandle accountHandle,
+            Uri handle,
+            Bundle extras,
+            int videoState,
+            String telecomCallId) {
         mAccountHandle = accountHandle;
         mAddress = handle;
         mExtras = extras;
         mVideoState = videoState;
+        mTelecomCallId = telecomCallId;
     }
 
     private ConnectionRequest(Parcel in) {
@@ -67,6 +86,7 @@
         mAddress = in.readParcelable(getClass().getClassLoader());
         mExtras = in.readParcelable(getClass().getClassLoader());
         mVideoState = in.readInt();
+        mTelecomCallId = in.readString();
     }
 
     /**
@@ -99,6 +119,16 @@
         return mVideoState;
     }
 
+    /**
+     * Returns the internal Telecom ID associated with the connection request.
+     *
+     * @return The Telecom ID.
+     * @hide
+     */
+    public String getTelecomCallId() {
+        return mTelecomCallId;
+    }
+
     @Override
     public String toString() {
         return String.format("ConnectionRequest %s %s",
@@ -134,5 +164,6 @@
         destination.writeParcelable(mAddress, 0);
         destination.writeParcelable(mExtras, 0);
         destination.writeInt(mVideoState);
+        destination.writeString(mTelecomCallId);
     }
 }
diff --git a/telecomm/java/android/telecom/ConnectionService.java b/telecomm/java/android/telecom/ConnectionService.java
index 4e330bdb..6223495 100644
--- a/telecomm/java/android/telecom/ConnectionService.java
+++ b/telecomm/java/android/telecom/ConnectionService.java
@@ -116,6 +116,8 @@
 
     private boolean mAreAccountsInitialized = false;
     private Conference sNullConference;
+    private Object mIdSyncRoot = new Object();
+    private int mId = 0;
 
     private final IBinder mBinder = new IConnectionService.Stub() {
         @Override
@@ -629,7 +631,8 @@
             boolean isIncoming,
             boolean isUnknown) {
         Log.d(this, "createConnection, callManagerAccount: %s, callId: %s, request: %s, " +
-                "isIncoming: %b, isUnknown: %b", callManagerAccount, callId, request, isIncoming,
+                        "isIncoming: %b, isUnknown: %b", callManagerAccount, callId, request,
+                isIncoming,
                 isUnknown);
 
         Connection connection = isUnknown ? onCreateUnknownConnection(callManagerAccount, request)
@@ -641,6 +644,7 @@
                     new DisconnectCause(DisconnectCause.ERROR));
         }
 
+        connection.setTelecomCallId(callId);
         if (connection.getState() != Connection.STATE_DISCONNECTED) {
             addConnection(callId, connection);
         }
@@ -953,6 +957,7 @@
                     connectionIds.add(mIdByConnection.get(connection));
                 }
             }
+            conference.setTelecomCallId(id);
             ParcelableConference parcelableConference = new ParcelableConference(
                     conference.getPhoneAccountHandle(),
                     conference.getState(),
@@ -989,7 +994,7 @@
     public final void addExistingConnection(PhoneAccountHandle phoneAccountHandle,
             Connection connection) {
 
-        String id = addExistingConnectionInternal(connection);
+        String id = addExistingConnectionInternal(phoneAccountHandle, connection);
         if (id != null) {
             List<String> emptyList = new ArrayList<>(0);
 
@@ -1151,18 +1156,29 @@
     }
 
     /**
-     * Adds an existing connection to the list of connections, identified by a new UUID.
+     * Adds an existing connection to the list of connections, identified by a new call ID unique
+     * to this connection service.
      *
      * @param connection The connection.
-     * @return The UUID of the connection (e.g. the call-id).
+     * @return The ID of the connection (e.g. the call-id).
      */
-    private String addExistingConnectionInternal(Connection connection) {
-        String id = UUID.randomUUID().toString();
+    private String addExistingConnectionInternal(PhoneAccountHandle handle, Connection connection) {
+        String id;
+        if (handle == null) {
+            // If no phone account handle was provided, we cannot be sure the call ID is unique,
+            // so just use a random UUID.
+            id = UUID.randomUUID().toString();
+        } else {
+            // Phone account handle was provided, so use the ConnectionService class name as a
+            // prefix for a unique incremental call ID.
+            id = handle.getComponentName().getClassName() + "@" + getNextCallId();
+        }
         addConnection(id, connection);
         return id;
     }
 
     private void addConnection(String callId, Connection connection) {
+        connection.setTelecomCallId(callId);
         mConnectionById.put(callId, connection);
         mIdByConnection.put(connection, callId);
         connection.addConnectionListener(mConnectionListener);
@@ -1183,6 +1199,9 @@
         if (mIdByConference.containsKey(conference)) {
             Log.w(this, "Re-adding an existing conference: %s.", conference);
         } else if (conference != null) {
+            // Conferences do not (yet) have a PhoneAccountHandle associated with them, so we
+            // cannot determine a ConnectionService class name to associate with the ID, so use
+            // a unique UUID (for now).
             String id = UUID.randomUUID().toString();
             mConferenceById.put(id, conference);
             mIdByConference.put(conference, id);
@@ -1284,4 +1303,15 @@
             conference.onDisconnect();
         }
     }
+
+    /**
+     * Retrieves the next call ID as maintainted by the connection service.
+     *
+     * @return The call ID.
+     */
+    private int getNextCallId() {
+        synchronized(mIdSyncRoot) {
+            return ++mId;
+        }
+    }
 }
diff --git a/telecomm/java/android/telecom/TelecomManager.java b/telecomm/java/android/telecom/TelecomManager.java
index 8779462..a8f2aca 100644
--- a/telecomm/java/android/telecom/TelecomManager.java
+++ b/telecomm/java/android/telecom/TelecomManager.java
@@ -14,6 +14,7 @@
 
 package android.telecom;
 
+import android.annotation.RequiresPermission;
 import android.annotation.SystemApi;
 import android.content.ComponentName;
 import android.content.Context;
@@ -116,6 +117,15 @@
             "android.telecom.action.PHONE_ACCOUNT_REGISTERED";
 
     /**
+     * The {@link android.content.Intent} action used indicate that a phone account was
+     * just unregistered.
+     * @hide
+     */
+    @SystemApi
+    public static final String ACTION_PHONE_ACCOUNT_UNREGISTERED =
+            "android.telecom.action.PHONE_ACCOUNT_UNREGISTERED";
+
+    /**
      * Activity action: Shows a dialog asking the user whether or not they want to replace the
      * current default Dialer with the one specified in
      * {@link #EXTRA_CHANGE_DEFAULT_DIALER_PACKAGE_NAME}.
@@ -472,9 +482,12 @@
      * <p>
      * If no {@link PhoneAccount} fits the criteria above, this method will return {@code null}.
      *
+     * Requires permission: {@link android.Manifest.permission#READ_PHONE_STATE}
+     *
      * @param uriScheme The URI scheme.
      * @return The {@link PhoneAccountHandle} corresponding to the account to be used.
      */
+    @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
     public PhoneAccountHandle getDefaultOutgoingPhoneAccount(String uriScheme) {
         try {
             if (isServiceConnected()) {
@@ -606,9 +619,12 @@
      * calls. The returned list includes only those accounts which have been explicitly enabled
      * by the user.
      *
+     * Requires permission: {@link android.Manifest.permission#READ_PHONE_STATE}
+     *
      * @see #EXTRA_PHONE_ACCOUNT_HANDLE
      * @return A list of {@code PhoneAccountHandle} objects.
      */
+    @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
     public List<PhoneAccountHandle> getCallCapablePhoneAccounts() {
         return getCallCapablePhoneAccounts(false);
     }
@@ -881,9 +897,12 @@
      * Return whether a given phone number is the configured voicemail number for a
      * particular phone account.
      *
+     * Requires permission: {@link android.Manifest.permission#READ_PHONE_STATE}
+     *
      * @param accountHandle The handle for the account to check the voicemail number against
      * @param number The number to look up.
      */
+    @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
     public boolean isVoiceMailNumber(PhoneAccountHandle accountHandle, String number) {
         try {
             if (isServiceConnected()) {
@@ -899,10 +918,13 @@
     /**
      * Return the voicemail number for a given phone account.
      *
+     * Requires permission: {@link android.Manifest.permission#READ_PHONE_STATE}
+     *
      * @param accountHandle The handle for the phone account.
      * @return The voicemail number for the phone account, and {@code null} if one has not been
      *         configured.
      */
+    @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
     public String getVoiceMailNumber(PhoneAccountHandle accountHandle) {
         try {
             if (isServiceConnected()) {
@@ -918,9 +940,12 @@
     /**
      * Return the line 1 phone number for given phone account.
      *
+     * Requires permission: {@link android.Manifest.permission#READ_PHONE_STATE}
+     *
      * @param accountHandle The handle for the account retrieve a number for.
      * @return A string representation of the line 1 phone number.
      */
+    @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
     public String getLine1Number(PhoneAccountHandle accountHandle) {
         try {
             if (isServiceConnected()) {
@@ -940,6 +965,7 @@
      * Requires permission: {@link android.Manifest.permission#READ_PHONE_STATE}
      * </p>
      */
+    @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
     public boolean isInCall() {
         try {
             if (isServiceConnected()) {
@@ -1031,7 +1057,10 @@
 
     /**
      * Silences the ringer if a ringing call exists.
+     *
+     * Requires permission: {@link android.Manifest.permission#MODIFY_PHONE_STATE}
      */
+    @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
     public void silenceRinger() {
         try {
             if (isServiceConnected()) {
@@ -1136,9 +1165,12 @@
      * Requires that the method-caller be set as the system dialer app.
      * </p>
      *
+     * Requires permission: {@link android.Manifest.permission#MODIFY_PHONE_STATE}
+     *
      * @param dialString The digits to dial.
      * @return True if the digits were processed as an MMI code, false otherwise.
      */
+    @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
     public boolean handleMmi(String dialString) {
         ITelecomService service = getTelecomService();
         if (service != null) {
@@ -1159,10 +1191,13 @@
      * Requires that the method-caller be set as the system dialer app.
      * </p>
      *
+     * Requires permission: {@link android.Manifest.permission#MODIFY_PHONE_STATE}
+     *
      * @param accountHandle The handle for the account the MMI code should apply to.
      * @param dialString The digits to dial.
      * @return True if the digits were processed as an MMI code, false otherwise.
      */
+    @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
     public boolean handleMmi(String dialString, PhoneAccountHandle accountHandle) {
         ITelecomService service = getTelecomService();
         if (service != null) {
@@ -1177,11 +1212,14 @@
     }
 
     /**
+     * Requires permission: {@link android.Manifest.permission#MODIFY_PHONE_STATE}
+     *
      * @param accountHandle The handle for the account to derive an adn query URI for or
      * {@code null} to return a URI which will use the default account.
      * @return The URI (with the content:// scheme) specific to the specified {@link PhoneAccount}
      * for the the content retrieve.
      */
+    @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
     public Uri getAdnUriForPhoneAccount(PhoneAccountHandle accountHandle) {
         ITelecomService service = getTelecomService();
         if (service != null && accountHandle != null) {
@@ -1199,7 +1237,10 @@
      * <p>
      * Requires that the method-caller be set as the system dialer app.
      * </p>
+     *
+     * Requires permission: {@link android.Manifest.permission#MODIFY_PHONE_STATE}
      */
+    @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
     public void cancelMissedCallsNotification() {
         ITelecomService service = getTelecomService();
         if (service != null) {
@@ -1221,6 +1262,7 @@
      *
      * @param showDialpad Brings up the in-call dialpad as part of showing the in-call screen.
      */
+    @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
     public void showInCallScreen(boolean showDialpad) {
         ITelecomService service = getTelecomService();
         if (service != null) {
@@ -1262,6 +1304,7 @@
      * @param address The address to make the call to.
      * @param extras Bundle of extras to use with the call.
      */
+    @RequiresPermission(android.Manifest.permission.CALL_PHONE)
     public void placeCall(Uri address, Bundle extras) {
         ITelecomService service = getTelecomService();
         if (service != null) {
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index b868832..e57964a 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -326,6 +326,14 @@
     public static final String KEY_CARRIER_FORCE_DISABLE_ETWS_CMAS_TEST_BOOL =
             "carrier_force_disable_etws_cmas_test_bool";
 
+    /**
+     * The default flag specifying whether "Turn on Notifications" option will be always shown in
+     * Settings->More->Emergency broadcasts menu regardless developer options is turned on or not.
+     * @hide
+     */
+    public static final String KEY_ALWAYS_SHOW_EMERGENCY_ALERT_ONOFF_BOOL =
+            "always_show_emergency_alert_onoff_bool";
+
     /* The following 3 fields are related to carrier visual voicemail. */
 
     /**
@@ -532,6 +540,7 @@
         sDefaults.putString(KEY_CI_ACTION_ON_SYS_UPDATE_EXTRA_STRING, "");
         sDefaults.putString(KEY_CI_ACTION_ON_SYS_UPDATE_EXTRA_VAL_STRING, "");
         sDefaults.putBoolean(KEY_CSP_ENABLED_BOOL, false);
+        sDefaults.putBoolean(KEY_ALWAYS_SHOW_EMERGENCY_ALERT_ONOFF_BOOL, false);
 
         sDefaults.putStringArray(KEY_GSM_ROAMING_NETWORKS_STRING_ARRAY, null);
         sDefaults.putStringArray(KEY_GSM_NONROAMING_NETWORKS_STRING_ARRAY, null);
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index f6e4bed..d22727d 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -3611,11 +3611,12 @@
      *
      * @hide
      */
-    public boolean setNetworkSelectionModeManual(int subId, OperatorInfo operator) {
+    public boolean setNetworkSelectionModeManual(int subId, OperatorInfo operator,
+            boolean persistSelection) {
         try {
             ITelephony telephony = getITelephony();
             if (telephony != null)
-                return telephony.setNetworkSelectionModeManual(subId, operator);
+                return telephony.setNetworkSelectionModeManual(subId, operator, persistSelection);
         } catch (RemoteException ex) {
             Rlog.e(TAG, "setNetworkSelectionModeManual RemoteException", ex);
         } catch (NullPointerException ex) {
diff --git a/telephony/java/com/android/internal/telephony/CallerInfo.java b/telephony/java/com/android/internal/telephony/CallerInfo.java
index be7e702..59a8a67 100644
--- a/telephony/java/com/android/internal/telephony/CallerInfo.java
+++ b/telephony/java/com/android/internal/telephony/CallerInfo.java
@@ -249,9 +249,17 @@
 
                 // look for the custom ringtone, create from the string stored
                 // in the database.
+                // An empty string ("") in the database indicates a silent ringtone,
+                // and we set contactRingtoneUri = Uri.EMPTY, so that no ringtone will be played.
+                // {null} in the database indicates the default ringtone,
+                // and we set contactRingtoneUri = null, so that default ringtone will be played.
                 columnIndex = cursor.getColumnIndex(PhoneLookup.CUSTOM_RINGTONE);
                 if ((columnIndex != -1) && (cursor.getString(columnIndex) != null)) {
-                    info.contactRingtoneUri = Uri.parse(cursor.getString(columnIndex));
+                    if (TextUtils.isEmpty(cursor.getString(columnIndex))) {
+                        info.contactRingtoneUri = Uri.EMPTY;
+                    } else {
+                        info.contactRingtoneUri = Uri.parse(cursor.getString(columnIndex));
+                    }
                 } else {
                     info.contactRingtoneUri = null;
                 }
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index 661f12d..dcece26 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -707,9 +707,13 @@
      *
      * @param subId the id of the subscription.
      * @param operatorInfo the operator to attach to.
+     * @param persistSelection should the selection persist till reboot or its
+     *        turned off? Will also result in notification being not shown to
+     *        the user if the signal is lost.
      * @return true if the request suceeded.
      */
-    boolean setNetworkSelectionModeManual(int subId, in OperatorInfo operator);
+    boolean setNetworkSelectionModeManual(int subId, in OperatorInfo operator,
+            boolean persistSelection);
 
     /**
      * Set the preferred network type.
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/tests/CameraPrewarmTest/src/com/google/android/test/cameraprewarm/CameraActivity.java b/tests/CameraPrewarmTest/src/com/google/android/test/cameraprewarm/CameraActivity.java
index 0b43666..8085db7 100644
--- a/tests/CameraPrewarmTest/src/com/google/android/test/cameraprewarm/CameraActivity.java
+++ b/tests/CameraPrewarmTest/src/com/google/android/test/cameraprewarm/CameraActivity.java
@@ -31,5 +31,7 @@
         super.onCreate(savedInstanceState);
         setContentView(R.layout.camera_activity);
         Log.i(TAG, "Activity created");
+        Log.i(TAG, "Source: "
+                + getIntent().getStringExtra("com.android.systemui.camera_launch_source"));
     }
 }
diff --git a/tests/CameraPrewarmTest/src/com/google/android/test/cameraprewarm/SecureCameraActivity.java b/tests/CameraPrewarmTest/src/com/google/android/test/cameraprewarm/SecureCameraActivity.java
index 530fe00..242d3b2 100644
--- a/tests/CameraPrewarmTest/src/com/google/android/test/cameraprewarm/SecureCameraActivity.java
+++ b/tests/CameraPrewarmTest/src/com/google/android/test/cameraprewarm/SecureCameraActivity.java
@@ -17,6 +17,7 @@
 package com.google.android.test.cameraprewarm;
 
 import android.app.Activity;
+import android.graphics.Camera;
 import android.os.Bundle;
 import android.util.Log;
 import android.view.WindowManager;
@@ -31,5 +32,7 @@
         setContentView(R.layout.camera_activity);
         getWindow().addFlags(WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED);
         Log.i(CameraActivity.TAG, "Activity created");
+        Log.i(CameraActivity.TAG, "Source: "
+                + getIntent().getStringExtra("com.android.systemui.camera_launch_source"));
     }
 }
diff --git a/tests/HwAccelerationTest/AndroidManifest.xml b/tests/HwAccelerationTest/AndroidManifest.xml
index 10cf5c1..b028ce6 100644
--- a/tests/HwAccelerationTest/AndroidManifest.xml
+++ b/tests/HwAccelerationTest/AndroidManifest.xml
@@ -944,5 +944,14 @@
             </intent-filter>
         </activity>
 
+        <activity
+                android:name="MultiProducerActivity"
+                android:label="Threads/Multiple Producers">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="com.android.test.hwui.TEST" />
+            </intent-filter>
+        </activity>
+
     </application>
 </manifest>
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/MultiProducerActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/MultiProducerActivity.java
new file mode 100644
index 0000000..b458d9b
--- /dev/null
+++ b/tests/HwAccelerationTest/src/com/android/test/hwui/MultiProducerActivity.java
@@ -0,0 +1,347 @@
+/*
+ * Copyright (C) 2014 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.test.hwui;
+
+import android.app.Activity;
+import android.graphics.Canvas;
+import android.graphics.ColorFilter;
+import android.graphics.Paint;
+import android.graphics.PixelFormat;
+import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
+import android.os.Bundle;
+import android.view.DisplayListCanvas;
+import android.view.HardwareRenderer;
+import android.view.RenderNode;
+import android.view.ThreadedRenderer;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.widget.AbsoluteLayout;
+import android.widget.AbsoluteLayout.LayoutParams;
+
+public class MultiProducerActivity extends Activity implements OnClickListener {
+    private static final int DURATION = 800;
+    private View mBackgroundTarget = null;
+    private View mFrameTarget = null;
+    private View mContent = null;
+    // The width & height of our "output drawing".
+    private final int WIDTH = 900;
+    private final int HEIGHT = 600;
+    // A border width around the drawing.
+    private static final int BORDER_WIDTH = 20;
+    // The Gap between the content and the frame which should get filled on the right and bottom
+    // side by the backdrop.
+    final int CONTENT_GAP = 100;
+
+    // For debug purposes - disable drawing of frame / background.
+    private final boolean USE_FRAME = true;
+    private final boolean USE_BACK = true;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        // To make things simple - we do a quick and dirty absolute layout.
+        final AbsoluteLayout layout = new AbsoluteLayout(this);
+
+        // Create the outer frame
+        if (USE_FRAME) {
+            mFrameTarget = new View(this);
+            LayoutParams frameLP = new LayoutParams(WIDTH, HEIGHT, 0, 0);
+            layout.addView(mFrameTarget, frameLP);
+        }
+
+        // Create the background which fills the gap between content and frame.
+        if (USE_BACK) {
+            mBackgroundTarget = new View(this);
+            LayoutParams backgroundLP = new LayoutParams(
+                    WIDTH - 2 * BORDER_WIDTH, HEIGHT - 2 * BORDER_WIDTH,
+                    BORDER_WIDTH, BORDER_WIDTH);
+            layout.addView(mBackgroundTarget, backgroundLP);
+        }
+
+        // Create the content
+        // Note: We reduce the size by CONTENT_GAP pixels on right and bottom, so that they get
+        // drawn by the backdrop.
+        mContent = new View(this);
+        mContent.setBackground(new ColorPulse(0xFFF44336, 0xFF9C27B0, null));
+        mContent.setOnClickListener(this);
+        LayoutParams contentLP = new LayoutParams(WIDTH - 2 * BORDER_WIDTH - CONTENT_GAP,
+                HEIGHT - 2 * BORDER_WIDTH - CONTENT_GAP, BORDER_WIDTH, BORDER_WIDTH);
+        layout.addView(mContent, contentLP);
+
+        setContentView(layout);
+    }
+
+    @Override
+    protected void onStart() {
+        super.onStart();
+        View view = mBackgroundTarget != null ? mBackgroundTarget : mFrameTarget;
+        if (view != null) {
+            view.post(mSetup);
+        }
+    }
+
+    @Override
+    protected void onStop() {
+        super.onStop();
+        View view = mBackgroundTarget != null ? mBackgroundTarget : mFrameTarget;
+        if (view != null) {
+            view.removeCallbacks(mSetup);
+        }
+        if (mBgRenderer != null) {
+            mBgRenderer.destroy();
+            mBgRenderer = null;
+        }
+    }
+
+    @Override
+    public void onClick(View view) {
+        sBlockThread.run();
+    }
+
+    private Runnable mSetup = new Runnable() {
+        @Override
+        public void run() {
+            View view = mBackgroundTarget != null ? mBackgroundTarget : mFrameTarget;
+            if (view == null) {
+                view.postDelayed(mSetup, 50);
+            }
+            HardwareRenderer renderer = view.getHardwareRenderer();
+            if (renderer == null || view.getWidth() == 0) {
+                view.postDelayed(mSetup, 50);
+            }
+            ThreadedRenderer threaded = (ThreadedRenderer) renderer;
+
+            mBgRenderer = new FakeFrame(threaded,mFrameTarget, mBackgroundTarget);
+            mBgRenderer.start();
+        }
+    };
+
+    private FakeFrame mBgRenderer;
+    private class FakeFrame extends Thread {
+        ThreadedRenderer mRenderer;
+        volatile boolean mRunning = true;
+        View mTargetFrame;
+        View mTargetBack;
+        Drawable mFrameContent;
+        Drawable mBackContent;
+        // The Z value where to place this.
+        int mZFrame;
+        int mZBack;
+        String mRenderNodeName;
+
+        FakeFrame(ThreadedRenderer renderer, View targetFrame, View targetBack) {
+            mRenderer = renderer;
+            mTargetFrame = targetFrame;
+
+            mTargetBack = targetBack;
+            mFrameContent = new ColorPulse(0xFF101010, 0xFF707070, new Rect(0, 0, WIDTH, HEIGHT));
+            mBackContent = new ColorPulse(0xFF909090, 0xFFe0e0e0, null);
+        }
+
+        @Override
+        public void run() {
+            Rect currentFrameBounds = new Rect();
+            Rect currentBackBounds = new Rect();
+            Rect newBounds = new Rect();
+            int[] surfaceOrigin = new int[2];
+            RenderNode nodeFrame = null;
+            RenderNode nodeBack = null;
+
+            // Since we are overriding the window painting logic we need to at least fill the
+            // surface with some window content (otherwise the world will go black).
+            try {
+                Thread.sleep(200);
+            } catch (InterruptedException e) {
+            }
+
+            if (mTargetBack != null) {
+                nodeBack = RenderNode.create("FakeBackdrop", null);
+                nodeBack.setClipToBounds(true);
+                mRenderer.addRenderNode(nodeBack, true);
+            }
+
+            if (mTargetFrame != null) {
+                nodeFrame = RenderNode.create("FakeFrame", null);
+                nodeFrame.setClipToBounds(true);
+                mRenderer.addRenderNode(nodeFrame, false);
+            }
+
+            while (mRunning) {
+                // Get the surface position to draw to within our surface.
+                surfaceOrigin[0] = 0;
+                surfaceOrigin[1] = 0;
+                // This call should be done while the rendernode's displaylist is produced.
+                // For simplicity of this test we do this before we kick off the draw.
+                mContent.getLocationInSurface(surfaceOrigin);
+                mRenderer.setContentOverdrawProtectionBounds(surfaceOrigin[0], surfaceOrigin[1],
+                        surfaceOrigin[0] + mContent.getWidth(),
+                        surfaceOrigin[1] + mContent.getHeight());
+                // Determine new position for frame.
+                if (nodeFrame != null) {
+                    surfaceOrigin[0] = 0;
+                    surfaceOrigin[1] = 0;
+                    mTargetFrame.getLocationInSurface(surfaceOrigin);
+                    newBounds.set(surfaceOrigin[0], surfaceOrigin[1],
+                            surfaceOrigin[0] + mTargetFrame.getWidth(),
+                            surfaceOrigin[1] + mTargetFrame.getHeight());
+                    if (!currentFrameBounds.equals(newBounds)) {
+                        currentFrameBounds.set(newBounds);
+                        nodeFrame.setLeftTopRightBottom(currentFrameBounds.left,
+                                currentFrameBounds.top,
+                                currentFrameBounds.right, currentFrameBounds.bottom);
+                    }
+
+                    // Draw frame
+                    DisplayListCanvas canvas = nodeFrame.start(currentFrameBounds.width(),
+                            currentFrameBounds.height());
+                    mFrameContent.draw(canvas);
+                    nodeFrame.end(canvas);
+                }
+
+                // Determine new position for backdrop
+                if (nodeBack != null) {
+                    surfaceOrigin[0] = 0;
+                    surfaceOrigin[1] = 0;
+                    mTargetBack.getLocationInSurface(surfaceOrigin);
+                    newBounds.set(surfaceOrigin[0], surfaceOrigin[1],
+                            surfaceOrigin[0] + mTargetBack.getWidth(),
+                            surfaceOrigin[1] + mTargetBack.getHeight());
+                    if (!currentBackBounds.equals(newBounds)) {
+                        currentBackBounds.set(newBounds);
+                        nodeBack.setLeftTopRightBottom(currentBackBounds.left,
+                                currentBackBounds.top,
+                                currentBackBounds.right, currentBackBounds.bottom);
+                    }
+
+                    // Draw Backdrop
+                    DisplayListCanvas canvas = nodeBack.start(currentBackBounds.width(),
+                            currentBackBounds.height());
+                    mBackContent.draw(canvas);
+                    nodeBack.end(canvas);
+                }
+
+                // we need to only render one guy - the rest will happen automatically (I think).
+                if (nodeFrame != null) {
+                    mRenderer.drawRenderNode(nodeFrame);
+                }
+                if (nodeBack != null) {
+                    mRenderer.drawRenderNode(nodeBack);
+                }
+                try {
+                    Thread.sleep(5);
+                } catch (InterruptedException e) {}
+            }
+            if (nodeFrame != null) {
+                mRenderer.removeRenderNode(nodeFrame);
+            }
+            if (nodeBack != null) {
+                mRenderer.removeRenderNode(nodeBack);
+            }
+        }
+
+        public void destroy() {
+            mRunning = false;
+            try {
+                join();
+            } catch (InterruptedException e) {}
+        }
+    }
+
+    private final static Runnable sBlockThread = new Runnable() {
+        @Override
+        public void run() {
+            try {
+                Thread.sleep(DURATION);
+            } catch (InterruptedException e) {
+            }
+        }
+    };
+
+    static class ColorPulse extends Drawable {
+
+        private int mColorStart;
+        private int mColorEnd;
+        private int mStep;
+        private Rect mRect;
+        private Paint mPaint = new Paint();
+
+        public ColorPulse(int color1, int color2, Rect rect) {
+            mColorStart = color1;
+            mColorEnd = color2;
+            if (rect != null) {
+                mRect = new Rect(rect.left + BORDER_WIDTH / 2, rect.top + BORDER_WIDTH / 2,
+                                 rect.right - BORDER_WIDTH / 2, rect.bottom - BORDER_WIDTH / 2);
+            }
+        }
+
+        static int evaluate(float fraction, int startInt, int endInt) {
+            int startA = (startInt >> 24) & 0xff;
+            int startR = (startInt >> 16) & 0xff;
+            int startG = (startInt >> 8) & 0xff;
+            int startB = startInt & 0xff;
+
+            int endA = (endInt >> 24) & 0xff;
+            int endR = (endInt >> 16) & 0xff;
+            int endG = (endInt >> 8) & 0xff;
+            int endB = endInt & 0xff;
+
+            return (int)((startA + (int)(fraction * (endA - startA))) << 24) |
+                    (int)((startR + (int)(fraction * (endR - startR))) << 16) |
+                    (int)((startG + (int)(fraction * (endG - startG))) << 8) |
+                    (int)((startB + (int)(fraction * (endB - startB))));
+        }
+
+        @Override
+        public void draw(Canvas canvas) {
+            float frac = mStep / 50.0f;
+            int color = evaluate(frac, mColorStart, mColorEnd);
+            if (mRect != null && !mRect.isEmpty()) {
+                mPaint.setStyle(Paint.Style.STROKE);
+                mPaint.setStrokeWidth(BORDER_WIDTH);
+                mPaint.setColor(color);
+                canvas.drawRect(mRect, mPaint);
+            } else {
+                canvas.drawColor(color);
+            }
+
+            mStep++;
+            if (mStep >= 50) {
+                mStep = 0;
+                int tmp = mColorStart;
+                mColorStart = mColorEnd;
+                mColorEnd = tmp;
+            }
+            invalidateSelf();
+        }
+
+        @Override
+        public void setAlpha(int alpha) {
+        }
+
+        @Override
+        public void setColorFilter(ColorFilter colorFilter) {
+        }
+
+        @Override
+        public int getOpacity() {
+            return mRect == null || mRect.isEmpty() ? PixelFormat.OPAQUE : PixelFormat.TRANSLUCENT;
+        }
+
+    }
+}
+
diff --git a/tests/VectorDrawableTest/res/drawable/vector_drawable01.xml b/tests/VectorDrawableTest/res/drawable/vector_drawable01.xml
index 705cc34..2be99be 100644
--- a/tests/VectorDrawableTest/res/drawable/vector_drawable01.xml
+++ b/tests/VectorDrawableTest/res/drawable/vector_drawable01.xml
@@ -21,10 +21,14 @@
 
     <group>
         <path
+            android:name="box0"
+            android:pathData="m0,0l480,0l0,480l-480,0l0-480z"
+            android:fillColor="@android:color/white" />
+        <path
             android:name="box1"
             android:pathData="m20,200l100,90l180-180l-35-35l-145,145l-60-60l-40,40z"
-            android:fillColor="?android:attr/colorControlActivated"
-            android:strokeColor="?android:attr/colorControlActivated"
+            android:fillColor="?android:attr/colorControlNormal"
+            android:strokeColor="?android:attr/colorControlNormal"
             android:strokeLineCap="round"
             android:strokeLineJoin="round" />
     </group>
diff --git a/tests/VectorDrawableTest/src/com/android/test/dynamic/VectorDrawable01.java b/tests/VectorDrawableTest/src/com/android/test/dynamic/VectorDrawable01.java
index a23d819..85fc452 100644
--- a/tests/VectorDrawableTest/src/com/android/test/dynamic/VectorDrawable01.java
+++ b/tests/VectorDrawableTest/src/com/android/test/dynamic/VectorDrawable01.java
@@ -18,7 +18,12 @@
 import android.os.Bundle;
 import android.util.Log;
 import android.view.View;
+import android.view.View.OnClickListener;
+import android.view.ViewGroup;
 import android.widget.Button;
+import android.widget.CheckBox;
+import android.widget.CompoundButton;
+import android.widget.CompoundButton.OnCheckedChangeListener;
 import android.widget.GridLayout;
 
 @SuppressWarnings({"UnusedDeclaration"})
@@ -55,6 +60,23 @@
         container.setBackgroundColor(0xFF888888);
         final Button []bArray = new Button[icon.length];
 
+        CheckBox toggle = new CheckBox(this);
+        toggle.setText("Toggle");
+        toggle.setChecked(true);
+        toggle.setOnCheckedChangeListener(new OnCheckedChangeListener() {
+            @Override
+            public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
+                ViewGroup vg = (ViewGroup) buttonView.getParent();
+                for (int i = 0, count = vg.getChildCount(); i < count; i++) {
+                    View child = vg.getChildAt(i);
+                    if (child != buttonView) {
+                        child.setEnabled(isChecked);
+                    }
+                }
+            }
+        });
+        container.addView(toggle);
+
         for (int i = 0; i < icon.length; i++) {
             Button button = new Button(this);
             bArray[i] = button;
diff --git a/tests/permission/src/com/android/framework/permission/tests/WindowManagerPermissionTests.java b/tests/permission/src/com/android/framework/permission/tests/WindowManagerPermissionTests.java
index 95f676e..a390b0c 100644
--- a/tests/permission/src/com/android/framework/permission/tests/WindowManagerPermissionTests.java
+++ b/tests/permission/src/com/android/framework/permission/tests/WindowManagerPermissionTests.java
@@ -94,7 +94,7 @@
 
         try {
             mWm.addAppToken(0, null, 0, 0, 0, false, false, 0, 0, false, false, null,
-                    Configuration.EMPTY);
+                    Configuration.EMPTY, false);
             fail("IWindowManager.addAppToken did not throw SecurityException as"
                     + " expected");
         } catch (SecurityException e) {
@@ -175,16 +175,6 @@
         }
 
         try {
-            mWm.setAppWillBeHidden(null);
-            fail("IWindowManager.setAppWillBeHidden did not throw SecurityException as"
-                    + " expected");
-        } catch (SecurityException e) {
-            // expected
-        } catch (RemoteException e) {
-            fail("Unexpected remote exception");
-        }
-
-        try {
             mWm.setAppVisibility(null, false);
             fail("IWindowManager.setAppVisibility did not throw SecurityException as"
                     + " expected");
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/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java b/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java
index ed8b56e..0da6bb6 100644
--- a/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java
+++ b/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java
@@ -76,7 +76,7 @@
     @Override
     public void addAppToken(int arg0, IApplicationToken arg1, int arg2, int arg3, int arg4,
             boolean arg5, boolean arg6, int arg7, int arg8, boolean arg9, boolean arg10,
-            Rect arg11, Configuration arg12) throws RemoteException {
+            Rect arg11, Configuration arg12, boolean arg13) throws RemoteException {
         // TODO Auto-generated method stub
     }
 
@@ -338,11 +338,6 @@
     }
 
     @Override
-    public void setAppWillBeHidden(IBinder arg0) throws RemoteException {
-        // TODO Auto-generated method stub
-    }
-
-    @Override
     public void setEventDispatching(boolean arg0) throws RemoteException {
         // TODO Auto-generated method stub
     }
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java
index 24e1ce7..2a4f583 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java
@@ -1050,11 +1050,7 @@
             }
             if (scrollPos != 0) {
                 view.scrollBy(0, scrollPos);
-            } else {
-                view.scrollBy(0, scrollPos);
             }
-        } else {
-            view.scrollBy(0, scrollPos);
         }
 
         if (!(view instanceof ViewGroup)) {
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/packages/SystemUI/res/values-sw600dp-port/dimens.xml b/wifi/java/android/net/wifi/ScanInfo.aidl
similarity index 69%
rename from packages/SystemUI/res/values-sw600dp-port/dimens.xml
rename to wifi/java/android/net/wifi/ScanInfo.aidl
index 7dc91d1..18ae508 100644
--- a/packages/SystemUI/res/values-sw600dp-port/dimens.xml
+++ b/wifi/java/android/net/wifi/ScanInfo.aidl
@@ -1,6 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- * Copyright (c) 2012, The Android Open Source Project
+/**
+ * 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.
@@ -13,9 +12,8 @@
  * 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.
-*/
--->
-<resources>
-    <!-- Recent Applications parameters -->
-    <dimen name="status_bar_recents_app_label_width">140dip</dimen>
-</resources>
+ */
+
+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