Merge "Close notification panel when user button is tapped" into nyc-dev
diff --git a/api/current.txt b/api/current.txt
index 936ca88..8a83024 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -5723,6 +5723,7 @@
     method public android.graphics.drawable.Drawable getFastDrawable();
     method public static android.app.WallpaperManager getInstance(android.content.Context);
     method public android.os.ParcelFileDescriptor getWallpaperFile(int);
+    method public int getWallpaperId(int);
     method public android.app.WallpaperInfo getWallpaperInfo();
     method public boolean hasResourceWallpaper(int);
     method public boolean isWallpaperSettingAllowed();
@@ -7101,9 +7102,10 @@
     method public void onServicesDiscovered(android.bluetooth.BluetoothGatt, int);
   }
 
-  public class BluetoothGattCharacteristic {
+  public class BluetoothGattCharacteristic implements android.os.Parcelable {
     ctor public BluetoothGattCharacteristic(java.util.UUID, int, int);
     method public boolean addDescriptor(android.bluetooth.BluetoothGattDescriptor);
+    method public int describeContents();
     method public android.bluetooth.BluetoothGattDescriptor getDescriptor(java.util.UUID);
     method public java.util.List<android.bluetooth.BluetoothGattDescriptor> getDescriptors();
     method public java.lang.Float getFloatValue(int, int);
@@ -7121,6 +7123,8 @@
     method public boolean setValue(int, int, int, int);
     method public boolean setValue(java.lang.String);
     method public void setWriteType(int);
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.bluetooth.BluetoothGattCharacteristic> CREATOR;
     field public static final int FORMAT_FLOAT = 52; // 0x34
     field public static final int FORMAT_SFLOAT = 50; // 0x32
     field public static final int FORMAT_SINT16 = 34; // 0x22
@@ -7151,13 +7155,16 @@
     field protected java.util.List<android.bluetooth.BluetoothGattDescriptor> mDescriptors;
   }
 
-  public class BluetoothGattDescriptor {
+  public class BluetoothGattDescriptor implements android.os.Parcelable {
     ctor public BluetoothGattDescriptor(java.util.UUID, int);
+    method public int describeContents();
     method public android.bluetooth.BluetoothGattCharacteristic getCharacteristic();
     method public int getPermissions();
     method public java.util.UUID getUuid();
     method public byte[] getValue();
     method public boolean setValue(byte[]);
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.bluetooth.BluetoothGattDescriptor> CREATOR;
     field public static final byte[] DISABLE_NOTIFICATION_VALUE;
     field public static final byte[] ENABLE_INDICATION_VALUE;
     field public static final byte[] ENABLE_NOTIFICATION_VALUE;
@@ -7200,16 +7207,19 @@
     method public void onServiceAdded(int, android.bluetooth.BluetoothGattService);
   }
 
-  public class BluetoothGattService {
+  public class BluetoothGattService implements android.os.Parcelable {
     ctor public BluetoothGattService(java.util.UUID, int);
     method public boolean addCharacteristic(android.bluetooth.BluetoothGattCharacteristic);
     method public boolean addService(android.bluetooth.BluetoothGattService);
+    method public int describeContents();
     method public android.bluetooth.BluetoothGattCharacteristic getCharacteristic(java.util.UUID);
     method public java.util.List<android.bluetooth.BluetoothGattCharacteristic> getCharacteristics();
     method public java.util.List<android.bluetooth.BluetoothGattService> getIncludedServices();
     method public int getInstanceId();
     method public int getType();
     method public java.util.UUID getUuid();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.bluetooth.BluetoothGattService> CREATOR;
     field public static final int SERVICE_TYPE_PRIMARY = 0; // 0x0
     field public static final int SERVICE_TYPE_SECONDARY = 1; // 0x1
     field protected java.util.List<android.bluetooth.BluetoothGattCharacteristic> mCharacteristics;
@@ -17750,9 +17760,6 @@
     method public android.icu.text.UnicodeSet addAll(java.lang.Iterable<?>);
     method public android.icu.text.UnicodeSet addAll(T...);
     method public T addAllTo(T);
-    method public java.lang.String[] addAllTo(java.lang.String[]);
-    method public static U addAllTo(java.lang.Iterable<T>, U);
-    method public static T[] addAllTo(java.lang.Iterable<T>, T[]);
     method public void addMatchSetTo(android.icu.text.UnicodeSet);
     method public android.icu.text.UnicodeSet applyIntPropertyValue(int, int);
     method public final android.icu.text.UnicodeSet applyPattern(java.lang.String);
@@ -17766,10 +17773,6 @@
     method public android.icu.text.UnicodeSet cloneAsThawed();
     method public android.icu.text.UnicodeSet closeOver(int);
     method public android.icu.text.UnicodeSet compact();
-    method public static int compare(java.lang.CharSequence, int);
-    method public static int compare(int, java.lang.CharSequence);
-    method public static int compare(java.lang.Iterable<T>, java.lang.Iterable<T>);
-    method public static int compare(java.util.Collection<T>, java.util.Collection<T>, android.icu.text.UnicodeSet.ComparisonStyle);
     method public int compareTo(android.icu.text.UnicodeSet);
     method public int compareTo(android.icu.text.UnicodeSet, android.icu.text.UnicodeSet.ComparisonStyle);
     method public int compareTo(java.lang.Iterable<java.lang.String>);
@@ -17812,7 +17815,6 @@
     method public android.icu.text.UnicodeSet removeAll(android.icu.text.UnicodeSet);
     method public android.icu.text.UnicodeSet removeAll(java.lang.Iterable<T>);
     method public final android.icu.text.UnicodeSet removeAllStrings();
-    method public static boolean resemblesPattern(java.lang.String, int);
     method public android.icu.text.UnicodeSet retain(int, int);
     method public final android.icu.text.UnicodeSet retain(int);
     method public final android.icu.text.UnicodeSet retain(java.lang.CharSequence);
@@ -17827,7 +17829,6 @@
     method public int spanBack(java.lang.CharSequence, android.icu.text.UnicodeSet.SpanCondition);
     method public int spanBack(java.lang.CharSequence, int, android.icu.text.UnicodeSet.SpanCondition);
     method public java.util.Collection<java.lang.String> strings();
-    method public static java.lang.String[] toArray(android.icu.text.UnicodeSet);
     method public java.lang.String toPattern(boolean);
     field public static final int ADD_CASE_MAPPINGS = 4; // 0x4
     field public static final android.icu.text.UnicodeSet ALL_CODE_POINTS;
@@ -17946,11 +17947,8 @@
     ctor protected CECalendar(int, int, int);
     ctor protected CECalendar(java.util.Date);
     ctor protected CECalendar(int, int, int, int, int, int);
-    method public static int ceToJD(long, int, int, int);
-    method protected abstract int getJDEpochOffset();
     method protected int handleComputeMonthStart(int, int, boolean);
     method protected int handleGetLimit(int, int);
-    method public static void jdToCE(int, int, int[]);
   }
 
   public abstract class Calendar implements java.lang.Cloneable java.lang.Comparable java.io.Serializable {
@@ -18178,7 +18176,6 @@
     ctor public CopticCalendar(int, int, int);
     ctor public CopticCalendar(java.util.Date);
     ctor public CopticCalendar(int, int, int, int, int, int);
-    method protected deprecated int getJDEpochOffset();
     method protected deprecated int handleGetExtendedYear();
     field public static final int AMSHIR = 5; // 0x5
     field public static final int BABA = 1; // 0x1
@@ -18349,11 +18346,11 @@
     ctor public IslamicCalendar(java.util.Date);
     ctor public IslamicCalendar(int, int, int);
     ctor public IslamicCalendar(int, int, int, int, int, int);
+    method public android.icu.util.IslamicCalendar.CalculationType getCalculationType();
     method protected int handleComputeMonthStart(int, int, boolean);
     method protected int handleGetExtendedYear();
     method protected int handleGetLimit(int, int);
-    method public boolean isCivil();
-    method public void setCivil(boolean);
+    method public void setCalculationType(android.icu.util.IslamicCalendar.CalculationType);
     field public static final int DHU_AL_HIJJAH = 11; // 0xb
     field public static final int DHU_AL_QIDAH = 10; // 0xa
     field public static final int JUMADA_1 = 4; // 0x4
@@ -18791,7 +18788,6 @@
     method public int getMicro();
     method public int getMilli();
     method public int getMinor();
-    method public static void main(java.lang.String[]);
     field public static final android.icu.util.VersionInfo ICU_VERSION;
     field public static final android.icu.util.VersionInfo UCOL_BUILDER_VERSION;
     field public static final android.icu.util.VersionInfo UCOL_RUNTIME_VERSION;
diff --git a/api/system-current.txt b/api/system-current.txt
index f8863e5..2cad670 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -5857,6 +5857,7 @@
     method public android.graphics.drawable.Drawable getFastDrawable();
     method public static android.app.WallpaperManager getInstance(android.content.Context);
     method public android.os.ParcelFileDescriptor getWallpaperFile(int);
+    method public int getWallpaperId(int);
     method public android.app.WallpaperInfo getWallpaperInfo();
     method public boolean hasResourceWallpaper(int);
     method public boolean isWallpaperSettingAllowed();
@@ -7376,9 +7377,10 @@
     method public void onServicesDiscovered(android.bluetooth.BluetoothGatt, int);
   }
 
-  public class BluetoothGattCharacteristic {
+  public class BluetoothGattCharacteristic implements android.os.Parcelable {
     ctor public BluetoothGattCharacteristic(java.util.UUID, int, int);
     method public boolean addDescriptor(android.bluetooth.BluetoothGattDescriptor);
+    method public int describeContents();
     method public android.bluetooth.BluetoothGattDescriptor getDescriptor(java.util.UUID);
     method public java.util.List<android.bluetooth.BluetoothGattDescriptor> getDescriptors();
     method public java.lang.Float getFloatValue(int, int);
@@ -7396,6 +7398,8 @@
     method public boolean setValue(int, int, int, int);
     method public boolean setValue(java.lang.String);
     method public void setWriteType(int);
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.bluetooth.BluetoothGattCharacteristic> CREATOR;
     field public static final int FORMAT_FLOAT = 52; // 0x34
     field public static final int FORMAT_SFLOAT = 50; // 0x32
     field public static final int FORMAT_SINT16 = 34; // 0x22
@@ -7426,13 +7430,16 @@
     field protected java.util.List<android.bluetooth.BluetoothGattDescriptor> mDescriptors;
   }
 
-  public class BluetoothGattDescriptor {
+  public class BluetoothGattDescriptor implements android.os.Parcelable {
     ctor public BluetoothGattDescriptor(java.util.UUID, int);
+    method public int describeContents();
     method public android.bluetooth.BluetoothGattCharacteristic getCharacteristic();
     method public int getPermissions();
     method public java.util.UUID getUuid();
     method public byte[] getValue();
     method public boolean setValue(byte[]);
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.bluetooth.BluetoothGattDescriptor> CREATOR;
     field public static final byte[] DISABLE_NOTIFICATION_VALUE;
     field public static final byte[] ENABLE_INDICATION_VALUE;
     field public static final byte[] ENABLE_NOTIFICATION_VALUE;
@@ -7475,16 +7482,19 @@
     method public void onServiceAdded(int, android.bluetooth.BluetoothGattService);
   }
 
-  public class BluetoothGattService {
+  public class BluetoothGattService implements android.os.Parcelable {
     ctor public BluetoothGattService(java.util.UUID, int);
     method public boolean addCharacteristic(android.bluetooth.BluetoothGattCharacteristic);
     method public boolean addService(android.bluetooth.BluetoothGattService);
+    method public int describeContents();
     method public android.bluetooth.BluetoothGattCharacteristic getCharacteristic(java.util.UUID);
     method public java.util.List<android.bluetooth.BluetoothGattCharacteristic> getCharacteristics();
     method public java.util.List<android.bluetooth.BluetoothGattService> getIncludedServices();
     method public int getInstanceId();
     method public int getType();
     method public java.util.UUID getUuid();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.bluetooth.BluetoothGattService> CREATOR;
     field public static final int SERVICE_TYPE_PRIMARY = 0; // 0x0
     field public static final int SERVICE_TYPE_SECONDARY = 1; // 0x1
     field protected java.util.List<android.bluetooth.BluetoothGattCharacteristic> mCharacteristics;
@@ -18922,9 +18932,6 @@
     method public android.icu.text.UnicodeSet addAll(java.lang.Iterable<?>);
     method public android.icu.text.UnicodeSet addAll(T...);
     method public T addAllTo(T);
-    method public java.lang.String[] addAllTo(java.lang.String[]);
-    method public static U addAllTo(java.lang.Iterable<T>, U);
-    method public static T[] addAllTo(java.lang.Iterable<T>, T[]);
     method public void addMatchSetTo(android.icu.text.UnicodeSet);
     method public android.icu.text.UnicodeSet applyIntPropertyValue(int, int);
     method public final android.icu.text.UnicodeSet applyPattern(java.lang.String);
@@ -18938,10 +18945,6 @@
     method public android.icu.text.UnicodeSet cloneAsThawed();
     method public android.icu.text.UnicodeSet closeOver(int);
     method public android.icu.text.UnicodeSet compact();
-    method public static int compare(java.lang.CharSequence, int);
-    method public static int compare(int, java.lang.CharSequence);
-    method public static int compare(java.lang.Iterable<T>, java.lang.Iterable<T>);
-    method public static int compare(java.util.Collection<T>, java.util.Collection<T>, android.icu.text.UnicodeSet.ComparisonStyle);
     method public int compareTo(android.icu.text.UnicodeSet);
     method public int compareTo(android.icu.text.UnicodeSet, android.icu.text.UnicodeSet.ComparisonStyle);
     method public int compareTo(java.lang.Iterable<java.lang.String>);
@@ -18984,7 +18987,6 @@
     method public android.icu.text.UnicodeSet removeAll(android.icu.text.UnicodeSet);
     method public android.icu.text.UnicodeSet removeAll(java.lang.Iterable<T>);
     method public final android.icu.text.UnicodeSet removeAllStrings();
-    method public static boolean resemblesPattern(java.lang.String, int);
     method public android.icu.text.UnicodeSet retain(int, int);
     method public final android.icu.text.UnicodeSet retain(int);
     method public final android.icu.text.UnicodeSet retain(java.lang.CharSequence);
@@ -18999,7 +19001,6 @@
     method public int spanBack(java.lang.CharSequence, android.icu.text.UnicodeSet.SpanCondition);
     method public int spanBack(java.lang.CharSequence, int, android.icu.text.UnicodeSet.SpanCondition);
     method public java.util.Collection<java.lang.String> strings();
-    method public static java.lang.String[] toArray(android.icu.text.UnicodeSet);
     method public java.lang.String toPattern(boolean);
     field public static final int ADD_CASE_MAPPINGS = 4; // 0x4
     field public static final android.icu.text.UnicodeSet ALL_CODE_POINTS;
@@ -19118,11 +19119,8 @@
     ctor protected CECalendar(int, int, int);
     ctor protected CECalendar(java.util.Date);
     ctor protected CECalendar(int, int, int, int, int, int);
-    method public static int ceToJD(long, int, int, int);
-    method protected abstract int getJDEpochOffset();
     method protected int handleComputeMonthStart(int, int, boolean);
     method protected int handleGetLimit(int, int);
-    method public static void jdToCE(int, int, int[]);
   }
 
   public abstract class Calendar implements java.lang.Cloneable java.lang.Comparable java.io.Serializable {
@@ -19350,7 +19348,6 @@
     ctor public CopticCalendar(int, int, int);
     ctor public CopticCalendar(java.util.Date);
     ctor public CopticCalendar(int, int, int, int, int, int);
-    method protected deprecated int getJDEpochOffset();
     method protected deprecated int handleGetExtendedYear();
     field public static final int AMSHIR = 5; // 0x5
     field public static final int BABA = 1; // 0x1
@@ -19521,11 +19518,11 @@
     ctor public IslamicCalendar(java.util.Date);
     ctor public IslamicCalendar(int, int, int);
     ctor public IslamicCalendar(int, int, int, int, int, int);
+    method public android.icu.util.IslamicCalendar.CalculationType getCalculationType();
     method protected int handleComputeMonthStart(int, int, boolean);
     method protected int handleGetExtendedYear();
     method protected int handleGetLimit(int, int);
-    method public boolean isCivil();
-    method public void setCivil(boolean);
+    method public void setCalculationType(android.icu.util.IslamicCalendar.CalculationType);
     field public static final int DHU_AL_HIJJAH = 11; // 0xb
     field public static final int DHU_AL_QIDAH = 10; // 0xa
     field public static final int JUMADA_1 = 4; // 0x4
@@ -19963,7 +19960,6 @@
     method public int getMicro();
     method public int getMilli();
     method public int getMinor();
-    method public static void main(java.lang.String[]);
     field public static final android.icu.util.VersionInfo ICU_VERSION;
     field public static final android.icu.util.VersionInfo UCOL_BUILDER_VERSION;
     field public static final android.icu.util.VersionInfo UCOL_RUNTIME_VERSION;
@@ -31648,6 +31644,7 @@
     method public android.os.Bundle getUserRestrictions(android.os.UserHandle);
     method public boolean hasUserRestriction(java.lang.String);
     method public boolean isManagedProfile();
+    method public boolean isManagedProfile(int);
     method public boolean isQuietModeEnabled(android.os.UserHandle);
     method public boolean isSystemUser();
     method public boolean isUserAGoat();
diff --git a/api/test-current.txt b/api/test-current.txt
index 6327dc2..d3a12ed 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -5727,6 +5727,7 @@
     method public android.graphics.drawable.Drawable getFastDrawable();
     method public static android.app.WallpaperManager getInstance(android.content.Context);
     method public android.os.ParcelFileDescriptor getWallpaperFile(int);
+    method public int getWallpaperId(int);
     method public android.app.WallpaperInfo getWallpaperInfo();
     method public boolean hasResourceWallpaper(int);
     method public boolean isWallpaperSettingAllowed();
@@ -7105,9 +7106,10 @@
     method public void onServicesDiscovered(android.bluetooth.BluetoothGatt, int);
   }
 
-  public class BluetoothGattCharacteristic {
+  public class BluetoothGattCharacteristic implements android.os.Parcelable {
     ctor public BluetoothGattCharacteristic(java.util.UUID, int, int);
     method public boolean addDescriptor(android.bluetooth.BluetoothGattDescriptor);
+    method public int describeContents();
     method public android.bluetooth.BluetoothGattDescriptor getDescriptor(java.util.UUID);
     method public java.util.List<android.bluetooth.BluetoothGattDescriptor> getDescriptors();
     method public java.lang.Float getFloatValue(int, int);
@@ -7125,6 +7127,8 @@
     method public boolean setValue(int, int, int, int);
     method public boolean setValue(java.lang.String);
     method public void setWriteType(int);
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.bluetooth.BluetoothGattCharacteristic> CREATOR;
     field public static final int FORMAT_FLOAT = 52; // 0x34
     field public static final int FORMAT_SFLOAT = 50; // 0x32
     field public static final int FORMAT_SINT16 = 34; // 0x22
@@ -7155,13 +7159,16 @@
     field protected java.util.List<android.bluetooth.BluetoothGattDescriptor> mDescriptors;
   }
 
-  public class BluetoothGattDescriptor {
+  public class BluetoothGattDescriptor implements android.os.Parcelable {
     ctor public BluetoothGattDescriptor(java.util.UUID, int);
+    method public int describeContents();
     method public android.bluetooth.BluetoothGattCharacteristic getCharacteristic();
     method public int getPermissions();
     method public java.util.UUID getUuid();
     method public byte[] getValue();
     method public boolean setValue(byte[]);
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.bluetooth.BluetoothGattDescriptor> CREATOR;
     field public static final byte[] DISABLE_NOTIFICATION_VALUE;
     field public static final byte[] ENABLE_INDICATION_VALUE;
     field public static final byte[] ENABLE_NOTIFICATION_VALUE;
@@ -7204,16 +7211,19 @@
     method public void onServiceAdded(int, android.bluetooth.BluetoothGattService);
   }
 
-  public class BluetoothGattService {
+  public class BluetoothGattService implements android.os.Parcelable {
     ctor public BluetoothGattService(java.util.UUID, int);
     method public boolean addCharacteristic(android.bluetooth.BluetoothGattCharacteristic);
     method public boolean addService(android.bluetooth.BluetoothGattService);
+    method public int describeContents();
     method public android.bluetooth.BluetoothGattCharacteristic getCharacteristic(java.util.UUID);
     method public java.util.List<android.bluetooth.BluetoothGattCharacteristic> getCharacteristics();
     method public java.util.List<android.bluetooth.BluetoothGattService> getIncludedServices();
     method public int getInstanceId();
     method public int getType();
     method public java.util.UUID getUuid();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.bluetooth.BluetoothGattService> CREATOR;
     field public static final int SERVICE_TYPE_PRIMARY = 0; // 0x0
     field public static final int SERVICE_TYPE_SECONDARY = 1; // 0x1
     field protected java.util.List<android.bluetooth.BluetoothGattCharacteristic> mCharacteristics;
@@ -17760,9 +17770,6 @@
     method public android.icu.text.UnicodeSet addAll(java.lang.Iterable<?>);
     method public android.icu.text.UnicodeSet addAll(T...);
     method public T addAllTo(T);
-    method public java.lang.String[] addAllTo(java.lang.String[]);
-    method public static U addAllTo(java.lang.Iterable<T>, U);
-    method public static T[] addAllTo(java.lang.Iterable<T>, T[]);
     method public void addMatchSetTo(android.icu.text.UnicodeSet);
     method public android.icu.text.UnicodeSet applyIntPropertyValue(int, int);
     method public final android.icu.text.UnicodeSet applyPattern(java.lang.String);
@@ -17776,10 +17783,6 @@
     method public android.icu.text.UnicodeSet cloneAsThawed();
     method public android.icu.text.UnicodeSet closeOver(int);
     method public android.icu.text.UnicodeSet compact();
-    method public static int compare(java.lang.CharSequence, int);
-    method public static int compare(int, java.lang.CharSequence);
-    method public static int compare(java.lang.Iterable<T>, java.lang.Iterable<T>);
-    method public static int compare(java.util.Collection<T>, java.util.Collection<T>, android.icu.text.UnicodeSet.ComparisonStyle);
     method public int compareTo(android.icu.text.UnicodeSet);
     method public int compareTo(android.icu.text.UnicodeSet, android.icu.text.UnicodeSet.ComparisonStyle);
     method public int compareTo(java.lang.Iterable<java.lang.String>);
@@ -17822,7 +17825,6 @@
     method public android.icu.text.UnicodeSet removeAll(android.icu.text.UnicodeSet);
     method public android.icu.text.UnicodeSet removeAll(java.lang.Iterable<T>);
     method public final android.icu.text.UnicodeSet removeAllStrings();
-    method public static boolean resemblesPattern(java.lang.String, int);
     method public android.icu.text.UnicodeSet retain(int, int);
     method public final android.icu.text.UnicodeSet retain(int);
     method public final android.icu.text.UnicodeSet retain(java.lang.CharSequence);
@@ -17837,7 +17839,6 @@
     method public int spanBack(java.lang.CharSequence, android.icu.text.UnicodeSet.SpanCondition);
     method public int spanBack(java.lang.CharSequence, int, android.icu.text.UnicodeSet.SpanCondition);
     method public java.util.Collection<java.lang.String> strings();
-    method public static java.lang.String[] toArray(android.icu.text.UnicodeSet);
     method public java.lang.String toPattern(boolean);
     field public static final int ADD_CASE_MAPPINGS = 4; // 0x4
     field public static final android.icu.text.UnicodeSet ALL_CODE_POINTS;
@@ -17956,11 +17957,8 @@
     ctor protected CECalendar(int, int, int);
     ctor protected CECalendar(java.util.Date);
     ctor protected CECalendar(int, int, int, int, int, int);
-    method public static int ceToJD(long, int, int, int);
-    method protected abstract int getJDEpochOffset();
     method protected int handleComputeMonthStart(int, int, boolean);
     method protected int handleGetLimit(int, int);
-    method public static void jdToCE(int, int, int[]);
   }
 
   public abstract class Calendar implements java.lang.Cloneable java.lang.Comparable java.io.Serializable {
@@ -18188,7 +18186,6 @@
     ctor public CopticCalendar(int, int, int);
     ctor public CopticCalendar(java.util.Date);
     ctor public CopticCalendar(int, int, int, int, int, int);
-    method protected deprecated int getJDEpochOffset();
     method protected deprecated int handleGetExtendedYear();
     field public static final int AMSHIR = 5; // 0x5
     field public static final int BABA = 1; // 0x1
@@ -18359,11 +18356,11 @@
     ctor public IslamicCalendar(java.util.Date);
     ctor public IslamicCalendar(int, int, int);
     ctor public IslamicCalendar(int, int, int, int, int, int);
+    method public android.icu.util.IslamicCalendar.CalculationType getCalculationType();
     method protected int handleComputeMonthStart(int, int, boolean);
     method protected int handleGetExtendedYear();
     method protected int handleGetLimit(int, int);
-    method public boolean isCivil();
-    method public void setCivil(boolean);
+    method public void setCalculationType(android.icu.util.IslamicCalendar.CalculationType);
     field public static final int DHU_AL_HIJJAH = 11; // 0xb
     field public static final int DHU_AL_QIDAH = 10; // 0xa
     field public static final int JUMADA_1 = 4; // 0x4
@@ -18801,7 +18798,6 @@
     method public int getMicro();
     method public int getMilli();
     method public int getMinor();
-    method public static void main(java.lang.String[]);
     field public static final android.icu.util.VersionInfo ICU_VERSION;
     field public static final android.icu.util.VersionInfo UCOL_BUILDER_VERSION;
     field public static final android.icu.util.VersionInfo UCOL_RUNTIME_VERSION;
diff --git a/cmds/am/src/com/android/commands/am/Am.java b/cmds/am/src/com/android/commands/am/Am.java
index e6c5768..86734b1 100644
--- a/cmds/am/src/com/android/commands/am/Am.java
+++ b/cmds/am/src/com/android/commands/am/Am.java
@@ -1814,7 +1814,7 @@
 
     private void resizeStackUnchecked(int stackId, Rect bounds, int delayMs, boolean animate) {
         try {
-            mAm.resizeStack(stackId, bounds, false, false, animate);
+            mAm.resizeStack(stackId, bounds, false, false, animate, -1);
             Thread.sleep(delayMs);
         } catch (RemoteException e) {
             showError("Error: resizing stack " + e);
diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java
index 5f317b0..f5d7e7e 100644
--- a/core/java/android/app/ActivityManagerNative.java
+++ b/core/java/android/app/ActivityManagerNative.java
@@ -820,7 +820,9 @@
             final boolean allowResizeInDockedMode = data.readInt() == 1;
             final boolean preserveWindows = data.readInt() == 1;
             final boolean animate = data.readInt() == 1;
-            resizeStack(stackId, r, allowResizeInDockedMode, preserveWindows, animate);
+            final int animationDuration = data.readInt();
+            resizeStack(stackId,
+                    r, allowResizeInDockedMode, preserveWindows, animate, animationDuration);
             reply.writeNoException();
             return true;
         }
@@ -3889,7 +3891,8 @@
     }
     @Override
     public void resizeStack(int stackId, Rect r, boolean allowResizeInDockedMode,
-            boolean preserveWindows, boolean animate) throws RemoteException {
+            boolean preserveWindows, boolean animate, int animationDuration)
+            throws RemoteException {
         Parcel data = Parcel.obtain();
         Parcel reply = Parcel.obtain();
         data.writeInterfaceToken(IActivityManager.descriptor);
@@ -3903,6 +3906,7 @@
         data.writeInt(allowResizeInDockedMode ? 1 : 0);
         data.writeInt(preserveWindows ? 1 : 0);
         data.writeInt(animate ? 1 : 0);
+        data.writeInt(animationDuration);
         mRemote.transact(RESIZE_STACK_TRANSACTION, data, reply, 0);
         reply.readException();
         data.recycle();
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index 5ef03d1..5b7dae6 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -1147,8 +1147,7 @@
     }
 
     private Drawable getManagedProfileIconForDensity(UserHandle user, int drawableId, int density) {
-        UserInfo userInfo = getUserInfo(user.getIdentifier());
-        if (userInfo != null && userInfo.isManagedProfile()) {
+        if (isManagedProfile(user.getIdentifier())) {
             return getDrawableForDensity(drawableId, density);
         }
         return null;
@@ -1156,8 +1155,7 @@
 
     @Override
     public CharSequence getUserBadgedLabel(CharSequence label, UserHandle user) {
-        UserInfo userInfo = getUserInfo(user.getIdentifier());
-        if (userInfo != null && userInfo.isManagedProfile()) {
+        if (isManagedProfile(user.getIdentifier())) {
             return Resources.getSystem().getString(
                     com.android.internal.R.string.managed_profile_label_badge, label);
         }
@@ -2259,17 +2257,16 @@
         return drawable;
     }
 
-    private int getBadgeResIdForUser(int userHandle) {
+    private int getBadgeResIdForUser(int userId) {
         // Return the framework-provided badge.
-        UserInfo userInfo = getUserInfo(userHandle);
-        if (userInfo != null && userInfo.isManagedProfile()) {
+        if (isManagedProfile(userId)) {
             return com.android.internal.R.drawable.ic_corp_icon_badge;
         }
         return 0;
     }
 
-    private UserInfo getUserInfo(int userHandle) {
-        return getUserManager().getUserInfo(userHandle);
+    private boolean isManagedProfile(int userId) {
+        return getUserManager().isManagedProfile(userId);
     }
 
     /** {@hide} */
diff --git a/core/java/android/app/DownloadManager.java b/core/java/android/app/DownloadManager.java
index ed4bb28..536c4a8 100644
--- a/core/java/android/app/DownloadManager.java
+++ b/core/java/android/app/DownloadManager.java
@@ -1319,7 +1319,7 @@
                     return getLocalUri();
                 case COLUMN_LOCAL_FILENAME:
                     if (!mAccessFilename) {
-                        throw new IllegalArgumentException(
+                        throw new SecurityException(
                                 "COLUMN_LOCAL_FILENAME is deprecated;"
                                         + " use ContentResolver.openFileDescriptor() instead");
                     }
diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java
index d0c21f5..639c207 100644
--- a/core/java/android/app/IActivityManager.java
+++ b/core/java/android/app/IActivityManager.java
@@ -148,8 +148,23 @@
     public boolean moveTaskToDockedStack(int taskId, int createMode, boolean toTop, boolean animate,
             Rect initialBounds) throws RemoteException;
     public boolean moveTopActivityToPinnedStack(int stackId, Rect bounds) throws RemoteException;
+
+    /**
+     * Resizes the input stack id to the given bounds.
+     *
+     * @param stackId Id of the stack to resize.
+     * @param bounds Bounds to resize the stack to or {@code null} for fullscreen.
+     * @param allowResizeInDockedMode True if the resize should be allowed when the docked stack is
+     *                                active.
+     * @param preserveWindows True if the windows of activities contained in the stack should be
+     *                        preserved.
+     * @param animate True if the stack resize should be animated.
+     * @param animationDuration The duration of the resize animation in milliseconds or -1 if the
+     *                          default animation duration should be used.
+     * @throws RemoteException
+     */
     public void resizeStack(int stackId, Rect bounds, boolean allowResizeInDockedMode,
-            boolean preserveWindows, boolean animate) throws RemoteException;
+            boolean preserveWindows, boolean animate, int animationDuration) throws RemoteException;
 
     /**
      * Moves all tasks from the docked stack in the fullscreen stack and puts the top task of the
diff --git a/core/java/android/app/IWallpaperManager.aidl b/core/java/android/app/IWallpaperManager.aidl
index 1143c6a..2fc6533 100644
--- a/core/java/android/app/IWallpaperManager.aidl
+++ b/core/java/android/app/IWallpaperManager.aidl
@@ -62,6 +62,11 @@
             out Bundle outParams, int userId);
 
     /**
+     * Retrieve the given user's current wallpaper ID of the given kind.
+     */
+    int getWallpaperIdForUser(int which, int userId);
+
+    /**
      * If the current system wallpaper is a live wallpaper component, return the
      * information about that wallpaper.  Otherwise, if it is a static image,
      * simply return null.
diff --git a/core/java/android/app/WallpaperManager.java b/core/java/android/app/WallpaperManager.java
index 52e5272..b52af27 100644
--- a/core/java/android/app/WallpaperManager.java
+++ b/core/java/android/app/WallpaperManager.java
@@ -726,6 +726,38 @@
     }
 
     /**
+     * Get the ID of the current wallpaper of the given kind.  If there is no
+     * such wallpaper configured, returns a negative number.
+     *
+     * @param which The wallpaper whose ID is to be returned.  Must be a single
+     *     defined kind of wallpaper, either {@link #FLAG_SET_SYSTEM} or
+     *     {@link #FLAG_SET_LOCK}.
+     * @return The positive numeric ID of the current wallpaper of the given kind,
+     *     or a negative value if no such wallpaper is configured.
+     */
+    public int getWallpaperId(int which) {
+        return getWallpaperIdForUser(which, mContext.getUserId());
+    }
+
+    /**
+     * Get the ID of the given user's current wallpaper of the given kind.  If there
+     * is no such wallpaper configured, returns a negative number.
+     * @hide
+     */
+    public int getWallpaperIdForUser(int which, int userId) {
+        try {
+            if (sGlobals.mService == null) {
+                Log.w(TAG, "WallpaperService not running");
+                return -1;
+            } else {
+                return sGlobals.mService.getWallpaperIdForUser(which, userId);
+            }
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
      * Gets an Intent that will launch an activity that crops the given
      * image and sets the device's wallpaper. If there is a default HOME activity
      * that supports cropping wallpapers, it will be preferred as the default.
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index fe5c45f..98d356b 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -145,19 +145,23 @@
      * {@link #EXTRA_PROVISIONING_DEVICE_ADMIN_COMPONENT_NAME} instead, although specifying only
      * {@link #EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_NAME} is still supported.
      *
-     * <p> The intent may also contain the following extras:
+     * <p>The intent may also contain the following extras:
      * <ul>
-     * <li> {@link #EXTRA_PROVISIONING_LOGO_URI}, optional </li>
-     * <li> {@link #EXTRA_PROVISIONING_MAIN_COLOR}, optional </li>
+     * <li>{@link #EXTRA_PROVISIONING_ACCOUNT_TO_MIGRATE}, optional </li>
+     * <li>{@link #EXTRA_PROVISIONING_SKIP_ENCRYPTION}, optional, supported from
+     * {@link android.os.Build.VERSION_CODES#N}</li>
+     * <li>{@link #EXTRA_PROVISIONING_ADMIN_EXTRAS_BUNDLE}, optional</li>
+     * <li>{@link #EXTRA_PROVISIONING_LOGO_URI}, optional</li>
+     * <li>{@link #EXTRA_PROVISIONING_MAIN_COLOR}, optional</li>
      * </ul>
      *
-     * <p> When managed provisioning has completed, broadcasts are sent to the application specified
+     * <p>When managed provisioning has completed, broadcasts are sent to the application specified
      * in the provisioning intent. The
      * {@link DeviceAdminReceiver#ACTION_PROFILE_PROVISIONING_COMPLETE} broadcast is sent in the
      * managed profile and the {@link #ACTION_MANAGED_PROFILE_PROVISIONED} broadcast is sent in
      * the primary profile.
      *
-     * <p> If provisioning fails, the managedProfile is removed so the device returns to its
+     * <p>If provisioning fails, the managedProfile is removed so the device returns to its
      * previous state.
      *
      * <p>If launched with {@link android.app.Activity#startActivityForResult(Intent, int)} a
@@ -171,7 +175,6 @@
         = "android.app.action.PROVISION_MANAGED_PROFILE";
 
     /**
-     * @hide
      * Activity action: Starts the provisioning flow which sets up a managed user.
      *
      * <p>This intent will typically be sent by a mobile device management application (MDM).
@@ -180,16 +183,24 @@
      * been completed. Use {@link #isProvisioningAllowed(String)} to check if provisioning is
      * allowed.
      *
-     * <p>This intent should contain the extra
-     * {@link #EXTRA_PROVISIONING_DEVICE_ADMIN_COMPONENT_NAME}.
+     * <p>The intent contains the following extras:
+     * <ul>
+     * <li>{@link #EXTRA_PROVISIONING_DEVICE_ADMIN_COMPONENT_NAME}</li>
+     * <li>{@link #EXTRA_PROVISIONING_SKIP_ENCRYPTION}, optional</li>
+     * <li>{@link #EXTRA_PROVISIONING_ADMIN_EXTRAS_BUNDLE}, optional</li>
+     * <li>{@link #EXTRA_PROVISIONING_LOGO_URI}, optional</li>
+     * <li>{@link #EXTRA_PROVISIONING_MAIN_COLOR}, optional</li>
+     * </ul>
      *
-     * <p> If provisioning fails, the device returns to its previous state.
+     * <p>If provisioning fails, the device returns to its previous state.
      *
      * <p>If launched with {@link android.app.Activity#startActivityForResult(Intent, int)} a
      * result code of {@link android.app.Activity#RESULT_OK} implies that the synchronous part of
      * the provisioning flow was successful, although this doesn't guarantee the full flow will
      * succeed. Conversely a result code of {@link android.app.Activity#RESULT_CANCELED} implies
      * that the user backed-out of provisioning, or some precondition for provisioning wasn't met.
+     *
+     * @hide
      */
     @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
     public static final String ACTION_PROVISION_MANAGED_USER
@@ -220,11 +231,11 @@
      * <li>{@link #EXTRA_PROVISIONING_MAIN_COLOR}, optional</li>
      * </ul>
      *
-     * <p> When device owner provisioning has completed, an intent of the type
+     * <p>When device owner provisioning has completed, an intent of the type
      * {@link DeviceAdminReceiver#ACTION_PROFILE_PROVISIONING_COMPLETE} is broadcast to the
      * device owner.
      *
-     * <p> If provisioning fails, the device is factory reset.
+     * <p>If provisioning fails, the device is factory reset.
      *
      * <p>A result code of {@link android.app.Activity#RESULT_OK} implies that the synchronous part
      * of the provisioning flow was successful, although this doesn't guarantee the full flow will
@@ -288,14 +299,14 @@
      * The primary benefit is that multiple non-system users are supported when provisioning using
      * this form of device management.
      *
-     * <p> During device owner provisioning a device admin app is set as the owner of the device.
+     * <p>During device owner provisioning a device admin app is set as the owner of the device.
      * A device owner has full control over the device. The device owner can not be modified by the
      * user.
      *
-     * <p> A typical use case would be a device that is owned by a company, but used by either an
+     * <p>A typical use case would be a device that is owned by a company, but used by either an
      * employee or client.
      *
-     * <p> An intent with this action can be sent only on an unprovisioned device.
+     * <p>An intent with this action can be sent only on an unprovisioned device.
      * It is possible to check if provisioning is allowed or not by querying the method
      * {@link #isProvisioningAllowed(String)}.
      *
@@ -305,13 +316,15 @@
      * <li>{@link #EXTRA_PROVISIONING_SKIP_ENCRYPTION}, optional</li>
      * <li>{@link #EXTRA_PROVISIONING_LEAVE_ALL_SYSTEM_APPS_ENABLED}, optional</li>
      * <li>{@link #EXTRA_PROVISIONING_ADMIN_EXTRAS_BUNDLE}, optional</li>
+     * <li>{@link #EXTRA_PROVISIONING_LOGO_URI}, optional</li>
+     * <li>{@link #EXTRA_PROVISIONING_MAIN_COLOR}, optional</li>
      * </ul>
      *
-     * <p> When device owner provisioning has completed, an intent of the type
+     * <p>When device owner provisioning has completed, an intent of the type
      * {@link DeviceAdminReceiver#ACTION_PROFILE_PROVISIONING_COMPLETE} is broadcast to the
      * device owner.
      *
-     * <p> If provisioning fails, the device is factory reset.
+     * <p>If provisioning fails, the device is factory reset.
      *
      * <p>A result code of {@link android.app.Activity#RESULT_OK} implies that the synchronous part
      * of the provisioning flow was successful, although this doesn't guarantee the full flow will
@@ -439,7 +452,7 @@
      *
      * <p> When this extra is set, the application must have exactly one device admin receiver.
      * This receiver will be set as the profile or device owner and active admin.
-
+     *
      * @see DeviceAdminReceiver
      * @deprecated Use {@link #EXTRA_PROVISIONING_DEVICE_ADMIN_COMPONENT_NAME}. This extra is still
      * supported, but only if there is only one device admin receiver in the package that requires
@@ -461,7 +474,7 @@
      * <p>This component is set as device owner and active admin when device owner provisioning is
      * started by an intent with action {@link #ACTION_PROVISION_MANAGED_DEVICE} or by an NFC
      * message containing an NFC record with MIME type
-     * {@link #MIME_TYPE_PROVISIONING_NFC}. For the NFC record, the component name should be
+     * {@link #MIME_TYPE_PROVISIONING_NFC}. For the NFC record, the component name must be
      * flattened to a string, via {@link ComponentName#flattenToShortString()}.
      *
      * @see DeviceAdminReceiver
@@ -664,8 +677,8 @@
      * the file at download location specified in
      * {@link #EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_DOWNLOAD_LOCATION}.
      *
-     * <p>Either this extra or {@link #EXTRA_PROVISIONING_DEVICE_ADMIN_SIGNATURE_CHECKSUM} should be
-     * present. The provided checksum should match the checksum of the file at the download
+     * <p>Either this extra or {@link #EXTRA_PROVISIONING_DEVICE_ADMIN_SIGNATURE_CHECKSUM} must be
+     * present. The provided checksum must match the checksum of the file at the download
      * location. If the checksum doesn't match an error will be shown to the user and the user will
      * be asked to factory reset the device.
      *
@@ -689,8 +702,8 @@
      * {@link android.content.pm.PackageManager#getPackageArchiveInfo} with flag
      * {@link android.content.pm.PackageManager#GET_SIGNATURES}.
      *
-     * <p>Either this extra or {@link #EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_CHECKSUM} should be
-     * present. The provided checksum should match the checksum of any signature of the file at
+     * <p>Either this extra or {@link #EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_CHECKSUM} must be
+     * present. The provided checksum must match the checksum of any signature of the file at
      * the download location. If the checksum does not match an error will be shown to the user and
      * the user will be asked to factory reset the device.
      *
@@ -715,11 +728,14 @@
         = "android.app.action.MANAGED_PROFILE_PROVISIONED";
 
     /**
-     * A boolean extra indicating whether device encryption can be skipped as part of Device Owner
-     * provisioning.
+     * A boolean extra indicating whether device encryption can be skipped as part of device owner
+     * or managed profile provisioning.
      *
      * <p>Use in an NFC record with {@link #MIME_TYPE_PROVISIONING_NFC} or an intent with action
      * {@link #ACTION_PROVISION_MANAGED_DEVICE} that starts device owner provisioning.
+     *
+     * <p>From {@link android.os.Build.VERSION_CODES#N} onwards, this is also supported for an
+     * intent with action {@link #ACTION_PROVISION_MANAGED_PROFILE}.
      */
     public static final String EXTRA_PROVISIONING_SKIP_ENCRYPTION =
              "android.app.extra.PROVISIONING_SKIP_ENCRYPTION";
@@ -762,7 +778,7 @@
             "android.app.extra.PROVISIONING_SKIP_USER_SETUP";
 
     /**
-     * This MIME type is used for starting the Device Owner provisioning.
+     * This MIME type is used for starting the device owner provisioning.
      *
      * <p>During device owner provisioning a device admin app is set as the owner of the device.
      * A device owner has full control over the device. The device owner can not be modified by the
@@ -772,7 +788,7 @@
      * <p> A typical use case would be a device that is owned by a company, but used by either an
      * employee or client.
      *
-     * <p> The NFC message should be send to an unprovisioned device.
+     * <p> The NFC message must be sent to an unprovisioned device.
      *
      * <p>The NFC record must contain a serialized {@link java.util.Properties} object which
      * contains the following properties:
diff --git a/core/java/android/bluetooth/BluetoothGatt.java b/core/java/android/bluetooth/BluetoothGatt.java
index ea2dca0..68442ea 100644
--- a/core/java/android/bluetooth/BluetoothGatt.java
+++ b/core/java/android/bluetooth/BluetoothGatt.java
@@ -197,109 +197,43 @@
             }
 
             /**
-             * A new GATT service has been discovered.
-             * The service is added to the internal list and the search
-             * continues.
-             * @hide
-             */
-            public void onGetService(String address, int srvcType,
-                                     int srvcInstId, ParcelUuid srvcUuid) {
-                if (VDBG) Log.d(TAG, "onGetService() - Device=" + address + " UUID=" + srvcUuid);
-                if (!address.equals(mDevice.getAddress())) {
-                    return;
-                }
-                mServices.add(new BluetoothGattService(mDevice, srvcUuid.getUuid(),
-                                                       srvcInstId, srvcType));
-            }
-
-            /**
-             * An included service has been found durig GATT discovery.
-             * The included service is added to the respective parent.
-             * @hide
-             */
-            public void onGetIncludedService(String address, int srvcType,
-                                             int srvcInstId, ParcelUuid srvcUuid,
-                                             int inclSrvcType, int inclSrvcInstId,
-                                             ParcelUuid inclSrvcUuid) {
-                if (VDBG) Log.d(TAG, "onGetIncludedService() - Device=" + address
-                    + " UUID=" + srvcUuid + " Included=" + inclSrvcUuid);
-
-                if (!address.equals(mDevice.getAddress())) {
-                    return;
-                }
-                BluetoothGattService service = getService(mDevice,
-                        srvcUuid.getUuid(), srvcInstId, srvcType);
-                BluetoothGattService includedService = getService(mDevice,
-                        inclSrvcUuid.getUuid(), inclSrvcInstId, inclSrvcType);
-
-                if (service != null && includedService != null) {
-                    service.addIncludedService(includedService);
-                }
-            }
-
-            /**
-             * A new GATT characteristic has been discovered.
-             * Add the new characteristic to the relevant service and continue
-             * the remote device inspection.
-             * @hide
-             */
-            public void onGetCharacteristic(String address, int srvcType,
-                             int srvcInstId, ParcelUuid srvcUuid,
-                             int charInstId, ParcelUuid charUuid,
-                             int charProps) {
-                if (VDBG) Log.d(TAG, "onGetCharacteristic() - Device=" + address + " UUID=" +
-                               charUuid);
-
-                if (!address.equals(mDevice.getAddress())) {
-                    return;
-                }
-                BluetoothGattService service = getService(mDevice, srvcUuid.getUuid(),
-                                                          srvcInstId, srvcType);
-                if (service != null) {
-                    service.addCharacteristic(new BluetoothGattCharacteristic(
-                           service, charUuid.getUuid(), charInstId, charProps, 0));
-                }
-            }
-
-            /**
-             * A new GATT descriptor has been discovered.
-             * Finally, add the descriptor to the related characteristic.
-             * This should conclude the remote device update.
-             * @hide
-             */
-            public void onGetDescriptor(String address, int srvcType,
-                             int srvcInstId, ParcelUuid srvcUuid,
-                             int charInstId, ParcelUuid charUuid,
-                             int descrInstId, ParcelUuid descUuid) {
-                if (VDBG) Log.d(TAG, "onGetDescriptor() - Device=" + address + " UUID=" + descUuid);
-
-                if (!address.equals(mDevice.getAddress())) {
-                    return;
-                }
-                BluetoothGattService service = getService(mDevice, srvcUuid.getUuid(),
-                                                          srvcInstId, srvcType);
-                if (service == null) return;
-
-                BluetoothGattCharacteristic characteristic = service.getCharacteristic(
-                    charUuid.getUuid(), charInstId);
-                if (characteristic == null) return;
-
-                characteristic.addDescriptor(new BluetoothGattDescriptor(
-                    characteristic, descUuid.getUuid(), descrInstId, 0));
-            }
-
-            /**
              * Remote search has been completed.
              * The internal object structure should now reflect the state
              * of the remote device database. Let the application know that
              * we are done at this point.
              * @hide
              */
-            public void onSearchComplete(String address, int status) {
+            public void onSearchComplete(String address, List<BluetoothGattService> services,
+                                         int status) {
                 if (DBG) Log.d(TAG, "onSearchComplete() = Device=" + address + " Status=" + status);
                 if (!address.equals(mDevice.getAddress())) {
                     return;
                 }
+
+                for (BluetoothGattService s : services) {
+                    //services we receive don't have device set properly.
+                    s.setDevice(mDevice);
+                }
+
+                mServices.addAll(services);
+
+                // Fix references to included services, as they doesn't point to right objects.
+                for (BluetoothGattService fixedService : mServices) {
+                    ArrayList<BluetoothGattService> includedServices =
+                        new ArrayList(fixedService.getIncludedServices());
+                    fixedService.getIncludedServices().clear();
+
+                    for(BluetoothGattService brokenRef : includedServices) {
+                        BluetoothGattService includedService = getService(mDevice,
+                            brokenRef.getUuid(), brokenRef.getInstanceId(), brokenRef.getType());
+                        if (includedService != null) {
+                            fixedService.addIncludedService(includedService);
+                        } else {
+                            Log.e(TAG, "Broken GATT database: can't find included service.");
+                        }
+                    }
+                }
+
                 try {
                     mCallback.onServicesDiscovered(BluetoothGatt.this, status);
                 } catch (Exception ex) {
@@ -312,11 +246,12 @@
              * Updates the internal value.
              * @hide
              */
-            public void onCharacteristicRead(String address, int status, int srvcType,
-                             int srvcInstId, ParcelUuid srvcUuid,
-                             int charInstId, ParcelUuid charUuid, byte[] value) {
+            public void onCharacteristicRead(String address, int status, int handle, byte[] value) {
                 if (VDBG) Log.d(TAG, "onCharacteristicRead() - Device=" + address
-                            + " UUID=" + charUuid + " Status=" + status);
+                            + " handle=" + handle + " Status=" + status);
+
+                 Log.w(TAG, "onCharacteristicRead() - Device=" + address
+                            + " handle=" + handle + " Status=" + status);
 
                 if (!address.equals(mDevice.getAddress())) {
                     return;
@@ -331,9 +266,7 @@
                   && mAuthRetry == false) {
                     try {
                         mAuthRetry = true;
-                        mService.readCharacteristic(mClientIf, address,
-                            srvcType, srvcInstId, srvcUuid,
-                            charInstId, charUuid, AUTHENTICATION_MITM);
+                        mService.readCharacteristic(mClientIf, address, handle, AUTHENTICATION_MITM);
                         return;
                     } catch (RemoteException e) {
                         Log.e(TAG,"",e);
@@ -342,13 +275,11 @@
 
                 mAuthRetry = false;
 
-                BluetoothGattService service = getService(mDevice, srvcUuid.getUuid(),
-                                                          srvcInstId, srvcType);
-                if (service == null) return;
-
-                BluetoothGattCharacteristic characteristic = service.getCharacteristic(
-                        charUuid.getUuid(), charInstId);
-                if (characteristic == null) return;
+                BluetoothGattCharacteristic characteristic = getCharacteristicById(mDevice, handle);
+                if (characteristic == null) {
+                    Log.w(TAG, "onCharacteristicRead() failed to find characteristic!");
+                    return;
+                }
 
                 if (status == 0) characteristic.setValue(value);
 
@@ -364,11 +295,9 @@
              * Let the app know how we did...
              * @hide
              */
-            public void onCharacteristicWrite(String address, int status, int srvcType,
-                             int srvcInstId, ParcelUuid srvcUuid,
-                             int charInstId, ParcelUuid charUuid) {
+            public void onCharacteristicWrite(String address, int status, int handle) {
                 if (VDBG) Log.d(TAG, "onCharacteristicWrite() - Device=" + address
-                            + " UUID=" + charUuid + " Status=" + status);
+                            + " handle=" + handle + " Status=" + status);
 
                 if (!address.equals(mDevice.getAddress())) {
                     return;
@@ -378,12 +307,7 @@
                     mDeviceBusy = false;
                 }
 
-                BluetoothGattService service = getService(mDevice, srvcUuid.getUuid(),
-                                                          srvcInstId, srvcType);
-                if (service == null) return;
-
-                BluetoothGattCharacteristic characteristic = service.getCharacteristic(
-                        charUuid.getUuid(), charInstId);
+                BluetoothGattCharacteristic characteristic = getCharacteristicById(mDevice, handle);
                 if (characteristic == null) return;
 
                 if ((status == GATT_INSUFFICIENT_AUTHENTICATION
@@ -391,8 +315,7 @@
                   && mAuthRetry == false) {
                     try {
                         mAuthRetry = true;
-                        mService.writeCharacteristic(mClientIf, address,
-                            srvcType, srvcInstId, srvcUuid, charInstId, charUuid,
+                        mService.writeCharacteristic(mClientIf, address, handle,
                             characteristic.getWriteType(), AUTHENTICATION_MITM,
                             characteristic.getValue());
                         return;
@@ -415,21 +338,14 @@
              * Updates the internal value.
              * @hide
              */
-            public void onNotify(String address, int srvcType,
-                             int srvcInstId, ParcelUuid srvcUuid,
-                             int charInstId, ParcelUuid charUuid,
-                             byte[] value) {
-                if (VDBG) Log.d(TAG, "onNotify() - Device=" + address + " UUID=" + charUuid);
+            public void onNotify(String address, int handle, byte[] value) {
+                if (VDBG) Log.d(TAG, "onNotify() - Device=" + address + " handle=" + handle);
 
                 if (!address.equals(mDevice.getAddress())) {
                     return;
                 }
-                BluetoothGattService service = getService(mDevice, srvcUuid.getUuid(),
-                                                          srvcInstId, srvcType);
-                if (service == null) return;
 
-                BluetoothGattCharacteristic characteristic = service.getCharacteristic(
-                        charUuid.getUuid(), charInstId);
+                BluetoothGattCharacteristic characteristic = getCharacteristicById(mDevice, handle);
                 if (characteristic == null) return;
 
                 characteristic.setValue(value);
@@ -445,12 +361,8 @@
              * Descriptor has been read.
              * @hide
              */
-            public void onDescriptorRead(String address, int status, int srvcType,
-                             int srvcInstId, ParcelUuid srvcUuid,
-                             int charInstId, ParcelUuid charUuid,
-                             int descrInstId, ParcelUuid descrUuid,
-                             byte[] value) {
-                if (VDBG) Log.d(TAG, "onDescriptorRead() - Device=" + address + " UUID=" + charUuid);
+            public void onDescriptorRead(String address, int status, int handle, byte[] value) {
+                if (VDBG) Log.d(TAG, "onDescriptorRead() - Device=" + address + " handle=" + handle);
 
                 if (!address.equals(mDevice.getAddress())) {
                     return;
@@ -460,16 +372,7 @@
                     mDeviceBusy = false;
                 }
 
-                BluetoothGattService service = getService(mDevice, srvcUuid.getUuid(),
-                                                          srvcInstId, srvcType);
-                if (service == null) return;
-
-                BluetoothGattCharacteristic characteristic = service.getCharacteristic(
-                        charUuid.getUuid(), charInstId);
-                if (characteristic == null) return;
-
-                BluetoothGattDescriptor descriptor = characteristic.getDescriptor(
-                        descrUuid.getUuid(), descrInstId);
+                BluetoothGattDescriptor descriptor = getDescriptorById(mDevice, handle);
                 if (descriptor == null) return;
 
                 if (status == 0) descriptor.setValue(value);
@@ -479,9 +382,7 @@
                   && mAuthRetry == false) {
                     try {
                         mAuthRetry = true;
-                        mService.readDescriptor(mClientIf, address,
-                            srvcType, srvcInstId, srvcUuid, charInstId, charUuid,
-                            descrInstId, descrUuid, AUTHENTICATION_MITM);
+                        mService.readDescriptor(mClientIf, address, handle, AUTHENTICATION_MITM);
                         return;
                     } catch (RemoteException e) {
                         Log.e(TAG,"",e);
@@ -501,11 +402,8 @@
              * Descriptor write operation complete.
              * @hide
              */
-            public void onDescriptorWrite(String address, int status, int srvcType,
-                             int srvcInstId, ParcelUuid srvcUuid,
-                             int charInstId, ParcelUuid charUuid,
-                             int descrInstId, ParcelUuid descrUuid) {
-                if (VDBG) Log.d(TAG, "onDescriptorWrite() - Device=" + address + " UUID=" + charUuid);
+            public void onDescriptorWrite(String address, int status, int handle) {
+                if (VDBG) Log.d(TAG, "onDescriptorWrite() - Device=" + address + " handle=" + handle);
 
                 if (!address.equals(mDevice.getAddress())) {
                     return;
@@ -515,16 +413,7 @@
                     mDeviceBusy = false;
                 }
 
-                BluetoothGattService service = getService(mDevice, srvcUuid.getUuid(),
-                                                          srvcInstId, srvcType);
-                if (service == null) return;
-
-                BluetoothGattCharacteristic characteristic = service.getCharacteristic(
-                        charUuid.getUuid(), charInstId);
-                if (characteristic == null) return;
-
-                BluetoothGattDescriptor descriptor = characteristic.getDescriptor(
-                        descrUuid.getUuid(), descrInstId);
+                BluetoothGattDescriptor descriptor = getDescriptorById(mDevice, handle);
                 if (descriptor == null) return;
 
                 if ((status == GATT_INSUFFICIENT_AUTHENTICATION
@@ -532,9 +421,8 @@
                   && mAuthRetry == false) {
                     try {
                         mAuthRetry = true;
-                        mService.writeDescriptor(mClientIf, address,
-                            srvcType, srvcInstId, srvcUuid, charInstId, charUuid,
-                            descrInstId, descrUuid, characteristic.getWriteType(),
+                        mService.writeDescriptor(mClientIf, address, handle,
+                            descriptor.getCharacteristic().getWriteType(),
                             AUTHENTICATION_MITM, descriptor.getValue());
                         return;
                     } catch (RemoteException e) {
@@ -651,6 +539,37 @@
 
 
     /**
+     * Returns a characteristic with id equal to instanceId.
+     * @hide
+     */
+    /*package*/ BluetoothGattCharacteristic getCharacteristicById(BluetoothDevice device, int instanceId) {
+        for(BluetoothGattService svc : mServices) {
+            for(BluetoothGattCharacteristic charac : svc.getCharacteristics()) {
+                Log.w(TAG, "getCharacteristicById() comparing " + charac.getInstanceId() + " and " + instanceId);
+                if (charac.getInstanceId() == instanceId)
+                    return charac;
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Returns a descriptor with id equal to instanceId.
+     * @hide
+     */
+    /*package*/ BluetoothGattDescriptor getDescriptorById(BluetoothDevice device, int instanceId) {
+        for(BluetoothGattService svc : mServices) {
+            for(BluetoothGattCharacteristic charac : svc.getCharacteristics()) {
+                for(BluetoothGattDescriptor desc : charac.getDescriptors()) {
+                    if (desc.getInstanceId() == instanceId)
+                        return desc;
+                }
+            }
+        }
+        return null;
+    }
+
+    /**
      * Register an application callback to start using GATT.
      *
      * <p>This is an asynchronous call. The callback {@link BluetoothGattCallback#onAppRegistered}
@@ -898,9 +817,7 @@
 
         try {
             mService.readCharacteristic(mClientIf, device.getAddress(),
-                service.getType(), service.getInstanceId(),
-                new ParcelUuid(service.getUuid()), characteristic.getInstanceId(),
-                new ParcelUuid(characteristic.getUuid()), AUTHENTICATION_NONE);
+                characteristic.getInstanceId(), AUTHENTICATION_NONE);
         } catch (RemoteException e) {
             Log.e(TAG,"",e);
             mDeviceBusy = false;
@@ -943,11 +860,8 @@
 
         try {
             mService.writeCharacteristic(mClientIf, device.getAddress(),
-                service.getType(), service.getInstanceId(),
-                new ParcelUuid(service.getUuid()), characteristic.getInstanceId(),
-                new ParcelUuid(characteristic.getUuid()),
-                characteristic.getWriteType(), AUTHENTICATION_NONE,
-                characteristic.getValue());
+                characteristic.getInstanceId(), characteristic.getWriteType(),
+                AUTHENTICATION_NONE, characteristic.getValue());
         } catch (RemoteException e) {
             Log.e(TAG,"",e);
             mDeviceBusy = false;
@@ -988,11 +902,8 @@
         }
 
         try {
-            mService.readDescriptor(mClientIf, device.getAddress(), service.getType(),
-                service.getInstanceId(), new ParcelUuid(service.getUuid()),
-                characteristic.getInstanceId(), new ParcelUuid(characteristic.getUuid()),
-                descriptor.getInstanceId(), new ParcelUuid(descriptor.getUuid()),
-                AUTHENTICATION_NONE);
+            mService.readDescriptor(mClientIf, device.getAddress(),
+                descriptor.getInstanceId(), AUTHENTICATION_NONE);
         } catch (RemoteException e) {
             Log.e(TAG,"",e);
             mDeviceBusy = false;
@@ -1032,12 +943,8 @@
         }
 
         try {
-            mService.writeDescriptor(mClientIf, device.getAddress(), service.getType(),
-                service.getInstanceId(), new ParcelUuid(service.getUuid()),
-                characteristic.getInstanceId(), new ParcelUuid(characteristic.getUuid()),
-                descriptor.getInstanceId(), new ParcelUuid(descriptor.getUuid()),
-                characteristic.getWriteType(), AUTHENTICATION_NONE,
-                descriptor.getValue());
+            mService.writeDescriptor(mClientIf, device.getAddress(), descriptor.getInstanceId(),
+                characteristic.getWriteType(), AUTHENTICATION_NONE, descriptor.getValue());
         } catch (RemoteException e) {
             Log.e(TAG,"",e);
             mDeviceBusy = false;
@@ -1168,10 +1075,7 @@
 
         try {
             mService.registerForNotification(mClientIf, device.getAddress(),
-                service.getType(), service.getInstanceId(),
-                new ParcelUuid(service.getUuid()), characteristic.getInstanceId(),
-                new ParcelUuid(characteristic.getUuid()),
-                enable);
+                characteristic.getInstanceId(), enable);
         } catch (RemoteException e) {
             Log.e(TAG,"",e);
             return false;
diff --git a/core/java/android/bluetooth/BluetoothGattCallbackWrapper.java b/core/java/android/bluetooth/BluetoothGattCallbackWrapper.java
index 01778b3..17e533a 100644
--- a/core/java/android/bluetooth/BluetoothGattCallbackWrapper.java
+++ b/core/java/android/bluetooth/BluetoothGattCallbackWrapper.java
@@ -18,6 +18,7 @@
 
 import android.bluetooth.le.AdvertiseSettings;
 import android.bluetooth.le.ScanResult;
+import android.bluetooth.BluetoothGattService;
 import android.os.ParcelUuid;
 import android.os.RemoteException;
 
@@ -48,41 +49,17 @@
     }
 
     @Override
-    public void onGetService(String address, int srvcType, int srvcInstId, ParcelUuid srvcUuid)
+    public void onSearchComplete(String address, List<BluetoothGattService> services,
+            int status) throws RemoteException {
+    }
+
+    @Override
+    public void onCharacteristicRead(String address, int status, int handle, byte[] value)
             throws RemoteException {
     }
 
     @Override
-    public void onGetIncludedService(String address, int srvcType, int srvcInstId,
-            ParcelUuid srvcUuid, int inclSrvcType, int inclSrvcInstId, ParcelUuid inclSrvcUuid)
-            throws RemoteException {
-    }
-
-    @Override
-    public void onGetCharacteristic(String address, int srvcType, int srvcInstId,
-            ParcelUuid srvcUuid, int charInstId, ParcelUuid charUuid, int charProps)
-            throws RemoteException {
-    }
-
-    @Override
-    public void onGetDescriptor(String address, int srvcType, int srvcInstId, ParcelUuid srvcUuid,
-            int charInstId, ParcelUuid charUuid, int descrInstId, ParcelUuid descrUuid)
-            throws RemoteException {
-    }
-
-    @Override
-    public void onSearchComplete(String address, int status) throws RemoteException {
-    }
-
-    @Override
-    public void onCharacteristicRead(String address, int status, int srvcType, int srvcInstId,
-            ParcelUuid srvcUuid, int charInstId, ParcelUuid charUuid, byte[] value)
-            throws RemoteException {
-    }
-
-    @Override
-    public void onCharacteristicWrite(String address, int status, int srvcType, int srvcInstId,
-            ParcelUuid srvcUuid, int charInstId, ParcelUuid charUuid) throws RemoteException {
+    public void onCharacteristicWrite(String address, int status, int handle) throws RemoteException {
     }
 
     @Override
@@ -90,20 +67,15 @@
     }
 
     @Override
-    public void onDescriptorRead(String address, int status, int srvcType, int srvcInstId,
-            ParcelUuid srvcUuid, int charInstId, ParcelUuid charUuid, int descrInstId,
-            ParcelUuid descrUuid, byte[] value) throws RemoteException {
+    public void onDescriptorRead(String address, int status, int handle, byte[] value) throws RemoteException {
     }
 
     @Override
-    public void onDescriptorWrite(String address, int status, int srvcType, int srvcInstId,
-            ParcelUuid srvcUuid, int charInstId, ParcelUuid charUuid, int descrInstId,
-            ParcelUuid descrUuid) throws RemoteException {
+    public void onDescriptorWrite(String address, int status, int handle) throws RemoteException {
     }
 
     @Override
-    public void onNotify(String address, int srvcType, int srvcInstId, ParcelUuid srvcUuid,
-            int charInstId, ParcelUuid charUuid, byte[] value) throws RemoteException {
+    public void onNotify(String address, int handle, byte[] value) throws RemoteException {
     }
 
     @Override
diff --git a/core/java/android/bluetooth/BluetoothGattCharacteristic.aidl b/core/java/android/bluetooth/BluetoothGattCharacteristic.aidl
new file mode 100644
index 0000000..bbb8623
--- /dev/null
+++ b/core/java/android/bluetooth/BluetoothGattCharacteristic.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2016 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.bluetooth;
+
+parcelable BluetoothGattCharacteristic;
diff --git a/core/java/android/bluetooth/BluetoothGattCharacteristic.java b/core/java/android/bluetooth/BluetoothGattCharacteristic.java
index 7cdcc2c..7d698b3 100644
--- a/core/java/android/bluetooth/BluetoothGattCharacteristic.java
+++ b/core/java/android/bluetooth/BluetoothGattCharacteristic.java
@@ -15,6 +15,9 @@
  */
 package android.bluetooth;
 
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.os.ParcelUuid;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.UUID;
@@ -26,7 +29,7 @@
  * {@link BluetoothGattService}. The characteristic contains a value as well as
  * additional information and optional GATT descriptors, {@link BluetoothGattDescriptor}.
  */
-public class BluetoothGattCharacteristic {
+public class BluetoothGattCharacteristic implements Parcelable {
 
     /**
      * Characteristic proprty: Characteristic is broadcastable.
@@ -242,6 +245,15 @@
         initCharacteristic(service, uuid, instanceId, properties, permissions);
     }
 
+    /**
+     * Create a new BluetoothGattCharacteristic
+     * @hide
+     */
+    public BluetoothGattCharacteristic(UUID uuid, int instanceId,
+                                       int properties, int permissions) {
+        initCharacteristic(null, uuid, instanceId, properties, permissions);
+    }
+
     private void initCharacteristic(BluetoothGattService service,
                                     UUID uuid, int instanceId,
                                     int properties, int permissions) {
@@ -261,6 +273,50 @@
     }
 
     /**
+     * @hide
+     */
+    public int describeContents() {
+        return 0;
+    }
+
+    public void writeToParcel(Parcel out, int flags) {
+        out.writeParcelable(new ParcelUuid(mUuid), 0);
+        out.writeInt(mInstance);
+        out.writeInt(mProperties);
+        out.writeInt(mPermissions);
+        out.writeTypedList(mDescriptors);
+    }
+
+    public static final Parcelable.Creator<BluetoothGattCharacteristic> CREATOR
+            = new Parcelable.Creator<BluetoothGattCharacteristic>() {
+        public BluetoothGattCharacteristic createFromParcel(Parcel in) {
+            return new BluetoothGattCharacteristic(in);
+        }
+
+        public BluetoothGattCharacteristic[] newArray(int size) {
+            return new BluetoothGattCharacteristic[size];
+        }
+    };
+
+    private BluetoothGattCharacteristic(Parcel in) {
+        mUuid = ((ParcelUuid)in.readParcelable(null)).getUuid();
+        mInstance = in.readInt();
+        mProperties = in.readInt();
+        mPermissions = in.readInt();
+
+        mDescriptors = new ArrayList<BluetoothGattDescriptor>();
+
+        ArrayList<BluetoothGattDescriptor> descs =
+                in.createTypedArrayList(BluetoothGattDescriptor.CREATOR);
+        if (descs != null) {
+            for (BluetoothGattDescriptor desc: descs) {
+                desc.setCharacteristic(this);
+                mDescriptors.add(desc);
+            }
+        }
+    }
+
+    /**
      * Returns the deisred key size.
      * @hide
      */
diff --git a/core/java/android/bluetooth/BluetoothGattDescriptor.aidl b/core/java/android/bluetooth/BluetoothGattDescriptor.aidl
new file mode 100644
index 0000000..4393273
--- /dev/null
+++ b/core/java/android/bluetooth/BluetoothGattDescriptor.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2016 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.bluetooth;
+
+parcelable BluetoothGattDescriptor;
diff --git a/core/java/android/bluetooth/BluetoothGattDescriptor.java b/core/java/android/bluetooth/BluetoothGattDescriptor.java
index 5f525dc..28317c4 100644
--- a/core/java/android/bluetooth/BluetoothGattDescriptor.java
+++ b/core/java/android/bluetooth/BluetoothGattDescriptor.java
@@ -16,6 +16,9 @@
 
 package android.bluetooth;
 
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.os.ParcelUuid;
 import java.util.UUID;
 
 /**
@@ -25,7 +28,7 @@
  * characteristic, {@link BluetoothGattCharacteristic}. They can be used to describe
  * the characteristic's features or to control certain behaviours of the characteristic.
  */
-public class BluetoothGattDescriptor {
+public class BluetoothGattDescriptor implements Parcelable {
 
     /**
      * Value used to enable notification for a client configuration descriptor
@@ -138,6 +141,13 @@
         initDescriptor(characteristic, uuid, instance, permissions);
     }
 
+    /**
+     * @hide
+     */
+    public BluetoothGattDescriptor(UUID uuid, int instance, int permissions) {
+        initDescriptor(null, uuid, instance, permissions);
+    }
+
     private void initDescriptor(BluetoothGattCharacteristic characteristic, UUID uuid,
                                 int instance, int permissions) {
         mCharacteristic = characteristic;
@@ -147,6 +157,36 @@
     }
 
     /**
+     * @hide
+     */
+    public int describeContents() {
+        return 0;
+    }
+
+    public void writeToParcel(Parcel out, int flags) {
+        out.writeParcelable(new ParcelUuid(mUuid), 0);
+        out.writeInt(mInstance);
+        out.writeInt(mPermissions);
+    }
+
+    public static final Parcelable.Creator<BluetoothGattDescriptor> CREATOR
+            = new Parcelable.Creator<BluetoothGattDescriptor>() {
+        public BluetoothGattDescriptor createFromParcel(Parcel in) {
+            return new BluetoothGattDescriptor(in);
+        }
+
+        public BluetoothGattDescriptor[] newArray(int size) {
+            return new BluetoothGattDescriptor[size];
+        }
+    };
+
+    private BluetoothGattDescriptor(Parcel in) {
+        mUuid = ((ParcelUuid)in.readParcelable(null)).getUuid();
+        mInstance = in.readInt();
+        mPermissions = in.readInt();
+    }
+
+    /**
      * Returns the characteristic this descriptor belongs to.
      * @return The characteristic.
      */
diff --git a/core/java/android/bluetooth/BluetoothGattIncludedService.aidl b/core/java/android/bluetooth/BluetoothGattIncludedService.aidl
new file mode 100644
index 0000000..1ef427e
--- /dev/null
+++ b/core/java/android/bluetooth/BluetoothGattIncludedService.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2016 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.bluetooth;
+
+parcelable BluetoothGattIncludedService;
diff --git a/core/java/android/bluetooth/BluetoothGattIncludedService.java b/core/java/android/bluetooth/BluetoothGattIncludedService.java
new file mode 100644
index 0000000..155dc57
--- /dev/null
+++ b/core/java/android/bluetooth/BluetoothGattIncludedService.java
@@ -0,0 +1,110 @@
+/*
+ * Copyright (C) 2016 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.bluetooth;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.os.ParcelUuid;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.UUID;
+
+/**
+ * Represents a Bluetooth GATT Included Service
+ * @hide
+ */
+public class BluetoothGattIncludedService implements Parcelable {
+
+    /**
+     * The UUID of this service.
+     */
+    protected UUID mUuid;
+
+    /**
+     * Instance ID for this service.
+     */
+    protected int mInstanceId;
+
+    /**
+     * Service type (Primary/Secondary).
+     */
+    protected int mServiceType;
+
+    /**
+     * Create a new BluetoothGattIncludedService
+     */
+    public BluetoothGattIncludedService(UUID uuid, int instanceId, int serviceType) {
+        mUuid = uuid;
+        mInstanceId = instanceId;
+        mServiceType = serviceType;
+    }
+
+    public int describeContents() {
+        return 0;
+    }
+
+    public void writeToParcel(Parcel out, int flags) {
+        out.writeParcelable(new ParcelUuid(mUuid), 0);
+        out.writeInt(mInstanceId);
+        out.writeInt(mServiceType);
+     }
+
+    public static final Parcelable.Creator<BluetoothGattIncludedService> CREATOR
+            = new Parcelable.Creator<BluetoothGattIncludedService>() {
+        public BluetoothGattIncludedService createFromParcel(Parcel in) {
+            return new BluetoothGattIncludedService(in);
+        }
+
+        public BluetoothGattIncludedService[] newArray(int size) {
+            return new BluetoothGattIncludedService[size];
+        }
+    };
+
+    private BluetoothGattIncludedService(Parcel in) {
+        mUuid = ((ParcelUuid)in.readParcelable(null)).getUuid();
+        mInstanceId = in.readInt();
+        mServiceType = in.readInt();
+    }
+
+    /**
+     * Returns the UUID of this service
+     *
+     * @return UUID of this service
+     */
+    public UUID getUuid() {
+        return mUuid;
+    }
+
+    /**
+     * Returns the instance ID for this service
+     *
+     * <p>If a remote device offers multiple services with the same UUID
+     * (ex. multiple battery services for different batteries), the instance
+     * ID is used to distuinguish services.
+     *
+     * @return Instance ID of this service
+     */
+    public int getInstanceId() {
+        return mInstanceId;
+    }
+
+    /**
+     * Get the type of this service (primary/secondary)
+     */
+    public int getType() {
+        return mServiceType;
+    }
+}
diff --git a/core/java/android/bluetooth/BluetoothGattService.aidl b/core/java/android/bluetooth/BluetoothGattService.aidl
new file mode 100644
index 0000000..84314d2
--- /dev/null
+++ b/core/java/android/bluetooth/BluetoothGattService.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2016 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.bluetooth;
+
+parcelable BluetoothGattService;
diff --git a/core/java/android/bluetooth/BluetoothGattService.java b/core/java/android/bluetooth/BluetoothGattService.java
index 52bc0f7..a4e1dc0 100644
--- a/core/java/android/bluetooth/BluetoothGattService.java
+++ b/core/java/android/bluetooth/BluetoothGattService.java
@@ -15,6 +15,9 @@
  */
 package android.bluetooth;
 
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.os.ParcelUuid;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.UUID;
@@ -25,7 +28,7 @@
  * <p> Gatt Service contains a collection of {@link BluetoothGattCharacteristic},
  * as well as referenced services.
  */
-public class BluetoothGattService {
+public class BluetoothGattService implements Parcelable {
 
     /**
      * Primary service
@@ -117,6 +120,81 @@
     }
 
     /**
+     * Create a new BluetoothGattService
+     * @hide
+     */
+    public BluetoothGattService(UUID uuid, int instanceId, int serviceType) {
+        mDevice = null;
+        mUuid = uuid;
+        mInstanceId = instanceId;
+        mServiceType = serviceType;
+        mCharacteristics = new ArrayList<BluetoothGattCharacteristic>();
+        mIncludedServices = new ArrayList<BluetoothGattService>();
+    }
+
+    /**
+     * @hide
+     */
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel out, int flags) {
+        out.writeParcelable(new ParcelUuid(mUuid), 0);
+        out.writeInt(mInstanceId);
+        out.writeInt(mServiceType);
+        out.writeTypedList(mCharacteristics);
+
+        ArrayList<BluetoothGattIncludedService> includedServices =
+                new ArrayList<BluetoothGattIncludedService>(mIncludedServices.size());
+        for(BluetoothGattService s : mIncludedServices) {
+            includedServices.add(new BluetoothGattIncludedService(s.getUuid(),
+                                                                  s.getInstanceId(), s.getType()));
+        }
+        out.writeTypedList(includedServices);
+     }
+
+    public static final Parcelable.Creator<BluetoothGattService> CREATOR
+            = new Parcelable.Creator<BluetoothGattService>() {
+        public BluetoothGattService createFromParcel(Parcel in) {
+            return new BluetoothGattService(in);
+        }
+
+        public BluetoothGattService[] newArray(int size) {
+            return new BluetoothGattService[size];
+        }
+    };
+
+    private BluetoothGattService(Parcel in) {
+        mUuid = ((ParcelUuid)in.readParcelable(null)).getUuid();
+        mInstanceId = in.readInt();
+        mServiceType = in.readInt();
+
+        mCharacteristics = new ArrayList<BluetoothGattCharacteristic>();
+
+        ArrayList<BluetoothGattCharacteristic> chrcs =
+                in.createTypedArrayList(BluetoothGattCharacteristic.CREATOR);
+        if (chrcs != null) {
+            for (BluetoothGattCharacteristic chrc : chrcs) {
+                chrc.setService(this);
+                mCharacteristics.add(chrc);
+            }
+        }
+
+        mIncludedServices = new ArrayList<BluetoothGattService>();
+
+        ArrayList<BluetoothGattIncludedService> inclSvcs =
+                in.createTypedArrayList(BluetoothGattIncludedService.CREATOR);
+        if (chrcs != null) {
+            for (BluetoothGattIncludedService isvc : inclSvcs) {
+                mIncludedServices.add(new BluetoothGattService(null, isvc.getUuid(),
+                                                            isvc.getInstanceId(), isvc.getType()));
+            }
+        }
+    }
+
+    /**
      * Returns the device associated with this service.
      * @hide
      */
@@ -125,6 +203,14 @@
     }
 
     /**
+     * Returns the device associated with this service.
+     * @hide
+     */
+    /*package*/ void setDevice(BluetoothDevice device) {
+        this.mDevice = device;
+    }
+
+    /**
      * Add an included service to this service.
      * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
      *
@@ -192,7 +278,7 @@
      * Add an included service to the internal map.
      * @hide
      */
-    /*package*/ void addIncludedService(BluetoothGattService includedService) {
+    public void addIncludedService(BluetoothGattService includedService) {
         mIncludedServices.add(includedService);
     }
 
diff --git a/core/java/android/bluetooth/IBluetoothGatt.aidl b/core/java/android/bluetooth/IBluetoothGatt.aidl
index 6b5f77f..adb07df 100644
--- a/core/java/android/bluetooth/IBluetoothGatt.aidl
+++ b/core/java/android/bluetooth/IBluetoothGatt.aidl
@@ -51,28 +51,13 @@
     void clientDisconnect(in int clientIf, in String address);
     void refreshDevice(in int clientIf, in String address);
     void discoverServices(in int clientIf, in String address);
-    void readCharacteristic(in int clientIf, in String address, in int srvcType,
-                            in int srvcInstanceId, in ParcelUuid srvcId,
-                            in int charInstanceId, in ParcelUuid charId,
-                            in int authReq);
-    void writeCharacteristic(in int clientIf, in String address, in int srvcType,
-                            in int srvcInstanceId, in ParcelUuid srvcId,
-                            in int charInstanceId, in ParcelUuid charId,
+    void readCharacteristic(in int clientIf, in String address, in int handle, in int authReq);
+    void writeCharacteristic(in int clientIf, in String address, in int handle,
                             in int writeType, in int authReq, in byte[] value);
-    void readDescriptor(in int clientIf, in String address, in int srvcType,
-                            in int srvcInstanceId, in ParcelUuid srvcId,
-                            in int charInstanceId, in ParcelUuid charId,
-                            in int descrInstanceId, in ParcelUuid descrUuid,
-                            in int authReq);
-    void writeDescriptor(in int clientIf, in String address, in int srvcType,
-                            in int srvcInstanceId, in ParcelUuid srvcId,
-                            in int charInstanceId, in ParcelUuid charId,
-                            in int descrInstanceId, in ParcelUuid descrId,
+    void readDescriptor(in int clientIf, in String address, in int handle, in int authReq);
+    void writeDescriptor(in int clientIf, in String address, in int handle,
                             in int writeType, in int authReq, in byte[] value);
-    void registerForNotification(in int clientIf, in String address, in int srvcType,
-                            in int srvcInstanceId, in ParcelUuid srvcId,
-                            in int charInstanceId, in ParcelUuid charId,
-                            in boolean enable);
+    void registerForNotification(in int clientIf, in String address, in int handle, in boolean enable);
     void beginReliableWrite(in int clientIf, in String address);
     void endReliableWrite(in int clientIf, in String address, in boolean execute);
     void readRemoteRssi(in int clientIf, in String address);
diff --git a/core/java/android/bluetooth/IBluetoothGattCallback.aidl b/core/java/android/bluetooth/IBluetoothGattCallback.aidl
index cbba9f0..7163c37 100644
--- a/core/java/android/bluetooth/IBluetoothGattCallback.aidl
+++ b/core/java/android/bluetooth/IBluetoothGattCallback.aidl
@@ -16,6 +16,7 @@
 package android.bluetooth;
 
 import android.os.ParcelUuid;
+import android.bluetooth.BluetoothGattService;
 import android.bluetooth.le.AdvertiseSettings;
 import android.bluetooth.le.ScanResult;
 
@@ -29,41 +30,13 @@
                                  in boolean connected, in String address);
     void onScanResult(in ScanResult scanResult);
     void onBatchScanResults(in List<ScanResult> batchResults);
-    void onGetService(in String address, in int srvcType, in int srvcInstId,
-                      in ParcelUuid srvcUuid);
-    void onGetIncludedService(in String address, in int srvcType, in int srvcInstId,
-                              in ParcelUuid srvcUuid, in int inclSrvcType,
-                              in int inclSrvcInstId, in ParcelUuid inclSrvcUuid);
-    void onGetCharacteristic(in String address, in int srvcType,
-                             in int srvcInstId, in ParcelUuid srvcUuid,
-                             in int charInstId, in ParcelUuid charUuid,
-                             in int charProps);
-    void onGetDescriptor(in String address, in int srvcType,
-                             in int srvcInstId, in ParcelUuid srvcUuid,
-                             in int charInstId, in ParcelUuid charUuid,
-                             in int descrInstId, in ParcelUuid descrUuid);
-    void onSearchComplete(in String address, in int status);
-    void onCharacteristicRead(in String address, in int status, in int srvcType,
-                             in int srvcInstId, in ParcelUuid srvcUuid,
-                             in int charInstId, in ParcelUuid charUuid,
-                             in byte[] value);
-    void onCharacteristicWrite(in String address, in int status, in int srvcType,
-                             in int srvcInstId, in ParcelUuid srvcUuid,
-                             in int charInstId, in ParcelUuid charUuid);
+    void onSearchComplete(in String address, in List<BluetoothGattService> services, in int status);
+    void onCharacteristicRead(in String address, in int status, in int handle, in byte[] value);
+    void onCharacteristicWrite(in String address, in int status, in int handle);
     void onExecuteWrite(in String address, in int status);
-    void onDescriptorRead(in String address, in int status, in int srvcType,
-                             in int srvcInstId, in ParcelUuid srvcUuid,
-                             in int charInstId, in ParcelUuid charUuid,
-                             in int descrInstId, in ParcelUuid descrUuid,
-                             in byte[] value);
-    void onDescriptorWrite(in String address, in int status, in int srvcType,
-                             in int srvcInstId, in ParcelUuid srvcUuid,
-                             in int charInstId, in ParcelUuid charUuid,
-                             in int descrInstId, in ParcelUuid descrUuid);
-    void onNotify(in String address, in int srvcType,
-                             in int srvcInstId, in ParcelUuid srvcUuid,
-                             in int charInstId, in ParcelUuid charUuid,
-                             in byte[] value);
+    void onDescriptorRead(in String address, in int status, in int handle, in byte[] value);
+    void onDescriptorWrite(in String address, in int status, in int handle);
+    void onNotify(in String address, in int handle, in byte[] value);
     void onReadRemoteRssi(in String address, in int rssi, in int status);
     void onMultiAdvertiseCallback(in int status, boolean isStart,
                                   in AdvertiseSettings advertiseSettings);
diff --git a/core/java/android/hardware/usb/UsbManager.java b/core/java/android/hardware/usb/UsbManager.java
index 3792e5c..f9a7d19 100644
--- a/core/java/android/hardware/usb/UsbManager.java
+++ b/core/java/android/hardware/usb/UsbManager.java
@@ -54,6 +54,8 @@
      * This is a sticky broadcast for clients that includes USB connected/disconnected state,
      * <ul>
      * <li> {@link #USB_CONNECTED} boolean indicating whether USB is connected or disconnected.
+     * <li> {@link #USB_HOST_CONNECTED} boolean indicating whether USB is connected or
+     *     disconnected as host.
      * <li> {@link #USB_CONFIGURED} boolean indicating whether USB is configured.
      * currently zero if not configured, one for configured.
      * <li> {@link #USB_FUNCTION_ADB} boolean extra indicating whether the
@@ -152,6 +154,14 @@
     public static final String USB_CONNECTED = "connected";
 
     /**
+     * Boolean extra indicating whether USB is connected or disconnected as host.
+     * Used in extras for the {@link #ACTION_USB_STATE} broadcast.
+     *
+     * {@hide}
+     */
+    public static final String USB_HOST_CONNECTED = "host_connected";
+
+    /**
      * Boolean extra indicating whether USB is configured.
      * Used in extras for the {@link #ACTION_USB_STATE} broadcast.
      *
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index 5f1043b..f0673ff 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -2744,7 +2744,9 @@
         if (networkCallback == null) {
             throw new IllegalArgumentException("null NetworkCallback");
         }
-        if (need == null) throw new IllegalArgumentException("null NetworkCapabilities");
+        if (need == null && action != REQUEST) {
+            throw new IllegalArgumentException("null NetworkCapabilities");
+        }
         try {
             incCallbackHandlerRefCount();
             synchronized(sNetworkCallback) {
@@ -2767,7 +2769,7 @@
     }
 
     /**
-     * Helper function to requests a network with a particular legacy type.
+     * Helper function to request a network with a particular legacy type.
      *
      * This is temporarily public @hide so it can be called by system code that uses the
      * NetworkRequest API to request networks but relies on CONNECTIVITY_ACTION broadcasts for
@@ -3011,6 +3013,28 @@
     }
 
     /**
+     * Registers to receive notifications about whichever network currently satisfies the
+     * system default {@link NetworkRequest}.  The callbacks will continue to be called until
+     * either the application exits or {@link #unregisterNetworkCallback} is called
+     * <p>This method requires the caller to hold the permission
+     * {@link android.Manifest.permission#ACCESS_NETWORK_STATE}.
+     *
+     * @param networkCallback The {@link NetworkCallback} that the system will call as the
+     *                        system default network changes.
+     * @hide
+     */
+    public void registerDefaultNetworkCallback(NetworkCallback networkCallback) {
+        // This works because if the NetworkCapabilities are null,
+        // ConnectivityService takes them from the default request.
+        //
+        // Since the capabilities are exactly the same as the default request's
+        // capabilities, this request is guaranteed, at all times, to be
+        // satisfied by the same network, if any, that satisfies the default
+        // request, i.e., the system default network.
+        sendRequestForNetwork(null, networkCallback, 0, REQUEST, TYPE_NONE);
+    }
+
+    /**
      * Requests bandwidth update for a given {@link Network} and returns whether the update request
      * is accepted by ConnectivityService. Once accepted, ConnectivityService will poll underlying
      * network connection for updated bandwidth information. The caller will be notified via
diff --git a/core/java/android/net/NetworkTemplate.java b/core/java/android/net/NetworkTemplate.java
index 5761d66..d847cd0 100644
--- a/core/java/android/net/NetworkTemplate.java
+++ b/core/java/android/net/NetworkTemplate.java
@@ -250,6 +250,16 @@
         }
     }
 
+    public boolean isPersistable() {
+        switch (mMatchRule) {
+            case MATCH_MOBILE_WILDCARD:
+            case MATCH_WIFI_WILDCARD:
+                return false;
+            default:
+                return true;
+        }
+    }
+
     public int getMatchRule() {
         return mMatchRule;
     }
diff --git a/core/java/android/net/NetworkUtils.java b/core/java/android/net/NetworkUtils.java
index 555032d..141af3d 100644
--- a/core/java/android/net/NetworkUtils.java
+++ b/core/java/android/net/NetworkUtils.java
@@ -39,23 +39,6 @@
 
     private static final String TAG = "NetworkUtils";
 
-    /** Setting bit 0 indicates reseting of IPv4 addresses required */
-    public static final int RESET_IPV4_ADDRESSES = 0x01;
-
-    /** Setting bit 1 indicates reseting of IPv4 addresses required */
-    public static final int RESET_IPV6_ADDRESSES = 0x02;
-
-    /** Reset all addresses */
-    public static final int RESET_ALL_ADDRESSES = RESET_IPV4_ADDRESSES | RESET_IPV6_ADDRESSES;
-
-    /**
-     * Reset IPv6 or IPv4 sockets that are connected via the named interface.
-     *
-     * @param interfaceName is the interface to reset
-     * @param mask {@see #RESET_IPV4_ADDRESSES} and {@see #RESET_IPV6_ADDRESSES}
-     */
-    public native static int resetConnections(String interfaceName, int mask);
-
     /**
      * Attaches a socket filter that accepts DHCP packets to the given socket.
      */
diff --git a/core/java/android/net/TrafficStats.java b/core/java/android/net/TrafficStats.java
index 95ffb44..cfd0468 100644
--- a/core/java/android/net/TrafficStats.java
+++ b/core/java/android/net/TrafficStats.java
@@ -147,8 +147,10 @@
     }
 
     /**
-     * System API for backup-related support components to tag network traffic
-     * appropriately.
+     * Set active tag to use when accounting {@link Socket} traffic originating
+     * from the current thread. The tag used internally is well-defined to
+     * distinguish all backup-related traffic.
+     *
      * @hide
      */
     @SystemApi
@@ -157,8 +159,10 @@
     }
 
     /**
-     * System API for restore-related support components to tag network traffic
-     * appropriately.
+     * Set active tag to use when accounting {@link Socket} traffic originating
+     * from the current thread. The tag used internally is well-defined to
+     * distinguish all restore-related traffic.
+     *
      * @hide
      */
     @SystemApi
@@ -205,7 +209,13 @@
         NetworkManagementSocketTagger.setThreadSocketStatsUid(uid);
     }
 
-    /** {@hide} */
+    /**
+     * Clear any active UID set to account {@link Socket} traffic originating
+     * from the current thread.
+     *
+     * @see #setThreadStatsUid(int)
+     * @hide
+     */
     @SystemApi
     public static void clearThreadStatsUid() {
         NetworkManagementSocketTagger.setThreadSocketStatsUid(-1);
diff --git a/core/java/android/os/Debug.java b/core/java/android/os/Debug.java
index f382241..55b107a 100644
--- a/core/java/android/os/Debug.java
+++ b/core/java/android/os/Debug.java
@@ -19,8 +19,11 @@
 import com.android.internal.util.FastPrintWriter;
 import com.android.internal.util.TypedProperties;
 
+import android.app.AppGlobals;
+import android.content.Context;
 import android.util.Log;
 
+import java.io.File;
 import java.io.FileDescriptor;
 import java.io.FileNotFoundException;
 import java.io.FileOutputStream;
@@ -100,14 +103,6 @@
      */
     private static final String DEFAULT_TRACE_BODY = "dmtrace";
     private static final String DEFAULT_TRACE_EXTENSION = ".trace";
-    private static class NoPreloadHolder {
-        private static final String DEFAULT_TRACE_PATH_PREFIX =
-                Environment.getLegacyExternalStorageDirectory().getPath() + "/";
-        private static final String DEFAULT_TRACE_FILE_PATH =
-                DEFAULT_TRACE_PATH_PREFIX + DEFAULT_TRACE_BODY
-                + DEFAULT_TRACE_EXTENSION;
-    }
-
 
     /**
      * This class is used to retrieved various statistics about the memory mappings for this
@@ -938,109 +933,171 @@
     }
 
     /**
-     * Start method tracing with default log name and buffer size. See <a
-href="{@docRoot}guide/developing/tools/traceview.html">Traceview: A Graphical Log Viewer</a> for
-     * information about reading these files. Call stopMethodTracing() to stop
-     * tracing.
+     * Start method tracing with default log name and buffer size.
+     * <p>
+     * By default, the trace file is called "dmtrace.trace" and it's placed
+     * under your package-specific directory on primary shared/external storage,
+     * as returned by {@link Context#getExternalFilesDir(String)}.
+     * <p>
+     * See <a href="{@docRoot}guide/developing/tools/traceview.html">Traceview:
+     * A Graphical Log Viewer</a> for information about reading trace files.
+     * <p class="note">
+     * When method tracing is enabled, the VM will run more slowly than usual,
+     * so the timings from the trace files should only be considered in relative
+     * terms (e.g. was run #1 faster than run #2). The times for native methods
+     * will not change, so don't try to use this to compare the performance of
+     * interpreted and native implementations of the same method. As an
+     * alternative, consider using sampling-based method tracing via
+     * {@link #startMethodTracingSampling(String, int, int)} or "native" tracing
+     * in the emulator via {@link #startNativeTracing()}.
+     * </p>
      */
     public static void startMethodTracing() {
-        VMDebug.startMethodTracing(fixTraceName(null), 0, 0, false, 0);
+        VMDebug.startMethodTracing(fixTracePath(null), 0, 0, false, 0);
     }
 
     /**
-     * Start method tracing, specifying the trace log file name.  The trace
-     * file will be put under "/sdcard" unless an absolute path is given.
-     * See <a
-       href="{@docRoot}guide/developing/tools/traceview.html">Traceview: A Graphical Log Viewer</a> for
-     * information about reading trace files.
-     *
-     * @param traceName Name for the trace log file to create.
-     * If {@code traceName} is null, this value defaults to "/sdcard/dmtrace.trace".
-     * If the files already exist, they will be truncated.
-     * If the trace file given does not end in ".trace", it will be appended for you.
-     */
-    public static void startMethodTracing(String traceName) {
-        startMethodTracing(traceName, 0, 0);
-    }
-
-    /**
-     * Start method tracing, specifying the trace log file name and the
-     * buffer size. The trace files will be put under "/sdcard" unless an
-     * absolute path is given. See <a
-       href="{@docRoot}guide/developing/tools/traceview.html">Traceview: A Graphical Log Viewer</a> for
-     * information about reading trace files.
-     * @param traceName    Name for the trace log file to create.
-     * If {@code traceName} is null, this value defaults to "/sdcard/dmtrace.trace".
-     * If the files already exist, they will be truncated.
-     * If the trace file given does not end in ".trace", it will be appended for you.
-     *
-     * @param bufferSize    The maximum amount of trace data we gather. If not given, it defaults to 8MB.
-     */
-    public static void startMethodTracing(String traceName, int bufferSize) {
-        startMethodTracing(traceName, bufferSize, 0);
-    }
-
-    /**
-     * Start method tracing, specifying the trace log file name and the
-     * buffer size. The trace files will be put under "/sdcard" unless an
-     * absolute path is given. See <a
-       href="{@docRoot}guide/developing/tools/traceview.html">Traceview: A Graphical Log Viewer</a> for
-     * information about reading trace files.
-     *
+     * Start method tracing, specifying the trace log file path.
      * <p>
-     * When method tracing is enabled, the VM will run more slowly than
-     * usual, so the timings from the trace files should only be considered
-     * in relative terms (e.g. was run #1 faster than run #2).  The times
-     * for native methods will not change, so don't try to use this to
-     * compare the performance of interpreted and native implementations of the
-     * same method.  As an alternative, consider using sampling-based method
-     * tracing via {@link #startMethodTracingSampling(String, int, int)} or
-     * "native" tracing in the emulator via {@link #startNativeTracing()}.
+     * When a relative file path is given, the trace file will be placed under
+     * your package-specific directory on primary shared/external storage, as
+     * returned by {@link Context#getExternalFilesDir(String)}.
+     * <p>
+     * See <a href="{@docRoot}guide/developing/tools/traceview.html">Traceview:
+     * A Graphical Log Viewer</a> for information about reading trace files.
+     * <p class="note">
+     * When method tracing is enabled, the VM will run more slowly than usual,
+     * so the timings from the trace files should only be considered in relative
+     * terms (e.g. was run #1 faster than run #2). The times for native methods
+     * will not change, so don't try to use this to compare the performance of
+     * interpreted and native implementations of the same method. As an
+     * alternative, consider using sampling-based method tracing via
+     * {@link #startMethodTracingSampling(String, int, int)} or "native" tracing
+     * in the emulator via {@link #startNativeTracing()}.
      * </p>
      *
-     * @param traceName    Name for the trace log file to create.
-     * If {@code traceName} is null, this value defaults to "/sdcard/dmtrace.trace".
-     * If the files already exist, they will be truncated.
-     * If the trace file given does not end in ".trace", it will be appended for you.
-     * @param bufferSize    The maximum amount of trace data we gather. If not given, it defaults to 8MB.
-     * @param flags    Flags to control method tracing. The only one that is currently defined is {@link #TRACE_COUNT_ALLOCS}.
+     * @param tracePath Path to the trace log file to create. If {@code null},
+     *            this will default to "dmtrace.trace". If the file already
+     *            exists, it will be truncated. If the path given does not end
+     *            in ".trace", it will be appended for you.
      */
-    public static void startMethodTracing(String traceName, int bufferSize,
-        int flags) {
-        VMDebug.startMethodTracing(fixTraceName(traceName), bufferSize, flags, false, 0);
+    public static void startMethodTracing(String tracePath) {
+        startMethodTracing(tracePath, 0, 0);
+    }
+
+    /**
+     * Start method tracing, specifying the trace log file name and the buffer
+     * size.
+     * <p>
+     * When a relative file path is given, the trace file will be placed under
+     * your package-specific directory on primary shared/external storage, as
+     * returned by {@link Context#getExternalFilesDir(String)}.
+     * <p>
+     * See <a href="{@docRoot}guide/developing/tools/traceview.html">Traceview:
+     * A Graphical Log Viewer</a> for information about reading trace files.
+     * <p class="note">
+     * When method tracing is enabled, the VM will run more slowly than usual,
+     * so the timings from the trace files should only be considered in relative
+     * terms (e.g. was run #1 faster than run #2). The times for native methods
+     * will not change, so don't try to use this to compare the performance of
+     * interpreted and native implementations of the same method. As an
+     * alternative, consider using sampling-based method tracing via
+     * {@link #startMethodTracingSampling(String, int, int)} or "native" tracing
+     * in the emulator via {@link #startNativeTracing()}.
+     * </p>
+     *
+     * @param tracePath Path to the trace log file to create. If {@code null},
+     *            this will default to "dmtrace.trace". If the file already
+     *            exists, it will be truncated. If the path given does not end
+     *            in ".trace", it will be appended for you.
+     * @param bufferSize The maximum amount of trace data we gather. If not
+     *            given, it defaults to 8MB.
+     */
+    public static void startMethodTracing(String tracePath, int bufferSize) {
+        startMethodTracing(tracePath, bufferSize, 0);
+    }
+
+    /**
+     * Start method tracing, specifying the trace log file name, the buffer
+     * size, and flags.
+     * <p>
+     * When a relative file path is given, the trace file will be placed under
+     * your package-specific directory on primary shared/external storage, as
+     * returned by {@link Context#getExternalFilesDir(String)}.
+     * <p>
+     * See <a href="{@docRoot}guide/developing/tools/traceview.html">Traceview:
+     * A Graphical Log Viewer</a> for information about reading trace files.
+     * <p class="note">
+     * When method tracing is enabled, the VM will run more slowly than usual,
+     * so the timings from the trace files should only be considered in relative
+     * terms (e.g. was run #1 faster than run #2). The times for native methods
+     * will not change, so don't try to use this to compare the performance of
+     * interpreted and native implementations of the same method. As an
+     * alternative, consider using sampling-based method tracing via
+     * {@link #startMethodTracingSampling(String, int, int)} or "native" tracing
+     * in the emulator via {@link #startNativeTracing()}.
+     * </p>
+     *
+     * @param tracePath Path to the trace log file to create. If {@code null},
+     *            this will default to "dmtrace.trace". If the file already
+     *            exists, it will be truncated. If the path given does not end
+     *            in ".trace", it will be appended for you.
+     * @param bufferSize The maximum amount of trace data we gather. If not
+     *            given, it defaults to 8MB.
+     * @param flags Flags to control method tracing. The only one that is
+     *            currently defined is {@link #TRACE_COUNT_ALLOCS}.
+     */
+    public static void startMethodTracing(String tracePath, int bufferSize, int flags) {
+        VMDebug.startMethodTracing(fixTracePath(tracePath), bufferSize, flags, false, 0);
     }
 
     /**
      * Start sampling-based method tracing, specifying the trace log file name,
-     * the buffer size, and the sampling interval. The trace files will be put
-     * under "/sdcard" unless an absolute path is given. See <a
-       href="{@docRoot}guide/developing/tools/traceview.html">Traceview: A Graphical Log Viewer</a>
-     * for information about reading trace files.
+     * the buffer size, and the sampling interval.
+     * <p>
+     * When a relative file path is given, the trace file will be placed under
+     * your package-specific directory on primary shared/external storage, as
+     * returned by {@link Context#getExternalFilesDir(String)}.
+     * <p>
+     * See <a href="{@docRoot}guide/developing/tools/traceview.html">Traceview:
+     * A Graphical Log Viewer</a> for information about reading trace files.
      *
-     * @param traceName    Name for the trace log file to create.
-     * If {@code traceName} is null, this value defaults to "/sdcard/dmtrace.trace".
-     * If the files already exist, they will be truncated.
-     * If the trace file given does not end in ".trace", it will be appended for you.
-     * @param bufferSize    The maximum amount of trace data we gather. If not given, it defaults to 8MB.
-     * @param intervalUs    The amount of time between each sample in microseconds.
+     * @param tracePath Path to the trace log file to create. If {@code null},
+     *            this will default to "dmtrace.trace". If the file already
+     *            exists, it will be truncated. If the path given does not end
+     *            in ".trace", it will be appended for you.
+     * @param bufferSize The maximum amount of trace data we gather. If not
+     *            given, it defaults to 8MB.
+     * @param intervalUs The amount of time between each sample in microseconds.
      */
-    public static void startMethodTracingSampling(String traceName,
-        int bufferSize, int intervalUs) {
-        VMDebug.startMethodTracing(fixTraceName(traceName), bufferSize, 0, true, intervalUs);
+    public static void startMethodTracingSampling(String tracePath, int bufferSize,
+            int intervalUs) {
+        VMDebug.startMethodTracing(fixTracePath(tracePath), bufferSize, 0, true, intervalUs);
     }
-
+    
     /**
      * Formats name of trace log file for method tracing.
      */
-    private static String fixTraceName(String traceName) {
-        if (traceName == null)
-            traceName = NoPreloadHolder.DEFAULT_TRACE_FILE_PATH;
-        if (traceName.charAt(0) != '/')
-            traceName = NoPreloadHolder.DEFAULT_TRACE_PATH_PREFIX + traceName;
-        if (!traceName.endsWith(DEFAULT_TRACE_EXTENSION))
-            traceName = traceName + DEFAULT_TRACE_EXTENSION;
+    private static String fixTracePath(String tracePath) {
+        if (tracePath == null || tracePath.charAt(0) != '/') {
+            final Context context = AppGlobals.getInitialApplication();
+            final File dir;
+            if (context != null) {
+                dir = context.getExternalFilesDir(null);
+            } else {
+                dir = Environment.getExternalStorageDirectory();
+            }
 
-        return traceName;
+            if (tracePath == null) {
+                tracePath = new File(dir, DEFAULT_TRACE_BODY).getAbsolutePath();
+            } else {
+                tracePath = new File(dir, tracePath).getAbsolutePath();
+            }
+        }
+        if (!tracePath.endsWith(DEFAULT_TRACE_EXTENSION)) {
+            tracePath += DEFAULT_TRACE_EXTENSION;
+        }
+        return tracePath;
     }
 
     /**
diff --git a/core/java/android/os/INetworkManagementService.aidl b/core/java/android/os/INetworkManagementService.aidl
index cd84c8f..100f6a1 100644
--- a/core/java/android/os/INetworkManagementService.aidl
+++ b/core/java/android/os/INetworkManagementService.aidl
@@ -97,12 +97,6 @@
     void setInterfaceIpv6NdOffload(String iface, boolean enable);
 
     /**
-     * Retrieves the network routes currently configured on the specified
-     * interface
-     */
-    RouteInfo[] getRoutes(String iface);
-
-    /**
      * Add the specified route to the interface.
      */
     void addRoute(int netId, in RouteInfo route);
diff --git a/core/java/android/os/IUserManager.aidl b/core/java/android/os/IUserManager.aidl
index bc2566b..c38bf3c 100644
--- a/core/java/android/os/IUserManager.aidl
+++ b/core/java/android/os/IUserManager.aidl
@@ -76,4 +76,5 @@
     PersistableBundle getSeedAccountOptions();
     void clearSeedAccountData();
     boolean someUserHasSeedAccount(in String accountName, in String accountType);
+    boolean isManagedProfile(int userId);
 }
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index 0ff0154..707d5f5 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -822,8 +822,28 @@
      */
     @SystemApi
     public boolean isManagedProfile() {
-        UserInfo user = getUserInfo(UserHandle.myUserId());
-        return user != null ? user.isManagedProfile() : false;
+        try {
+            return mService.isManagedProfile(UserHandle.myUserId());
+        } catch (RemoteException re) {
+            throw re.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Checks if the specified user is a managed profile.
+     * Requires {@link android.Manifest.permission#MANAGE_USERS} permission, otherwise the caller
+     * must be in the same profile group of specified user.
+     *
+     * @return whether the specified user is a managed profile.
+     * @hide
+     */
+    @SystemApi
+    public boolean isManagedProfile(@UserIdInt int userId) {
+        try {
+            return mService.isManagedProfile(userId);
+        } catch (RemoteException re) {
+            throw re.rethrowFromSystemServer();
+        }
     }
 
     /**
@@ -964,8 +984,7 @@
 
     /**
      * Returns the UserInfo object describing a specific user.
-     * Requires {@link android.Manifest.permission#MANAGE_USERS} permission or the caller is
-     * in the same profile group of target user.
+     * Requires {@link android.Manifest.permission#MANAGE_USERS} permission.
      * @param userHandle the user handle of the user whose information is being requested.
      * @return the UserInfo object for a specific user.
      * @hide
diff --git a/core/java/android/os/storage/VolumeInfo.java b/core/java/android/os/storage/VolumeInfo.java
index ea0597d..4b70649 100644
--- a/core/java/android/os/storage/VolumeInfo.java
+++ b/core/java/android/os/storage/VolumeInfo.java
@@ -438,6 +438,10 @@
         final Intent intent = new Intent(DocumentsContract.ACTION_BROWSE);
         intent.addCategory(Intent.CATEGORY_DEFAULT);
         intent.setData(uri);
+
+        // note that docsui treats this as *force* show advanced. So sending
+        // false permits advanced to be shown based on user preferences.
+        intent.putExtra(DocumentsContract.EXTRA_SHOW_ADVANCED, isPrimary());
         intent.putExtra(DocumentsContract.EXTRA_SHOW_FILESIZE, true);
         return intent;
     }
diff --git a/core/java/android/provider/DocumentsContract.java b/core/java/android/provider/DocumentsContract.java
index 4ad7969..4412459 100644
--- a/core/java/android/provider/DocumentsContract.java
+++ b/core/java/android/provider/DocumentsContract.java
@@ -93,6 +93,9 @@
     public static final String EXTRA_PACKAGE_NAME = "android.content.extra.PACKAGE_NAME";
 
     /** {@hide} */
+    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} */
@@ -556,13 +559,22 @@
         public static final int FLAG_EMPTY = 1 << 16;
 
         /**
+         * Flag indicating that this root should only be visible to advanced
+         * users.
+         *
+         * @see #COLUMN_FLAGS
+         * @hide
+         */
+        public static final int FLAG_ADVANCED = 1 << 17;
+
+        /**
          * Flag indicating that this root has settings.
          *
          * @see #COLUMN_FLAGS
          * @see DocumentsContract#ACTION_DOCUMENT_ROOT_SETTINGS
          * @hide
          */
-        public static final int FLAG_HAS_SETTINGS = 1 << 17;
+        public static final int FLAG_HAS_SETTINGS = 1 << 18;
 
         /**
          * Flag indicating that this root is on removable SD card storage.
@@ -570,7 +582,7 @@
          * @see #COLUMN_FLAGS
          * @hide
          */
-        public static final int FLAG_REMOVABLE_SD = 1 << 18;
+        public static final int FLAG_REMOVABLE_SD = 1 << 19;
 
         /**
          * Flag indicating that this root is on removable USB storage.
@@ -578,7 +590,7 @@
          * @see #COLUMN_FLAGS
          * @hide
          */
-        public static final int FLAG_REMOVABLE_USB = 1 << 19;
+        public static final int FLAG_REMOVABLE_USB = 1 << 20;
     }
 
     /**
diff --git a/core/java/android/provider/UserDictionary.java b/core/java/android/provider/UserDictionary.java
index a9b106a..c6e58cb 100644
--- a/core/java/android/provider/UserDictionary.java
+++ b/core/java/android/provider/UserDictionary.java
@@ -28,6 +28,9 @@
  * A provider of user defined words for input methods to use for predictive text input.
  * Applications and input methods may add words into the dictionary. Words can have associated
  * frequency information and locale information.
+ *
+ * <p><strong>NOTE: </strong>Starting on API 23, the user dictionary is only accessible through
+ * IME and spellchecker.
  */
 public class UserDictionary {
 
diff --git a/core/java/android/util/apk/ApkSignatureSchemeV2Verifier.java b/core/java/android/util/apk/ApkSignatureSchemeV2Verifier.java
index dcf987b..d9227ce 100644
--- a/core/java/android/util/apk/ApkSignatureSchemeV2Verifier.java
+++ b/core/java/android/util/apk/ApkSignatureSchemeV2Verifier.java
@@ -16,17 +16,20 @@
 
 package android.util.apk;
 
+import android.system.ErrnoException;
+import android.system.OsConstants;
+import android.util.ArrayMap;
 import android.util.Pair;
 
 import java.io.ByteArrayInputStream;
+import java.io.FileDescriptor;
 import java.io.IOException;
 import java.io.RandomAccessFile;
 import java.math.BigInteger;
 import java.nio.BufferUnderflowException;
 import java.nio.ByteBuffer;
 import java.nio.ByteOrder;
-import java.nio.MappedByteBuffer;
-import java.nio.channels.FileChannel;
+import java.nio.DirectByteBuffer;
 import java.security.DigestException;
 import java.security.InvalidAlgorithmParameterException;
 import java.security.InvalidKeyException;
@@ -52,11 +55,13 @@
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Date;
-import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
 
+import libcore.io.Libcore;
+import libcore.io.Os;
+
 /**
  * APK Signature Scheme v2 verifier.
  *
@@ -75,44 +80,17 @@
     public static final int SF_ATTRIBUTE_ANDROID_APK_SIGNED_ID = 2;
 
     /**
-     * Returns {@code true} if the provided APK contains an APK Signature Scheme V2
-     * signature. The signature will not be verified.
+     * Returns {@code true} if the provided APK contains an APK Signature Scheme V2 signature.
+     *
+     * <p><b>NOTE: This method does not verify the signature.</b>
      */
     public static boolean hasSignature(String apkFile) throws IOException {
         try (RandomAccessFile apk = new RandomAccessFile(apkFile, "r")) {
-            long fileSize = apk.length();
-            if (fileSize > Integer.MAX_VALUE) {
-                return false;
-            }
-            MappedByteBuffer apkContents;
-            try {
-                apkContents = apk.getChannel().map(FileChannel.MapMode.READ_ONLY, 0, fileSize);
-            } catch (IOException e) {
-                if (e.getCause() instanceof OutOfMemoryError) {
-                    // TODO: Remove this temporary workaround once verifying large APKs is
-                    // supported. Very large APKs cannot be memory-mapped. This verification code
-                    // needs to change to use a different approach for verifying such APKs.
-                    return false; // Pretend that this APK does not have a v2 signature.
-                } else {
-                    throw new IOException("Failed to memory-map APK", e);
-                }
-            }
-            // ZipUtils and APK Signature Scheme v2 verifier expect little-endian byte order.
-            apkContents.order(ByteOrder.LITTLE_ENDIAN);
-
-            final int centralDirOffset =
-                    (int) getCentralDirOffset(apkContents, getEocdOffset(apkContents));
-            // Find the APK Signing Block.
-            int apkSigningBlockOffset = findApkSigningBlock(apkContents, centralDirOffset);
-            ByteBuffer apkSigningBlock =
-                    sliceFromTo(apkContents, apkSigningBlockOffset, centralDirOffset);
-
-            // Find the APK Signature Scheme v2 Block inside the APK Signing Block.
-            findApkSignatureSchemeV2Block(apkSigningBlock);
+            findSignature(apk);
             return true;
         } catch (SignatureNotFoundException e) {
+            return false;
         }
-        return false;
     }
 
     /**
@@ -135,90 +113,97 @@
      * associated with each signer.
      *
      * @throws SignatureNotFoundException if the APK is not signed using APK Signature Scheme v2.
-     * @throws SecurityException if a APK Signature Scheme v2 signature of this APK does not verify.
+     * @throws SecurityException if an APK Signature Scheme v2 signature of this APK does not
+     *         verify.
      * @throws IOException if an I/O error occurs while reading the APK file.
      */
-    public static X509Certificate[][] verify(RandomAccessFile apk)
+    private static X509Certificate[][] verify(RandomAccessFile apk)
             throws SignatureNotFoundException, SecurityException, IOException {
-
-        long fileSize = apk.length();
-        if (fileSize > Integer.MAX_VALUE) {
-            throw new IOException("File too large: " + apk.length() + " bytes");
-        }
-        MappedByteBuffer apkContents;
-        try {
-            apkContents = apk.getChannel().map(FileChannel.MapMode.READ_ONLY, 0, fileSize);
-            // Attempt to preload the contents into memory for faster overall verification (v2 and
-            // older) at the expense of somewhat increased latency for rejecting malformed APKs.
-            apkContents.load();
-        } catch (IOException e) {
-            if (e.getCause() instanceof OutOfMemoryError) {
-                // TODO: Remove this temporary workaround once verifying large APKs is supported.
-                // Very large APKs cannot be memory-mapped. This verification code needs to change
-                // to use a different approach for verifying such APKs.
-                // This workaround pretends that this APK does not have a v2 signature. This works
-                // fine provided the APK is not actually v2-signed. If the APK is v2 signed, v2
-                // signature stripping protection inside v1 signature verification code will reject
-                // this APK.
-                throw new SignatureNotFoundException("Failed to memory-map APK", e);
-            } else {
-                throw new IOException("Failed to memory-map APK", e);
-            }
-        }
-        return verify(apkContents);
+        SignatureInfo signatureInfo = findSignature(apk);
+        return verify(apk.getFD(), signatureInfo);
     }
 
     /**
-     * Verifies APK Signature Scheme v2 signatures of the provided APK and returns the certificates
-     * associated with each signer.
-     *
-     * @param apkContents contents of the APK. The contents start at the current position and end
-     *        at the limit of the buffer.
+     * APK Signature Scheme v2 block and additional information relevant to verifying the signatures
+     * contained in the block against the file.
+     */
+    private static class SignatureInfo {
+        /** Contents of APK Signature Scheme v2 block. */
+        private final ByteBuffer signatureBlock;
+
+        /** Position of the APK Signing Block in the file. */
+        private final long apkSigningBlockOffset;
+
+        /** Position of the ZIP Central Directory in the file. */
+        private final long centralDirOffset;
+
+        /** Position of the ZIP End of Central Directory (EoCD) in the file. */
+        private final long eocdOffset;
+
+        /** Contents of ZIP End of Central Directory (EoCD) of the file. */
+        private final ByteBuffer eocd;
+
+        private SignatureInfo(
+                ByteBuffer signatureBlock,
+                long apkSigningBlockOffset,
+                long centralDirOffset,
+                long eocdOffset,
+                ByteBuffer eocd) {
+            this.signatureBlock = signatureBlock;
+            this.apkSigningBlockOffset = apkSigningBlockOffset;
+            this.centralDirOffset = centralDirOffset;
+            this.eocdOffset = eocdOffset;
+            this.eocd = eocd;
+        }
+    }
+
+    /**
+     * Returns the APK Signature Scheme v2 block contained in the provided APK file and the
+     * additional information relevant for verifying the block against the file.
      *
      * @throws SignatureNotFoundException if the APK is not signed using APK Signature Scheme v2.
-     * @throws SecurityException if a APK Signature Scheme v2 signature of this APK does not verify.
+     * @throws IOException if an I/O error occurs while reading the APK file.
      */
-    public static X509Certificate[][] verify(ByteBuffer apkContents)
-            throws SignatureNotFoundException, SecurityException {
-        // Avoid modifying byte order, position, limit, and mark of the original apkContents.
-        apkContents = apkContents.slice();
+    private static SignatureInfo findSignature(RandomAccessFile apk)
+            throws IOException, SignatureNotFoundException {
+        // Find the ZIP End of Central Directory (EoCD) record.
+        Pair<ByteBuffer, Long> eocdAndOffsetInFile = getEocd(apk);
+        ByteBuffer eocd = eocdAndOffsetInFile.first;
+        long eocdOffset = eocdAndOffsetInFile.second;
+        if (ZipUtils.isZip64EndOfCentralDirectoryLocatorPresent(apk, eocdOffset)) {
+            throw new SignatureNotFoundException("ZIP64 APK not supported");
+        }
 
-        // ZipUtils and APK Signature Scheme v2 verifier expect little-endian byte order.
-        apkContents.order(ByteOrder.LITTLE_ENDIAN);
-
-        final int eocdOffset = getEocdOffset(apkContents);
-        final int centralDirOffset = (int) getCentralDirOffset(apkContents, eocdOffset);
-
-        // Find the APK Signing Block.
-        int apkSigningBlockOffset = findApkSigningBlock(apkContents, centralDirOffset);
-        ByteBuffer apkSigningBlock =
-                sliceFromTo(apkContents, apkSigningBlockOffset, centralDirOffset);
+        // Find the APK Signing Block. The block immediately precedes the Central Directory.
+        long centralDirOffset = getCentralDirOffset(eocd, eocdOffset);
+        Pair<ByteBuffer, Long> apkSigningBlockAndOffsetInFile =
+                findApkSigningBlock(apk, centralDirOffset);
+        ByteBuffer apkSigningBlock = apkSigningBlockAndOffsetInFile.first;
+        long apkSigningBlockOffset = apkSigningBlockAndOffsetInFile.second;
 
         // Find the APK Signature Scheme v2 Block inside the APK Signing Block.
         ByteBuffer apkSignatureSchemeV2Block = findApkSignatureSchemeV2Block(apkSigningBlock);
 
-        // Verify the contents of the APK outside of the APK Signing Block using the APK Signature
-        // Scheme v2 Block.
-        return verify(
-                apkContents,
+        return new SignatureInfo(
                 apkSignatureSchemeV2Block,
                 apkSigningBlockOffset,
                 centralDirOffset,
-                eocdOffset);
+                eocdOffset,
+                eocd);
     }
 
     /**
-     * Verifies the contents outside of the APK Signing Block using the provided APK Signature
-     * Scheme v2 Block.
+     * Verifies the contents of the provided APK file against the provided APK Signature Scheme v2
+     * Block.
+     *
+     * @param signatureInfo APK Signature Scheme v2 Block and information relevant for verifying it
+     *        against the APK file.
      */
     private static X509Certificate[][] verify(
-            ByteBuffer apkContents,
-            ByteBuffer v2Block,
-            int apkSigningBlockOffset,
-            int centralDirOffset,
-            int eocdOffset) throws SecurityException {
+            FileDescriptor apkFileDescriptor,
+            SignatureInfo signatureInfo) throws SecurityException {
         int signerCount = 0;
-        Map<Integer, byte[]> contentDigests = new HashMap<>();
+        Map<Integer, byte[]> contentDigests = new ArrayMap<>();
         List<X509Certificate[]> signerCerts = new ArrayList<>();
         CertificateFactory certFactory;
         try {
@@ -228,7 +213,7 @@
         }
         ByteBuffer signers;
         try {
-            signers = getLengthPrefixedSlice(v2Block);
+            signers = getLengthPrefixedSlice(signatureInfo.signatureBlock);
         } catch (IOException e) {
             throw new SecurityException("Failed to read list of signers", e);
         }
@@ -255,10 +240,11 @@
 
         verifyIntegrity(
                 contentDigests,
-                apkContents,
-                apkSigningBlockOffset,
-                centralDirOffset,
-                eocdOffset);
+                apkFileDescriptor,
+                signatureInfo.apkSigningBlockOffset,
+                signatureInfo.centralDirOffset,
+                signatureInfo.eocdOffset,
+                signatureInfo.eocd);
 
         return signerCerts.toArray(new X509Certificate[signerCerts.size()][]);
     }
@@ -401,25 +387,38 @@
 
     private static void verifyIntegrity(
             Map<Integer, byte[]> expectedDigests,
-            ByteBuffer apkContents,
-            int apkSigningBlockOffset,
-            int centralDirOffset,
-            int eocdOffset) throws SecurityException {
+            FileDescriptor apkFileDescriptor,
+            long apkSigningBlockOffset,
+            long centralDirOffset,
+            long eocdOffset,
+            ByteBuffer eocdBuf) throws SecurityException {
 
         if (expectedDigests.isEmpty()) {
             throw new SecurityException("No digests provided");
         }
 
-        ByteBuffer beforeApkSigningBlock = sliceFromTo(apkContents, 0, apkSigningBlockOffset);
-        ByteBuffer centralDir = sliceFromTo(apkContents, centralDirOffset, eocdOffset);
+        // We need to verify the integrity of the following three sections of the file:
+        // 1. Everything up to the start of the APK Signing Block.
+        // 2. ZIP Central Directory.
+        // 3. ZIP End of Central Directory (EoCD).
+        // Each of these sections is represented as a separate DataSource instance below.
+
+        // To handle large APKs, these sections are read in 1 MB chunks using memory-mapped I/O to
+        // avoid wasting physical memory. In most APK verification scenarios, the contents of the
+        // APK are already there in the OS's page cache and thus mmap does not use additional
+        // physical memory.
+        DataSource beforeApkSigningBlock =
+                new MemoryMappedFileDataSource(apkFileDescriptor, 0, apkSigningBlockOffset);
+        DataSource centralDir =
+                new MemoryMappedFileDataSource(
+                        apkFileDescriptor, centralDirOffset, eocdOffset - centralDirOffset);
+
         // For the purposes of integrity verification, ZIP End of Central Directory's field Start of
         // Central Directory must be considered to point to the offset of the APK Signing Block.
-        byte[] eocdBytes = new byte[apkContents.capacity() - eocdOffset];
-        apkContents.position(eocdOffset);
-        apkContents.get(eocdBytes);
-        ByteBuffer eocd = ByteBuffer.wrap(eocdBytes);
-        eocd.order(apkContents.order());
-        ZipUtils.setZipEocdCentralDirectoryOffset(eocd, apkSigningBlockOffset);
+        eocdBuf = eocdBuf.duplicate();
+        eocdBuf.order(ByteOrder.LITTLE_ENDIAN);
+        ZipUtils.setZipEocdCentralDirectoryOffset(eocdBuf, apkSigningBlockOffset);
+        DataSource eocd = new ByteBufferDataSource(eocdBuf);
 
         int[] digestAlgorithms = new int[expectedDigests.size()];
         int digestAlgorithmCount = 0;
@@ -427,30 +426,30 @@
             digestAlgorithms[digestAlgorithmCount] = digestAlgorithm;
             digestAlgorithmCount++;
         }
-        Map<Integer, byte[]> actualDigests;
+        byte[][] actualDigests;
         try {
             actualDigests =
                     computeContentDigests(
                             digestAlgorithms,
-                            new ByteBuffer[] {beforeApkSigningBlock, centralDir, eocd});
+                            new DataSource[] {beforeApkSigningBlock, centralDir, eocd});
         } catch (DigestException e) {
             throw new SecurityException("Failed to compute digest(s) of contents", e);
         }
-        for (Map.Entry<Integer, byte[]> entry : expectedDigests.entrySet()) {
-            int digestAlgorithm = entry.getKey();
-            byte[] expectedDigest = entry.getValue();
-            byte[] actualDigest = actualDigests.get(digestAlgorithm);
+        for (int i = 0; i < digestAlgorithms.length; i++) {
+            int digestAlgorithm = digestAlgorithms[i];
+            byte[] expectedDigest = expectedDigests.get(digestAlgorithm);
+            byte[] actualDigest = actualDigests[i];
             if (!MessageDigest.isEqual(expectedDigest, actualDigest)) {
                 throw new SecurityException(
                         getContentDigestAlgorithmJcaDigestAlgorithm(digestAlgorithm)
-                        + " digest of contents did not verify");
+                                + " digest of contents did not verify");
             }
         }
     }
 
-    private static Map<Integer, byte[]> computeContentDigests(
+    private static byte[][] computeContentDigests(
             int[] digestAlgorithms,
-            ByteBuffer[] contents) throws DigestException {
+            DataSource[] contents) throws DigestException {
         // For each digest algorithm the result is computed as follows:
         // 1. Each segment of contents is split into consecutive chunks of 1 MB in size.
         //    The final chunk will be shorter iff the length of segment is not a multiple of 1 MB.
@@ -461,13 +460,18 @@
         //    chunks (uint32 little-endian) and the concatenation of digests of chunks of all
         //    segments in-order.
 
-        int totalChunkCount = 0;
-        for (ByteBuffer input : contents) {
-            totalChunkCount += getChunkCount(input.remaining());
+        long totalChunkCountLong = 0;
+        for (DataSource input : contents) {
+            totalChunkCountLong += getChunkCount(input.size());
         }
+        if (totalChunkCountLong >= Integer.MAX_VALUE / 1024) {
+            throw new DigestException("Too many chunks: " + totalChunkCountLong);
+        }
+        int totalChunkCount = (int) totalChunkCountLong;
 
-        Map<Integer, byte[]> digestsOfChunks = new HashMap<>(totalChunkCount);
-        for (int digestAlgorithm : digestAlgorithms) {
+        byte[][] digestsOfChunks = new byte[digestAlgorithms.length][];
+        for (int i = 0; i < digestAlgorithms.length; i++) {
+            int digestAlgorithm = digestAlgorithms[i];
             int digestOutputSizeBytes = getContentDigestAlgorithmOutputSizeBytes(digestAlgorithm);
             byte[] concatenationOfChunkCountAndChunkDigests =
                     new byte[5 + totalChunkCount * digestOutputSizeBytes];
@@ -476,49 +480,71 @@
                     totalChunkCount,
                     concatenationOfChunkCountAndChunkDigests,
                     1);
-            digestsOfChunks.put(digestAlgorithm, concatenationOfChunkCountAndChunkDigests);
+            digestsOfChunks[i] = concatenationOfChunkCountAndChunkDigests;
         }
 
         byte[] chunkContentPrefix = new byte[5];
         chunkContentPrefix[0] = (byte) 0xa5;
         int chunkIndex = 0;
-        for (ByteBuffer input : contents) {
-            while (input.hasRemaining()) {
-                int chunkSize = Math.min(input.remaining(), CHUNK_SIZE_BYTES);
-                ByteBuffer chunk = getByteBuffer(input, chunkSize);
-                for (int digestAlgorithm : digestAlgorithms) {
-                    String jcaAlgorithmName =
-                            getContentDigestAlgorithmJcaDigestAlgorithm(digestAlgorithm);
-                    MessageDigest md;
-                    try {
-                        md = MessageDigest.getInstance(jcaAlgorithmName);
-                    } catch (NoSuchAlgorithmException e) {
-                        throw new RuntimeException(jcaAlgorithmName + " digest not supported", e);
-                    }
-                    chunk.clear();
-                    setUnsignedInt32LittleEndian(chunk.remaining(), chunkContentPrefix, 1);
-                    md.update(chunkContentPrefix);
-                    md.update(chunk);
-                    byte[] concatenationOfChunkCountAndChunkDigests =
-                            digestsOfChunks.get(digestAlgorithm);
+        MessageDigest[] mds = new MessageDigest[digestAlgorithms.length];
+        for (int i = 0; i < digestAlgorithms.length; i++) {
+            String jcaAlgorithmName =
+                    getContentDigestAlgorithmJcaDigestAlgorithm(digestAlgorithms[i]);
+            try {
+                mds[i] = MessageDigest.getInstance(jcaAlgorithmName);
+            } catch (NoSuchAlgorithmException e) {
+                throw new RuntimeException(jcaAlgorithmName + " digest not supported", e);
+            }
+        }
+        // TODO: Compute digests of chunks in parallel when beneficial. This requires some research
+        // into how to parallelize (if at all) based on the capabilities of the hardware on which
+        // this code is running and based on the size of input.
+        int dataSourceIndex = 0;
+        for (DataSource input : contents) {
+            long inputOffset = 0;
+            long inputRemaining = input.size();
+            while (inputRemaining > 0) {
+                int chunkSize = (int) Math.min(inputRemaining, CHUNK_SIZE_BYTES);
+                setUnsignedInt32LittleEndian(chunkSize, chunkContentPrefix, 1);
+                for (int i = 0; i < mds.length; i++) {
+                    mds[i].update(chunkContentPrefix);
+                }
+                try {
+                    input.feedIntoMessageDigests(mds, inputOffset, chunkSize);
+                } catch (IOException e) {
+                    throw new DigestException(
+                            "Failed to digest chunk #" + chunkIndex + " of section #"
+                                    + dataSourceIndex,
+                            e);
+                }
+                for (int i = 0; i < digestAlgorithms.length; i++) {
+                    int digestAlgorithm = digestAlgorithms[i];
+                    byte[] concatenationOfChunkCountAndChunkDigests = digestsOfChunks[i];
                     int expectedDigestSizeBytes =
                             getContentDigestAlgorithmOutputSizeBytes(digestAlgorithm);
-                    int actualDigestSizeBytes = md.digest(concatenationOfChunkCountAndChunkDigests,
-                            5 + chunkIndex * expectedDigestSizeBytes, expectedDigestSizeBytes);
+                    MessageDigest md = mds[i];
+                    int actualDigestSizeBytes =
+                            md.digest(
+                                    concatenationOfChunkCountAndChunkDigests,
+                                    5 + chunkIndex * expectedDigestSizeBytes,
+                                    expectedDigestSizeBytes);
                     if (actualDigestSizeBytes != expectedDigestSizeBytes) {
                         throw new RuntimeException(
                                 "Unexpected output size of " + md.getAlgorithm() + " digest: "
                                         + actualDigestSizeBytes);
                     }
                 }
+                inputOffset += chunkSize;
+                inputRemaining -= chunkSize;
                 chunkIndex++;
             }
+            dataSourceIndex++;
         }
 
-        Map<Integer, byte[]> result = new HashMap<>(digestAlgorithms.length);
-        for (Map.Entry<Integer, byte[]> entry : digestsOfChunks.entrySet()) {
-            int digestAlgorithm = entry.getKey();
-            byte[] input = entry.getValue();
+        byte[][] result = new byte[digestAlgorithms.length][];
+        for (int i = 0; i < digestAlgorithms.length; i++) {
+            int digestAlgorithm = digestAlgorithms[i];
+            byte[] input = digestsOfChunks[i];
             String jcaAlgorithmName = getContentDigestAlgorithmJcaDigestAlgorithm(digestAlgorithm);
             MessageDigest md;
             try {
@@ -527,49 +553,47 @@
                 throw new RuntimeException(jcaAlgorithmName + " digest not supported", e);
             }
             byte[] output = md.digest(input);
-            result.put(digestAlgorithm, output);
+            result[i] = output;
         }
         return result;
     }
 
     /**
-     * Finds the offset of ZIP End of Central Directory (EoCD).
+     * Returns the ZIP End of Central Directory (EoCD) and its offset in the file.
      *
-     * @throws SignatureNotFoundException If the EoCD could not be found
+     * @throws IOException if an I/O error occurs while reading the file.
+     * @throws SignatureNotFoundException if the EoCD could not be found.
      */
-    private static int getEocdOffset(ByteBuffer apkContents) throws SignatureNotFoundException {
-        int eocdOffset = ZipUtils.findZipEndOfCentralDirectoryRecord(apkContents);
-        if (eocdOffset == -1) {
+    private static Pair<ByteBuffer, Long> getEocd(RandomAccessFile apk)
+            throws IOException, SignatureNotFoundException {
+        Pair<ByteBuffer, Long> eocdAndOffsetInFile =
+                ZipUtils.findZipEndOfCentralDirectoryRecord(apk);
+        if (eocdAndOffsetInFile == null) {
             throw new SignatureNotFoundException(
                     "Not an APK file: ZIP End of Central Directory record not found");
         }
-        return eocdOffset;
+        return eocdAndOffsetInFile;
     }
 
-    private static long getCentralDirOffset(ByteBuffer apkContents, int eocdOffset)
+    private static long getCentralDirOffset(ByteBuffer eocd, long eocdOffset)
             throws SignatureNotFoundException {
-        if (ZipUtils.isZip64EndOfCentralDirectoryLocatorPresent(apkContents, eocdOffset)) {
-            throw new SignatureNotFoundException("ZIP64 APK not supported");
-        }
-        ByteBuffer eocd = sliceFromTo(apkContents, eocdOffset, apkContents.capacity());
-
         // Look up the offset of ZIP Central Directory.
-        long centralDirOffsetLong = ZipUtils.getZipEocdCentralDirectoryOffset(eocd);
-        if (centralDirOffsetLong >= eocdOffset) {
+        long centralDirOffset = ZipUtils.getZipEocdCentralDirectoryOffset(eocd);
+        if (centralDirOffset >= eocdOffset) {
             throw new SignatureNotFoundException(
-                    "ZIP Central Directory offset out of range: " + centralDirOffsetLong
+                    "ZIP Central Directory offset out of range: " + centralDirOffset
                     + ". ZIP End of Central Directory offset: " + eocdOffset);
         }
-        long centralDirSizeLong = ZipUtils.getZipEocdCentralDirectorySizeBytes(eocd);
-        if (centralDirOffsetLong + centralDirSizeLong != eocdOffset) {
+        long centralDirSize = ZipUtils.getZipEocdCentralDirectorySizeBytes(eocd);
+        if (centralDirOffset + centralDirSize != eocdOffset) {
             throw new SignatureNotFoundException(
                     "ZIP Central Directory is not immediately followed by End of Central"
                     + " Directory");
         }
-        return centralDirOffsetLong;
+        return centralDirOffset;
     }
 
-    private static final int getChunkCount(int inputSizeBytes) {
+    private static final long getChunkCount(long inputSizeBytes) {
         return (inputSizeBytes + CHUNK_SIZE_BYTES - 1) / CHUNK_SIZE_BYTES;
     }
 
@@ -837,10 +861,9 @@
 
     private static final int APK_SIGNATURE_SCHEME_V2_BLOCK_ID = 0x7109871a;
 
-    private static int findApkSigningBlock(ByteBuffer apkContents, int centralDirOffset)
-            throws SignatureNotFoundException {
-        checkByteOrderLittleEndian(apkContents);
-
+    private static Pair<ByteBuffer, Long> findApkSigningBlock(
+            RandomAccessFile apk, long centralDirOffset)
+                    throws IOException, SignatureNotFoundException {
         // FORMAT:
         // OFFSET       DATA TYPE  DESCRIPTION
         // * @+0  bytes uint64:    size in bytes (excluding this field)
@@ -853,32 +876,42 @@
                     "APK too small for APK Signing Block. ZIP Central Directory offset: "
                             + centralDirOffset);
         }
-        // Check magic field present
-        if ((apkContents.getLong(centralDirOffset - 16) != APK_SIG_BLOCK_MAGIC_LO)
-                || (apkContents.getLong(centralDirOffset - 8) != APK_SIG_BLOCK_MAGIC_HI)) {
+        // Read the magic and offset in file from the footer section of the block:
+        // * uint64:   size of block
+        // * 16 bytes: magic
+        ByteBuffer footer = ByteBuffer.allocate(24);
+        footer.order(ByteOrder.LITTLE_ENDIAN);
+        apk.seek(centralDirOffset - footer.capacity());
+        apk.readFully(footer.array(), footer.arrayOffset(), footer.capacity());
+        if ((footer.getLong(8) != APK_SIG_BLOCK_MAGIC_LO)
+                || (footer.getLong(16) != APK_SIG_BLOCK_MAGIC_HI)) {
             throw new SignatureNotFoundException(
                     "No APK Signing Block before ZIP Central Directory");
         }
         // Read and compare size fields
-        long apkSigBlockSizeLong = apkContents.getLong(centralDirOffset - 24);
-        if ((apkSigBlockSizeLong < 24) || (apkSigBlockSizeLong > Integer.MAX_VALUE - 8)) {
+        long apkSigBlockSizeInFooter = footer.getLong(0);
+        if ((apkSigBlockSizeInFooter < footer.capacity())
+                || (apkSigBlockSizeInFooter > Integer.MAX_VALUE - 8)) {
             throw new SignatureNotFoundException(
-                    "APK Signing Block size out of range: " + apkSigBlockSizeLong);
+                    "APK Signing Block size out of range: " + apkSigBlockSizeInFooter);
         }
-        int apkSigBlockSizeFromFooter = (int) apkSigBlockSizeLong;
-        int totalSize = apkSigBlockSizeFromFooter + 8;
-        int apkSigBlockOffset = centralDirOffset - totalSize;
+        int totalSize = (int) (apkSigBlockSizeInFooter + 8);
+        long apkSigBlockOffset = centralDirOffset - totalSize;
         if (apkSigBlockOffset < 0) {
             throw new SignatureNotFoundException(
                     "APK Signing Block offset out of range: " + apkSigBlockOffset);
         }
-        long apkSigBlockSizeFromHeader = apkContents.getLong(apkSigBlockOffset);
-        if (apkSigBlockSizeFromHeader != apkSigBlockSizeFromFooter) {
+        ByteBuffer apkSigBlock = ByteBuffer.allocate(totalSize);
+        apkSigBlock.order(ByteOrder.LITTLE_ENDIAN);
+        apk.seek(apkSigBlockOffset);
+        apk.readFully(apkSigBlock.array(), apkSigBlock.arrayOffset(), apkSigBlock.capacity());
+        long apkSigBlockSizeInHeader = apkSigBlock.getLong(0);
+        if (apkSigBlockSizeInHeader != apkSigBlockSizeInFooter) {
             throw new SignatureNotFoundException(
                     "APK Signing Block sizes in header and footer do not match: "
-                            + apkSigBlockSizeFromHeader + " vs " + apkSigBlockSizeFromFooter);
+                            + apkSigBlockSizeInHeader + " vs " + apkSigBlockSizeInFooter);
         }
-        return apkSigBlockOffset;
+        return Pair.create(apkSigBlock, apkSigBlockOffset);
     }
 
     private static ByteBuffer findApkSignatureSchemeV2Block(ByteBuffer apkSigningBlock)
@@ -930,6 +963,8 @@
     }
 
     public static class SignatureNotFoundException extends Exception {
+        private static final long serialVersionUID = 1L;
+
         public SignatureNotFoundException(String message) {
             super(message);
         }
@@ -940,6 +975,159 @@
     }
 
     /**
+     * Source of data to be digested.
+     */
+    private static interface DataSource {
+
+        /**
+         * Returns the size (in bytes) of the data offered by this source.
+         */
+        long size();
+
+        /**
+         * Feeds the specified region of this source's data into the provided digests. Each digest
+         * instance gets the same data.
+         *
+         * @param offset offset of the region inside this data source.
+         * @param size size (in bytes) of the region.
+         */
+        void feedIntoMessageDigests(MessageDigest[] mds, long offset, int size) throws IOException;
+    }
+
+    /**
+     * {@link DataSource} which provides data from a file descriptor by memory-mapping the sections
+     * of the file requested by
+     * {@link DataSource#feedIntoMessageDigests(MessageDigest[], long, int) feedIntoMessageDigests}.
+     */
+    private static final class MemoryMappedFileDataSource implements DataSource {
+        private static final Os OS = Libcore.os;
+        private static final long MEMORY_PAGE_SIZE_BYTES = OS.sysconf(OsConstants._SC_PAGESIZE);
+
+        private final FileDescriptor mFd;
+        private final long mFilePosition;
+        private final long mSize;
+
+        /**
+         * Constructs a new {@code MemoryMappedFileDataSource} for the specified region of the file.
+         *
+         * @param position start position of the region in the file.
+         * @param size size (in bytes) of the region.
+         */
+        public MemoryMappedFileDataSource(FileDescriptor fd, long position, long size) {
+            mFd = fd;
+            mFilePosition = position;
+            mSize = size;
+        }
+
+        @Override
+        public long size() {
+            return mSize;
+        }
+
+        @Override
+        public void feedIntoMessageDigests(
+                MessageDigest[] mds, long offset, int size) throws IOException {
+            // IMPLEMENTATION NOTE: After a lot of experimentation, the implementation of this
+            // method was settled on a straightforward mmap with prefaulting.
+            //
+            // This method is not using FileChannel.map API because that API does not offset a way
+            // to "prefault" the resulting memory pages. Without prefaulting, performance is about
+            // 10% slower on small to medium APKs, but is significantly worse for APKs in 500+ MB
+            // range. FileChannel.load (which currently uses madvise) doesn't help. Finally,
+            // invoking madvise (MADV_SEQUENTIAL) after mmap with prefaulting wastes quite a bit of
+            // time, which is not compensated for by faster reads.
+
+            // We mmap the smallest region of the file containing the requested data. mmap requires
+            // that the start offset in the file must be a multiple of memory page size. We thus may
+            // need to mmap from an offset less than the requested offset.
+            long filePosition = mFilePosition + offset;
+            long mmapFilePosition =
+                    (filePosition / MEMORY_PAGE_SIZE_BYTES) * MEMORY_PAGE_SIZE_BYTES;
+            int dataStartOffsetInMmapRegion = (int) (filePosition - mmapFilePosition);
+            long mmapRegionSize = size + dataStartOffsetInMmapRegion;
+            long mmapPtr = 0;
+            try {
+                mmapPtr = OS.mmap(
+                        0, // let the OS choose the start address of the region in memory
+                        mmapRegionSize,
+                        OsConstants.PROT_READ,
+                        OsConstants.MAP_SHARED | OsConstants.MAP_POPULATE, // "prefault" all pages
+                        mFd,
+                        mmapFilePosition);
+                // Feeding a memory region into MessageDigest requires the region to be represented
+                // as a direct ByteBuffer.
+                ByteBuffer buf = new DirectByteBuffer(
+                        size,
+                        mmapPtr + dataStartOffsetInMmapRegion,
+                        mFd,  // not really needed, but just in case
+                        null, // no need to clean up -- it's taken care of by the finally block
+                        true  // read only buffer
+                        );
+                for (MessageDigest md : mds) {
+                    buf.position(0);
+                    md.update(buf);
+                }
+            } catch (ErrnoException e) {
+                throw new IOException("Failed to mmap " + mmapRegionSize + " bytes", e);
+            } finally {
+                if (mmapPtr != 0) {
+                    try {
+                        OS.munmap(mmapPtr, mmapRegionSize);
+                    } catch (ErrnoException ignored) {}
+                }
+            }
+        }
+    }
+
+    /**
+     * {@link DataSource} which provides data from a {@link ByteBuffer}.
+     */
+    private static final class ByteBufferDataSource implements DataSource {
+        /**
+         * Underlying buffer. The data is stored between position 0 and the buffer's capacity.
+         * The buffer's position is 0 and limit is equal to capacity.
+         */
+        private final ByteBuffer mBuf;
+
+        public ByteBufferDataSource(ByteBuffer buf) {
+            // Defensive copy, to avoid changes to mBuf being visible in buf.
+            mBuf = buf.slice();
+        }
+
+        @Override
+        public long size() {
+            return mBuf.capacity();
+        }
+
+        @Override
+        public void feedIntoMessageDigests(
+                MessageDigest[] mds, long offset, int size) throws IOException {
+            // There's no way to tell MessageDigest to read data from ByteBuffer from a position
+            // other than the buffer's current position. We thus need to change the buffer's
+            // position to match the requested offset.
+            //
+            // In the future, it may be necessary to compute digests of multiple regions in
+            // parallel. Given that digest computation is a slow operation, we enable multiple
+            // such requests to be fulfilled by this instance. This is achieved by serially
+            // creating a new ByteBuffer corresponding to the requested data range and then,
+            // potentially concurrently, feeding these buffers into MessageDigest instances.
+            ByteBuffer region;
+            synchronized (mBuf) {
+                mBuf.position((int) offset);
+                mBuf.limit((int) offset + size);
+                region = mBuf.slice();
+            }
+
+            for (MessageDigest md : mds) {
+                // Need to reset position to 0 at the start of each iteration because
+                // MessageDigest.update below sets it to the buffer's limit.
+                region.position(0);
+                md.update(region);
+            }
+        }
+    }
+
+    /**
      * For legacy reasons we need to return exactly the original encoded certificate bytes, instead
      * of letting the underlying implementation have a shot at re-encoding the data.
      */
diff --git a/core/java/android/util/apk/ZipUtils.java b/core/java/android/util/apk/ZipUtils.java
index a383d5c..cdbac18 100644
--- a/core/java/android/util/apk/ZipUtils.java
+++ b/core/java/android/util/apk/ZipUtils.java
@@ -16,13 +16,17 @@
 
 package android.util.apk;
 
+import android.util.Pair;
+
+import java.io.IOException;
+import java.io.RandomAccessFile;
 import java.nio.ByteBuffer;
 import java.nio.ByteOrder;
 
 /**
  * Assorted ZIP format helpers.
  *
- * <p>NOTE: Most helper methods operating on {@code ByteBuffer} instances except that the byte
+ * <p>NOTE: Most helper methods operating on {@code ByteBuffer} instances expect that the byte
  * order of these buffers is little-endian.
  */
 abstract class ZipUtils {
@@ -35,9 +39,101 @@
     private static final int ZIP_EOCD_COMMENT_LENGTH_FIELD_OFFSET = 20;
 
     private static final int ZIP64_EOCD_LOCATOR_SIZE = 20;
-    private static final int ZIP64_EOCD_LOCATOR_SIG = 0x07064b50;
+    private static final int ZIP64_EOCD_LOCATOR_SIG_REVERSE_BYTE_ORDER = 0x504b0607;
 
-    private static final int UINT32_MAX_VALUE = 0xffff;
+    private static final int UINT16_MAX_VALUE = 0xffff;
+
+    /**
+     * Returns the ZIP End of Central Directory record of the provided ZIP file.
+     *
+     * @return contents of the ZIP End of Central Directory record and the record's offset in the
+     *         file or {@code null} if the file does not contain the record.
+     *
+     * @throws IOException if an I/O error occurs while reading the file.
+     */
+    static Pair<ByteBuffer, Long> findZipEndOfCentralDirectoryRecord(RandomAccessFile zip)
+            throws IOException {
+        // ZIP End of Central Directory (EOCD) record is located at the very end of the ZIP archive.
+        // The record can be identified by its 4-byte signature/magic which is located at the very
+        // beginning of the record. A complication is that the record is variable-length because of
+        // the comment field.
+        // The algorithm for locating the ZIP EOCD record is as follows. We search backwards from
+        // end of the buffer for the EOCD record signature. Whenever we find a signature, we check
+        // the candidate record's comment length is such that the remainder of the record takes up
+        // exactly the remaining bytes in the buffer. The search is bounded because the maximum
+        // size of the comment field is 65535 bytes because the field is an unsigned 16-bit number.
+
+        long fileSize = zip.length();
+        if (fileSize < ZIP_EOCD_REC_MIN_SIZE) {
+            return null;
+        }
+
+        // Optimization: 99.99% of APKs have a zero-length comment field in the EoCD record and thus
+        // the EoCD record offset is known in advance. Try that offset first to avoid unnecessarily
+        // reading more data.
+        Pair<ByteBuffer, Long> result = findZipEndOfCentralDirectoryRecord(zip, 0);
+        if (result != null) {
+            return result;
+        }
+
+        // EoCD does not start where we expected it to. Perhaps it contains a non-empty comment
+        // field. Expand the search. The maximum size of the comment field in EoCD is 65535 because
+        // the comment length field is an unsigned 16-bit number.
+        return findZipEndOfCentralDirectoryRecord(zip, UINT16_MAX_VALUE);
+    }
+
+    /**
+     * Returns the ZIP End of Central Directory record of the provided ZIP file.
+     *
+     * @param maxCommentSize maximum accepted size (in bytes) of EoCD comment field. The permitted
+     *        value is from 0 to 65535 inclusive. The smaller the value, the faster this method
+     *        locates the record, provided its comment field is no longer than this value.
+     *
+     * @return contents of the ZIP End of Central Directory record and the record's offset in the
+     *         file or {@code null} if the file does not contain the record.
+     *
+     * @throws IOException if an I/O error occurs while reading the file.
+     */
+    private static Pair<ByteBuffer, Long> findZipEndOfCentralDirectoryRecord(
+            RandomAccessFile zip, int maxCommentSize) throws IOException {
+        // ZIP End of Central Directory (EOCD) record is located at the very end of the ZIP archive.
+        // The record can be identified by its 4-byte signature/magic which is located at the very
+        // beginning of the record. A complication is that the record is variable-length because of
+        // the comment field.
+        // The algorithm for locating the ZIP EOCD record is as follows. We search backwards from
+        // end of the buffer for the EOCD record signature. Whenever we find a signature, we check
+        // the candidate record's comment length is such that the remainder of the record takes up
+        // exactly the remaining bytes in the buffer. The search is bounded because the maximum
+        // size of the comment field is 65535 bytes because the field is an unsigned 16-bit number.
+
+        if ((maxCommentSize < 0) || (maxCommentSize > UINT16_MAX_VALUE)) {
+            throw new IllegalArgumentException("maxCommentSize: " + maxCommentSize);
+        }
+
+        long fileSize = zip.length();
+        if (fileSize < ZIP_EOCD_REC_MIN_SIZE) {
+            // No space for EoCD record in the file.
+            return null;
+        }
+        // Lower maxCommentSize if the file is too small.
+        maxCommentSize = (int) Math.min(maxCommentSize, fileSize - ZIP_EOCD_REC_MIN_SIZE);
+
+        ByteBuffer buf = ByteBuffer.allocate(ZIP_EOCD_REC_MIN_SIZE + maxCommentSize);
+        buf.order(ByteOrder.LITTLE_ENDIAN);
+        long bufOffsetInFile = fileSize - buf.capacity();
+        zip.seek(bufOffsetInFile);
+        zip.readFully(buf.array(), buf.arrayOffset(), buf.capacity());
+        int eocdOffsetInBuf = findZipEndOfCentralDirectoryRecord(buf);
+        if (eocdOffsetInBuf == -1) {
+            // No EoCD record found in the buffer
+            return null;
+        }
+        // EoCD found
+        buf.position(eocdOffsetInBuf);
+        ByteBuffer eocd = buf.slice();
+        eocd.order(ByteOrder.LITTLE_ENDIAN);
+        return Pair.create(eocd, bufOffsetInFile + eocdOffsetInBuf);
+    }
 
     /**
      * Returns the position at which ZIP End of Central Directory record starts in the provided
@@ -45,7 +141,7 @@
      *
      * <p>NOTE: Byte order of {@code zipContents} must be little-endian.
      */
-    public static int findZipEndOfCentralDirectoryRecord(ByteBuffer zipContents) {
+    private static int findZipEndOfCentralDirectoryRecord(ByteBuffer zipContents) {
         assertByteOrderLittleEndian(zipContents);
 
         // ZIP End of Central Directory (EOCD) record is located at the very end of the ZIP archive.
@@ -56,14 +152,13 @@
         // end of the buffer for the EOCD record signature. Whenever we find a signature, we check
         // the candidate record's comment length is such that the remainder of the record takes up
         // exactly the remaining bytes in the buffer. The search is bounded because the maximum
-        // size of the comment field is 65535 bytes because the field is an unsigned 32-bit number.
+        // size of the comment field is 65535 bytes because the field is an unsigned 16-bit number.
 
         int archiveSize = zipContents.capacity();
         if (archiveSize < ZIP_EOCD_REC_MIN_SIZE) {
-            System.out.println("File size smaller than EOCD min size");
             return -1;
         }
-        int maxCommentLength = Math.min(archiveSize - ZIP_EOCD_REC_MIN_SIZE, UINT32_MAX_VALUE);
+        int maxCommentLength = Math.min(archiveSize - ZIP_EOCD_REC_MIN_SIZE, UINT16_MAX_VALUE);
         int eocdWithEmptyCommentStartPosition = archiveSize - ZIP_EOCD_REC_MIN_SIZE;
         for (int expectedCommentLength = 0; expectedCommentLength < maxCommentLength;
                 expectedCommentLength++) {
@@ -82,24 +177,28 @@
     }
 
     /**
-     * Returns {@code true} if the provided buffer contains a ZIP64 End of Central Directory
+     * Returns {@code true} if the provided file contains a ZIP64 End of Central Directory
      * Locator.
      *
-     * <p>NOTE: Byte order of {@code zipContents} must be little-endian.
+     * @param zipEndOfCentralDirectoryPosition offset of the ZIP End of Central Directory record
+     *        in the file.
+     *
+     * @throws IOException if an I/O error occurs while reading the file.
      */
     public static final boolean isZip64EndOfCentralDirectoryLocatorPresent(
-            ByteBuffer zipContents, int zipEndOfCentralDirectoryPosition) {
-        assertByteOrderLittleEndian(zipContents);
+            RandomAccessFile zip, long zipEndOfCentralDirectoryPosition) throws IOException {
 
         // ZIP64 End of Central Directory Locator immediately precedes the ZIP End of Central
         // Directory Record.
-
-        int locatorPosition = zipEndOfCentralDirectoryPosition - ZIP64_EOCD_LOCATOR_SIZE;
+        long locatorPosition = zipEndOfCentralDirectoryPosition - ZIP64_EOCD_LOCATOR_SIZE;
         if (locatorPosition < 0) {
             return false;
         }
 
-        return zipContents.getInt(locatorPosition) == ZIP64_EOCD_LOCATOR_SIG;
+        zip.seek(locatorPosition);
+        // RandomAccessFile.readInt assumes big-endian byte order, but ZIP format uses
+        // little-endian.
+        return zip.readInt() == ZIP64_EOCD_LOCATOR_SIG_REVERSE_BYTE_ORDER;
     }
 
     /**
diff --git a/core/java/android/view/IWindowSession.aidl b/core/java/android/view/IWindowSession.aidl
index 6a2cc80..a1e2e94 100644
--- a/core/java/android/view/IWindowSession.aidl
+++ b/core/java/android/view/IWindowSession.aidl
@@ -112,15 +112,18 @@
      *
      *  @param window The window being modified. Must be attached to a parent window
      *  or this call will fail.
-     *  @param x The new x position
-     *  @param y The new y position
-     *  @param width The new width
-     *  @param height The new height
+     *  @param left The new left position
+     *  @param top The new top position
+     *  @param right The new right position
+     *  @param bottom The new bottom position
+     *  @param requestedWidth The new requested width
+     *  @param requestedHeight The new requested height
      *  @param deferTransactionUntilFrame Frame number from our parent (attached) to
      *  defer this action until.
      *  @param outFrame Rect in which is placed the new position/size on screen.
      */
     void repositionChild(IWindow childWindow, int left, int top, int right, int bottom,
+            int requestedWidth, int requestedHeight,
             long deferTransactionUntilFrame, out Rect outFrame);
 
     /*
diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java
index 2c9d691..477ffd9 100644
--- a/core/java/android/view/SurfaceView.java
+++ b/core/java/android/view/SurfaceView.java
@@ -665,7 +665,9 @@
                             "postion = [%d, %d, %d, %d]", mWindowSpaceLeft, mWindowSpaceTop,
                             mLocation[0], mLocation[1]));
                     mSession.repositionChild(mWindow, mWindowSpaceLeft, mWindowSpaceTop,
-                            mLocation[0], mLocation[1], -1, mWinFrame);
+                            mLocation[0], mLocation[1],
+                            mWindowSpaceWidth, mWindowSpaceHeight,
+                            -1, mWinFrame);
                 } catch (RemoteException ex) {
                     Log.e(TAG, "Exception from relayout", ex);
                 }
@@ -700,7 +702,9 @@
                         right, bottom));
             }
             // Just using mRTLastReportedPosition as a dummy rect here
-            session.repositionChild(window, left, top, right, bottom, frameNumber,
+            session.repositionChild(window, left, top, right, bottom,
+                    mWindowSpaceWidth, mWindowSpaceHeight,
+                    frameNumber,
                     mRTLastReportedPosition);
             // Now overwrite mRTLastReportedPosition with our values
             mRTLastReportedPosition.set(left, top, right, bottom);
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index e5ab971..6d35a58 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -5020,8 +5020,8 @@
 
         if (scrollabilityCache.scrollBar == null) {
             scrollabilityCache.scrollBar = new ScrollBarDrawable();
-            scrollabilityCache.scrollBar.setCallback(this);
             scrollabilityCache.scrollBar.setState(getDrawableState());
+            scrollabilityCache.scrollBar.setCallback(this);
         }
 
         final boolean fadeScrollbars = a.getBoolean(R.styleable.View_fadeScrollbars, true);
@@ -13295,8 +13295,8 @@
 
         if (scrollCache.scrollBar == null) {
             scrollCache.scrollBar = new ScrollBarDrawable();
-            scrollCache.scrollBar.setCallback(this);
             scrollCache.scrollBar.setState(getDrawableState());
+            scrollCache.scrollBar.setCallback(this);
         }
 
         if (isHorizontalScrollBarEnabled() || isVerticalScrollBarEnabled()) {
@@ -18002,7 +18002,13 @@
          * to clear the previous drawable. setVisible first while we still have the callback set.
          */
         if (mBackground != null) {
-            if (isAttachedToWindow()) {
+            // It's possible for this method to be invoked from the View constructor before
+            // subclass constructors have run. Drawables can and should trigger invalidations
+            // and other activity with their callback on visibility changes, which shouldn't
+            // happen before subclass constructors finish. However, we won't have set the
+            // drawable as visible until the view becomes attached. This guard below keeps
+            // multiple calls to this method from constructors from causing issues.
+            if (mBackground.isVisible()) {
                 mBackground.setVisible(false, false);
             }
             mBackground.setCallback(null);
@@ -18049,7 +18055,6 @@
             // which requires the view set as the callback (us) to recognize the drawable as
             // belonging to it as per verifyDrawable.
             mBackground = background;
-            background.setCallback(this);
             if (background.isStateful()) {
                 background.setState(getDrawableState());
             }
@@ -18059,6 +18064,9 @@
 
             applyBackgroundTint();
 
+            // Set callback last, since the view may still be initializing.
+            background.setCallback(this);
+
             if ((mPrivateFlags & PFLAG_SKIP_DRAW) != 0) {
                 mPrivateFlags &= ~PFLAG_SKIP_DRAW;
                 requestLayout = true;
@@ -18235,7 +18243,13 @@
         }
 
         if (mForegroundInfo.mDrawable != null) {
-            if (isAttachedToWindow()) {
+            // It's possible for this method to be invoked from the View constructor before
+            // subclass constructors have run. Drawables can and should trigger invalidations
+            // and other activity with their callback on visibility changes, which shouldn't
+            // happen before subclass constructors finish. However, we won't have set the
+            // drawable as visible until the view becomes attached. This guard below keeps
+            // multiple calls to this method from constructors from causing issues.
+            if (mForegroundInfo.mDrawable.isVisible()) {
                 mForegroundInfo.mDrawable.setVisible(false, false);
             }
             mForegroundInfo.mDrawable.setCallback(null);
@@ -18248,7 +18262,6 @@
             if ((mPrivateFlags & PFLAG_SKIP_DRAW) != 0) {
                 mPrivateFlags &= ~PFLAG_SKIP_DRAW;
             }
-            foreground.setCallback(this);
             foreground.setLayoutDirection(getLayoutDirection());
             if (foreground.isStateful()) {
                 foreground.setState(getDrawableState());
@@ -18257,6 +18270,8 @@
             if (isAttachedToWindow()) {
                 foreground.setVisible(getWindowVisibility() == VISIBLE && isShown(), false);
             }
+            // Set callback last, since the view may still be initializing.
+            foreground.setCallback(this);
         } else if ((mViewFlags & WILL_NOT_DRAW) != 0 && mBackground == null) {
             mPrivateFlags |= PFLAG_SKIP_DRAW;
         }
diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java
index 97cafb7..cf5cc3e 100644
--- a/core/java/android/view/inputmethod/InputMethodManager.java
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -975,7 +975,17 @@
      * shown to the user, if needed.  Call this if the user interacts with
      * your view in such a way that they have expressed they would like to
      * start performing input into it.
-     * 
+     *
+     * <p><strong>Caveat:</strong> {@link ResultReceiver} instance passed to
+     * this method can be a long-lived object, because it may not be
+     * garbage-collected until all the corresponding {@link ResultReceiver}
+     * objects transferred to different processes get garbage-collected.
+     * Follow the general patterns to avoid memory leaks in Android.
+     * Consider to use {@link java.lang.ref.WeakReference} so that application
+     * logic objects such as {@link android.app.Activity} and {@link Context}
+     * can be garbage collected regardless of the lifetime of
+     * {@link ResultReceiver}.
+     *
      * @param view The currently focused view, which would like to receive
      * soft keyboard input.
      * @param flags Provides additional operating flags.  Currently may be
@@ -1044,7 +1054,17 @@
      * that is currently accepting input.  This should be called as a result
      * of the user doing some actually than fairly explicitly requests to
      * have the input window hidden.
-     * 
+     *
+     * <p><strong>Caveat:</strong> {@link ResultReceiver} instance passed to
+     * this method can be a long-lived object, because it may not be
+     * garbage-collected until all the corresponding {@link ResultReceiver}
+     * objects transferred to different processes get garbage-collected.
+     * Follow the general patterns to avoid memory leaks in Android.
+     * Consider to use {@link java.lang.ref.WeakReference} so that application
+     * logic objects such as {@link android.app.Activity} and {@link Context}
+     * can be garbage collected regardless of the lifetime of
+     * {@link ResultReceiver}.
+     *
      * @param windowToken The token of the window that is making the request,
      * as returned by {@link View#getWindowToken() View.getWindowToken()}.
      * @param flags Provides additional operating flags.  Currently may be
diff --git a/core/java/android/widget/ImageView.java b/core/java/android/widget/ImageView.java
index 222a040..0206577 100644
--- a/core/java/android/widget/ImageView.java
+++ b/core/java/android/widget/ImageView.java
@@ -911,11 +911,17 @@
         }
 
         if (mDrawable != null) {
-            mDrawable.setCallback(null);
-            unscheduleDrawable(mDrawable);
-            if (isAttachedToWindow()) {
+            // It's possible for this method to be invoked from the constructor before
+            // subclass constructors have run. Drawables can and should trigger invalidations
+            // and other activity with their callback on visibility changes, which shouldn't
+            // happen before subclass constructors finish. However, we won't have set the
+            // drawable as visible until the view becomes attached. This guard below keeps
+            // multiple calls to this method from constructors from causing issues.
+            if (mDrawable.isVisible()) {
                 mDrawable.setVisible(false, false);
             }
+            mDrawable.setCallback(null);
+            unscheduleDrawable(mDrawable);
         }
 
         mDrawable = d;
diff --git a/core/java/android/widget/ListPopupWindow.java b/core/java/android/widget/ListPopupWindow.java
index 36e0c77..6a10743 100644
--- a/core/java/android/widget/ListPopupWindow.java
+++ b/core/java/android/widget/ListPopupWindow.java
@@ -1183,28 +1183,28 @@
         }
 
         // getMaxAvailableHeight() subtracts the padding, so we put it back
-        // to get the available height for the whole window
-        int padding = 0;
-        Drawable background = mPopup.getBackground();
+        // to get the available height for the whole window.
+        final int padding;
+        final Drawable background = mPopup.getBackground();
         if (background != null) {
             background.getPadding(mTempRect);
             padding = mTempRect.top + mTempRect.bottom;
 
-            // If we don't have an explicit vertical offset, determine one from the window
-            // background so that content will line up.
+            // If we don't have an explicit vertical offset, determine one from
+            // the window background so that content will line up.
             if (!mDropDownVerticalOffsetSet) {
                 mDropDownVerticalOffset = -mTempRect.top;
             }
         } else {
             mTempRect.setEmpty();
+            padding = 0;
         }
 
         // Max height available on the screen for a popup.
-        boolean ignoreBottomDecorations =
+        final boolean ignoreBottomDecorations =
                 mPopup.getInputMethodMode() == PopupWindow.INPUT_METHOD_NOT_NEEDED;
         final int maxHeight = mPopup.getMaxAvailableHeight(
                 getAnchorView(), mDropDownVerticalOffset, ignoreBottomDecorations);
-
         if (mDropDownAlwaysVisible || mDropDownHeight == ViewGroup.LayoutParams.MATCH_PARENT) {
             return maxHeight + padding;
         }
@@ -1213,25 +1213,30 @@
         switch (mDropDownWidth) {
             case ViewGroup.LayoutParams.WRAP_CONTENT:
                 childWidthSpec = MeasureSpec.makeMeasureSpec(
-                        mContext.getResources().getDisplayMetrics().widthPixels -
-                        (mTempRect.left + mTempRect.right),
+                        mContext.getResources().getDisplayMetrics().widthPixels
+                                - (mTempRect.left + mTempRect.right),
                         MeasureSpec.AT_MOST);
                 break;
             case ViewGroup.LayoutParams.MATCH_PARENT:
                 childWidthSpec = MeasureSpec.makeMeasureSpec(
-                        mContext.getResources().getDisplayMetrics().widthPixels -
-                        (mTempRect.left + mTempRect.right),
+                        mContext.getResources().getDisplayMetrics().widthPixels
+                                - (mTempRect.left + mTempRect.right),
                         MeasureSpec.EXACTLY);
                 break;
             default:
                 childWidthSpec = MeasureSpec.makeMeasureSpec(mDropDownWidth, MeasureSpec.EXACTLY);
                 break;
         }
+
+        // Add padding only if the list has items in it, that way we don't show
+        // the popup if it is not needed.
         final int listContent = mDropDownList.measureHeightOfChildren(childWidthSpec,
-                0, ListView.NO_POSITION, maxHeight - otherHeights, -1);
-        // add padding only if the list has items in it, that way we don't show
-        // the popup if it is not needed
-        if (listContent > 0) otherHeights += padding;
+                0, DropDownListView.NO_POSITION, maxHeight - otherHeights, -1);
+        if (listContent > 0) {
+            final int listPadding = mDropDownList.getPaddingTop()
+                    + mDropDownList.getPaddingBottom();
+            otherHeights += padding + listPadding;
+        }
 
         return listContent + otherHeights;
     }
diff --git a/core/java/android/widget/PopupWindow.java b/core/java/android/widget/PopupWindow.java
index a1417f0..d1b5fc8 100644
--- a/core/java/android/widget/PopupWindow.java
+++ b/core/java/android/widget/PopupWindow.java
@@ -2027,21 +2027,24 @@
             mAnchorYoff = yoff;
         }
 
+        final WindowManager.LayoutParams p =
+                (WindowManager.LayoutParams) mDecorView.getLayoutParams();
+
         if (updateDimension) {
             if (width == -1) {
                 width = mPopupWidth;
             } else {
                 mPopupWidth = width;
+                p.width = width;
             }
             if (height == -1) {
                 height = mPopupHeight;
             } else {
                 mPopupHeight = height;
+                p.height  = height;
             }
         }
 
-        final WindowManager.LayoutParams p =
-                (WindowManager.LayoutParams) mDecorView.getLayoutParams();
         final int x = p.x;
         final int y = p.y;
         if (updateLocation) {
diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java
index 063288e..8bd63df 100644
--- a/core/java/android/widget/RemoteViews.java
+++ b/core/java/android/widget/RemoteViews.java
@@ -60,6 +60,7 @@
 import libcore.util.Objects;
 
 import com.android.internal.R;
+import com.android.internal.util.Preconditions;
 
 import java.lang.annotation.ElementType;
 import java.lang.annotation.Retention;
@@ -1096,6 +1097,13 @@
                 memoryCounter.addBitmapMemory(mBitmaps.get(i));
             }
         }
+
+        @Override
+        protected BitmapCache clone() {
+            BitmapCache bitmapCache = new BitmapCache();
+            bitmapCache.mBitmaps.addAll(mBitmaps);
+            return bitmapCache;
+        }
     }
 
     private class BitmapReflectionAction extends Action {
@@ -2227,10 +2235,21 @@
 
 
     public RemoteViews clone() {
+        Preconditions.checkState(mIsRoot, "RemoteView has been attached to another RemoteView. "
+                + "May only clone the root of a RemoteView hierarchy.");
+
         Parcel p = Parcel.obtain();
+
+        // Do not parcel the Bitmap cache - doing so creates an expensive copy of all bitmaps.
+        // Instead pretend we're not owning the cache while parceling.
+        mIsRoot = false;
         writeToParcel(p, 0);
         p.setDataPosition(0);
-        RemoteViews rv = new RemoteViews(p);
+        mIsRoot = true;
+
+        RemoteViews rv = new RemoteViews(p, mBitmapCache.clone());
+        rv.mIsRoot = true;
+
         p.recycle();
         return rv;
     }
@@ -2240,7 +2259,7 @@
     }
 
     /**
-     * Reutrns the layout id of the root layout associated with this RemoteViews. In the case
+     * Returns the layout id of the root layout associated with this RemoteViews. In the case
      * that the RemoteViews has both a landscape and portrait root, this will return the layout
      * id associated with the portrait layout.
      *
diff --git a/core/java/com/android/internal/app/ResolverActivity.java b/core/java/com/android/internal/app/ResolverActivity.java
index 80e1db0..5726de1 100644
--- a/core/java/com/android/internal/app/ResolverActivity.java
+++ b/core/java/com/android/internal/app/ResolverActivity.java
@@ -76,6 +76,7 @@
 import java.util.Objects;
 import java.util.Set;
 
+import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
 import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR;
 import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;
 
@@ -507,7 +508,9 @@
             mPackageMonitor.unregister();
             mRegistered = false;
         }
-        if ((getIntent().getFlags()&Intent.FLAG_ACTIVITY_NEW_TASK) != 0 && !isVoiceInteraction()) {
+        final Intent intent = getIntent();
+        if ((intent.getFlags() & FLAG_ACTIVITY_NEW_TASK) != 0 && !isVoiceInteraction()
+                && !mResolvingHome) {
             // This resolver is in the unusual situation where it has been
             // launched at the top of a new task.  We don't let it be added
             // to the recent tasks shown to the user, and we need to make sure
diff --git a/core/jni/Android.mk b/core/jni/Android.mk
index c6db0ed..c685b0c 100644
--- a/core/jni/Android.mk
+++ b/core/jni/Android.mk
@@ -117,13 +117,10 @@
     android/graphics/Interpolator.cpp \
     android/graphics/MaskFilter.cpp \
     android/graphics/Matrix.cpp \
-    android/graphics/MinikinSkia.cpp \
-    android/graphics/MinikinUtils.cpp \
     android/graphics/Movie.cpp \
     android/graphics/NinePatch.cpp \
     android/graphics/NinePatchPeeker.cpp \
     android/graphics/Paint.cpp \
-    android/graphics/PaintImpl.cpp \
     android/graphics/Path.cpp \
     android/graphics/PathMeasure.cpp \
     android/graphics/PathEffect.cpp \
@@ -135,7 +132,6 @@
     android/graphics/Shader.cpp \
     android/graphics/SurfaceTexture.cpp \
     android/graphics/Typeface.cpp \
-    android/graphics/TypefaceImpl.cpp \
     android/graphics/Utils.cpp \
     android/graphics/Xfermode.cpp \
     android/graphics/YuvToJpegEncoder.cpp \
diff --git a/core/jni/android/graphics/Bitmap.cpp b/core/jni/android/graphics/Bitmap.cpp
index 43e26b3..27b9830 100755
--- a/core/jni/android/graphics/Bitmap.cpp
+++ b/core/jni/android/graphics/Bitmap.cpp
@@ -1,7 +1,6 @@
 #define LOG_TAG "Bitmap"
 #include "Bitmap.h"
 
-#include "Paint.h"
 #include "SkBitmap.h"
 #include "SkPixelRef.h"
 #include "SkImageEncoder.h"
@@ -18,6 +17,7 @@
 #include "android_nio_utils.h"
 #include "CreateJavaOutputStreamAdaptor.h"
 #include <Caches.h>
+#include <hwui/Paint.h>
 
 #include "core_jni_helpers.h"
 
diff --git a/core/jni/android/graphics/Camera.cpp b/core/jni/android/graphics/Camera.cpp
index 6fcf689..76d6851 100644
--- a/core/jni/android/graphics/Camera.cpp
+++ b/core/jni/android/graphics/Camera.cpp
@@ -3,8 +3,8 @@
 
 #include "SkCamera.h"
 
-#include "Canvas.h"
 #include "GraphicsJNI.h"
+#include <hwui/Canvas.h>
 
 static jfieldID gNativeInstanceFieldID;
 
diff --git a/core/jni/android/graphics/CanvasProperty.cpp b/core/jni/android/graphics/CanvasProperty.cpp
index 728bc1c..c841d6a 100644
--- a/core/jni/android/graphics/CanvasProperty.cpp
+++ b/core/jni/android/graphics/CanvasProperty.cpp
@@ -16,9 +16,9 @@
 
 #include "jni.h"
 #include "GraphicsJNI.h"
-#include "Paint.h"
 #include <core_jni_helpers.h>
 
+#include <hwui/Paint.h>
 #include <utils/RefBase.h>
 #include <CanvasProperty.h>
 
diff --git a/core/jni/android/graphics/FontFamily.cpp b/core/jni/android/graphics/FontFamily.cpp
index 2e974a3..6dc251c 100644
--- a/core/jni/android/graphics/FontFamily.cpp
+++ b/core/jni/android/graphics/FontFamily.cpp
@@ -31,9 +31,9 @@
 #include <androidfw/AssetManager.h>
 #include "Utils.h"
 
-#include "TypefaceImpl.h"
+#include <hwui/MinikinSkia.h>
+#include <hwui/Typeface.h>
 #include <minikin/FontFamily.h>
-#include "MinikinSkia.h"
 
 #include <memory>
 
diff --git a/core/jni/android/graphics/Graphics.cpp b/core/jni/android/graphics/Graphics.cpp
index bd01c73..1ead618 100644
--- a/core/jni/android/graphics/Graphics.cpp
+++ b/core/jni/android/graphics/Graphics.cpp
@@ -7,13 +7,13 @@
 #include "JNIHelp.h"
 #include "GraphicsJNI.h"
 
-#include "Canvas.h"
 #include "SkCanvas.h"
 #include "SkDevice.h"
 #include "SkMath.h"
 #include "SkRegion.h"
 #include <android_runtime/AndroidRuntime.h>
 #include <cutils/ashmem.h>
+#include <hwui/Canvas.h>
 
 #include <Caches.h>
 #include <TextureCache.h>
diff --git a/core/jni/android/graphics/GraphicsJNI.h b/core/jni/android/graphics/GraphicsJNI.h
index e99a3ff..5baa8f8 100644
--- a/core/jni/android/graphics/GraphicsJNI.h
+++ b/core/jni/android/graphics/GraphicsJNI.h
@@ -11,15 +11,15 @@
 #include "SkPoint.h"
 #include "SkRect.h"
 #include "SkImageDecoder.h"
-#include <Canvas.h>
 #include <jni.h>
+#include <hwui/Canvas.h>
 
 class SkBitmapRegionDecoder;
 class SkCanvas;
 
 namespace android {
 class Paint;
-struct TypefaceImpl;
+struct Typeface;
 }
 
 class GraphicsJNI {
diff --git a/core/jni/android/graphics/Movie.cpp b/core/jni/android/graphics/Movie.cpp
index 498c590..71988f9c 100644
--- a/core/jni/android/graphics/Movie.cpp
+++ b/core/jni/android/graphics/Movie.cpp
@@ -1,7 +1,5 @@
-#include "Canvas.h"
 #include "CreateJavaOutputStreamAdaptor.h"
 #include "GraphicsJNI.h"
-#include "Paint.h"
 #include "ScopedLocalRef.h"
 #include "SkFrontBufferedStream.h"
 #include "SkMovie.h"
@@ -12,6 +10,8 @@
 
 #include <androidfw/Asset.h>
 #include <androidfw/ResourceTypes.h>
+#include <hwui/Canvas.h>
+#include <hwui/Paint.h>
 #include <jni.h>
 #include <netinet/in.h>
 
diff --git a/core/jni/android/graphics/NinePatch.cpp b/core/jni/android/graphics/NinePatch.cpp
index 3ccbb35..4f2f389 100644
--- a/core/jni/android/graphics/NinePatch.cpp
+++ b/core/jni/android/graphics/NinePatch.cpp
@@ -19,12 +19,12 @@
 #define LOG_NDEBUG 1
 
 #include <androidfw/ResourceTypes.h>
+#include <hwui/Canvas.h>
+#include <hwui/Paint.h>
 #include <utils/Log.h>
 
 #include <ResourceCache.h>
 
-#include "Paint.h"
-#include "Canvas.h"
 #include "SkCanvas.h"
 #include "SkRegion.h"
 #include "GraphicsJNI.h"
diff --git a/core/jni/android/graphics/Paint.cpp b/core/jni/android/graphics/Paint.cpp
index d00e94c..27d8fed 100644
--- a/core/jni/android/graphics/Paint.cpp
+++ b/core/jni/android/graphics/Paint.cpp
@@ -37,13 +37,13 @@
 #include "unicode/ushape.h"
 #include "utils/Blur.h"
 
+#include <hwui/MinikinSkia.h>
+#include <hwui/MinikinUtils.h>
+#include <hwui/Paint.h>
+#include <hwui/Typeface.h>
 #include <minikin/GraphemeBreak.h>
 #include <minikin/Measurement.h>
 #include <unicode/utf16.h>
-#include "MinikinSkia.h"
-#include "MinikinUtils.h"
-#include "Paint.h"
-#include "TypefaceImpl.h"
 
 #include <cassert>
 #include <cstring>
@@ -402,8 +402,8 @@
         const int kElegantDescent = -500;
         const int kElegantLeading = 0;
         Paint* paint = reinterpret_cast<Paint*>(paintHandle);
-        TypefaceImpl* typeface = reinterpret_cast<TypefaceImpl*>(typefaceHandle);
-        typeface = TypefaceImpl_resolveDefault(typeface);
+        Typeface* typeface = reinterpret_cast<Typeface*>(typefaceHandle);
+        typeface = Typeface::resolveDefault(typeface);
         FakedFont baseFont = typeface->fFontCollection->baseFontFaked(typeface->fStyle);
         float saveSkewX = paint->getTextSkewX();
         bool savefakeBold = paint->isFakeBoldText();
@@ -474,7 +474,7 @@
         return descent - ascent + leading;
     }
 
-    static jfloat doTextAdvances(JNIEnv *env, Paint *paint, TypefaceImpl* typeface,
+    static jfloat doTextAdvances(JNIEnv *env, Paint *paint, Typeface* typeface,
             const jchar *text, jint start, jint count, jint contextCount, jint bidiFlags,
             jfloatArray advances, jint advancesIndex) {
         NPE_CHECK_RETURN_ZERO(env, text);
@@ -510,7 +510,7 @@
             jcharArray text, jint index, jint count, jint contextIndex, jint contextCount,
             jint bidiFlags, jfloatArray advances, jint advancesIndex) {
         Paint* paint = reinterpret_cast<Paint*>(paintHandle);
-        TypefaceImpl* typeface = reinterpret_cast<TypefaceImpl*>(typefaceHandle);
+        Typeface* typeface = reinterpret_cast<Typeface*>(typefaceHandle);
         jchar* textArray = env->GetCharArrayElements(text, NULL);
         jfloat result = doTextAdvances(env, paint, typeface, textArray + contextIndex,
                 index - contextIndex, count, contextCount, bidiFlags, advances, advancesIndex);
@@ -523,7 +523,7 @@
             jstring text, jint start, jint end, jint contextStart, jint contextEnd, jint bidiFlags,
             jfloatArray advances, jint advancesIndex) {
         Paint* paint = reinterpret_cast<Paint*>(paintHandle);
-        TypefaceImpl* typeface = reinterpret_cast<TypefaceImpl*>(typefaceHandle);
+        Typeface* typeface = reinterpret_cast<Typeface*>(typefaceHandle);
         const jchar* textArray = env->GetStringChars(text, NULL);
         jfloat result = doTextAdvances(env, paint, typeface, textArray + contextStart,
                 start - contextStart, end - start, contextEnd - contextStart, bidiFlags,
@@ -590,7 +590,7 @@
         SkPath tmpPath;
     };
 
-    static void getTextPath(JNIEnv* env, Paint* paint, TypefaceImpl* typeface, const jchar* text,
+    static void getTextPath(JNIEnv* env, Paint* paint, Typeface* typeface, const jchar* text,
             jint count, jint bidiFlags, jfloat x, jfloat y, SkPath* path) {
         Layout layout;
         MinikinUtils::doLayout(&layout, paint, bidiFlags, typeface, text, 0, count, count);
@@ -613,7 +613,7 @@
             jlong typefaceHandle, jint bidiFlags,
             jcharArray text, jint index, jint count, jfloat x, jfloat y, jlong pathHandle) {
         Paint* paint = reinterpret_cast<Paint*>(paintHandle);
-        TypefaceImpl* typeface = reinterpret_cast<TypefaceImpl*>(typefaceHandle);
+        Typeface* typeface = reinterpret_cast<Typeface*>(typefaceHandle);
         SkPath* path = reinterpret_cast<SkPath*>(pathHandle);
         const jchar* textArray = env->GetCharArrayElements(text, NULL);
         getTextPath(env, paint, typeface, textArray + index, count, bidiFlags, x, y, path);
@@ -624,7 +624,7 @@
             jlong typefaceHandle, jint bidiFlags,
             jstring text, jint start, jint end, jfloat x, jfloat y, jlong pathHandle) {
         Paint* paint = reinterpret_cast<Paint*>(paintHandle);
-        TypefaceImpl* typeface = reinterpret_cast<TypefaceImpl*>(typefaceHandle);
+        Typeface* typeface = reinterpret_cast<Typeface*>(typefaceHandle);
         SkPath* path = reinterpret_cast<SkPath*>(pathHandle);
         const jchar* textArray = env->GetStringChars(text, NULL);
         getTextPath(env, paint, typeface, textArray + start, end - start, bidiFlags, x, y, path);
@@ -648,7 +648,7 @@
         return paint->getLooper() && paint->getLooper()->asABlurShadow(NULL);
     }
 
-    static int breakText(JNIEnv* env, const Paint& paint, TypefaceImpl* typeface, const jchar text[],
+    static int breakText(JNIEnv* env, const Paint& paint, Typeface* typeface, const jchar text[],
                          int count, float maxWidth, jint bidiFlags, jfloatArray jmeasured,
                          const bool forwardScan) {
         size_t measuredCount = 0;
@@ -685,7 +685,7 @@
         NPE_CHECK_RETURN_ZERO(env, jtext);
 
         Paint* paint = reinterpret_cast<Paint*>(paintHandle);
-        TypefaceImpl* typeface = reinterpret_cast<TypefaceImpl*>(typefaceHandle);
+        Typeface* typeface = reinterpret_cast<Typeface*>(typefaceHandle);
 
         bool forwardTextDirection;
         if (count < 0) {
@@ -714,7 +714,7 @@
         NPE_CHECK_RETURN_ZERO(env, jtext);
 
         Paint* paint = reinterpret_cast<Paint*>(paintHandle);
-        TypefaceImpl* typeface = reinterpret_cast<TypefaceImpl*>(typefaceHandle);
+        Typeface* typeface = reinterpret_cast<Typeface*>(typefaceHandle);
 
         int count = env->GetStringLength(jtext);
         const jchar* text = env->GetStringChars(jtext, NULL);
@@ -724,7 +724,7 @@
     }
 
     static void doTextBounds(JNIEnv* env, const jchar* text, int count, jobject bounds,
-            const Paint& paint, TypefaceImpl* typeface, jint bidiFlags) {
+            const Paint& paint, Typeface* typeface, jint bidiFlags) {
         SkRect  r;
         SkIRect ir;
 
@@ -743,7 +743,7 @@
     static void getStringBounds(JNIEnv* env, jobject, jlong paintHandle, jlong typefaceHandle,
                                 jstring text, jint start, jint end, jint bidiFlags, jobject bounds) {
         const Paint* paint = reinterpret_cast<Paint*>(paintHandle);
-        TypefaceImpl* typeface = reinterpret_cast<TypefaceImpl*>(typefaceHandle);
+        Typeface* typeface = reinterpret_cast<Typeface*>(typefaceHandle);
         const jchar* textArray = env->GetStringChars(text, NULL);
         doTextBounds(env, textArray + start, end - start, bounds, *paint, typeface, bidiFlags);
         env->ReleaseStringChars(text, textArray);
@@ -752,7 +752,7 @@
     static void getCharArrayBounds(JNIEnv* env, jobject, jlong paintHandle, jlong typefaceHandle,
                         jcharArray text, jint index, jint count, jint bidiFlags, jobject bounds) {
         const Paint* paint = reinterpret_cast<Paint*>(paintHandle);
-        TypefaceImpl* typeface = reinterpret_cast<TypefaceImpl*>(typefaceHandle);
+        Typeface* typeface = reinterpret_cast<Typeface*>(typefaceHandle);
         const jchar* textArray = env->GetCharArrayElements(text, NULL);
         doTextBounds(env, textArray + index, count, bounds, *paint, typeface, bidiFlags);
         env->ReleaseCharArrayElements(text, const_cast<jchar*>(textArray),
@@ -771,7 +771,7 @@
     static jboolean hasGlyph(JNIEnv *env, jclass, jlong paintHandle, jlong typefaceHandle,
             jint bidiFlags, jstring string) {
         const Paint* paint = reinterpret_cast<Paint*>(paintHandle);
-        TypefaceImpl* typeface = reinterpret_cast<TypefaceImpl*>(typefaceHandle);
+        Typeface* typeface = reinterpret_cast<Typeface*>(typefaceHandle);
         ScopedStringChars str(env, string);
 
         /* Start by rejecting unsupported base code point and variation selector pairs. */
@@ -820,7 +820,7 @@
         return nGlyphs > 0 && !layoutContainsNotdef(layout);
     }
 
-    static jfloat doRunAdvance(const Paint* paint, TypefaceImpl* typeface, const jchar buf[],
+    static jfloat doRunAdvance(const Paint* paint, Typeface* typeface, const jchar buf[],
             jint start, jint count, jint bufSize, jboolean isRtl, jint offset) {
         int bidiFlags = isRtl ? kBidi_Force_RTL : kBidi_Force_LTR;
         if (offset == count) {
@@ -837,7 +837,7 @@
             jlong typefaceHandle, jcharArray text, jint start, jint end, jint contextStart,
             jint contextEnd, jboolean isRtl, jint offset) {
         const Paint* paint = reinterpret_cast<Paint*>(paintHandle);
-        TypefaceImpl* typeface = reinterpret_cast<TypefaceImpl*>(typefaceHandle);
+        Typeface* typeface = reinterpret_cast<Typeface*>(typefaceHandle);
         jchar* textArray = (jchar*) env->GetPrimitiveArrayCritical(text, NULL);
         jfloat result = doRunAdvance(paint, typeface, textArray + contextStart,
                 start - contextStart, end - start, contextEnd - contextStart, isRtl,
@@ -846,7 +846,7 @@
         return result;
     }
 
-    static jint doOffsetForAdvance(const Paint* paint, TypefaceImpl* typeface, const jchar buf[],
+    static jint doOffsetForAdvance(const Paint* paint, Typeface* typeface, const jchar buf[],
             jint start, jint count, jint bufSize, jboolean isRtl, jfloat advance) {
         int bidiFlags = isRtl ? kBidi_Force_RTL : kBidi_Force_LTR;
         std::unique_ptr<float[]> advancesArray(new float[count]);
@@ -859,7 +859,7 @@
             jlong typefaceHandle, jcharArray text, jint start, jint end, jint contextStart,
             jint contextEnd, jboolean isRtl, jfloat advance) {
         const Paint* paint = reinterpret_cast<Paint*>(paintHandle);
-        TypefaceImpl* typeface = reinterpret_cast<TypefaceImpl*>(typefaceHandle);
+        Typeface* typeface = reinterpret_cast<Typeface*>(typefaceHandle);
         jchar* textArray = (jchar*) env->GetPrimitiveArrayCritical(text, NULL);
         jint result = doOffsetForAdvance(paint, typeface, textArray + contextStart,
                 start - contextStart, end - start, contextEnd - contextStart, isRtl, advance);
diff --git a/core/jni/android/graphics/Picture.cpp b/core/jni/android/graphics/Picture.cpp
index 6e83f1b..07e14a2 100644
--- a/core/jni/android/graphics/Picture.cpp
+++ b/core/jni/android/graphics/Picture.cpp
@@ -14,11 +14,11 @@
  * limitations under the License.
  */
 
-#include "Canvas.h"
 #include "Picture.h"
 #include "SkStream.h"
 
 #include <memory>
+#include <hwui/Canvas.h>
 
 namespace android {
 
diff --git a/core/jni/android/graphics/Rasterizer.cpp b/core/jni/android/graphics/Rasterizer.cpp
index a106ecf..3784f0d 100644
--- a/core/jni/android/graphics/Rasterizer.cpp
+++ b/core/jni/android/graphics/Rasterizer.cpp
@@ -22,10 +22,11 @@
 
 #include "jni.h"
 #include "GraphicsJNI.h"
-#include "Paint.h"
 #include "SkLayerRasterizer.h"
 #include "core_jni_helpers.h"
 
+#include <hwui/Paint.h>
+
 // Rasterizer.java holds a pointer (jlong) to this guy
 class NativeRasterizer {
 public:
diff --git a/core/jni/android/graphics/Typeface.cpp b/core/jni/android/graphics/Typeface.cpp
index e97b768d..9a53cad 100644
--- a/core/jni/android/graphics/Typeface.cpp
+++ b/core/jni/android/graphics/Typeface.cpp
@@ -20,50 +20,57 @@
 #include "GraphicsJNI.h"
 #include "ScopedPrimitiveArray.h"
 #include "SkTypeface.h"
-#include "TypefaceImpl.h"
 #include <android_runtime/android_util_AssetManager.h>
 #include <androidfw/AssetManager.h>
+#include <hwui/Typeface.h>
 
 using namespace android;
 
 static jlong Typeface_createFromTypeface(JNIEnv* env, jobject, jlong familyHandle, jint style) {
-    TypefaceImpl* family = reinterpret_cast<TypefaceImpl*>(familyHandle);
-    TypefaceImpl* face = TypefaceImpl_createFromTypeface(family, (SkTypeface::Style)style);
+    Typeface* family = reinterpret_cast<Typeface*>(familyHandle);
+    Typeface* face = Typeface::createFromTypeface(family, (SkTypeface::Style)style);
     // TODO: the following logic shouldn't be necessary, the above should always succeed.
     // Try to find the closest matching font, using the standard heuristic
     if (NULL == face) {
-        face = TypefaceImpl_createFromTypeface(family, (SkTypeface::Style)(style ^ SkTypeface::kItalic));
+        face = Typeface::createFromTypeface(family, (SkTypeface::Style)(style ^ SkTypeface::kItalic));
     }
     for (int i = 0; NULL == face && i < 4; i++) {
-        face = TypefaceImpl_createFromTypeface(family, (SkTypeface::Style)i);
+        face = Typeface::createFromTypeface(family, (SkTypeface::Style)i);
     }
     return reinterpret_cast<jlong>(face);
 }
 
 static jlong Typeface_createWeightAlias(JNIEnv* env, jobject, jlong familyHandle, jint weight) {
-    TypefaceImpl* family = reinterpret_cast<TypefaceImpl*>(familyHandle);
-    TypefaceImpl* face = TypefaceImpl_createWeightAlias(family, weight);
+    Typeface* family = reinterpret_cast<Typeface*>(familyHandle);
+    Typeface* face = Typeface::createWeightAlias(family, weight);
     return reinterpret_cast<jlong>(face);
 }
 
 static void Typeface_unref(JNIEnv* env, jobject obj, jlong faceHandle) {
-    TypefaceImpl* face = reinterpret_cast<TypefaceImpl*>(faceHandle);
-    TypefaceImpl_unref(face);
+    Typeface* face = reinterpret_cast<Typeface*>(faceHandle);
+    if (face != NULL) {
+        face->unref();
+    }
 }
 
 static jint Typeface_getStyle(JNIEnv* env, jobject obj, jlong faceHandle) {
-    TypefaceImpl* face = reinterpret_cast<TypefaceImpl*>(faceHandle);
-    return TypefaceImpl_getStyle(face);
+    Typeface* face = reinterpret_cast<Typeface*>(faceHandle);
+    return face->fSkiaStyle;
 }
 
 static jlong Typeface_createFromArray(JNIEnv *env, jobject, jlongArray familyArray) {
     ScopedLongArrayRO families(env, familyArray);
-    return reinterpret_cast<jlong>(TypefaceImpl_createFromFamilies(families.get(), families.size()));
+    std::vector<FontFamily*> familyVec;
+    for (size_t i = 0; i < families.size(); i++) {
+        FontFamily* family = reinterpret_cast<FontFamily*>(families[i]);
+        familyVec.push_back(family);
+    }
+    return reinterpret_cast<jlong>(Typeface::createFromFamilies(familyVec));
 }
 
 static void Typeface_setDefault(JNIEnv *env, jobject, jlong faceHandle) {
-    TypefaceImpl* face = reinterpret_cast<TypefaceImpl*>(faceHandle);
-    return TypefaceImpl_setDefault(face);
+    Typeface* face = reinterpret_cast<Typeface*>(faceHandle);
+    return Typeface::setDefault(face);
 }
 
 ///////////////////////////////////////////////////////////////////////////////
diff --git a/core/jni/android/graphics/TypefaceImpl.h b/core/jni/android/graphics/TypefaceImpl.h
deleted file mode 100644
index 4b14917..0000000
--- a/core/jni/android/graphics/TypefaceImpl.h
+++ /dev/null
@@ -1,65 +0,0 @@
-/*
- * 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.
- */
-
-
-#ifndef _ANDROID_GRAPHICS_TYPEFACE_IMPL_H_
-#define _ANDROID_GRAPHICS_TYPEFACE_IMPL_H_
-
-#include "jni.h"  // for jlong, eventually remove
-#include "SkTypeface.h"
-#include <androidfw/AssetManager.h>
-
-#include <minikin/FontCollection.h>
-
-namespace android {
-
-struct TypefaceImpl {
-    FontCollection *fFontCollection;
-
-    // style used for constructing and querying Typeface objects
-    SkTypeface::Style fSkiaStyle;
-    // base weight in CSS-style units, 100..900
-    int fBaseWeight;
-
-    // resolved style actually used for rendering
-    FontStyle fStyle;
-};
-
-// Note: it would be cleaner if the following functions were member
-// functions (static or otherwise) of the TypefaceImpl class. However,
-// that can't be easily accommodated in the case where TypefaceImpl
-// is just a pointer to SkTypeface, in the non-USE_MINIKIN case.
-// TODO: when #ifdef USE_MINIKIN is removed, move to member functions.
-
-TypefaceImpl* TypefaceImpl_resolveDefault(TypefaceImpl* src);
-
-TypefaceImpl* TypefaceImpl_createFromTypeface(TypefaceImpl* src, SkTypeface::Style style);
-
-TypefaceImpl* TypefaceImpl_createWeightAlias(TypefaceImpl* src, int baseweight);
-
-// When we remove the USE_MINIKIN ifdef, probably a good idea to move the casting
-// (from jlong to FontFamily*) to the caller in Typeface.cpp.
-TypefaceImpl* TypefaceImpl_createFromFamilies(const jlong* families, size_t size);
-
-void TypefaceImpl_unref(TypefaceImpl* face);
-
-int TypefaceImpl_getStyle(TypefaceImpl* face);
-
-void TypefaceImpl_setDefault(TypefaceImpl* face);
-
-}
-
-#endif  // _ANDROID_GRAPHICS_TYPEFACE_IMPL_H_
diff --git a/core/jni/android/graphics/pdf/PdfDocument.cpp b/core/jni/android/graphics/pdf/PdfDocument.cpp
index 5d496e5..88e37e5 100644
--- a/core/jni/android/graphics/pdf/PdfDocument.cpp
+++ b/core/jni/android/graphics/pdf/PdfDocument.cpp
@@ -19,7 +19,6 @@
 #include "core_jni_helpers.h"
 #include <vector>
 
-#include "Canvas.h"
 #include "CreateJavaOutputStreamAdaptor.h"
 
 #include "SkDocument.h"
@@ -28,6 +27,8 @@
 #include "SkStream.h"
 #include "SkRect.h"
 
+#include <hwui/Canvas.h>
+
 namespace android {
 
 struct PageRecord {
diff --git a/core/jni/android_graphics_Canvas.cpp b/core/jni/android_graphics_Canvas.cpp
index cf73316..ded4dac 100644
--- a/core/jni/android_graphics_Canvas.cpp
+++ b/core/jni/android_graphics_Canvas.cpp
@@ -19,15 +19,14 @@
 #include "core_jni_helpers.h"
 
 #include <androidfw/ResourceTypes.h>
-#include <Canvas.h>
+#include <hwui/Canvas.h>
+#include <hwui/Paint.h>
+#include <hwui/Typeface.h>
+#include <minikin/Layout.h>
 
 #include "Bitmap.h"
 #include "SkDrawFilter.h"
 #include "SkGraphics.h"
-#include "Paint.h"
-#include "TypefaceImpl.h"
-
-#include "MinikinUtils.h"
 
 namespace android {
 
@@ -475,111 +474,13 @@
                                              vertA.ptr(), colorA.ptr(), paint);
 }
 
-static void simplifyPaint(int color, SkPaint* paint) {
-    paint->setColor(color);
-    paint->setShader(nullptr);
-    paint->setColorFilter(nullptr);
-    paint->setLooper(nullptr);
-    paint->setStrokeWidth(4 + 0.04 * paint->getTextSize());
-    paint->setStrokeJoin(SkPaint::kRound_Join);
-    paint->setLooper(nullptr);
-}
-
-class DrawTextFunctor {
-public:
-    DrawTextFunctor(const Layout& layout, Canvas* canvas, uint16_t* glyphs, float* pos,
-                    const SkPaint& paint, float x, float y, MinikinRect& bounds,
-                    float totalAdvance)
-            : layout(layout), canvas(canvas), glyphs(glyphs), pos(pos), paint(paint),
-              x(x), y(y), bounds(bounds), totalAdvance(totalAdvance) { }
-
-    void operator()(size_t start, size_t end) {
-        if (canvas->drawTextAbsolutePos()) {
-            for (size_t i = start; i < end; i++) {
-                glyphs[i] = layout.getGlyphId(i);
-                pos[2 * i] = x + layout.getX(i);
-                pos[2 * i + 1] = y + layout.getY(i);
-            }
-        } else {
-            for (size_t i = start; i < end; i++) {
-                glyphs[i] = layout.getGlyphId(i);
-                pos[2 * i] = layout.getX(i);
-                pos[2 * i + 1] = layout.getY(i);
-            }
-        }
-
-        size_t glyphCount = end - start;
-
-        if (CC_UNLIKELY(canvas->isHighContrastText() && paint.getAlpha() != 0)) {
-            // high contrast draw path
-            int color = paint.getColor();
-            int channelSum = SkColorGetR(color) + SkColorGetG(color) + SkColorGetB(color);
-            bool darken = channelSum < (128 * 3);
-
-            // outline
-            SkPaint outlinePaint(paint);
-            simplifyPaint(darken ? SK_ColorWHITE : SK_ColorBLACK, &outlinePaint);
-            outlinePaint.setStyle(SkPaint::kStrokeAndFill_Style);
-            canvas->drawText(glyphs + start, pos + (2 * start), glyphCount, outlinePaint, x, y,
-                    bounds.mLeft, bounds.mTop, bounds.mRight, bounds.mBottom, totalAdvance);
-
-            // inner
-            SkPaint innerPaint(paint);
-            simplifyPaint(darken ? SK_ColorBLACK : SK_ColorWHITE, &innerPaint);
-            innerPaint.setStyle(SkPaint::kFill_Style);
-            canvas->drawText(glyphs + start, pos + (2 * start), glyphCount, innerPaint, x, y,
-                    bounds.mLeft, bounds.mTop, bounds.mRight, bounds.mBottom, totalAdvance);
-        } else {
-            // standard draw path
-            canvas->drawText(glyphs + start, pos + (2 * start), glyphCount, paint, x, y,
-                             bounds.mLeft, bounds.mTop, bounds.mRight, bounds.mBottom,
-                             totalAdvance);
-        }
-    }
-private:
-    const Layout& layout;
-    Canvas* canvas;
-    uint16_t* glyphs;
-    float* pos;
-    const SkPaint& paint;
-    float x;
-    float y;
-    MinikinRect& bounds;
-    float totalAdvance;
-};
-
-void drawText(Canvas* canvas, const uint16_t* text, int start, int count, int contextCount,
-             float x, float y, int bidiFlags, const Paint& origPaint, TypefaceImpl* typeface) {
-    // minikin may modify the original paint
-    Paint paint(origPaint);
-
-    Layout layout;
-    MinikinUtils::doLayout(&layout, &paint, bidiFlags, typeface, text, start, count, contextCount);
-
-    size_t nGlyphs = layout.nGlyphs();
-    std::unique_ptr<uint16_t[]> glyphs(new uint16_t[nGlyphs]);
-    std::unique_ptr<float[]> pos(new float[nGlyphs * 2]);
-
-    x += MinikinUtils::xOffsetForTextAlign(&paint, layout);
-
-    MinikinRect bounds;
-    layout.getBounds(&bounds);
-    if (!canvas->drawTextAbsolutePos()) {
-        bounds.offset(x, y);
-    }
-
-    DrawTextFunctor f(layout, canvas, glyphs.get(), pos.get(),
-            paint, x, y, bounds, layout.getAdvance());
-    MinikinUtils::forFontRun(layout, &paint, f);
-}
-
 static void drawTextChars(JNIEnv* env, jobject, jlong canvasHandle, jcharArray text,
                           jint index, jint count, jfloat x, jfloat y, jint bidiFlags,
                           jlong paintHandle, jlong typefaceHandle) {
     Paint* paint = reinterpret_cast<Paint*>(paintHandle);
-    TypefaceImpl* typeface = reinterpret_cast<TypefaceImpl*>(typefaceHandle);
+    Typeface* typeface = reinterpret_cast<Typeface*>(typefaceHandle);
     jchar* jchars = env->GetCharArrayElements(text, NULL);
-    drawText(get_canvas(canvasHandle), jchars + index, 0, count, count, x, y,
+    get_canvas(canvasHandle)->drawText(jchars + index, 0, count, count, x, y,
                                        bidiFlags, *paint, typeface);
     env->ReleaseCharArrayElements(text, jchars, JNI_ABORT);
 }
@@ -588,10 +489,10 @@
                            jint start, jint end, jfloat x, jfloat y, jint bidiFlags,
                            jlong paintHandle, jlong typefaceHandle) {
     Paint* paint = reinterpret_cast<Paint*>(paintHandle);
-    TypefaceImpl* typeface = reinterpret_cast<TypefaceImpl*>(typefaceHandle);
+    Typeface* typeface = reinterpret_cast<Typeface*>(typefaceHandle);
     const int count = end - start;
     const jchar* jchars = env->GetStringChars(text, NULL);
-    drawText(get_canvas(canvasHandle), jchars + start, 0, count, count, x, y,
+    get_canvas(canvasHandle)->drawText(jchars + start, 0, count, count, x, y,
                                        bidiFlags, *paint, typeface);
     env->ReleaseStringChars(text, jchars);
 }
@@ -600,11 +501,11 @@
                              jint count, jint contextIndex, jint contextCount, jfloat x, jfloat y,
                              jboolean isRtl, jlong paintHandle, jlong typefaceHandle) {
     Paint* paint = reinterpret_cast<Paint*>(paintHandle);
-    TypefaceImpl* typeface = reinterpret_cast<TypefaceImpl*>(typefaceHandle);
+    Typeface* typeface = reinterpret_cast<Typeface*>(typefaceHandle);
 
     const int bidiFlags = isRtl ? kBidi_Force_RTL : kBidi_Force_LTR;
     jchar* jchars = env->GetCharArrayElements(text, NULL);
-    drawText(get_canvas(canvasHandle), jchars + contextIndex, index - contextIndex, count,
+    get_canvas(canvasHandle)->drawText(jchars + contextIndex, index - contextIndex, count,
                                        contextCount, x, y, bidiFlags, *paint, typeface);
     env->ReleaseCharArrayElements(text, jchars, JNI_ABORT);
 }
@@ -614,70 +515,28 @@
                               jfloat x, jfloat y, jboolean isRtl, jlong paintHandle,
                               jlong typefaceHandle) {
     Paint* paint = reinterpret_cast<Paint*>(paintHandle);
-    TypefaceImpl* typeface = reinterpret_cast<TypefaceImpl*>(typefaceHandle);
+    Typeface* typeface = reinterpret_cast<Typeface*>(typefaceHandle);
 
     int bidiFlags = isRtl ? kBidi_Force_RTL : kBidi_Force_LTR;
     jint count = end - start;
     jint contextCount = contextEnd - contextStart;
     const jchar* jchars = env->GetStringChars(text, NULL);
-    drawText(get_canvas(canvasHandle), jchars + contextStart, start - contextStart, count,
+    get_canvas(canvasHandle)->drawText(jchars + contextStart, start - contextStart, count,
                                        contextCount, x, y, bidiFlags, *paint, typeface);
     env->ReleaseStringChars(text, jchars);
 }
 
-class DrawTextOnPathFunctor {
-public:
-    DrawTextOnPathFunctor(const Layout& layout, Canvas* canvas, float hOffset,
-                float vOffset, const Paint& paint, const SkPath& path)
-            : layout(layout), canvas(canvas), hOffset(hOffset), vOffset(vOffset),
-                paint(paint), path(path) {
-    }
-    void operator()(size_t start, size_t end) {
-        uint16_t glyphs[1];
-        for (size_t i = start; i < end; i++) {
-            glyphs[0] = layout.getGlyphId(i);
-            float x = hOffset + layout.getX(i);
-            float y = vOffset + layout.getY(i);
-            canvas->drawTextOnPath(glyphs, 1, path, x, y, paint);
-        }
-    }
-private:
-    const Layout& layout;
-    Canvas* canvas;
-    float hOffset;
-    float vOffset;
-    const Paint& paint;
-    const SkPath& path;
-};
-
-static void drawTextOnPath(Canvas* canvas, const uint16_t* text, int count, int bidiFlags,
-                           const SkPath& path, float hOffset, float vOffset,
-                           const Paint& paint, TypefaceImpl* typeface) {
-    Paint paintCopy(paint);
-    Layout layout;
-    MinikinUtils::doLayout(&layout, &paintCopy, bidiFlags, typeface, text, 0, count, count);
-    hOffset += MinikinUtils::hOffsetForTextAlign(&paintCopy, layout, path);
-
-    // Set align to left for drawing, as we don't want individual
-    // glyphs centered or right-aligned; the offset above takes
-    // care of all alignment.
-    paintCopy.setTextAlign(Paint::kLeft_Align);
-
-    DrawTextOnPathFunctor f(layout, canvas, hOffset, vOffset, paintCopy, path);
-    MinikinUtils::forFontRun(layout, &paintCopy, f);
-}
-
 static void drawTextOnPathChars(JNIEnv* env, jobject, jlong canvasHandle, jcharArray text,
                                 jint index, jint count, jlong pathHandle, jfloat hOffset,
                                 jfloat vOffset, jint bidiFlags, jlong paintHandle,
                                 jlong typefaceHandle) {
     SkPath* path = reinterpret_cast<SkPath*>(pathHandle);
     Paint* paint = reinterpret_cast<Paint*>(paintHandle);
-    TypefaceImpl* typeface = reinterpret_cast<TypefaceImpl*>(typefaceHandle);
+    Typeface* typeface = reinterpret_cast<Typeface*>(typefaceHandle);
 
     jchar* jchars = env->GetCharArrayElements(text, NULL);
 
-    drawTextOnPath(get_canvas(canvasHandle), jchars + index, count, bidiFlags, *path,
+    get_canvas(canvasHandle)->drawTextOnPath(jchars + index, count, bidiFlags, *path,
                    hOffset, vOffset, *paint, typeface);
 
     env->ReleaseCharArrayElements(text, jchars, 0);
@@ -688,12 +547,12 @@
                                  jint bidiFlags, jlong paintHandle, jlong typefaceHandle) {
     SkPath* path = reinterpret_cast<SkPath*>(pathHandle);
     Paint* paint = reinterpret_cast<Paint*>(paintHandle);
-    TypefaceImpl* typeface = reinterpret_cast<TypefaceImpl*>(typefaceHandle);
+    Typeface* typeface = reinterpret_cast<Typeface*>(typefaceHandle);
 
     const jchar* jchars = env->GetStringChars(text, NULL);
     int count = env->GetStringLength(text);
 
-    drawTextOnPath(get_canvas(canvasHandle), jchars, count, bidiFlags, *path,
+    get_canvas(canvasHandle)->drawTextOnPath(jchars, count, bidiFlags, *path,
                    hOffset, vOffset, *paint, typeface);
 
     env->ReleaseStringChars(text, jchars);
diff --git a/core/jni/android_graphics_drawable_VectorDrawable.cpp b/core/jni/android_graphics_drawable_VectorDrawable.cpp
index e17de17..a2662f9 100644
--- a/core/jni/android_graphics_drawable_VectorDrawable.cpp
+++ b/core/jni/android_graphics_drawable_VectorDrawable.cpp
@@ -19,9 +19,10 @@
 #include "core_jni_helpers.h"
 #include "log/log.h"
 
-#include "Paint.h"
 #include "VectorDrawable.h"
 
+#include <hwui/Paint.h>
+
 namespace android {
 using namespace uirenderer;
 using namespace uirenderer::VectorDrawable;
diff --git a/core/jni/android_net_NetUtils.cpp b/core/jni/android_net_NetUtils.cpp
index 880a79c..2364787 100644
--- a/core/jni/android_net_NetUtils.cpp
+++ b/core/jni/android_net_NetUtils.cpp
@@ -41,7 +41,6 @@
 extern "C" {
 int ifc_enable(const char *ifname);
 int ifc_disable(const char *ifname);
-int ifc_reset_connections(const char *ifname, int reset_mask);
 }
 
 #define NETUTILS_PKG_NAME "android/net/NetworkUtils"
@@ -50,21 +49,6 @@
 
 static const uint16_t kDhcpClientPort = 68;
 
-static jint android_net_utils_resetConnections(JNIEnv* env, jobject clazz,
-      jstring ifname, jint mask)
-{
-    int result;
-
-    const char *nameStr = env->GetStringUTFChars(ifname, NULL);
-
-    ALOGD("android_net_utils_resetConnections in env=%p clazz=%p iface=%s mask=0x%x\n",
-          env, clazz, nameStr, mask);
-
-    result = ::ifc_reset_connections(nameStr, mask);
-    env->ReleaseStringUTFChars(ifname, nameStr);
-    return (jint)result;
-}
-
 static void android_net_utils_attachDhcpFilter(JNIEnv *env, jobject clazz, jobject javaFd)
 {
     uint32_t ip_offset = sizeof(ether_header);
@@ -181,7 +165,6 @@
  */
 static const JNINativeMethod gNetworkUtilMethods[] = {
     /* name, signature, funcPtr */
-    { "resetConnections", "(Ljava/lang/String;I)I",  (void *)android_net_utils_resetConnections },
     { "bindProcessToNetwork", "(I)Z", (void*) android_net_utils_bindProcessToNetwork },
     { "getBoundNetworkForProcess", "()I", (void*) android_net_utils_getBoundNetworkForProcess },
     { "bindProcessToNetworkForHostResolution", "(I)Z", (void*) android_net_utils_bindProcessToNetworkForHostResolution },
diff --git a/core/jni/android_text_StaticLayout.cpp b/core/jni/android_text_StaticLayout.cpp
index 83f76ea..13e4f1a 100644
--- a/core/jni/android_text_StaticLayout.cpp
+++ b/core/jni/android_text_StaticLayout.cpp
@@ -32,10 +32,12 @@
 
 #include "SkPaint.h"
 #include "SkTypeface.h"
-#include "MinikinSkia.h"
-#include "MinikinUtils.h"
-#include "Paint.h"
-#include "minikin/LineBreaker.h"
+#include <hwui/MinikinSkia.h>
+#include <hwui/MinikinUtils.h>
+#include <hwui/Paint.h>
+#include <minikin/FontCollection.h>
+#include <minikin/LineBreaker.h>
+#include <minikin/MinikinFont.h>
 
 namespace android {
 
@@ -154,7 +156,7 @@
         jlong nativePaint, jlong nativeTypeface, jint start, jint end, jboolean isRtl) {
     LineBreaker* b = reinterpret_cast<LineBreaker*>(nativePtr);
     Paint* paint = reinterpret_cast<Paint*>(nativePaint);
-    TypefaceImpl* typeface = reinterpret_cast<TypefaceImpl*>(nativeTypeface);
+    Typeface* typeface = reinterpret_cast<Typeface*>(nativeTypeface);
     FontCollection *font;
     MinikinPaint minikinPaint;
     FontStyle style = MinikinUtils::prepareMinikinPaint(&minikinPaint, &font, paint, typeface);
diff --git a/core/jni/android_view_DisplayListCanvas.cpp b/core/jni/android_view_DisplayListCanvas.cpp
index c87a770..6aac0e4 100644
--- a/core/jni/android_view_DisplayListCanvas.cpp
+++ b/core/jni/android_view_DisplayListCanvas.cpp
@@ -28,11 +28,11 @@
 #include <SkRegion.h>
 
 
-#include <Canvas.h>
 #include <Rect.h>
 #include <RenderNode.h>
 #include <CanvasProperty.h>
-#include <Paint.h>
+#include <hwui/Canvas.h>
+#include <hwui/Paint.h>
 #include <renderthread/RenderProxy.h>
 
 #include "core_jni_helpers.h"
diff --git a/core/jni/android_view_HardwareLayer.cpp b/core/jni/android_view_HardwareLayer.cpp
index 3a0ddc9..6b774e8 100644
--- a/core/jni/android_view_HardwareLayer.cpp
+++ b/core/jni/android_view_HardwareLayer.cpp
@@ -24,8 +24,8 @@
 #include <android_runtime/android_graphics_SurfaceTexture.h>
 
 #include <gui/GLConsumer.h>
+#include <hwui/Paint.h>
 
-#include <Paint.h>
 #include <SkBitmap.h>
 #include <SkCanvas.h>
 #include <SkMatrix.h>
diff --git a/core/jni/android_view_RenderNode.cpp b/core/jni/android_view_RenderNode.cpp
index a7ac5b8..79b518f 100644
--- a/core/jni/android_view_RenderNode.cpp
+++ b/core/jni/android_view_RenderNode.cpp
@@ -30,7 +30,7 @@
 #include <RenderNode.h>
 #include <renderthread/CanvasContext.h>
 #include <TreeInfo.h>
-#include <Paint.h>
+#include <hwui/Paint.h>
 
 #include "core_jni_helpers.h"
 
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 30607dd..60f05448 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -417,6 +417,7 @@
     <protected-broadcast android:name="android.os.action.ACTION_EFFECTS_SUPPRESSOR_CHANGED" />
     <protected-broadcast android:name="android.os.action.LIGHT_DEVICE_IDLE_MODE_CHANGED" />
     <protected-broadcast android:name="android.os.storage.action.VOLUME_STATE_CHANGED" />
+    <protected-broadcast android:name="android.os.storage.action.DISK_SCANNED" />
     <protected-broadcast android:name="com.android.server.action.UPDATE_TWILIGHT_STATE" />
     <protected-broadcast android:name="com.android.server.device_idle.STEP_IDLE_STATE" />
     <protected-broadcast android:name="com.android.server.device_idle.STEP_LIGHT_IDLE_STATE" />
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index ba9bcaa..74ce753 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -3368,8 +3368,8 @@
          tapping/clicking the whole thing is going to do. -->
     <string name="action_bar_home_subtitle_description_format">%1$s, %2$s, %3$s</string>
 
-    <!-- Storage description for internal storage. [CHAR LIMIT=NONE] -->
-    <string name="storage_internal">Internal storage</string>
+    <!-- Storage description for internal shared storage. [CHAR LIMIT=NONE] -->
+    <string name="storage_internal">Internal shared storage</string>
 
     <!-- Storage description for a generic SD card. [CHAR LIMIT=NONE] -->
     <string name="storage_sd_card">SD card</string>
diff --git a/core/tests/coretests/res/layout/remote_views_test.xml b/core/tests/coretests/res/layout/remote_views_test.xml
new file mode 100644
index 0000000..c5f7a47
--- /dev/null
+++ b/core/tests/coretests/res/layout/remote_views_test.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2016 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="match_parent"
+        android:id="@+id/layout"
+        android:orientation="vertical">
+
+    <TextView android:id="@+id/text"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content" />
+
+    <ImageView android:id="@+id/image"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content" />
+
+</LinearLayout>
diff --git a/core/tests/coretests/src/android/view/RemoteViewsTest.java b/core/tests/coretests/src/android/view/RemoteViewsTest.java
new file mode 100644
index 0000000..9c4fd70
--- /dev/null
+++ b/core/tests/coretests/src/android/view/RemoteViewsTest.java
@@ -0,0 +1,127 @@
+/*
+ * Copyright (C) 2016 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 org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.junit.runner.RunWith;
+
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.Drawable;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+import android.widget.RemoteViews;
+import android.widget.TextView;
+
+import com.android.frameworks.coretests.R;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertSame;
+
+/**
+ * Tests for RemoteViews.
+ */
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class RemoteViewsTest {
+
+    @Rule
+    public final ExpectedException exception = ExpectedException.none();
+
+    private Context mContext;
+    private String mPackage;
+    private LinearLayout mContainer;
+
+    @Before
+    public void setup() {
+        mContext = InstrumentationRegistry.getContext();
+        mPackage = mPackage;
+        mContainer = new LinearLayout(mContext);
+    }
+
+    @Test
+    public void clone_doesNotCopyBitmap() {
+        RemoteViews original = new RemoteViews(mPackage, R.layout.remote_views_test);
+        Bitmap bitmap = Bitmap.createBitmap(10, 10, Bitmap.Config.ARGB_8888);
+
+        original.setImageViewBitmap(R.id.image, bitmap);
+        RemoteViews clone = original.clone();
+        View inflated = clone.apply(mContext, mContainer);
+
+        Drawable drawable = ((ImageView) inflated.findViewById(R.id.image)).getDrawable();
+        assertSame(bitmap, ((BitmapDrawable)drawable).getBitmap());
+    }
+
+    @Test
+    public void clone_originalCanStillBeApplied() {
+        RemoteViews original = new RemoteViews(mPackage, R.layout.remote_views_test);
+
+        RemoteViews clone = original.clone();
+
+        clone.apply(mContext, mContainer);
+    }
+
+    @Test
+    public void clone_clones() {
+        RemoteViews original = new RemoteViews(mPackage, R.layout.remote_views_test);
+
+        RemoteViews clone = original.clone();
+        original.setTextViewText(R.id.text, "test");
+        View inflated = clone.apply(mContext, mContainer);
+
+        TextView textView = (TextView) inflated.findViewById(R.id.text);
+        assertEquals("", textView.getText());
+    }
+
+    @Test
+    public void clone_child_fails() {
+        RemoteViews original = new RemoteViews(mPackage, R.layout.remote_views_test);
+        RemoteViews child = new RemoteViews(mPackage, R.layout.remote_views_test);
+
+        original.addView(R.id.layout, child);
+
+        exception.expect(IllegalStateException.class);
+        RemoteViews clone = child.clone();
+    }
+
+    @Test
+    public void clone_repeatedly() {
+        RemoteViews original = new RemoteViews(mPackage, R.layout.remote_views_test);
+
+        original.clone();
+        original.clone();
+
+        original.apply(mContext, mContainer);
+    }
+
+    @Test
+    public void clone_chained() {
+        RemoteViews original = new RemoteViews(mPackage, R.layout.remote_views_test);
+
+        RemoteViews clone = original.clone().clone();
+
+        clone.apply(mContext, mContainer);
+    }
+
+}
diff --git a/core/tests/coretests/src/android/widget/TextViewActivityMouseTest.java b/core/tests/coretests/src/android/widget/TextViewActivityMouseTest.java
index 923b829..3fbc16a 100644
--- a/core/tests/coretests/src/android/widget/TextViewActivityMouseTest.java
+++ b/core/tests/coretests/src/android/widget/TextViewActivityMouseTest.java
@@ -44,6 +44,7 @@
 import android.support.test.espresso.Espresso;
 import android.test.ActivityInstrumentationTestCase2;
 import android.test.suitebuilder.annotation.SmallTest;
+import android.test.suitebuilder.annotation.Suppress;
 import android.view.MotionEvent;
 import android.widget.espresso.ContextMenuUtils;
 
@@ -97,6 +98,7 @@
     }
 
     @SmallTest
+    @Suppress
     public void testContextMenu() throws Exception {
         final String text = "abc def ghi.";
         onView(withId(R.id.textview)).perform(click());
diff --git a/libs/hwui/Android.mk b/libs/hwui/Android.mk
index ca07738..516591b 100644
--- a/libs/hwui/Android.mk
+++ b/libs/hwui/Android.mk
@@ -12,6 +12,11 @@
 hwui_src_files := \
     font/CacheTexture.cpp \
     font/Font.cpp \
+    hwui/Canvas.cpp \
+    hwui/MinikinSkia.cpp \
+    hwui/MinikinUtils.cpp \
+    hwui/PaintImpl.cpp \
+    hwui/Typeface.cpp \
     renderstate/Blend.cpp \
     renderstate/MeshState.cpp \
     renderstate/OffscreenBufferPool.cpp \
@@ -41,7 +46,6 @@
     AnimatorManager.cpp \
     AssetAtlas.cpp \
     Caches.cpp \
-    Canvas.cpp \
     CanvasState.cpp \
     ClipArea.cpp \
     DamageAccumulator.cpp \
@@ -143,7 +147,9 @@
 
 hwui_c_includes += \
     external/skia/include/private \
-    external/skia/src/core
+    external/skia/src/core \
+    external/harfbuzz_ng/src \
+    external/freetype/include
 
 ifneq (false,$(ANDROID_ENABLE_RENDERSCRIPT))
     hwui_cflags += -DANDROID_ENABLE_RENDERSCRIPT
diff --git a/libs/hwui/BakedOpState.cpp b/libs/hwui/BakedOpState.cpp
index 26653f7..85903654 100644
--- a/libs/hwui/BakedOpState.cpp
+++ b/libs/hwui/BakedOpState.cpp
@@ -82,6 +82,16 @@
     }
 }
 
+ResolvedRenderState::ResolvedRenderState(LinearAllocator& allocator, Snapshot& snapshot,
+        const Matrix4& localTransform, const ClipBase* localClip) {
+    transform.loadMultiply(*snapshot.transform, localTransform);
+    clipState = snapshot.mutateClipArea().serializeIntersectedClip(allocator,
+            localClip, *(snapshot.transform));
+    clippedBounds = clipState->rect;
+    clipSideFlags = OpClipSideFlags::Full;
+    localProjectionPathMask = nullptr;
+}
+
 ResolvedRenderState::ResolvedRenderState(LinearAllocator& allocator, Snapshot& snapshot)
         : transform(*snapshot.transform)
         , clipState(snapshot.mutateClipArea().serializeClip(allocator))
diff --git a/libs/hwui/BakedOpState.h b/libs/hwui/BakedOpState.h
index ffe2901..4e3cb8a 100644
--- a/libs/hwui/BakedOpState.h
+++ b/libs/hwui/BakedOpState.h
@@ -55,6 +55,10 @@
     ResolvedRenderState(LinearAllocator& allocator, Snapshot& snapshot,
             const RecordedOp& recordedOp, bool expandForStroke);
 
+    // Constructor for unbounded ops *with* transform/clip
+    ResolvedRenderState(LinearAllocator& allocator, Snapshot& snapshot,
+            const Matrix4& localTransform, const ClipBase* localClip);
+
     // Constructor for unbounded ops without transform/clip (namely shadows)
     ResolvedRenderState(LinearAllocator& allocator, Snapshot& snapshot);
 
@@ -111,8 +115,14 @@
         return bakedState;
     }
 
+    static BakedOpState* tryConstructUnbounded(LinearAllocator& allocator,
+            Snapshot& snapshot, const RecordedOp& recordedOp) {
+        if (CC_UNLIKELY(snapshot.getRenderTargetClip().isEmpty())) return nullptr;
+        return allocator.create_trivial<BakedOpState>(allocator, snapshot, recordedOp);
+    }
+
     enum class StrokeBehavior {
-        // stroking is forced, regardless of style on paint
+        // stroking is forced, regardless of style on paint (such as for lines)
         Forced,
         // stroking is defined by style on paint
         StyleDefined,
@@ -167,6 +177,13 @@
             , roundRectClipState(snapshot.roundRectClipState)
             , op(&recordedOp) {}
 
+    // TODO: fix this brittleness
+    BakedOpState(LinearAllocator& allocator, Snapshot& snapshot, const RecordedOp& recordedOp)
+            : computedState(allocator, snapshot, recordedOp.localMatrix, recordedOp.localClip)
+            , alpha(snapshot.alpha)
+            , roundRectClipState(snapshot.roundRectClipState)
+            , op(&recordedOp) {}
+
     BakedOpState(LinearAllocator& allocator, Snapshot& snapshot, const ShadowOp* shadowOpPtr)
             : computedState(allocator, snapshot)
             , alpha(snapshot.alpha)
diff --git a/libs/hwui/Canvas.cpp b/libs/hwui/Canvas.cpp
deleted file mode 100644
index 11ae1a1..0000000
--- a/libs/hwui/Canvas.cpp
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- * 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 "Canvas.h"
-
-#include "DisplayListCanvas.h"
-#include "RecordingCanvas.h"
-#include <SkDrawFilter.h>
-
-namespace android {
-
-Canvas* Canvas::create_recording_canvas(int width, int height) {
-#if HWUI_NEW_OPS
-    return new uirenderer::RecordingCanvas(width, height);
-#else
-    return new uirenderer::DisplayListCanvas(width, height);
-#endif
-}
-
-void Canvas::drawTextDecorations(float x, float y, float length, const SkPaint& paint) {
-    uint32_t flags;
-    SkDrawFilter* drawFilter = getDrawFilter();
-    if (drawFilter) {
-        SkPaint paintCopy(paint);
-        drawFilter->filter(&paintCopy, SkDrawFilter::kText_Type);
-        flags = paintCopy.getFlags();
-    } else {
-        flags = paint.getFlags();
-    }
-    if (flags & (SkPaint::kUnderlineText_Flag | SkPaint::kStrikeThruText_Flag)) {
-        // Same values used by Skia
-        const float kStdStrikeThru_Offset   = (-6.0f / 21.0f);
-        const float kStdUnderline_Offset    = (1.0f / 9.0f);
-        const float kStdUnderline_Thickness = (1.0f / 18.0f);
-
-        SkScalar left = x;
-        SkScalar right = x + length;
-        float textSize = paint.getTextSize();
-        float strokeWidth = fmax(textSize * kStdUnderline_Thickness, 1.0f);
-        if (flags & SkPaint::kUnderlineText_Flag) {
-            SkScalar top = y + textSize * kStdUnderline_Offset - 0.5f * strokeWidth;
-            SkScalar bottom = y + textSize * kStdUnderline_Offset + 0.5f * strokeWidth;
-            drawRect(left, top, right, bottom, paint);
-        }
-        if (flags & SkPaint::kStrikeThruText_Flag) {
-            SkScalar top = y + textSize * kStdStrikeThru_Offset - 0.5f * strokeWidth;
-            SkScalar bottom = y + textSize * kStdStrikeThru_Offset + 0.5f * strokeWidth;
-            drawRect(left, top, right, bottom, paint);
-        }
-    }
-}
-
-} // namespace android
diff --git a/libs/hwui/CanvasState.cpp b/libs/hwui/CanvasState.cpp
index 43ff33f..e2149d1 100644
--- a/libs/hwui/CanvasState.cpp
+++ b/libs/hwui/CanvasState.cpp
@@ -14,8 +14,8 @@
  * limitations under the License.
  */
 
-#include "Canvas.h"
 #include "CanvasState.h"
+#include "hwui/Canvas.h"
 #include "utils/MathUtils.h"
 
 namespace android {
diff --git a/libs/hwui/DisplayListCanvas.cpp b/libs/hwui/DisplayListCanvas.cpp
index a14bdc4..2dccf32 100644
--- a/libs/hwui/DisplayListCanvas.cpp
+++ b/libs/hwui/DisplayListCanvas.cpp
@@ -418,7 +418,7 @@
     addDrawOp(new (alloc()) DrawVectorDrawableOp(tree));
 }
 
-void DisplayListCanvas::drawTextOnPath(const uint16_t* glyphs, int count,
+void DisplayListCanvas::drawGlyphsOnPath(const uint16_t* glyphs, int count,
         const SkPath& path, float hOffset, float vOffset, const SkPaint& paint) {
     if (!glyphs || count <= 0) return;
 
@@ -429,7 +429,7 @@
     addDrawOp(op);
 }
 
-void DisplayListCanvas::drawText(const uint16_t* glyphs, const float* positions,
+void DisplayListCanvas::drawGlyphs(const uint16_t* glyphs, const float* positions,
         int count, const SkPaint& paint, float x, float y,
         float boundsLeft, float boundsTop, float boundsRight, float boundsBottom,
         float totalAdvance) {
diff --git a/libs/hwui/DisplayListCanvas.h b/libs/hwui/DisplayListCanvas.h
index a703e22..d6a5794 100644
--- a/libs/hwui/DisplayListCanvas.h
+++ b/libs/hwui/DisplayListCanvas.h
@@ -17,12 +17,12 @@
 #ifndef ANDROID_HWUI_DISPLAY_LIST_RENDERER_H
 #define ANDROID_HWUI_DISPLAY_LIST_RENDERER_H
 
-#include "Canvas.h"
 #include "CanvasState.h"
 #include "DisplayList.h"
 #include "RenderNode.h"
 #include "ResourceCache.h"
 #include "SkiaCanvasProxy.h"
+#include "hwui/Canvas.h"
 #include "utils/Macros.h"
 
 #include <SkDrawFilter.h>
@@ -209,10 +209,10 @@
     virtual void drawVectorDrawable(VectorDrawableRoot* tree) override;
 
     // Text
-    virtual void drawText(const uint16_t* glyphs, const float* positions, int count,
+    virtual void drawGlyphs(const uint16_t* glyphs, const float* positions, int count,
             const SkPaint& paint, float x, float y, float boundsLeft, float boundsTop,
             float boundsRight, float boundsBottom, float totalAdvance) override;
-    virtual void drawTextOnPath(const uint16_t* glyphs, int count, const SkPath& path,
+    virtual void drawGlyphsOnPath(const uint16_t* glyphs, int count, const SkPath& path,
             float hOffset, float vOffset, const SkPaint& paint) override;
     virtual bool drawTextAbsolutePos() const override { return false; }
 
diff --git a/libs/hwui/FrameBuilder.cpp b/libs/hwui/FrameBuilder.cpp
index 50b21a4..dc967e0 100644
--- a/libs/hwui/FrameBuilder.cpp
+++ b/libs/hwui/FrameBuilder.cpp
@@ -16,11 +16,11 @@
 
 #include "FrameBuilder.h"
 
-#include "Canvas.h"
 #include "LayerUpdateQueue.h"
 #include "RenderNode.h"
 #include "VectorDrawable.h"
 #include "renderstate/OffscreenBufferPool.h"
+#include "hwui/Canvas.h"
 #include "utils/FatVector.h"
 #include "utils/PaintUtils.h"
 #include "utils/TraceUtils.h"
@@ -574,7 +574,7 @@
 }
 
 void FrameBuilder::deferFunctorOp(const FunctorOp& op) {
-    BakedOpState* bakedState = tryBakeOpState(op);
+    BakedOpState* bakedState = tryBakeUnboundedOpState(op);
     if (!bakedState) return; // quick rejected
     currentLayer().deferUnmergeableOp(mAllocator, bakedState, OpBatchType::Functor);
 }
@@ -663,7 +663,7 @@
 }
 
 void FrameBuilder::deferTextOnPathOp(const TextOnPathOp& op) {
-    BakedOpState* bakedState = tryBakeOpState(op);
+    BakedOpState* bakedState = tryBakeUnboundedOpState(op);
     if (!bakedState) return; // quick rejected
     currentLayer().deferUnmergeableOp(mAllocator, bakedState, textBatchId(*(op.paint)));
 }
diff --git a/libs/hwui/FrameBuilder.h b/libs/hwui/FrameBuilder.h
index f44306a..8a00d33 100644
--- a/libs/hwui/FrameBuilder.h
+++ b/libs/hwui/FrameBuilder.h
@@ -173,6 +173,10 @@
     BakedOpState* tryBakeOpState(const RecordedOp& recordedOp) {
         return BakedOpState::tryConstruct(mAllocator, *mCanvasState.writableSnapshot(), recordedOp);
     }
+    BakedOpState* tryBakeUnboundedOpState(const RecordedOp& recordedOp) {
+        return BakedOpState::tryConstructUnbounded(mAllocator, *mCanvasState.writableSnapshot(), recordedOp);
+    }
+
 
     // should always be surrounded by a save/restore pair, and not called if DisplayList is null
     void deferNodePropsAndOps(RenderNode& node);
diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp
index 7693fdc..c099427 100644
--- a/libs/hwui/OpenGLRenderer.cpp
+++ b/libs/hwui/OpenGLRenderer.cpp
@@ -17,7 +17,6 @@
 #include <GpuMemoryTracker.h>
 #include "OpenGLRenderer.h"
 
-#include "Canvas.h"
 #include "DeferredDisplayList.h"
 #include "GammaFontRenderer.h"
 #include "Glop.h"
@@ -32,6 +31,7 @@
 #include "SkiaShader.h"
 #include "Vector.h"
 #include "VertexBuffer.h"
+#include "hwui/Canvas.h"
 #include "utils/GLUtils.h"
 #include "utils/PaintUtils.h"
 #include "utils/TraceUtils.h"
diff --git a/libs/hwui/RecordedOp.h b/libs/hwui/RecordedOp.h
index c37458d..96a57b6 100644
--- a/libs/hwui/RecordedOp.h
+++ b/libs/hwui/RecordedOp.h
@@ -257,8 +257,10 @@
 };
 
 struct FunctorOp : RecordedOp {
-    FunctorOp(BASE_PARAMS_PAINTLESS, Functor* functor)
-            : SUPER_PAINTLESS(FunctorOp)
+    // Note: undefined record-time bounds, since this op fills the clip
+    // TODO: explicitly define bounds
+    FunctorOp(const Matrix4& localMatrix, const ClipBase* localClip, Functor* functor)
+            : RecordedOp(RecordedOpId::FunctorOp, Rect(), localMatrix, localClip, nullptr)
             , functor(functor) {}
     Functor* functor;
 };
@@ -385,9 +387,10 @@
 };
 
 struct TextOnPathOp : RecordedOp {
-    TextOnPathOp(BASE_PARAMS, const glyph_t* glyphs, int glyphCount,
-            const SkPath* path, float hOffset, float vOffset)
-            : SUPER(TextOnPathOp)
+    // TODO: explicitly define bounds
+    TextOnPathOp(const Matrix4& localMatrix, const ClipBase* localClip, const SkPaint* paint,
+            const glyph_t* glyphs, int glyphCount, const SkPath* path, float hOffset, float vOffset)
+            : RecordedOp(RecordedOpId::TextOnPathOp, Rect(), localMatrix, localClip, paint)
             , glyphs(glyphs)
             , glyphCount(glyphCount)
             , path(path)
diff --git a/libs/hwui/RecordingCanvas.cpp b/libs/hwui/RecordingCanvas.cpp
index 11eb825..cf78781 100644
--- a/libs/hwui/RecordingCanvas.cpp
+++ b/libs/hwui/RecordingCanvas.cpp
@@ -24,14 +24,6 @@
 namespace android {
 namespace uirenderer {
 
-#define MIL_PIX 1000000
-static Rect sUnreasonablyLargeBounds(-MIL_PIX, -MIL_PIX, MIL_PIX, MIL_PIX);
-
-static const Rect& getConservativeOpBounds(const ClipBase* clip) {
-    // if op is clipped, that rect can be used, but otherwise just use a conservatively large rect
-    return clip ? clip->rect : sUnreasonablyLargeBounds;
-}
-
 RecordingCanvas::RecordingCanvas(size_t width, size_t height)
         : mState(*this)
         , mResourceCache(ResourceCache::getInstance()) {
@@ -249,12 +241,10 @@
 }
 
 void RecordingCanvas::drawPaint(const SkPaint& paint) {
-    const ClipBase* clip = getRecordedClip();
-    addOp(alloc().create_trivial<RectOp>(
-            getConservativeOpBounds(clip),
-            Matrix4::identity(),
-            clip,
-            refPaint(&paint)));
+    SkRect bounds;
+    if (getClipBounds(&bounds)) {
+        drawRect(bounds.fLeft, bounds.fTop, bounds.fRight, bounds.fBottom, paint);
+    }
 }
 
 static Rect calcBoundsOfPoints(const float* points, int floatCount) {
@@ -524,7 +514,7 @@
 }
 
 // Text
-void RecordingCanvas::drawText(const uint16_t* glyphs, const float* positions, int glyphCount,
+void RecordingCanvas::drawGlyphs(const uint16_t* glyphs, const float* positions, int glyphCount,
             const SkPaint& paint, float x, float y, float boundsLeft, float boundsTop,
             float boundsRight, float boundsBottom, float totalAdvance) {
     if (!glyphs || !positions || glyphCount <= 0 || PaintUtils::paintWillNotDrawText(paint)) return;
@@ -540,15 +530,13 @@
     drawTextDecorations(x, y, totalAdvance, paint);
 }
 
-void RecordingCanvas::drawTextOnPath(const uint16_t* glyphs, int glyphCount, const SkPath& path,
+void RecordingCanvas::drawGlyphsOnPath(const uint16_t* glyphs, int glyphCount, const SkPath& path,
             float hOffset, float vOffset, const SkPaint& paint) {
     if (!glyphs || glyphCount <= 0 || PaintUtils::paintWillNotDrawText(paint)) return;
     glyphs = refBuffer<glyph_t>(glyphs, glyphCount);
-    auto clip = getRecordedClip();
     addOp(alloc().create_trivial<TextOnPathOp>(
-            getConservativeOpBounds(clip), // TODO: explicitly define bounds
             *(mState.currentSnapshot()->transform),
-            clip,
+            getRecordedClip(),
             refPaint(&paint), glyphs, glyphCount, refPath(&path), hOffset, vOffset));
 }
 
@@ -599,11 +587,9 @@
 
 void RecordingCanvas::callDrawGLFunction(Functor* functor) {
     mDisplayList->functors.push_back(functor);
-    auto clip = getRecordedClip();
     addOp(alloc().create_trivial<FunctorOp>(
-            getConservativeOpBounds(clip),
             *(mState.currentSnapshot()->transform),
-            clip,
+            getRecordedClip(),
             functor));
 }
 
diff --git a/libs/hwui/RecordingCanvas.h b/libs/hwui/RecordingCanvas.h
index 719872d..1eb4fa0 100644
--- a/libs/hwui/RecordingCanvas.h
+++ b/libs/hwui/RecordingCanvas.h
@@ -17,12 +17,12 @@
 #ifndef ANDROID_HWUI_RECORDING_CANVAS_H
 #define ANDROID_HWUI_RECORDING_CANVAS_H
 
-#include "Canvas.h"
 #include "CanvasState.h"
 #include "DisplayList.h"
 #include "ResourceCache.h"
 #include "SkiaCanvasProxy.h"
 #include "Snapshot.h"
+#include "hwui/Canvas.h"
 #include "utils/LinearAllocator.h"
 #include "utils/Macros.h"
 #include "utils/NinePatch.h"
@@ -191,10 +191,10 @@
             const SkPaint* paint) override;
 
     // Text
-    virtual void drawText(const uint16_t* glyphs, const float* positions, int glyphCount,
+    virtual void drawGlyphs(const uint16_t* glyphs, const float* positions, int glyphCount,
             const SkPaint& paint, float x, float y, float boundsLeft, float boundsTop,
             float boundsRight, float boundsBottom, float totalAdvance) override;
-    virtual void drawTextOnPath(const uint16_t* glyphs, int glyphCount, const SkPath& path,
+    virtual void drawGlyphsOnPath(const uint16_t* glyphs, int glyphCount, const SkPath& path,
             float hOffset, float vOffset, const SkPaint& paint) override;
     virtual bool drawTextAbsolutePos() const override { return false; }
 
diff --git a/libs/hwui/RenderProperties.cpp b/libs/hwui/RenderProperties.cpp
index 0b0f0fa..f577785 100644
--- a/libs/hwui/RenderProperties.cpp
+++ b/libs/hwui/RenderProperties.cpp
@@ -23,9 +23,9 @@
 #include <SkPath.h>
 #include <SkPathOps.h>
 
-#include "Canvas.h"
 #include "Matrix.h"
 #include "OpenGLRenderer.h"
+#include "hwui/Canvas.h"
 #include "utils/MathUtils.h"
 
 namespace android {
diff --git a/libs/hwui/SkiaCanvas.cpp b/libs/hwui/SkiaCanvas.cpp
index bd4442d..b1ecb71 100644
--- a/libs/hwui/SkiaCanvas.cpp
+++ b/libs/hwui/SkiaCanvas.cpp
@@ -14,10 +14,10 @@
  * limitations under the License.
  */
 
-#include "Canvas.h"
 #include "CanvasProperty.h"
 #include "Layer.h"
 #include "RenderNode.h"
+#include "hwui/Canvas.h"
 
 #include <SkCanvas.h>
 #include <SkClipStack.h>
@@ -147,11 +147,11 @@
             float dstLeft, float dstTop, float dstRight, float dstBottom,
             const SkPaint* paint) override;
 
-    virtual void drawText(const uint16_t* text, const float* positions, int count,
+    virtual void drawGlyphs(const uint16_t* text, const float* positions, int count,
             const SkPaint& paint, float x, float y,
             float boundsLeft, float boundsTop, float boundsRight, float boundsBottom,
             float totalAdvance) override;
-    virtual void drawTextOnPath(const uint16_t* glyphs, int count, const SkPath& path,
+    virtual void drawGlyphsOnPath(const uint16_t* glyphs, int count, const SkPath& path,
             float hOffset, float vOffset, const SkPaint& paint) override;
 
     virtual bool drawTextAbsolutePos() const  override { return true; }
@@ -757,7 +757,7 @@
 // Canvas draw operations: Text
 // ----------------------------------------------------------------------------
 
-void SkiaCanvas::drawText(const uint16_t* text, const float* positions, int count,
+void SkiaCanvas::drawGlyphs(const uint16_t* text, const float* positions, int count,
         const SkPaint& paint, float x, float y,
         float boundsLeft, float boundsTop, float boundsRight, float boundsBottom,
         float totalAdvance) {
@@ -772,7 +772,7 @@
     drawTextDecorations(x, y, totalAdvance, paint);
 }
 
-void SkiaCanvas::drawTextOnPath(const uint16_t* glyphs, int count, const SkPath& path,
+void SkiaCanvas::drawGlyphsOnPath(const uint16_t* glyphs, int count, const SkPath& path,
         float hOffset, float vOffset, const SkPaint& paint) {
     mCanvas->drawTextOnPathHV(glyphs, count << 1, path, hOffset, vOffset, paint);
 }
diff --git a/libs/hwui/SkiaCanvasProxy.cpp b/libs/hwui/SkiaCanvasProxy.cpp
index 6530d4ed8..c612480 100644
--- a/libs/hwui/SkiaCanvasProxy.cpp
+++ b/libs/hwui/SkiaCanvasProxy.cpp
@@ -290,7 +290,7 @@
     }
 
     static_assert(sizeof(SkPoint) == sizeof(float)*2, "SkPoint is no longer two floats");
-    mCanvas->drawText(glyphs.glyphIDs, &pointStorage[0].fX, glyphs.count, glyphs.paint,
+    mCanvas->drawGlyphs(glyphs.glyphIDs, &pointStorage[0].fX, glyphs.count, glyphs.paint,
                       x, y, bounds.fLeft, bounds.fTop, bounds.fRight, bounds.fBottom, 0);
 }
 
@@ -326,7 +326,7 @@
     bounds.offset(x, y);
 
     static_assert(sizeof(SkPoint) == sizeof(float)*2, "SkPoint is no longer two floats");
-    mCanvas->drawText(glyphs.glyphIDs, &posArray[0].fX, glyphs.count, glyphs.paint, x, y,
+    mCanvas->drawGlyphs(glyphs.glyphIDs, &posArray[0].fX, glyphs.count, glyphs.paint, x, y,
                       bounds.fLeft, bounds.fTop, bounds.fRight, bounds.fBottom, 0);
 }
 
@@ -344,7 +344,7 @@
         const SkMatrix* matrix, const SkPaint& origPaint) {
     // convert to glyphIDs if necessary
     GlyphIDConverter glyphs(text, byteLength, origPaint);
-    mCanvas->drawTextOnPath(glyphs.glyphIDs, glyphs.count, path, 0, 0, glyphs.paint);
+    mCanvas->drawGlyphsOnPath(glyphs.glyphIDs, glyphs.count, path, 0, 0, glyphs.paint);
 }
 
 void SkiaCanvasProxy::onDrawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y,
diff --git a/libs/hwui/SkiaCanvasProxy.h b/libs/hwui/SkiaCanvasProxy.h
index e342d192..973c55f 100644
--- a/libs/hwui/SkiaCanvasProxy.h
+++ b/libs/hwui/SkiaCanvasProxy.h
@@ -20,7 +20,7 @@
 #include <cutils/compiler.h>
 #include <SkCanvas.h>
 
-#include "Canvas.h"
+#include "hwui/Canvas.h"
 
 namespace android {
 namespace uirenderer {
diff --git a/libs/hwui/Snapshot.cpp b/libs/hwui/Snapshot.cpp
index cf5e69a..d784280 100644
--- a/libs/hwui/Snapshot.cpp
+++ b/libs/hwui/Snapshot.cpp
@@ -16,7 +16,7 @@
 
 #include "Snapshot.h"
 
-#include "Canvas.h"
+#include "hwui/Canvas.h"
 
 namespace android {
 namespace uirenderer {
diff --git a/libs/hwui/VectorDrawable.h b/libs/hwui/VectorDrawable.h
index 4d2fed0..7a45bf5 100644
--- a/libs/hwui/VectorDrawable.h
+++ b/libs/hwui/VectorDrawable.h
@@ -17,7 +17,7 @@
 #ifndef ANDROID_HWUI_VPATH_H
 #define ANDROID_HWUI_VPATH_H
 
-#include "Canvas.h"
+#include "hwui/Canvas.h"
 
 #include <SkBitmap.h>
 #include <SkColor.h>
diff --git a/libs/hwui/hwui/Canvas.cpp b/libs/hwui/hwui/Canvas.cpp
new file mode 100644
index 0000000..8c3eea3
--- /dev/null
+++ b/libs/hwui/hwui/Canvas.cpp
@@ -0,0 +1,220 @@
+/*
+ * 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 "Canvas.h"
+
+#include "DisplayListCanvas.h"
+#include "RecordingCanvas.h"
+#include "MinikinUtils.h"
+#include "Paint.h"
+#include "Typeface.h"
+
+#include <SkDrawFilter.h>
+
+namespace android {
+
+Canvas* Canvas::create_recording_canvas(int width, int height) {
+#if HWUI_NEW_OPS
+    return new uirenderer::RecordingCanvas(width, height);
+#else
+    return new uirenderer::DisplayListCanvas(width, height);
+#endif
+}
+
+void Canvas::drawTextDecorations(float x, float y, float length, const SkPaint& paint) {
+    uint32_t flags;
+    SkDrawFilter* drawFilter = getDrawFilter();
+    if (drawFilter) {
+        SkPaint paintCopy(paint);
+        drawFilter->filter(&paintCopy, SkDrawFilter::kText_Type);
+        flags = paintCopy.getFlags();
+    } else {
+        flags = paint.getFlags();
+    }
+    if (flags & (SkPaint::kUnderlineText_Flag | SkPaint::kStrikeThruText_Flag)) {
+        // Same values used by Skia
+        const float kStdStrikeThru_Offset   = (-6.0f / 21.0f);
+        const float kStdUnderline_Offset    = (1.0f / 9.0f);
+        const float kStdUnderline_Thickness = (1.0f / 18.0f);
+
+        SkScalar left = x;
+        SkScalar right = x + length;
+        float textSize = paint.getTextSize();
+        float strokeWidth = fmax(textSize * kStdUnderline_Thickness, 1.0f);
+        if (flags & SkPaint::kUnderlineText_Flag) {
+            SkScalar top = y + textSize * kStdUnderline_Offset - 0.5f * strokeWidth;
+            SkScalar bottom = y + textSize * kStdUnderline_Offset + 0.5f * strokeWidth;
+            drawRect(left, top, right, bottom, paint);
+        }
+        if (flags & SkPaint::kStrikeThruText_Flag) {
+            SkScalar top = y + textSize * kStdStrikeThru_Offset - 0.5f * strokeWidth;
+            SkScalar bottom = y + textSize * kStdStrikeThru_Offset + 0.5f * strokeWidth;
+            drawRect(left, top, right, bottom, paint);
+        }
+    }
+}
+
+static void simplifyPaint(int color, SkPaint* paint) {
+    paint->setColor(color);
+    paint->setShader(nullptr);
+    paint->setColorFilter(nullptr);
+    paint->setLooper(nullptr);
+    paint->setStrokeWidth(4 + 0.04 * paint->getTextSize());
+    paint->setStrokeJoin(SkPaint::kRound_Join);
+    paint->setLooper(nullptr);
+}
+
+class DrawTextFunctor {
+public:
+    DrawTextFunctor(const Layout& layout, Canvas* canvas, uint16_t* glyphs, float* pos,
+            const SkPaint& paint, float x, float y, MinikinRect& bounds, float totalAdvance)
+        : layout(layout)
+        , canvas(canvas)
+        , glyphs(glyphs)
+        , pos(pos)
+        , paint(paint)
+        , x(x)
+        , y(y)
+        , bounds(bounds)
+        , totalAdvance(totalAdvance) {
+    }
+
+    void operator()(size_t start, size_t end) {
+        if (canvas->drawTextAbsolutePos()) {
+            for (size_t i = start; i < end; i++) {
+                glyphs[i] = layout.getGlyphId(i);
+                pos[2 * i] = x + layout.getX(i);
+                pos[2 * i + 1] = y + layout.getY(i);
+            }
+        } else {
+            for (size_t i = start; i < end; i++) {
+                glyphs[i] = layout.getGlyphId(i);
+                pos[2 * i] = layout.getX(i);
+                pos[2 * i + 1] = layout.getY(i);
+            }
+        }
+
+        size_t glyphCount = end - start;
+
+        if (CC_UNLIKELY(canvas->isHighContrastText() && paint.getAlpha() != 0)) {
+            // high contrast draw path
+            int color = paint.getColor();
+            int channelSum = SkColorGetR(color) + SkColorGetG(color) + SkColorGetB(color);
+            bool darken = channelSum < (128 * 3);
+
+            // outline
+            SkPaint outlinePaint(paint);
+            simplifyPaint(darken ? SK_ColorWHITE : SK_ColorBLACK, &outlinePaint);
+            outlinePaint.setStyle(SkPaint::kStrokeAndFill_Style);
+            canvas->drawGlyphs(glyphs + start, pos + (2 * start), glyphCount, outlinePaint, x, y,
+                    bounds.mLeft, bounds.mTop, bounds.mRight, bounds.mBottom, totalAdvance);
+
+            // inner
+            SkPaint innerPaint(paint);
+            simplifyPaint(darken ? SK_ColorBLACK : SK_ColorWHITE, &innerPaint);
+            innerPaint.setStyle(SkPaint::kFill_Style);
+            canvas->drawGlyphs(glyphs + start, pos + (2 * start), glyphCount, innerPaint, x, y,
+                    bounds.mLeft, bounds.mTop, bounds.mRight, bounds.mBottom, totalAdvance);
+        } else {
+            // standard draw path
+            canvas->drawGlyphs(glyphs + start, pos + (2 * start), glyphCount, paint, x, y,
+                    bounds.mLeft, bounds.mTop, bounds.mRight, bounds.mBottom, totalAdvance);
+        }
+    }
+private:
+    const Layout& layout;
+    Canvas* canvas;
+    uint16_t* glyphs;
+    float* pos;
+    const SkPaint& paint;
+    float x;
+    float y;
+    MinikinRect& bounds;
+    float totalAdvance;
+};
+
+void Canvas::drawText(const uint16_t* text, int start, int count, int contextCount,
+        float x, float y, int bidiFlags, const Paint& origPaint, Typeface* typeface) {
+    // minikin may modify the original paint
+    Paint paint(origPaint);
+
+    Layout layout;
+    MinikinUtils::doLayout(&layout, &paint, bidiFlags, typeface, text, start, count, contextCount);
+
+    size_t nGlyphs = layout.nGlyphs();
+    std::unique_ptr<uint16_t[]> glyphs(new uint16_t[nGlyphs]);
+    std::unique_ptr<float[]> pos(new float[nGlyphs * 2]);
+
+    x += MinikinUtils::xOffsetForTextAlign(&paint, layout);
+
+    MinikinRect bounds;
+    layout.getBounds(&bounds);
+    if (!drawTextAbsolutePos()) {
+        bounds.offset(x, y);
+    }
+
+    DrawTextFunctor f(layout, this, glyphs.get(), pos.get(),
+            paint, x, y, bounds, layout.getAdvance());
+    MinikinUtils::forFontRun(layout, &paint, f);
+}
+
+class DrawTextOnPathFunctor {
+public:
+    DrawTextOnPathFunctor(const Layout& layout, Canvas* canvas, float hOffset,
+            float vOffset, const Paint& paint, const SkPath& path)
+        : layout(layout)
+        , canvas(canvas)
+        , hOffset(hOffset)
+        , vOffset(vOffset)
+        , paint(paint)
+        , path(path) {
+    }
+
+    void operator()(size_t start, size_t end) {
+        uint16_t glyphs[1];
+        for (size_t i = start; i < end; i++) {
+            glyphs[0] = layout.getGlyphId(i);
+            float x = hOffset + layout.getX(i);
+            float y = vOffset + layout.getY(i);
+            canvas->drawGlyphsOnPath(glyphs, 1, path, x, y, paint);
+        }
+    }
+private:
+    const Layout& layout;
+    Canvas* canvas;
+    float hOffset;
+    float vOffset;
+    const Paint& paint;
+    const SkPath& path;
+};
+
+void Canvas::drawTextOnPath(const uint16_t* text, int count, int bidiFlags, const SkPath& path,
+        float hOffset, float vOffset, const Paint& paint, Typeface* typeface) {
+    Paint paintCopy(paint);
+    Layout layout;
+    MinikinUtils::doLayout(&layout, &paintCopy, bidiFlags, typeface, text, 0, count, count);
+    hOffset += MinikinUtils::hOffsetForTextAlign(&paintCopy, layout, path);
+
+    // Set align to left for drawing, as we don't want individual
+    // glyphs centered or right-aligned; the offset above takes
+    // care of all alignment.
+    paintCopy.setTextAlign(Paint::kLeft_Align);
+
+    DrawTextOnPathFunctor f(layout, this, hOffset, vOffset, paintCopy, path);
+    MinikinUtils::forFontRun(layout, &paintCopy, f);
+}
+
+} // namespace android
diff --git a/libs/hwui/Canvas.h b/libs/hwui/hwui/Canvas.h
similarity index 92%
rename from libs/hwui/Canvas.h
rename to libs/hwui/hwui/Canvas.h
index 27facdf..dc669f0 100644
--- a/libs/hwui/Canvas.h
+++ b/libs/hwui/hwui/Canvas.h
@@ -59,6 +59,9 @@
 };
 typedef uirenderer::VectorDrawable::Tree VectorDrawableRoot;
 
+class Paint;
+struct Typeface;
+
 class ANDROID_API Canvas {
 public:
     virtual ~Canvas() {};
@@ -207,12 +210,12 @@
      * drawText: count is of glyphs
      * totalAdvance: used to define width of text decorations (underlines, strikethroughs).
      */
-    virtual void drawText(const uint16_t* glyphs, const float* positions, int count,
+    virtual void drawGlyphs(const uint16_t* glyphs, const float* positions, int count,
             const SkPaint& paint, float x, float y,
             float boundsLeft, float boundsTop, float boundsRight, float boundsBottom,
             float totalAdvance) = 0;
     /** drawTextOnPath: count is of glyphs */
-    virtual void drawTextOnPath(const uint16_t* glyphs, int count, const SkPath& path,
+    virtual void drawGlyphsOnPath(const uint16_t* glyphs, int count, const SkPath& path,
             float hOffset, float vOffset, const SkPaint& paint) = 0;
 
     /**
@@ -229,6 +232,16 @@
      */
     virtual void drawVectorDrawable(VectorDrawableRoot* tree);
 
+    /**
+     * Converts utf16 text to glyphs, calculating position and boundary,
+     * and delegating the final draw to virtual drawGlyphs method.
+     */
+    void drawText(const uint16_t* text, int start, int count, int contextCount,
+            float x, float y, int bidiFlags, const Paint& origPaint, Typeface* typeface);
+
+    void drawTextOnPath(const uint16_t* text, int count, int bidiFlags, const SkPath& path,
+            float hOffset, float vOffset, const Paint& paint, Typeface* typeface);
+
 protected:
     void drawTextDecorations(float x, float y, float length, const SkPaint& paint);
 };
diff --git a/core/jni/android/graphics/MinikinSkia.cpp b/libs/hwui/hwui/MinikinSkia.cpp
similarity index 98%
rename from core/jni/android/graphics/MinikinSkia.cpp
rename to libs/hwui/hwui/MinikinSkia.cpp
index 8ac5d46..b9e3358 100644
--- a/core/jni/android/graphics/MinikinSkia.cpp
+++ b/libs/hwui/hwui/MinikinSkia.cpp
@@ -14,14 +14,12 @@
  * limitations under the License.
  */
 
-#include <SkTypeface.h>
-#include <SkPaint.h>
-
-#define LOG_TAG "Minikin"
-#include <cutils/log.h>
-
 #include "MinikinSkia.h"
 
+#include <SkPaint.h>
+#include <SkTypeface.h>
+#include <cutils/log.h>
+
 namespace android {
 
 MinikinFontSkia::MinikinFontSkia(SkTypeface *typeface) :
diff --git a/core/jni/android/graphics/MinikinSkia.h b/libs/hwui/hwui/MinikinSkia.h
similarity index 92%
rename from core/jni/android/graphics/MinikinSkia.h
rename to libs/hwui/hwui/MinikinSkia.h
index 8f469ba..1d50168 100644
--- a/core/jni/android/graphics/MinikinSkia.h
+++ b/libs/hwui/hwui/MinikinSkia.h
@@ -17,11 +17,15 @@
 #ifndef _ANDROID_GRAPHICS_MINIKIN_SKIA_H_
 #define _ANDROID_GRAPHICS_MINIKIN_SKIA_H_
 
+#include <cutils/compiler.h>
 #include <minikin/MinikinFont.h>
 
+class SkPaint;
+class SkTypeface;
+
 namespace android {
 
-class MinikinFontSkia : public MinikinFont {
+class ANDROID_API MinikinFontSkia : public MinikinFont {
 public:
     // Note: this takes ownership of the reference (will unref on dtor)
     explicit MinikinFontSkia(SkTypeface *typeface);
diff --git a/core/jni/android/graphics/MinikinUtils.cpp b/libs/hwui/hwui/MinikinUtils.cpp
similarity index 88%
rename from core/jni/android/graphics/MinikinUtils.cpp
rename to libs/hwui/hwui/MinikinUtils.cpp
index 309d35b..67b775d 100644
--- a/core/jni/android/graphics/MinikinUtils.cpp
+++ b/libs/hwui/hwui/MinikinUtils.cpp
@@ -13,22 +13,20 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+#include "MinikinUtils.h"
 
-#define LOG_TAG "Minikin"
+#include "Paint.h"
+#include "SkPathMeasure.h"
+#include "Typeface.h"
+
 #include <cutils/log.h>
 #include <string>
 
-#include "SkPathMeasure.h"
-#include "Paint.h"
-#include "TypefaceImpl.h"
-
-#include "MinikinUtils.h"
-
 namespace android {
 
 FontStyle MinikinUtils::prepareMinikinPaint(MinikinPaint* minikinPaint, FontCollection** pFont,
-        const Paint* paint, TypefaceImpl* typeface) {
-    const TypefaceImpl* resolvedFace = TypefaceImpl_resolveDefault(typeface);
+        const Paint* paint, Typeface* typeface) {
+    const Typeface* resolvedFace = Typeface::resolveDefault(typeface);
     *pFont = resolvedFace->fFontCollection;
     FontStyle resolved = resolvedFace->fStyle;
 
@@ -53,7 +51,7 @@
 }
 
 void MinikinUtils::doLayout(Layout* layout, const Paint* paint, int bidiFlags,
-        TypefaceImpl* typeface, const uint16_t* buf, size_t start, size_t count,
+        Typeface* typeface, const uint16_t* buf, size_t start, size_t count,
         size_t bufSize) {
     FontCollection *font;
     MinikinPaint minikinPaint;
@@ -62,7 +60,7 @@
     layout->doLayout(buf, start, count, bufSize, bidiFlags, minikinStyle, minikinPaint);
 }
 
-float MinikinUtils::measureText(const Paint* paint, int bidiFlags, TypefaceImpl* typeface,
+float MinikinUtils::measureText(const Paint* paint, int bidiFlags, Typeface* typeface,
         const uint16_t* buf, size_t start, size_t count, size_t bufSize, float *advances) {
     FontCollection *font;
     MinikinPaint minikinPaint;
@@ -71,8 +69,8 @@
             font, advances);
 }
 
-bool MinikinUtils::hasVariationSelector(TypefaceImpl* typeface, uint32_t codepoint, uint32_t vs) {
-    const TypefaceImpl* resolvedFace = TypefaceImpl_resolveDefault(typeface);
+bool MinikinUtils::hasVariationSelector(Typeface* typeface, uint32_t codepoint, uint32_t vs) {
+    const Typeface* resolvedFace = Typeface::resolveDefault(typeface);
     return resolvedFace->fFontCollection->hasVariationSelector(codepoint, vs);
 }
 
diff --git a/core/jni/android/graphics/MinikinUtils.h b/libs/hwui/hwui/MinikinUtils.h
similarity index 72%
rename from core/jni/android/graphics/MinikinUtils.h
rename to libs/hwui/hwui/MinikinUtils.h
index 9152539..cfaa961 100644
--- a/core/jni/android/graphics/MinikinUtils.h
+++ b/libs/hwui/hwui/MinikinUtils.h
@@ -24,33 +24,34 @@
 #ifndef _ANDROID_GRAPHICS_MINIKIN_UTILS_H_
 #define _ANDROID_GRAPHICS_MINIKIN_UTILS_H_
 
+#include <cutils/compiler.h>
 #include <minikin/Layout.h>
 #include "Paint.h"
 #include "MinikinSkia.h"
-#include "TypefaceImpl.h"
+#include "Typeface.h"
 
 namespace android {
 
 class MinikinUtils {
 public:
-    static FontStyle prepareMinikinPaint(MinikinPaint* minikinPaint, FontCollection** pFont,
-            const Paint* paint, TypefaceImpl* typeface);
+    ANDROID_API static FontStyle prepareMinikinPaint(MinikinPaint* minikinPaint, FontCollection** pFont,
+            const Paint* paint, Typeface* typeface);
 
-    static void doLayout(Layout* layout, const Paint* paint, int bidiFlags,
-            TypefaceImpl* typeface, const uint16_t* buf, size_t start, size_t count,
+    ANDROID_API static void doLayout(Layout* layout, const Paint* paint, int bidiFlags,
+            Typeface* typeface, const uint16_t* buf, size_t start, size_t count,
             size_t bufSize);
 
-    static float measureText(const Paint* paint, int bidiFlags, TypefaceImpl* typeface,
+    ANDROID_API static float measureText(const Paint* paint, int bidiFlags, Typeface* typeface,
             const uint16_t* buf, size_t start, size_t count, size_t bufSize, float *advances);
 
-    static bool hasVariationSelector(TypefaceImpl* typeface, uint32_t codepoint, uint32_t vs);
+    ANDROID_API static bool hasVariationSelector(Typeface* typeface, uint32_t codepoint, uint32_t vs);
 
-    static float xOffsetForTextAlign(Paint* paint, const Layout& layout);
+    ANDROID_API static float xOffsetForTextAlign(Paint* paint, const Layout& layout);
 
-    static float hOffsetForTextAlign(Paint* paint, const Layout& layout, const SkPath& path);
+    ANDROID_API static float hOffsetForTextAlign(Paint* paint, const Layout& layout, const SkPath& path);
     // f is a functor of type void f(size_t start, size_t end);
     template <typename F>
-    static void forFontRun(const Layout& layout, Paint* paint, F& f) {
+    ANDROID_API static void forFontRun(const Layout& layout, Paint* paint, F& f) {
         float saveSkewX = paint->getTextSkewX();
         bool savefakeBold = paint->isFakeBoldText();
         MinikinFont* curFont = NULL;
diff --git a/core/jni/android/graphics/Paint.h b/libs/hwui/hwui/Paint.h
similarity index 93%
rename from core/jni/android/graphics/Paint.h
rename to libs/hwui/hwui/Paint.h
index cb6e622..69c321c 100644
--- a/core/jni/android/graphics/Paint.h
+++ b/libs/hwui/hwui/Paint.h
@@ -17,6 +17,8 @@
 #ifndef ANDROID_GRAPHICS_PAINT_H_
 #define ANDROID_GRAPHICS_PAINT_H_
 
+#include <cutils/compiler.h>
+
 #include <SkPaint.h>
 #include <string>
 
@@ -24,7 +26,7 @@
 
 namespace android {
 
-class Paint : public SkPaint {
+class ANDROID_API Paint : public SkPaint {
 public:
     Paint();
     Paint(const Paint& paint);
@@ -45,7 +47,7 @@
         return mLetterSpacing;
     }
 
-    void setFontFeatureSettings(const std::string &fontFeatureSettings) {
+    void setFontFeatureSettings(const std::string& fontFeatureSettings) {
         mFontFeatureSettings = fontFeatureSettings;
     }
 
diff --git a/core/jni/android/graphics/PaintImpl.cpp b/libs/hwui/hwui/PaintImpl.cpp
similarity index 95%
rename from core/jni/android/graphics/PaintImpl.cpp
rename to libs/hwui/hwui/PaintImpl.cpp
index bd513ae..1172a0e 100644
--- a/core/jni/android/graphics/PaintImpl.cpp
+++ b/libs/hwui/hwui/PaintImpl.cpp
@@ -15,10 +15,6 @@
  */
 
 #include "Paint.h"
-#include <SkPaint.h>
-
-#define LOG_TAG "Paint"
-#include <cutils/log.h>
 
 namespace android {
 
diff --git a/core/jni/android/graphics/TypefaceImpl.cpp b/libs/hwui/hwui/Typeface.cpp
similarity index 75%
rename from core/jni/android/graphics/TypefaceImpl.cpp
rename to libs/hwui/hwui/Typeface.cpp
index da56290..fa8ad5d 100644
--- a/core/jni/android/graphics/TypefaceImpl.cpp
+++ b/libs/hwui/hwui/Typeface.cpp
@@ -20,26 +20,21 @@
  * being, that choice is hidden under the USE_MINIKIN compile-time flag.
  */
 
-#define LOG_TAG "TypefaceImpl"
+#include "Typeface.h"
 
-#include "jni.h"  // for jlong, remove when being passed proper type
-
+#include "MinikinSkia.h"
 #include "SkTypeface.h"
+#include "SkPaint.h"
 
-#include <vector>
 #include <minikin/FontCollection.h>
 #include <minikin/FontFamily.h>
 #include <minikin/Layout.h>
-#include "SkPaint.h"
-#include "MinikinSkia.h"
-
-#include "TypefaceImpl.h"
-#include "Utils.h"
+#include <utils/Log.h>
 
 namespace android {
 
 // Resolve the 1..9 weight based on base weight and bold flag
-static void resolveStyle(TypefaceImpl* typeface) {
+static void resolveStyle(Typeface* typeface) {
     int weight = typeface->fBaseWeight / 100;
     if (typeface->fSkiaStyle & SkTypeface::kBold) {
         weight += 3;
@@ -51,7 +46,7 @@
     typeface->fStyle = FontStyle(weight, italic);
 }
 
-TypefaceImpl* gDefaultTypeface = NULL;
+Typeface* gDefaultTypeface = NULL;
 pthread_once_t gDefaultTypefaceOnce = PTHREAD_ONCE_INIT;
 
 // This installs a default typeface (from a hardcoded path) that allows
@@ -90,7 +85,7 @@
     if (gDefaultTypeface == NULL) {
         // We expect the client to set a default typeface, but provide a
         // default so we can make progress before that happens.
-        gDefaultTypeface = new TypefaceImpl;
+        gDefaultTypeface = new Typeface;
         gDefaultTypeface->fFontCollection = makeFontCollection();
         gDefaultTypeface->fSkiaStyle = SkTypeface::kNormal;
         gDefaultTypeface->fBaseWeight = 400;
@@ -98,7 +93,7 @@
     }
 }
 
-TypefaceImpl* TypefaceImpl_resolveDefault(TypefaceImpl* src) {
+Typeface* Typeface::resolveDefault(Typeface* src) {
     if (src == NULL) {
         pthread_once(&gDefaultTypefaceOnce, getDefaultTypefaceOnce);
         return gDefaultTypeface;
@@ -107,9 +102,9 @@
     }
 }
 
-TypefaceImpl* TypefaceImpl_createFromTypeface(TypefaceImpl* src, SkTypeface::Style style) {
-    TypefaceImpl* resolvedFace = TypefaceImpl_resolveDefault(src);
-    TypefaceImpl* result = new TypefaceImpl;
+Typeface* Typeface::createFromTypeface(Typeface* src, SkTypeface::Style style) {
+    Typeface* resolvedFace = Typeface::resolveDefault(src);
+    Typeface* result = new Typeface;
     if (result != 0) {
         result->fFontCollection = resolvedFace->fFontCollection;
         result->fFontCollection->Ref();
@@ -120,9 +115,9 @@
     return result;
 }
 
-TypefaceImpl* TypefaceImpl_createWeightAlias(TypefaceImpl* src, int weight) {
-    TypefaceImpl* resolvedFace = TypefaceImpl_resolveDefault(src);
-    TypefaceImpl* result = new TypefaceImpl;
+Typeface* Typeface::createWeightAlias(Typeface* src, int weight) {
+    Typeface* resolvedFace = Typeface::resolveDefault(src);
+    Typeface* result = new Typeface;
     if (result != 0) {
         result->fFontCollection = resolvedFace->fFontCollection;
         result->fFontCollection->Ref();
@@ -133,15 +128,10 @@
     return result;
 }
 
-TypefaceImpl* TypefaceImpl_createFromFamilies(const jlong* families, size_t size) {
-    std::vector<FontFamily *>familyVec;
-    for (size_t i = 0; i < size; i++) {
-        FontFamily* family = reinterpret_cast<FontFamily*>(families[i]);
-        familyVec.push_back(family);
-    }
-    TypefaceImpl* result = new TypefaceImpl;
-    result->fFontCollection = new FontCollection(familyVec);
-    if (size == 0) {
+Typeface* Typeface::createFromFamilies(const std::vector<FontFamily*>& families) {
+    Typeface* result = new Typeface;
+    result->fFontCollection = new FontCollection(families);
+    if (families.empty()) {
         ALOGW("createFromFamilies creating empty collection");
         result->fSkiaStyle = SkTypeface::kNormal;
     } else {
@@ -162,18 +152,12 @@
     return result;
 }
 
-void TypefaceImpl_unref(TypefaceImpl* face) {
-    if (face != NULL) {
-        face->fFontCollection->Unref();
-    }
-    delete face;
+void Typeface::unref() {
+    fFontCollection->Unref();
+    delete this;
 }
 
-int TypefaceImpl_getStyle(TypefaceImpl* face) {
-    return face->fSkiaStyle;
-}
-
-void TypefaceImpl_setDefault(TypefaceImpl* face) {
+void Typeface::setDefault(Typeface* face) {
     gDefaultTypeface = face;
 }
 
diff --git a/libs/hwui/hwui/Typeface.h b/libs/hwui/hwui/Typeface.h
new file mode 100644
index 0000000..8862e5a
--- /dev/null
+++ b/libs/hwui/hwui/Typeface.h
@@ -0,0 +1,55 @@
+/*
+ * 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.
+ */
+
+
+#ifndef _ANDROID_GRAPHICS_TYPEFACE_IMPL_H_
+#define _ANDROID_GRAPHICS_TYPEFACE_IMPL_H_
+
+#include "SkTypeface.h"
+
+#include <cutils/compiler.h>
+#include <minikin/FontCollection.h>
+#include <vector>
+
+namespace android {
+
+struct ANDROID_API Typeface {
+    FontCollection *fFontCollection;
+
+    // style used for constructing and querying Typeface objects
+    SkTypeface::Style fSkiaStyle;
+    // base weight in CSS-style units, 100..900
+    int fBaseWeight;
+
+    // resolved style actually used for rendering
+    FontStyle fStyle;
+
+    void unref();
+
+    static Typeface* resolveDefault(Typeface* src);
+
+    static Typeface* createFromTypeface(Typeface* src, SkTypeface::Style style);
+
+    static Typeface* createWeightAlias(Typeface* src, int baseweight);
+
+    static Typeface* createFromFamilies(const std::vector<FontFamily*>& families);
+
+    static void setDefault(Typeface* face);
+};
+
+}
+
+#endif  // _ANDROID_GRAPHICS_TYPEFACE_IMPL_H_
diff --git a/libs/hwui/hwui_static_deps.mk b/libs/hwui/hwui_static_deps.mk
index 7d4ef0f..2990952 100644
--- a/libs/hwui/hwui_static_deps.mk
+++ b/libs/hwui/hwui_static_deps.mk
@@ -21,8 +21,11 @@
     libskia \
     libui \
     libgui \
-    libprotobuf-cpp-lite
+    libprotobuf-cpp-lite \
+    libharfbuzz_ng \
+    libft2 \
+    libminikin
 
 ifneq (false,$(ANDROID_ENABLE_RENDERSCRIPT))
     LOCAL_SHARED_LIBRARIES += libRS libRScpp
-endif
\ No newline at end of file
+endif
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index c539d63..31eec8c 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -19,7 +19,6 @@
 
 #include "AnimationContext.h"
 #include "Caches.h"
-#include "Canvas.h"
 #include "DeferredLayerUpdater.h"
 #include "EglManager.h"
 #include "LayerUpdateQueue.h"
@@ -27,6 +26,7 @@
 #include "OpenGLRenderer.h"
 #include "Properties.h"
 #include "RenderThread.h"
+#include "hwui/Canvas.h"
 #include "renderstate/RenderState.h"
 #include "renderstate/Stencil.h"
 #include "protos/hwui.pb.h"
diff --git a/libs/hwui/tests/common/TestUtils.cpp b/libs/hwui/tests/common/TestUtils.cpp
index c809ff4..a4aee61 100644
--- a/libs/hwui/tests/common/TestUtils.cpp
+++ b/libs/hwui/tests/common/TestUtils.cpp
@@ -87,7 +87,7 @@
     *outTotalAdvance = totalAdvance;
 }
 
-void TestUtils::drawTextToCanvas(TestCanvas* canvas, const char* text,
+void TestUtils::drawUtf8ToCanvas(TestCanvas* canvas, const char* text,
         const SkPaint& paint, float x, float y) {
     // drawing text requires GlyphID TextEncoding (which JNI layer would have done)
     LOG_ALWAYS_FATAL_IF(paint.getTextEncoding() != SkPaint::kGlyphID_TextEncoding,
@@ -113,11 +113,11 @@
     // Force left alignment, since alignment offset is already baked in
     SkPaint alignPaintCopy(paint);
     alignPaintCopy.setTextAlign(SkPaint::kLeft_Align);
-    canvas->drawText(glyphs.data(), positions.data(), glyphs.size(), alignPaintCopy, x, y,
+    canvas->drawGlyphs(glyphs.data(), positions.data(), glyphs.size(), alignPaintCopy, x, y,
                 bounds.left, bounds.top, bounds.right, bounds.bottom, totalAdvance);
 }
 
-void TestUtils::drawTextToCanvas(TestCanvas* canvas, const char* text,
+void TestUtils::drawUtf8ToCanvas(TestCanvas* canvas, const char* text,
         const SkPaint& paint, const SkPath& path) {
     // drawing text requires GlyphID TextEncoding (which JNI layer would have done)
     LOG_ALWAYS_FATAL_IF(paint.getTextEncoding() != SkPaint::kGlyphID_TextEncoding,
@@ -130,7 +130,7 @@
         SkUnichar unichar = SkUTF8_NextUnichar(&text);
         glyphs.push_back(autoCache.getCache()->unicharToGlyph(unichar));
     }
-    canvas->drawTextOnPath(glyphs.data(), glyphs.size(), path, 0, 0, paint);
+    canvas->drawGlyphsOnPath(glyphs.data(), glyphs.size(), path, 0, 0, paint);
 }
 
 void TestUtils::TestTask::run() {
@@ -143,5 +143,13 @@
     renderState.onGLContextDestroyed();
 }
 
+std::unique_ptr<uint16_t[]> TestUtils::utf8ToUtf16(const char* str) {
+    const size_t strLen = strlen(str);
+    const ssize_t utf16Len = utf8_to_utf16_length((uint8_t*) str, strLen);
+    std::unique_ptr<uint16_t[]> dst(new uint16_t[utf16Len + 1]);
+    utf8_to_utf16((uint8_t*) str, strLen, (char16_t*) dst.get());
+    return dst;
+}
+
 } /* namespace uirenderer */
 } /* namespace android */
diff --git a/libs/hwui/tests/common/TestUtils.h b/libs/hwui/tests/common/TestUtils.h
index 28ac116..a5e7a5f 100644
--- a/libs/hwui/tests/common/TestUtils.h
+++ b/libs/hwui/tests/common/TestUtils.h
@@ -207,12 +207,14 @@
             std::vector<glyph_t>* outGlyphs, std::vector<float>* outPositions,
             float* outTotalAdvance, Rect* outBounds);
 
-    static void drawTextToCanvas(TestCanvas* canvas, const char* text,
+    static void drawUtf8ToCanvas(TestCanvas* canvas, const char* text,
             const SkPaint& paint, float x, float y);
 
-    static void drawTextToCanvas(TestCanvas* canvas, const char* text,
+    static void drawUtf8ToCanvas(TestCanvas* canvas, const char* text,
             const SkPaint& paint, const SkPath& path);
 
+    static std::unique_ptr<uint16_t[]> utf8ToUtf16(const char* str);
+
 private:
     static void syncHierarchyPropertiesAndDisplayListImpl(RenderNode* node) {
         node->syncProperties();
diff --git a/libs/hwui/tests/common/scenes/ListViewAnimation.cpp b/libs/hwui/tests/common/scenes/ListViewAnimation.cpp
index 43e247e..ab368c05 100644
--- a/libs/hwui/tests/common/scenes/ListViewAnimation.cpp
+++ b/libs/hwui/tests/common/scenes/ListViewAnimation.cpp
@@ -136,9 +136,9 @@
             textPaint.setAntiAlias(true);
             char buf[256];
             snprintf(buf, sizeof(buf), "This card is #%d", cardId);
-            TestUtils::drawTextToCanvas(&canvas, buf, textPaint, cardHeight, dp(25));
+            TestUtils::drawUtf8ToCanvas(&canvas, buf, textPaint, cardHeight, dp(25));
             textPaint.setTextSize(dp(15));
-            TestUtils::drawTextToCanvas(&canvas, "This is some more text on the card", textPaint,
+            TestUtils::drawUtf8ToCanvas(&canvas, "This is some more text on the card", textPaint,
                     cardHeight, dp(45));
 
             canvas.drawBitmap(createRandomCharIcon(), dp(10), dp(10), nullptr);
diff --git a/libs/hwui/tests/common/scenes/TextAnimation.cpp b/libs/hwui/tests/common/scenes/TextAnimation.cpp
index 1823db2..be8f48b9 100644
--- a/libs/hwui/tests/common/scenes/TextAnimation.cpp
+++ b/libs/hwui/tests/common/scenes/TextAnimation.cpp
@@ -39,14 +39,14 @@
 
             paint.setColor(Color::Black);
             for (int i = 0; i < 10; i++) {
-                TestUtils::drawTextToCanvas(&canvas, "Test string", paint, 400, i * 100);
+                TestUtils::drawUtf8ToCanvas(&canvas, "Test string", paint, 400, i * 100);
             }
 
             SkPath path;
             path.addOval(SkRect::MakeLTRB(100, 100, 300, 300));
 
             paint.setColor(Color::Blue_500);
-            TestUtils::drawTextToCanvas(&canvas, "This is a neat circle of text!", paint, path);
+            TestUtils::drawUtf8ToCanvas(&canvas, "This is a neat circle of text!", paint, path);
         });
         canvas.drawRenderNode(card.get());
     }
diff --git a/libs/hwui/tests/unit/CanvasStateTests.cpp b/libs/hwui/tests/unit/CanvasStateTests.cpp
index 68d74ee..0afabd8 100644
--- a/libs/hwui/tests/unit/CanvasStateTests.cpp
+++ b/libs/hwui/tests/unit/CanvasStateTests.cpp
@@ -16,9 +16,9 @@
 
 #include "CanvasState.h"
 
-#include "Canvas.h"
 #include "Matrix.h"
 #include "Rect.h"
+#include "hwui/Canvas.h"
 #include "utils/LinearAllocator.h"
 
 #include <gtest/gtest.h>
diff --git a/libs/hwui/tests/unit/FrameBuilderTests.cpp b/libs/hwui/tests/unit/FrameBuilderTests.cpp
index 31555f2..a467b5c 100644
--- a/libs/hwui/tests/unit/FrameBuilderTests.cpp
+++ b/libs/hwui/tests/unit/FrameBuilderTests.cpp
@@ -274,8 +274,8 @@
         paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
         paint.setAntiAlias(true);
         paint.setTextSize(50);
-        TestUtils::drawTextToCanvas(&canvas, "Test string1", paint, 100, 0); // will be top clipped
-        TestUtils::drawTextToCanvas(&canvas, "Test string1", paint, 100, 100); // not clipped
+        TestUtils::drawUtf8ToCanvas(&canvas, "Test string1", paint, 100, 0); // will be top clipped
+        TestUtils::drawUtf8ToCanvas(&canvas, "Test string1", paint, 100, 100); // not clipped
     });
     FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(400, 400), 400, 400,
             TestUtils::createSyncedNodeList(node), sLightGeometry, nullptr);
@@ -305,7 +305,7 @@
         textPaint.setStrikeThruText(true);
         textPaint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
         for (int i = 0; i < LOOPS; i++) {
-            TestUtils::drawTextToCanvas(&canvas, "test text", textPaint, 10, 100 * (i + 1));
+            TestUtils::drawUtf8ToCanvas(&canvas, "test text", textPaint, 10, 100 * (i + 1));
         }
     });
     FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 2000), 200, 2000,
@@ -361,7 +361,7 @@
         // They'll get merged, but with
         for (auto style : styles) {
             paint.setStyle(style);
-            TestUtils::drawTextToCanvas(&canvas, "Test string1", paint, 100, 100);
+            TestUtils::drawUtf8ToCanvas(&canvas, "Test string1", paint, 100, 100);
         }
     });
     FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(400, 400), 400, 400,
diff --git a/libs/hwui/tests/unit/RecordingCanvasTests.cpp b/libs/hwui/tests/unit/RecordingCanvasTests.cpp
index 5e613fd..6ab5110 100644
--- a/libs/hwui/tests/unit/RecordingCanvasTests.cpp
+++ b/libs/hwui/tests/unit/RecordingCanvasTests.cpp
@@ -18,6 +18,8 @@
 
 #include <RecordedOp.h>
 #include <RecordingCanvas.h>
+#include <hwui/Paint.h>
+#include <minikin/Layout.h>
 #include <tests/common/TestUtils.h>
 #include <utils/Color.h>
 
@@ -131,13 +133,13 @@
         << "Non-rounded rects should be converted";
 }
 
-TEST(RecordingCanvas, drawText) {
+TEST(RecordingCanvas, drawGlyphs) {
     auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
         SkPaint paint;
         paint.setAntiAlias(true);
         paint.setTextSize(20);
         paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
-        TestUtils::drawTextToCanvas(&canvas, "test text", paint, 25, 25);
+        TestUtils::drawUtf8ToCanvas(&canvas, "test text", paint, 25, 25);
     });
 
     int count = 0;
@@ -152,7 +154,7 @@
     ASSERT_EQ(1, count);
 }
 
-TEST(RecordingCanvas, drawText_strikeThruAndUnderline) {
+TEST(RecordingCanvas, drawGlyphs_strikeThruAndUnderline) {
     auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
         SkPaint paint;
         paint.setAntiAlias(true);
@@ -162,7 +164,7 @@
             for (int j = 0; j < 2; j++) {
                 paint.setUnderlineText(i != 0);
                 paint.setStrikeThruText(j != 0);
-                TestUtils::drawTextToCanvas(&canvas, "test text", paint, 25, 25);
+                TestUtils::drawUtf8ToCanvas(&canvas, "test text", paint, 25, 25);
             }
         }
     });
@@ -184,18 +186,18 @@
     EXPECT_EQ(RecordedOpId::RectOp, ops[index++]->opId); // strikethrough
 }
 
-TEST(RecordingCanvas, drawText_forceAlignLeft) {
+TEST(RecordingCanvas, drawGlyphs_forceAlignLeft) {
     auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
         SkPaint paint;
         paint.setAntiAlias(true);
         paint.setTextSize(20);
         paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
         paint.setTextAlign(SkPaint::kLeft_Align);
-        TestUtils::drawTextToCanvas(&canvas, "test text", paint, 25, 25);
+        TestUtils::drawUtf8ToCanvas(&canvas, "test text", paint, 25, 25);
         paint.setTextAlign(SkPaint::kCenter_Align);
-        TestUtils::drawTextToCanvas(&canvas, "test text", paint, 25, 25);
+        TestUtils::drawUtf8ToCanvas(&canvas, "test text", paint, 25, 25);
         paint.setTextAlign(SkPaint::kRight_Align);
-        TestUtils::drawTextToCanvas(&canvas, "test text", paint, 25, 25);
+        TestUtils::drawUtf8ToCanvas(&canvas, "test text", paint, 25, 25);
     });
 
     int count = 0;
@@ -223,8 +225,7 @@
     auto op = *(dl->getOps()[0]);
     EXPECT_EQ(RecordedOpId::RectOp, op.opId);
     EXPECT_EQ(nullptr, op.localClip);
-    EXPECT_TRUE(op.unmappedBounds.contains(Rect(-1000, -1000, 1000, 1000)))
-            << "no clip, unmappedBounds should resolve to be much larger than DL bounds";
+    EXPECT_TRUE(op.unmappedBounds.contains(Rect(200, 200))) << "Expect recording/clip bounds";
 }
 
 TEST(RecordingCanvas, backgroundAndImage) {
@@ -577,7 +578,7 @@
         canvas.drawRect(0, 0, 200, 10, paint);
         SkPaint paintCopy(paint);
         canvas.drawRect(0, 10, 200, 20, paintCopy);
-        TestUtils::drawTextToCanvas(&canvas, "helloworld", paint, 50, 25);
+        TestUtils::drawUtf8ToCanvas(&canvas, "helloworld", paint, 50, 25);
 
         // only here do we use different paint ptr
         paint.setColor(SK_ColorRED);
@@ -598,5 +599,54 @@
     EXPECT_NE(&paint, ops[3]->paint);
 }
 
+TEST(RecordingCanvas, drawText) {
+    auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
+        Paint paint;
+        paint.setAntiAlias(true);
+        paint.setTextSize(20);
+        paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
+        std::unique_ptr<uint16_t[]> dst = TestUtils::utf8ToUtf16("HELLO");
+        canvas.drawText(dst.get(), 0, 5, 5, 25, 25, kBidi_Force_LTR, paint, NULL);
+    });
+
+    int count = 0;
+    playbackOps(*dl, [&count](const RecordedOp& op) {
+        count++;
+        ASSERT_EQ(RecordedOpId::TextOp, op.opId);
+        EXPECT_EQ(nullptr, op.localClip);
+        EXPECT_TRUE(op.localMatrix.isIdentity());
+        EXPECT_TRUE(op.unmappedBounds.getHeight() >= 10);
+        EXPECT_TRUE(op.unmappedBounds.getWidth() >= 25);
+    });
+    ASSERT_EQ(1, count);
+}
+
+TEST(RecordingCanvas, drawTextInHighContrast) {
+    auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
+        canvas.setHighContrastText(true);
+        Paint paint;
+        paint.setColor(SK_ColorWHITE);
+        paint.setAntiAlias(true);
+        paint.setTextSize(20);
+        paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
+        std::unique_ptr<uint16_t[]> dst = TestUtils::utf8ToUtf16("HELLO");
+        canvas.drawText(dst.get(), 0, 5, 5, 25, 25, kBidi_Force_LTR, paint, NULL);
+    });
+
+    int count = 0;
+    playbackOps(*dl, [&count](const RecordedOp& op) {
+        ASSERT_EQ(RecordedOpId::TextOp, op.opId);
+        if (count++ == 0) {
+            EXPECT_EQ(SK_ColorBLACK, op.paint->getColor());
+            EXPECT_EQ(SkPaint::kStrokeAndFill_Style, op.paint->getStyle());
+        } else {
+            EXPECT_EQ(SK_ColorWHITE, op.paint->getColor());
+            EXPECT_EQ(SkPaint::kFill_Style, op.paint->getStyle());
+        }
+
+    });
+    ASSERT_EQ(2, count);
+}
+
 } // namespace uirenderer
 } // namespace android
diff --git a/media/java/android/media/AudioFormat.java b/media/java/android/media/AudioFormat.java
index abb6f4e..a4484e7 100644
--- a/media/java/android/media/AudioFormat.java
+++ b/media/java/android/media/AudioFormat.java
@@ -256,6 +256,11 @@
     public static final int ENCODING_AAC_HE_V2 = 12;
     /** Audio data format: compressed audio wrapped in PCM for HDMI
      * or S/PDIF passthrough.
+     * IEC61937 uses a stereo stream of 16-bit samples as the wrapper.
+     * So the channel mask for the track must be {@link #CHANNEL_OUT_STEREO}.
+     * Data should be written to the stream in a short[] array.
+     * If the data is written in a byte[] array then there may be endian problems
+     * on some platforms when converting to short internally.
      */
     public static final int ENCODING_IEC61937 = 13;
 
diff --git a/media/java/android/media/AudioTrack.java b/media/java/android/media/AudioTrack.java
index e1dab09..d9caf03 100644
--- a/media/java/android/media/AudioTrack.java
+++ b/media/java/android/media/AudioTrack.java
@@ -806,6 +806,15 @@
         }
         mSampleRate = sampleRateInHz;
 
+        // IEC61937 is based on stereo. We could coerce it to stereo.
+        // But the application needs to know the stream is stereo so that
+        // it is encoded and played correctly. So better to just reject it.
+        if (audioFormat == AudioFormat.ENCODING_IEC61937
+                && channelConfig != AudioFormat.CHANNEL_OUT_STEREO) {
+            throw new IllegalArgumentException(
+                    "ENCODING_IEC61937 must be configured as CHANNEL_OUT_STEREO");
+        }
+
         //--------------
         // channel config
         mChannelConfiguration = channelConfig;
diff --git a/media/java/android/media/ExifInterface.java b/media/java/android/media/ExifInterface.java
index ed358d3..dbedf34 100644
--- a/media/java/android/media/ExifInterface.java
+++ b/media/java/android/media/ExifInterface.java
@@ -830,7 +830,9 @@
         } catch (IOException e) {
             // Ignore exceptions in order to keep the compatibility with the old versions of
             // ExifInterface.
-            Log.w(TAG, "Invalid JPEG", e);
+            Log.w(TAG, "Invalid JPEG: ExifInterface got an unsupported image format file"
+                    + "(ExifInterface supports JPEG and some RAW image formats only) "
+                    + "or a corrupted JPEG file to ExifInterface.", e);
         }
 
         if (DEBUG) {
@@ -1189,6 +1191,10 @@
         ++bytesRead;
         while (true) {
             marker = dataInputStream.readByte();
+            if (marker == -1) {
+                Log.w(TAG, "Reading JPEG has ended unexpectedly");
+                break;
+            }
             if (marker != MARKER) {
                 throw new IOException("Invalid marker:" + Integer.toHexString(marker & 0xff));
             }
@@ -1207,7 +1213,8 @@
             int length = dataInputStream.readUnsignedShort() - 2;
             bytesRead += 2;
             if (DEBUG) {
-                Log.d(TAG, "JPEG segment: " + marker + " (length: " + (length + 2) + ")");
+                Log.d(TAG, "JPEG segment: " + Integer.toHexString(marker & 0xff) + " (length: "
+                        + (length + 2) + ")");
             }
             if (length < 0) {
                 throw new IOException("Invalid length");
@@ -1270,7 +1277,9 @@
                 case MARKER_SOF13:
                 case MARKER_SOF14:
                 case MARKER_SOF15: {
-                    dataInputStream.skipBytes(1);
+                    if (dataInputStream.skipBytes(1) != 1) {
+                        throw new IOException("Invalid SOFx");
+                    }
                     setAttribute("ImageLength",
                             String.valueOf(dataInputStream.readUnsignedShort()));
                     setAttribute("ImageWidth", String.valueOf(dataInputStream.readUnsignedShort()));
@@ -1285,7 +1294,9 @@
             if (length < 0) {
                 throw new IOException("Invalid length");
             }
-            dataInputStream.skipBytes(length);
+            if (dataInputStream.skipBytes(length) != length) {
+                throw new IOException("Invalid JPEG segment");
+            }
             bytesRead += length;
         }
     }
@@ -1317,10 +1328,15 @@
         byte[] bytes = new byte[4096];
 
         while (true) {
-            if (dataInputStream.readByte() != MARKER) {
+            byte marker = dataInputStream.readByte();
+            if (marker == -1) {
+                Log.w(TAG, "Reading JPEG has ended unexpectedly");
+                break;
+            }
+            if (marker != MARKER) {
                 throw new IOException("Invalid marker");
             }
-            byte marker = dataInputStream.readByte();
+            marker = dataInputStream.readByte();
             switch (marker) {
                 case MARKER_APP1: {
                     int length = dataInputStream.readUnsignedShort() - 2;
@@ -1644,7 +1660,7 @@
             String tagName = (String) sExifTagMapsForReading[hint].get(tagNumber);
 
             if (DEBUG) {
-                Log.d(TAG, String.format("hint: %d, tagNumber: %d, tagName: %s, dataFormat: %d," +
+                Log.d(TAG, String.format("hint: %d, tagNumber: %d, tagName: %s, dataFormat: %d, " +
                         "numberOfComponents: %d", hint, tagNumber, tagName, dataFormat,
                         numberOfComponents));
             }
@@ -2025,6 +2041,12 @@
         int bytesWritten = 0;
         int dataFormat = getDataFormatOfExifEntryValue(entryValue);
 
+        if (dataFormat == IFD_FORMAT_STRING) {
+            byte[] asciiArray = (entryValue + '\0').getBytes(Charset.forName("US-ASCII"));
+            dataOutputStream.write(asciiArray);
+            return asciiArray.length;
+        }
+
         // Values can be composed of several components. Each component is separated by char ','.
         String[] components = entryValue.split(",");
         for (String component : components) {
@@ -2037,11 +2059,6 @@
                     dataOutputStream.writeDouble(Double.parseDouble(component));
                     bytesWritten += 8;
                     break;
-                case IFD_FORMAT_STRING:
-                    byte[] asciiArray = (component + '\0').getBytes(Charset.forName("US-ASCII"));
-                    dataOutputStream.write(asciiArray);
-                    bytesWritten += asciiArray.length;
-                    break;
                 case IFD_FORMAT_SRATIONAL:
                     String[] rationalNumber = component.split("/");
                     dataOutputStream.writeInt(Integer.parseInt(rationalNumber[0]));
@@ -2060,11 +2077,31 @@
         // See TIFF 6.0 spec Types. page 15.
         // Take the first component if there are more than one component.
         if (entryValue.contains(",")) {
-            entryValue = entryValue.split(",")[0];
+            String[] entryValues = entryValue.split(",");
+            int dataFormat = getDataFormatOfExifEntryValue(entryValues[0]);
+            if (dataFormat == IFD_FORMAT_STRING) {
+                return IFD_FORMAT_STRING;
+            }
+            for (int i = 1; i < entryValues.length; ++i) {
+                if (getDataFormatOfExifEntryValue(entryValues[i]) != dataFormat) {
+                    return IFD_FORMAT_STRING;
+                }
+            }
+            return dataFormat;
         }
 
         if (entryValue.contains("/")) {
-            return IFD_FORMAT_SRATIONAL;
+            String[] rationalNumber = entryValue.split("/");
+            if (rationalNumber.length == 2) {
+                try {
+                    Integer.parseInt(rationalNumber[0]);
+                    Integer.parseInt(rationalNumber[1]);
+                    return IFD_FORMAT_SRATIONAL;
+                } catch (NumberFormatException e)  {
+                    // Ignored
+                }
+            }
+            return IFD_FORMAT_STRING;
         }
         try {
             Integer.parseInt(entryValue);
@@ -2084,6 +2121,9 @@
     // Determines the size of EXIF entry value.
     private static int getSizeOfExifEntryValue(int dataFormat, String entryValue) {
         // See TIFF 6.0 spec Types page 15.
+        if (dataFormat == IFD_FORMAT_STRING) {
+            return (entryValue + '\0').getBytes(Charset.forName("US-ASCII")).length;
+        }
         int bytesEstimated = 0;
         String[] components = entryValue.split(",");
         for (String component : components) {
@@ -2094,10 +2134,6 @@
                 case IFD_FORMAT_DOUBLE:
                     bytesEstimated += 8;
                     break;
-                case IFD_FORMAT_STRING:
-                    bytesEstimated
-                            += (component + '\0').getBytes(Charset.forName("US-ASCII")).length;
-                    break;
                 case IFD_FORMAT_SRATIONAL:
                     bytesEstimated += 8;
                     break;
diff --git a/media/java/android/media/MediaMetadata.java b/media/java/android/media/MediaMetadata.java
index 722605f..9ebf10f 100644
--- a/media/java/android/media/MediaMetadata.java
+++ b/media/java/android/media/MediaMetadata.java
@@ -365,7 +365,7 @@
     }
 
     private MediaMetadata(Parcel in) {
-        mBundle = in.readBundle();
+        mBundle = Bundle.setDefusable(in.readBundle(), true);
     }
 
     /**
diff --git a/packages/DocumentsUI/Android.mk b/packages/DocumentsUI/Android.mk
index d5e48b5..3197abd 100644
--- a/packages/DocumentsUI/Android.mk
+++ b/packages/DocumentsUI/Android.mk
@@ -31,9 +31,13 @@
   --extra-packages android.support.v7.recyclerview
 
 LOCAL_JACK_FLAGS := \
-  -D jack.assert.policy=enable \
   -D jack.optimization.inner-class.accessors=true
 
+# Only enable asserts on userdebug/eng builds
+ifneq (,$(filter userdebug eng, $(TARGET_BUILD_VARIANT)))
+LOCAL_JACK_FLAGS += -D jack.assert.policy=enable
+endif
+
 LOCAL_PACKAGE_NAME := DocumentsUI
 LOCAL_CERTIFICATE := platform
 
diff --git a/packages/DocumentsUI/AndroidManifest.xml b/packages/DocumentsUI/AndroidManifest.xml
index a4acf7e..be08385 100644
--- a/packages/DocumentsUI/AndroidManifest.xml
+++ b/packages/DocumentsUI/AndroidManifest.xml
@@ -45,7 +45,7 @@
             android:name=".LauncherActivity"
             android:theme="@android:style/Theme.NoDisplay"
             android:icon="@drawable/ic_files_app"
-            android:label="@string/files_label">
+            android:label="@string/downloads_label">
             <intent-filter>
                 <action android:name="android.intent.action.MAIN" />
                 <category android:name="android.intent.category.LAUNCHER" />
@@ -56,7 +56,7 @@
             android:name=".FilesActivity"
             android:theme="@style/DocumentsTheme"
             android:icon="@drawable/ic_files_app"
-            android:label="@string/files_label"
+            android:label="@string/downloads_label"
             android:documentLaunchMode="intoExisting">
             <intent-filter>
                 <action android:name="android.intent.action.MAIN" />
diff --git a/packages/DocumentsUI/res/drawable-hdpi/ic_launcher_download.png b/packages/DocumentsUI/res/drawable-hdpi/ic_launcher_download.png
new file mode 100644
index 0000000..f958bbd
--- /dev/null
+++ b/packages/DocumentsUI/res/drawable-hdpi/ic_launcher_download.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-mdpi/ic_launcher_download.png b/packages/DocumentsUI/res/drawable-mdpi/ic_launcher_download.png
new file mode 100644
index 0000000..f2e9376
--- /dev/null
+++ b/packages/DocumentsUI/res/drawable-mdpi/ic_launcher_download.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xhdpi/ic_launcher_download.png b/packages/DocumentsUI/res/drawable-xhdpi/ic_launcher_download.png
new file mode 100644
index 0000000..4dc5336
--- /dev/null
+++ b/packages/DocumentsUI/res/drawable-xhdpi/ic_launcher_download.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xxhdpi/ic_launcher_download.png b/packages/DocumentsUI/res/drawable-xxhdpi/ic_launcher_download.png
new file mode 100644
index 0000000..8716290
--- /dev/null
+++ b/packages/DocumentsUI/res/drawable-xxhdpi/ic_launcher_download.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xxxhdpi/ic_launcher_download.png b/packages/DocumentsUI/res/drawable-xxxhdpi/ic_launcher_download.png
new file mode 100644
index 0000000..f5be219
--- /dev/null
+++ b/packages/DocumentsUI/res/drawable-xxxhdpi/ic_launcher_download.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable/ic_files_app.xml b/packages/DocumentsUI/res/drawable/ic_files_app.xml
index ff7189e..76e3ba6 100644
--- a/packages/DocumentsUI/res/drawable/ic_files_app.xml
+++ b/packages/DocumentsUI/res/drawable/ic_files_app.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
 <bitmap xmlns:android="http://schemas.android.com/apk/res/android"
-    android:src="@drawable/icon256"
+    android:src="@drawable/ic_launcher_download"
     android:tint="?android:attr/colorControlNormal"
     android:autoMirrored="true" />
diff --git a/packages/DocumentsUI/res/drawable/icon256.png b/packages/DocumentsUI/res/drawable/icon256.png
deleted file mode 100644
index 631c951..0000000
--- a/packages/DocumentsUI/res/drawable/icon256.png
+++ /dev/null
Binary files differ
diff --git a/packages/DocumentsUI/res/values/config.xml b/packages/DocumentsUI/res/values/config.xml
index ebb3969..408603e 100644
--- a/packages/DocumentsUI/res/values/config.xml
+++ b/packages/DocumentsUI/res/values/config.xml
@@ -24,5 +24,6 @@
     <!-- Indicates if the home directory should be hidden in the roots list, that is presented
          in the drawer/left side panel ) -->
     <bool name="home_root_hidden">true</bool>
-
+    <!-- Indicates if the advanced roots like internal storage should be hidden in the roots list) -->
+    <bool name="advanced_roots_hidden">true</bool>
 </resources>
diff --git a/packages/DocumentsUI/res/values/styles.xml b/packages/DocumentsUI/res/values/styles.xml
index a548d89..b16554c 100644
--- a/packages/DocumentsUI/res/values/styles.xml
+++ b/packages/DocumentsUI/res/values/styles.xml
@@ -36,6 +36,8 @@
         <item name="android:windowActionBar">false</item>
         <item name="android:windowActionModeOverlay">true</item>
         <item name="android:windowNoTitle">true</item>
+        <item name="android:windowTranslucentStatus">true</item>
+        <item name="android:fitsSystemWindows">false</item>
 
         <item name="android:windowSoftInputMode">stateUnspecified|adjustUnspecified</item>
     </style>
@@ -43,7 +45,7 @@
     <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>    
+        <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 fe61094..3b300d0 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/BaseActivity.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/BaseActivity.java
@@ -127,6 +127,7 @@
         mSearchManager = new SearchViewManager(this, icicle);
 
         DocumentsToolbar toolbar = (DocumentsToolbar) findViewById(R.id.toolbar);
+        Display.adjustToolbar(toolbar, this);
         setActionBar(toolbar);
         mNavigator = new NavigationView(
                 mDrawer,
diff --git a/packages/DocumentsUI/src/com/android/documentsui/Display.java b/packages/DocumentsUI/src/com/android/documentsui/Display.java
index bae2d58..d46a3ea 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/Display.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/Display.java
@@ -20,13 +20,15 @@
 import android.content.Context;
 import android.graphics.Point;
 import android.util.TypedValue;
+import android.view.WindowManager;
+import android.widget.Toolbar;
 
 /*
  * Convenience class for getting display related attributes
  */
 public final class Display {
     /*
-     * Returns the screen width in pixels.
+     * Returns the screen width in raw pixels.
      */
     public static float screenWidth(Activity activity) {
         Point size = new Point();
@@ -42,15 +44,44 @@
     }
 
     /*
-     * Returns action bar height in pixels.
+     * Returns action bar height in raw pixels.
      */
     public static float actionBarHeight(Context context) {
-        int actionBarHeight = 0;
+        int height = 0;
         TypedValue tv = new TypedValue();
         if (context.getTheme().resolveAttribute(android.R.attr.actionBarSize, tv, true)) {
-            actionBarHeight = TypedValue.complexToDimensionPixelSize(tv.data,
+            height = TypedValue.complexToDimensionPixelSize(tv.data,
                     context.getResources().getDisplayMetrics());
         }
-        return actionBarHeight;
+        return height;
+    }
+
+    /*
+     * Returns status bar height in raw pixels.
+     */
+    private static int statusBarHeight(Context context) {
+        int height = 0;
+        int resourceId = context.getResources().getIdentifier("status_bar_height", "dimen",
+                "android");
+        if (resourceId > 0) {
+            height = context.getResources().getDimensionPixelSize(resourceId);
+        }
+        return height;
+    }
+
+    /*
+     * Adjusts toolbar for the layout with translucent status bar. Increases the
+     * height of the toolbar and adds padding at the top to accommodate status bar visible above
+     * toolbar.
+     */
+    public static void adjustToolbar(Toolbar toolbar, Activity activity) {
+        if ((activity.getWindow().getAttributes().flags
+                & WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS) != 0) {
+            int statusBarHeight = Display.statusBarHeight(activity);
+            toolbar.getLayoutParams().height = (int) (Display.actionBarHeight(activity)
+                    + statusBarHeight);
+            toolbar.setPadding(toolbar.getPaddingLeft(), statusBarHeight, toolbar.getPaddingRight(),
+                    toolbar.getPaddingBottom());
+        }
     }
 }
diff --git a/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java b/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java
index ba593dc..805d877 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java
@@ -95,7 +95,7 @@
         }
 
         if (mState.restored) {
-            if (DEBUG) Log.d(TAG, "Stack already resolved");
+            refreshCurrentRootAndDirectory(AnimationView.ANIM_NONE);
         } else {
             // We set the activity title in AsyncTask.onPostExecute().
             // To prevent talkback from reading aloud the default title, we clear it here.
@@ -108,7 +108,9 @@
             // we restore the stack as last used from that app.
             if (mState.action == ACTION_PICK_COPY_DESTINATION) {
                 if (DEBUG) Log.d(TAG, "Launching directly into Home directory.");
-                loadRoot(DocumentsContract.buildHomeUri());
+                Uri homeUri = DocumentsContract.buildHomeUri();
+                new LoadRootTask(this, homeUri).executeOnExecutor(
+                        ProviderExecutor.forAuthority(homeUri.getAuthority()));
             } else {
                 if (DEBUG) Log.d(TAG, "Attempting to load last used stack for calling package.");
                 new LoadLastUsedStackTask(this).execute();
@@ -154,6 +156,30 @@
         }
     }
 
+    private void onStackRestored(boolean restored, boolean external) {
+        // Show drawer when no stack restored, but only when requesting
+        // non-visual content. However, if we last used an external app,
+        // drawer is always shown.
+
+        boolean showDrawer = false;
+        if (!restored) {
+            showDrawer = true;
+        }
+        if (MimePredicate.mimeMatches(MimePredicate.VISUAL_MIMES, mState.acceptMimes)) {
+            showDrawer = false;
+        }
+        if (external && mState.action == ACTION_GET_CONTENT) {
+            showDrawer = true;
+        }
+        if (mState.action == ACTION_PICK_COPY_DESTINATION) {
+            showDrawer = true;
+        }
+
+        if (showDrawer) {
+            mNavigator.revealRootsDrawer(true);
+        }
+    }
+
     public void onAppPicked(ResolveInfo info) {
         final Intent intent = new Intent(getIntent());
         intent.setFlags(intent.getFlags() & ~Intent.FLAG_ACTIVITY_FORWARD_RESULT);
@@ -164,7 +190,7 @@
 
     @Override
     protected void onActivityResult(int requestCode, int resultCode, Intent data) {
-        Log.d(TAG, "onActivityResult() code=" + resultCode);
+        if (DEBUG) Log.d(TAG, "onActivityResult() code=" + resultCode);
 
         // Only relay back results when not canceled; otherwise stick around to
         // let the user pick another app/backend.
@@ -203,8 +229,8 @@
                        mState.action == ACTION_PICK_COPY_DESTINATION) {
                 title = getResources().getString(R.string.title_save);
             } else {
-                // If all else fails, just call it "Files".
-                title = getResources().getString(R.string.files_label);
+                // If all else fails, just call it "Downloads".
+                title = getResources().getString(R.string.downloads_label);
             }
         }
 
@@ -388,7 +414,7 @@
 
     @Override
     void onTaskFinished(Uri... uris) {
-        Log.d(TAG, "onFinished() " + Arrays.toString(uris));
+        if (DEBUG) Log.d(TAG, "onFinished() " + Arrays.toString(uris));
 
         final Intent intent = new Intent();
         if (uris.length == 1) {
@@ -491,8 +517,8 @@
         @Override
         protected void finish(Void result) {
             mState.restored = true;
-            mState.external = mExternal;
             mOwner.refreshCurrentRootAndDirectory(AnimationView.ANIM_NONE);
+            mOwner.onStackRestored(mRestoredStack, mExternal);
         }
     }
 
diff --git a/packages/DocumentsUI/src/com/android/documentsui/DrawerController.java b/packages/DocumentsUI/src/com/android/documentsui/DrawerController.java
index 020f2c0..2dbb730 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/DrawerController.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/DrawerController.java
@@ -55,7 +55,7 @@
 
         View drawer = activity.findViewById(R.id.drawer_roots);
         Toolbar toolbar = (Toolbar) activity.findViewById(R.id.roots_toolbar);
-
+        Display.adjustToolbar(toolbar, activity);
         drawer.getLayoutParams().width = calculateDrawerWidth(activity);
 
         ActionBarDrawerToggle toggle = new ActionBarDrawerToggle(
diff --git a/packages/DocumentsUI/src/com/android/documentsui/FilesActivity.java b/packages/DocumentsUI/src/com/android/documentsui/FilesActivity.java
index 99f306a..a6eba41 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/FilesActivity.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/FilesActivity.java
@@ -182,7 +182,7 @@
 
     @Override
     public String getDrawerTitle() {
-        return getResources().getString(R.string.files_label);
+        return getResources().getString(R.string.downloads_label);
     }
 
     @Override
@@ -414,7 +414,7 @@
 
     @Override
     void onTaskFinished(Uri... uris) {
-        Log.d(TAG, "onFinished() " + Arrays.toString(uris));
+        if (DEBUG) Log.d(TAG, "onFinished() " + Arrays.toString(uris));
 
         final Intent intent = new Intent();
         if (uris.length == 1) {
diff --git a/packages/DocumentsUI/src/com/android/documentsui/OpenExternalDirectoryActivity.java b/packages/DocumentsUI/src/com/android/documentsui/OpenExternalDirectoryActivity.java
index 2b6f396..ab45af1 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/OpenExternalDirectoryActivity.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/OpenExternalDirectoryActivity.java
@@ -282,7 +282,7 @@
             logInvalidScopedAccessRequest(context, SCOPED_DIRECTORY_ACCESS_ERROR);
             return null;
         }
-        Log.d(TAG, "doc id for " + file + ": " + docId);
+        if (DEBUG) Log.d(TAG, "doc id for " + file + ": " + docId);
 
         final Uri uri = DocumentsContract.buildTreeDocumentUri(EXTERNAL_STORAGE_AUTH, docId);
         if (uri == null) {
diff --git a/packages/DocumentsUI/src/com/android/documentsui/RecentsProvider.java b/packages/DocumentsUI/src/com/android/documentsui/RecentsProvider.java
index e1b1c09..6ef9154 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/RecentsProvider.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/RecentsProvider.java
@@ -16,6 +16,7 @@
 
 package com.android.documentsui;
 
+import static com.android.documentsui.Shared.DEBUG;
 import static com.android.documentsui.model.DocumentInfo.getCursorString;
 
 import android.content.ContentProvider;
@@ -338,7 +339,7 @@
                 if (predicate.apply(authority)) {
                     db.delete(TABLE_STATE, StateColumns.AUTHORITY + "=?", new String[] {
                             authority });
-                    Log.d(TAG, "Purged state for " + authority);
+                    if (DEBUG) Log.d(TAG, "Purged state for " + authority);
                 }
             }
         } finally {
diff --git a/packages/DocumentsUI/src/com/android/documentsui/RootsFragment.java b/packages/DocumentsUI/src/com/android/documentsui/RootsFragment.java
index 54e6287..1864431e 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/RootsFragment.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/RootsFragment.java
@@ -320,6 +320,8 @@
 
                 if (root.isHome() && isHomeRootHidden(context)) {
                     continue;
+                } else if (root.isAdvanced() && areAdvancedRootsHidden(context)) {
+                    continue;
                 } else if (root.isLibrary()) {
                     if (DEBUG) Log.d(TAG, "Adding " + root + " as library.");
                     libraries.add(item);
@@ -377,6 +379,13 @@
             return context.getResources().getBoolean(R.bool.home_root_hidden);
         }
 
+        /*
+         * Indicates if the advanced roots should be hidden.
+         */
+        private boolean areAdvancedRootsHidden(Context context) {
+            return context.getResources().getBoolean(R.bool.advanced_roots_hidden);
+        }
+
         @Override
         public View getView(int position, View convertView, ViewGroup parent) {
             final Item item = getItem(position);
diff --git a/packages/DocumentsUI/src/com/android/documentsui/SearchViewManager.java b/packages/DocumentsUI/src/com/android/documentsui/SearchViewManager.java
index 63dc2ee..4d0ba4b 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/SearchViewManager.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/SearchViewManager.java
@@ -16,6 +16,8 @@
 
 package com.android.documentsui;
 
+import static com.android.documentsui.Shared.DEBUG;
+
 import android.annotation.Nullable;
 import android.os.Bundle;
 import android.provider.DocumentsContract.Root;
@@ -80,7 +82,7 @@
      */
     void update(RootInfo root) {
         if (mMenu == null) {
-            Log.d(TAG, "update called before Search MenuItem installed.");
+            if (DEBUG) Log.d(TAG, "update called before Search MenuItem installed.");
             return;
         }
 
@@ -108,7 +110,7 @@
 
     void showMenu(boolean visible) {
         if (mMenu == null) {
-            Log.d(TAG, "showMenu called before Search MenuItem installed.");
+            if (DEBUG) Log.d(TAG, "showMenu called before Search MenuItem installed.");
             return;
         }
 
diff --git a/packages/DocumentsUI/src/com/android/documentsui/Shared.java b/packages/DocumentsUI/src/com/android/documentsui/Shared.java
index 6f1863e..11c5933 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/Shared.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/Shared.java
@@ -33,7 +33,7 @@
 
     public static final String TAG = "Documents";
 
-    public static final boolean DEBUG = true;
+    public static final boolean DEBUG = false;
 
     /** Intent action name to pick a copy destination. */
     public static final String ACTION_PICK_COPY_DESTINATION =
diff --git a/packages/DocumentsUI/src/com/android/documentsui/State.java b/packages/DocumentsUI/src/com/android/documentsui/State.java
index 16b7660..43468e3 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/State.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/State.java
@@ -85,10 +85,6 @@
     public boolean showSize;
     public boolean localOnly;
     public boolean restored;
-    /*
-     * Indicates handler was an external app, like photos.
-     */
-    public boolean external;
 
     // Indicates that a copy operation (or move) includes a directory.
     // Why? Directory creation isn't supported by some roots (like Downloads).
@@ -186,7 +182,6 @@
         out.writeInt(showSize ? 1 : 0);
         out.writeInt(localOnly ? 1 : 0);
         out.writeInt(restored ? 1 : 0);
-        out.writeInt(external ? 1 : 0);
         DurableUtils.writeToParcel(out, stack);
         out.writeMap(dirState);
         out.writeParcelable(selectedDocuments, 0);
@@ -215,7 +210,6 @@
             state.showSize = in.readInt() != 0;
             state.localOnly = in.readInt() != 0;
             state.restored = in.readInt() != 0;
-            state.external = in.readInt() != 0;
             DurableUtils.readFromParcel(in, state.stack);
             in.readMap(state.dirState, loader);
             state.selectedDocuments = in.readParcelable(loader);
diff --git a/packages/DocumentsUI/src/com/android/documentsui/dirlist/DirectoryFragment.java b/packages/DocumentsUI/src/com/android/documentsui/dirlist/DirectoryFragment.java
index 60e4b9a..0810d194 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/dirlist/DirectoryFragment.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/dirlist/DirectoryFragment.java
@@ -506,8 +506,10 @@
             getActivity().getWindow().setStatusBarColor(color.data);
 
             if (mActionMode != null) {
-                mActionMode.setTitle(Shared.getQuantityString(getActivity(),
-                        R.plurals.elements_selected, mSelected.size()));
+                final String title = Shared.getQuantityString(getActivity(),
+                        R.plurals.elements_selected, mSelected.size());
+                mActionMode.setTitle(title);
+                mRecView.announceForAccessibility(title);
             }
         }
 
diff --git a/packages/DocumentsUI/src/com/android/documentsui/dirlist/FragmentTuner.java b/packages/DocumentsUI/src/com/android/documentsui/dirlist/FragmentTuner.java
index ea1deb4..06cb9aa 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/dirlist/FragmentTuner.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/dirlist/FragmentTuner.java
@@ -157,27 +157,8 @@
 
         @Override
         void onModelLoaded(Model model, @ResultType int resultType, boolean isSearch) {
-            boolean showDrawer = false;
-
-            if (mState.restored) {
-                showDrawer = true;
-            }
-            if (MimePredicate.mimeMatches(MimePredicate.VISUAL_MIMES, mState.acceptMimes)) {
-                showDrawer = false;
-            }
-            if (mState.external && mState.action == ACTION_GET_CONTENT) {
-                showDrawer = true;
-            }
-            if (mState.action == ACTION_PICK_COPY_DESTINATION) {
-                showDrawer = true;
-            }
-
             // When launched into empty root, open drawer.
-            if (model.isEmpty()) {
-                showDrawer = true;
-            }
-
-            if (showDrawer && !mState.hasInitialLocationChanged() && !isSearch) {
+            if (model.isEmpty() && !mState.hasInitialLocationChanged() && !isSearch) {
                 // This noops on layouts without drawer, so no need to guard.
                 ((BaseActivity) mContext).setRootsDrawerOpen(true);
             }
diff --git a/packages/DocumentsUI/src/com/android/documentsui/model/RootInfo.java b/packages/DocumentsUI/src/com/android/documentsui/model/RootInfo.java
index 3960475..0709652 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/model/RootInfo.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/model/RootInfo.java
@@ -298,6 +298,10 @@
         return (flags & Root.FLAG_SUPPORTS_SEARCH) != 0;
     }
 
+    public boolean isAdvanced() {
+        return (flags & Root.FLAG_ADVANCED) != 0;
+    }
+
     public boolean isLocalOnly() {
         return (flags & Root.FLAG_LOCAL_ONLY) != 0;
     }
diff --git a/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java b/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java
index 9a51b05..62f33bf 100644
--- a/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java
+++ b/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java
@@ -196,6 +196,7 @@
             if (volume.isPrimary()) {
                 // save off the primary volume for subsequent "Home" dir initialization.
                 primaryVolume = volume;
+                root.flags |= Root.FLAG_ADVANCED;
             }
             // Dunno when this would NOT be the case, but never hurts to be correct.
             if (volume.isMountedWritable()) {
diff --git a/packages/MtpDocumentsProvider/Android.mk b/packages/MtpDocumentsProvider/Android.mk
index b31b0b1..0f945ee8 100644
--- a/packages/MtpDocumentsProvider/Android.mk
+++ b/packages/MtpDocumentsProvider/Android.mk
@@ -9,5 +9,10 @@
 LOCAL_JNI_SHARED_LIBRARIES := libappfuse_jni
 LOCAL_PROGUARD_FLAG_FILES := proguard.flags
 
+# Only enable asserts on userdebug/eng builds
+ifneq (,$(filter userdebug eng, $(TARGET_BUILD_VARIANT)))
+LOCAL_JACK_FLAGS += -D jack.assert.policy=enable
+endif
+
 include $(BUILD_PACKAGE)
 include $(call all-makefiles-under, $(LOCAL_PATH))
diff --git a/packages/MtpDocumentsProvider/res/values/strings.xml b/packages/MtpDocumentsProvider/res/values/strings.xml
index f3a3fcf..4a94ef4 100644
--- a/packages/MtpDocumentsProvider/res/values/strings.xml
+++ b/packages/MtpDocumentsProvider/res/values/strings.xml
@@ -16,7 +16,7 @@
 
 <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <!-- Title of the external storage application [CHAR LIMIT=32] -->
-    <string name="app_label">Files</string>
+    <string name="app_label">MTP Host</string>
     <!-- Name of MTP root shown in UI. Please align the two strings (device
          model and storage name) in proper order in the language.
          [CHAR LIMIT=32] -->
diff --git a/packages/MtpDocumentsProvider/src/com/android/mtp/DocumentLoader.java b/packages/MtpDocumentsProvider/src/com/android/mtp/DocumentLoader.java
index 0705214..68f426f 100644
--- a/packages/MtpDocumentsProvider/src/com/android/mtp/DocumentLoader.java
+++ b/packages/MtpDocumentsProvider/src/com/android/mtp/DocumentLoader.java
@@ -69,7 +69,8 @@
      */
     synchronized Cursor queryChildDocuments(String[] columnNames, Identifier parent)
             throws IOException {
-        Preconditions.checkArgument(parent.mDeviceId == mDevice.deviceId);
+        assert parent.mDeviceId == mDevice.deviceId;
+
         LoaderTask task = mTaskList.findTask(parent);
         if (task == null) {
             if (parent.mDocumentId == null) {
@@ -81,11 +82,9 @@
             // 3. startAddingChildDocuemnts.
             // 4. stopAddingChildDocuments - It removes the new document added at the step 2,
             //     because it is not updated between start/stopAddingChildDocuments.
-            task = LoaderTask.create(mDatabase, mMtpManager, mDevice.operationsSupported, parent);
-            task.fillDocuments(loadDocuments(
-                    mMtpManager,
-                    parent.mDeviceId,
-                    task.getUnloadedObjectHandles(NUM_INITIAL_ENTRIES)));
+            task = new LoaderTask(mMtpManager, mDatabase, mDevice.operationsSupported, parent);
+            task.loadObjectHandles();
+            task.loadObjectInfoList(NUM_INITIAL_ENTRIES);
         } else {
             // Once remove the existing task in order to add it to the head of the list.
             mTaskList.remove(task);
@@ -130,15 +129,11 @@
                 Preconditions.checkState(existingTask.getState() != LoaderTask.STATE_LOADING);
                 mTaskList.remove(existingTask);
             }
-            try {
-                final LoaderTask newTask = LoaderTask.create(
-                        mDatabase, mMtpManager, mDevice.operationsSupported, identifier);
-                mTaskList.addFirst(newTask);
-                return newTask;
-            } catch (IOException exception) {
-                Log.e(MtpDocumentsProvider.TAG, "Failed to create a task for mapping", exception);
-                // Continue to release the background thread.
-            }
+            final LoaderTask newTask = new LoaderTask(
+                    mMtpManager, mDatabase, mDevice.operationsSupported, identifier);
+            newTask.loadObjectHandles();
+            mTaskList.addFirst(newTask);
+            return newTask;
         }
 
         mBackgroundThread = null;
@@ -170,24 +165,6 @@
     }
 
     /**
-     * Helper method to loads multiple object info.
-     */
-    private static MtpObjectInfo[] loadDocuments(MtpManager manager, int deviceId, int[] handles)
-            throws IOException {
-        final ArrayList<MtpObjectInfo> objects = new ArrayList<>();
-        for (int i = 0; i < handles.length; i++) {
-            final MtpObjectInfo info = manager.getObjectInfo(deviceId, handles[i]);
-            if (info == null) {
-                Log.e(MtpDocumentsProvider.TAG,
-                        "Failed to obtain object info handle=" + handles[i]);
-                continue;
-            }
-            objects.add(info);
-        }
-        return objects.toArray(new MtpObjectInfo[objects.size()]);
-    }
-
-    /**
      * Background thread to fetch object info.
      */
     private class BackgroundLoaderThread extends Thread {
@@ -203,21 +180,13 @@
                 if (task == null) {
                     return;
                 }
-                try {
-                    final MtpObjectInfo[] objectInfos = loadDocuments(
-                            mMtpManager,
-                            task.mIdentifier.mDeviceId,
-                            task.getUnloadedObjectHandles(NUM_LOADING_ENTRIES));
-                    task.fillDocuments(objectInfos);
-                    final boolean shouldNotify =
-                            task.mLastNotified.getTime() <
-                            new Date().getTime() - NOTIFY_PERIOD_MS ||
-                            task.getState() != LoaderTask.STATE_LOADING;
-                    if (shouldNotify) {
-                        task.notify(mResolver);
-                    }
-                } catch (IOException exception) {
-                    task.setError(exception);
+                task.loadObjectInfoList(NUM_LOADING_ENTRIES);
+                final boolean shouldNotify =
+                        task.mLastNotified.getTime() <
+                        new Date().getTime() - NOTIFY_PERIOD_MS ||
+                        task.getState() != LoaderTask.STATE_LOADING;
+                if (shouldNotify) {
+                    task.notify(mResolver);
                 }
             }
         }
@@ -271,43 +240,67 @@
      * Each task is responsible for fetching child documents for the given parent document.
      */
     private static class LoaderTask {
-        static final int STATE_LOADING = 0;
-        static final int STATE_COMPLETED = 1;
-        static final int STATE_ERROR = 2;
+        static final int STATE_START = 0;
+        static final int STATE_LOADING = 1;
+        static final int STATE_COMPLETED = 2;
+        static final int STATE_ERROR = 3;
 
+        final MtpManager mManager;
         final MtpDatabase mDatabase;
         final int[] mOperationsSupported;
         final Identifier mIdentifier;
-        final int[] mObjectHandles;
+        int[] mObjectHandles;
+        int mState;
         Date mLastNotified;
-        int mNumLoaded;
-        Exception mError;
+        int mPosition;
+        IOException mError;
 
-        LoaderTask(MtpDatabase database, int[] operationsSupported, Identifier identifier,
-                int[] objectHandles) {
-            Preconditions.checkNotNull(operationsSupported);
-            Preconditions.checkNotNull(objectHandles);
+        LoaderTask(MtpManager manager, MtpDatabase database, int[] operationsSupported,
+                Identifier identifier) {
+            assert operationsSupported != null;
+            assert identifier.mDocumentType != MtpDatabaseConstants.DOCUMENT_TYPE_DEVICE;
+            mManager = manager;
             mDatabase = database;
             mOperationsSupported = operationsSupported;
             mIdentifier = identifier;
-            mObjectHandles = objectHandles;
-            mNumLoaded = 0;
+            mObjectHandles = null;
+            mState = STATE_START;
+            mPosition = 0;
             mLastNotified = new Date();
         }
 
+        synchronized void loadObjectHandles() {
+            assert mState == STATE_START;
+            int parentHandle = mIdentifier.mObjectHandle;
+            // Need to pass the special value MtpManager.OBJECT_HANDLE_ROOT_CHILDREN to
+            // getObjectHandles if we would like to obtain children under the root.
+            if (mIdentifier.mDocumentType == MtpDatabaseConstants.DOCUMENT_TYPE_STORAGE) {
+                parentHandle = MtpManager.OBJECT_HANDLE_ROOT_CHILDREN;
+            }
+            try {
+                mObjectHandles = mManager.getObjectHandles(
+                        mIdentifier.mDeviceId, mIdentifier.mStorageId, parentHandle);
+                mState = STATE_LOADING;
+            } catch (IOException error) {
+                mError = error;
+                mState = STATE_ERROR;
+            }
+        }
+
         /**
          * Returns a cursor that traverses the child document of the parent document handled by the
          * task.
          * The returned task may have a EXTRA_LOADING flag.
          */
-        Cursor createCursor(ContentResolver resolver, String[] columnNames) throws IOException {
+        synchronized Cursor createCursor(ContentResolver resolver, String[] columnNames)
+                throws IOException {
             final Bundle extras = new Bundle();
             switch (getState()) {
                 case STATE_LOADING:
                     extras.putBoolean(DocumentsContract.EXTRA_LOADING, true);
                     break;
                 case STATE_ERROR:
-                    throw new IOException(mError);
+                    throw mError;
             }
 
             final Cursor cursor =
@@ -319,26 +312,67 @@
         }
 
         /**
-         * Returns a state of the task.
+         * Stores object information into database.
          */
-        int getState() {
-            if (mError != null) {
-                return STATE_ERROR;
-            } else if (mNumLoaded == mObjectHandles.length) {
-                return STATE_COMPLETED;
-            } else {
-                return STATE_LOADING;
+        void loadObjectInfoList(int count) {
+            synchronized (this) {
+                if (mState != STATE_LOADING) {
+                    return;
+                }
+                if (mPosition == 0) {
+                    try{
+                        mDatabase.getMapper().startAddingDocuments(mIdentifier.mDocumentId);
+                    } catch (FileNotFoundException error) {
+                        mError = error;
+                        mState = STATE_ERROR;
+                        return;
+                    }
+                }
+            }
+            final ArrayList<MtpObjectInfo> infoList = new ArrayList<>();
+            for (int chunkEnd = mPosition + count;
+                    mPosition < mObjectHandles.length && mPosition < chunkEnd;
+                    mPosition++) {
+                try {
+                    infoList.add(mManager.getObjectInfo(
+                            mIdentifier.mDeviceId, mObjectHandles[mPosition]));
+                } catch (IOException error) {
+                    Log.e(MtpDocumentsProvider.TAG, "Failed to load object info", error);
+                }
+            }
+            synchronized (this) {
+                try {
+                    mDatabase.getMapper().putChildDocuments(
+                            mIdentifier.mDeviceId,
+                            mIdentifier.mDocumentId,
+                            mOperationsSupported,
+                            infoList.toArray(new MtpObjectInfo[infoList.size()]));
+                } catch (FileNotFoundException error) {
+                    // Looks like the parent document information is removed.
+                    // Adding documents has already cancelled in Mapper so we don't need to invoke
+                    // stopAddingDocuments.
+                    mError = error;
+                    mState = STATE_ERROR;
+                    return;
+                }
+                if (mPosition >= mObjectHandles.length) {
+                    try{
+                        mDatabase.getMapper().stopAddingDocuments(mIdentifier.mDocumentId);
+                        mState = STATE_COMPLETED;
+                    } catch (FileNotFoundException error) {
+                        mError = error;
+                        mState = STATE_ERROR;
+                        return;
+                    }
+                }
             }
         }
 
         /**
-         * Obtains object handles that have not been loaded yet.
+         * Returns a state of the task.
          */
-        int[] getUnloadedObjectHandles(int count) {
-            return Arrays.copyOfRange(
-                    mObjectHandles,
-                    mNumLoaded,
-                    Math.min(mNumLoaded + count, mObjectHandles.length));
+        int getState() {
+            return mState;
         }
 
         /**
@@ -349,69 +383,9 @@
             mLastNotified = new Date();
         }
 
-        /**
-         * Stores object information into database.
-         */
-        void fillDocuments(MtpObjectInfo[] objectInfoList) {
-            if (objectInfoList.length == 0 || getState() != STATE_LOADING) {
-                return;
-            }
-            try{
-                if (mNumLoaded == 0) {
-                    mDatabase.getMapper().startAddingDocuments(mIdentifier.mDocumentId);
-                }
-                mDatabase.getMapper().putChildDocuments(
-                        mIdentifier.mDeviceId, mIdentifier.mDocumentId, mOperationsSupported,
-                        objectInfoList);
-                mNumLoaded += objectInfoList.length;
-                if (getState() != STATE_LOADING) {
-                    mDatabase.getMapper().stopAddingDocuments(mIdentifier.mDocumentId);
-                }
-            } catch (FileNotFoundException exception) {
-                setErrorInternal(exception);
-            }
-        }
-
-        /**
-         * Marks the loading task as error.
-         */
-        void setError(Exception error) {
-            final int lastState = getState();
-            setErrorInternal(error);
-            if (lastState == STATE_LOADING) {
-                try {
-                    mDatabase.getMapper().stopAddingDocuments(mIdentifier.mDocumentId);
-                } catch (FileNotFoundException exception) {
-                    setErrorInternal(exception);
-                }
-            }
-        }
-
-        private void setErrorInternal(Exception error) {
-            Log.e(MtpDocumentsProvider.TAG, "Error in DocumentLoader thread", error);
-            mError = error;
-            mNumLoaded = 0;
-        }
-
         private Uri createUri() {
             return DocumentsContract.buildChildDocumentsUri(
                     MtpDocumentsProvider.AUTHORITY, mIdentifier.mDocumentId);
         }
-
-        /**
-         * Creates a LoaderTask that loads children of the given document.
-         */
-        static LoaderTask create(MtpDatabase database, MtpManager manager,
-                int[] operationsSupported, Identifier parent)
-                throws IOException {
-            int parentHandle = parent.mObjectHandle;
-            // Need to pass the special value MtpManager.OBJECT_HANDLE_ROOT_CHILDREN to
-            // getObjectHandles if we would like to obtain children under the root.
-            if (parent.mDocumentType == MtpDatabaseConstants.DOCUMENT_TYPE_STORAGE) {
-                parentHandle = MtpManager.OBJECT_HANDLE_ROOT_CHILDREN;
-            }
-            return new LoaderTask(database, operationsSupported, parent, manager.getObjectHandles(
-                    parent.mDeviceId, parent.mStorageId, parentHandle));
-        }
     }
 }
diff --git a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDatabase.java b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDatabase.java
index 8c73211..4564018 100644
--- a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDatabase.java
+++ b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDatabase.java
@@ -617,6 +617,7 @@
         final String whereClosure =
                 "parent." + COLUMN_DEVICE_ID + " = ? AND " +
                 "parent." + COLUMN_ROW_STATE + " IN (?, ?) AND " +
+                "parent." + COLUMN_DOCUMENT_TYPE + " != ? AND " +
                 "child." + COLUMN_ROW_STATE + " = ?";
         try (final Cursor cursor = mDatabase.query(
                 fromClosure,
@@ -626,7 +627,7 @@
                         "parent." + Document.COLUMN_DOCUMENT_ID,
                         "parent." + COLUMN_DOCUMENT_TYPE),
                 whereClosure,
-                strings(deviceId, ROW_STATE_VALID, ROW_STATE_INVALIDATED,
+                strings(deviceId, ROW_STATE_VALID, ROW_STATE_INVALIDATED, DOCUMENT_TYPE_DEVICE,
                         ROW_STATE_DISCONNECTED),
                 null,
                 null,
@@ -750,7 +751,12 @@
         values.putNull(Document.COLUMN_SUMMARY);
         values.putNull(Document.COLUMN_LAST_MODIFIED);
         values.put(Document.COLUMN_ICON, R.drawable.ic_root_mtp);
-        values.put(Document.COLUMN_FLAGS, 0);
+        values.put(Document.COLUMN_FLAGS, getDocumentFlags(
+                device.operationsSupported,
+                Document.MIME_TYPE_DIR,
+                0,
+                MtpConstants.PROTECTION_STATUS_NONE,
+                DOCUMENT_TYPE_DEVICE));
         values.putNull(Document.COLUMN_SIZE);
 
         extraValues.clear();
@@ -765,7 +771,7 @@
      * @param values {@link ContentValues} that receives values.
      * @param extraValues {@link ContentValues} that receives extra values for roots.
      * @param parentDocumentId Parent document ID.
-     * @param supportedOperations Array of Operation code supported by the device.
+     * @param operationsSupported Array of Operation code supported by the device.
      * @param root Root to be converted {@link ContentValues}.
      */
     static void getStorageDocumentValues(
@@ -786,7 +792,12 @@
         values.putNull(Document.COLUMN_SUMMARY);
         values.putNull(Document.COLUMN_LAST_MODIFIED);
         values.put(Document.COLUMN_ICON, R.drawable.ic_root_mtp);
-        values.put(Document.COLUMN_FLAGS, 0);
+        values.put(Document.COLUMN_FLAGS, getDocumentFlags(
+                operationsSupported,
+                Document.MIME_TYPE_DIR,
+                0,
+                MtpConstants.PROTECTION_STATUS_NONE,
+                DOCUMENT_TYPE_STORAGE));
         values.put(Document.COLUMN_SIZE, root.mMaxCapacity - root.mFreeSpace);
 
         extraValues.put(Root.COLUMN_FLAGS, getRootFlags(operationsSupported));
@@ -803,8 +814,8 @@
      * @param info MTP object info.
      */
     static void getObjectDocumentValues(
-            ContentValues values, int deviceId, String parentId, int[] operationsSupported,
-            MtpObjectInfo info) {
+            ContentValues values, int deviceId, String parentId,
+            int[] operationsSupported, MtpObjectInfo info) {
         values.clear();
         final String mimeType = getMimeType(info);
         values.put(COLUMN_DEVICE_ID, deviceId);
@@ -822,7 +833,7 @@
         values.putNull(Document.COLUMN_ICON);
         values.put(Document.COLUMN_FLAGS, getDocumentFlags(
                 operationsSupported, mimeType, info.getThumbCompressedSizeLong(),
-                info.getProtectionStatus()));
+                info.getProtectionStatus(), DOCUMENT_TYPE_OBJECT));
         values.put(Document.COLUMN_SIZE, info.getCompressedSizeLong());
     }
 
@@ -861,16 +872,19 @@
     }
 
     private static int getDocumentFlags(
-            int[] operationsSupported, String mimeType, long thumbnailSize, int protectionState) {
+            @Nullable int[] operationsSupported, String mimeType, long thumbnailSize,
+            int protectionState, @DocumentType int documentType) {
         int flag = 0;
-        if (MtpDeviceRecord.isWritingSupported(operationsSupported) &&
+        if (!mimeType.equals(Document.MIME_TYPE_DIR) &&
+                MtpDeviceRecord.isWritingSupported(operationsSupported) &&
                 protectionState == MtpConstants.PROTECTION_STATUS_NONE) {
             flag |= Document.FLAG_SUPPORTS_WRITE;
         }
         if (MtpDeviceRecord.isSupported(
                 operationsSupported, MtpConstants.OPERATION_DELETE_OBJECT) &&
                 (protectionState == MtpConstants.PROTECTION_STATUS_NONE ||
-                 protectionState == MtpConstants.PROTECTION_STATUS_NON_TRANSFERABLE_DATA)) {
+                 protectionState == MtpConstants.PROTECTION_STATUS_NON_TRANSFERABLE_DATA) &&
+                documentType == DOCUMENT_TYPE_OBJECT) {
             flag |= Document.FLAG_SUPPORTS_DELETE;
         }
         if (mimeType.equals(Document.MIME_TYPE_DIR) &&
diff --git a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDocumentsProvider.java b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDocumentsProvider.java
index 68c1992..cf5bee5 100644
--- a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDocumentsProvider.java
+++ b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDocumentsProvider.java
@@ -360,8 +360,12 @@
                     if (i == 0) {
                         infoUniqueName = info;
                     } else {
-                        infoUniqueName = new MtpObjectInfo.Builder(info).setName(
-                                baseName + " (" + i + ")." + extension).build();
+                        String suffixedName = baseName + " (" + i + " )";
+                        if (!extension.isEmpty()) {
+                            suffixedName += "." + extension;
+                        }
+                        infoUniqueName =
+                                new MtpObjectInfo.Builder(info).setName(suffixedName).build();
                     }
                     try {
                         objectHandle = mMtpManager.createDocument(
diff --git a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpManager.java b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpManager.java
index 6fb2a78..0599b70 100644
--- a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpManager.java
+++ b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpManager.java
@@ -130,11 +130,14 @@
         return devices.toArray(new MtpDeviceRecord[devices.size()]);
     }
 
-    MtpObjectInfo getObjectInfo(int deviceId, int objectHandle)
-            throws IOException {
+    MtpObjectInfo getObjectInfo(int deviceId, int objectHandle) throws IOException {
         final MtpDevice device = getDevice(deviceId);
         synchronized (device) {
-            return device.getObjectInfo(objectHandle);
+            final MtpObjectInfo info = device.getObjectInfo(objectHandle);
+            if (info == null) {
+                throw new IOException("Failed to get object info: " + objectHandle);
+            }
+            return info;
         }
     }
 
@@ -291,7 +294,7 @@
             if (usbInterface.getInterfaceClass() == UsbConstants.USB_SUBCLASS_VENDOR_SPEC &&
                     usbInterface.getInterfaceSubclass() == SUBCLASS_MTP &&
                     usbInterface.getInterfaceProtocol() == PROTOCOL_MTP &&
-                    usbInterface.getName().equals("MTP")) {
+                    "MTP".equals(usbInterface.getName())) {
                 return true;
             }
         }
diff --git a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/DocumentLoaderTest.java b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/DocumentLoaderTest.java
index db25421..45f89e4 100644
--- a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/DocumentLoaderTest.java
+++ b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/DocumentLoaderTest.java
@@ -55,13 +55,6 @@
 
         mManager = new BlockableTestMtpManager(getContext());
         mResolver = new TestContentResolver();
-        mLoader = new DocumentLoader(
-                new MtpDeviceRecord(
-                        0, "Device", "Key", true, new MtpRoot[0],
-                        TestUtil.OPERATIONS_SUPPORTED, new int[0]),
-                mManager,
-                mResolver,
-                mDatabase);
     }
 
     @Override
@@ -71,6 +64,8 @@
     }
 
     public void testBasic() throws Exception {
+        setUpLoader();
+
         final Uri uri = DocumentsContract.buildChildDocumentsUri(
                 MtpDocumentsProvider.AUTHORITY, mParentIdentifier.mDocumentId);
         setUpDocument(mManager, 40);
@@ -107,6 +102,55 @@
         assertEquals(2, mResolver.getChangeCount(uri));
     }
 
+    public void testError_GetObjectHandles() throws Exception {
+        mManager = new BlockableTestMtpManager(getContext()) {
+            @Override
+            int[] getObjectHandles(int deviceId, int storageId, int parentObjectHandle)
+                    throws IOException {
+                throw new IOException();
+            }
+        };
+        setUpLoader();
+        mManager.setObjectHandles(0, 0, MtpManager.OBJECT_HANDLE_ROOT_CHILDREN, null);
+        try {
+            try (final Cursor cursor = mLoader.queryChildDocuments(
+                    MtpDocumentsProvider.DEFAULT_DOCUMENT_PROJECTION, mParentIdentifier)) {}
+            fail();
+        } catch (IOException exception) {
+            // Expect exception.
+        }
+    }
+
+    public void testError_GetObjectInfo() throws Exception {
+        mManager = new BlockableTestMtpManager(getContext()) {
+            @Override
+            MtpObjectInfo getObjectInfo(int deviceId, int objectHandle) throws IOException {
+                if (objectHandle == DocumentLoader.NUM_INITIAL_ENTRIES) {
+                    throw new IOException();
+                } else {
+                    return super.getObjectInfo(deviceId, objectHandle);
+                }
+            }
+        };
+        setUpLoader();
+        setUpDocument(mManager, DocumentLoader.NUM_INITIAL_ENTRIES);
+        try (final Cursor cursor = mLoader.queryChildDocuments(
+                MtpDocumentsProvider.DEFAULT_DOCUMENT_PROJECTION, mParentIdentifier)) {
+            // Even if MtpManager returns an error for a document, loading must complete.
+            assertFalse(cursor.getExtras().getBoolean(DocumentsContract.EXTRA_LOADING));
+        }
+    }
+
+    private void setUpLoader() {
+        mLoader = new DocumentLoader(
+                new MtpDeviceRecord(
+                        0, "Device", "Key", true, new MtpRoot[0],
+                        TestUtil.OPERATIONS_SUPPORTED, new int[0]),
+                mManager,
+                mResolver,
+                mDatabase);
+    }
+
     private void setUpDocument(TestMtpManager manager, int count) {
         int[] childDocuments = new int[count];
         for (int i = 0; i < childDocuments.length; i++) {
diff --git a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDatabaseTest.java b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDatabaseTest.java
index b74069a..284223b 100644
--- a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDatabaseTest.java
+++ b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDatabaseTest.java
@@ -103,7 +103,7 @@
             assertTrue(isNull(cursor, COLUMN_SUMMARY));
             assertTrue(isNull(cursor, COLUMN_LAST_MODIFIED));
             assertEquals(R.drawable.ic_root_mtp, getInt(cursor, COLUMN_ICON));
-            assertEquals(0, getInt(cursor, COLUMN_FLAGS));
+            assertEquals(Document.FLAG_DIR_SUPPORTS_CREATE, getInt(cursor, COLUMN_FLAGS));
             assertEquals(1000, getInt(cursor, COLUMN_SIZE));
             assertEquals(
                     MtpDatabaseConstants.DOCUMENT_TYPE_STORAGE,
@@ -165,7 +165,7 @@
             assertTrue(isNull(cursor, COLUMN_SUMMARY));
             assertTrue(isNull(cursor, COLUMN_LAST_MODIFIED));
             assertEquals(R.drawable.ic_root_mtp, getInt(cursor, COLUMN_ICON));
-            assertEquals(0, getInt(cursor, COLUMN_FLAGS));
+            assertEquals(Document.FLAG_DIR_SUPPORTS_CREATE, getInt(cursor, COLUMN_FLAGS));
             assertEquals(1000, getInt(cursor, COLUMN_SIZE));
             assertEquals(
                     MtpDatabaseConstants.DOCUMENT_TYPE_STORAGE, getInt(cursor, COLUMN_DOCUMENT_TYPE));
diff --git a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDocumentsProviderTest.java b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDocumentsProviderTest.java
index 9c1880a..da6af37 100644
--- a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDocumentsProviderTest.java
+++ b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDocumentsProviderTest.java
@@ -351,7 +351,6 @@
         assertEquals(1422716400000L, cursor.getLong(3));
         assertEquals(
                 DocumentsContract.Document.FLAG_SUPPORTS_DELETE |
-                DocumentsContract.Document.FLAG_SUPPORTS_WRITE |
                 DocumentsContract.Document.FLAG_DIR_SUPPORTS_CREATE,
                 cursor.getInt(4));
         assertEquals(0, cursor.getInt(5));
@@ -419,20 +418,16 @@
         try {
             mProvider.queryChildDocuments("1", null, null);
             fail();
-        } catch (Throwable error) {
-            assertTrue(error instanceof FileNotFoundException);
-        }
+        } catch (FileNotFoundException error) {}
     }
 
     public void testQueryChildDocuments_documentError() throws Exception {
         setupProvider(MtpDatabaseConstants.FLAG_DATABASE_IN_MEMORY);
         setupRoots(0, new MtpRoot[] { new MtpRoot(0, 0, "Storage", 1000, 1000, "") });
         mMtpManager.setObjectHandles(0, 0, -1, new int[] { 1 });
-        try {
-            mProvider.queryChildDocuments("1", null, null);
-            fail();
-        } catch (Throwable error) {
-            assertTrue(error instanceof FileNotFoundException);
+        try (final Cursor cursor = mProvider.queryChildDocuments("1", null, null)) {
+            assertEquals(0, cursor.getCount());
+            assertFalse(cursor.getExtras().getBoolean(DocumentsContract.EXTRA_LOADING));
         }
     }
 
diff --git a/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java b/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java
index 4e1180d..bb34fcf 100644
--- a/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java
+++ b/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java
@@ -159,7 +159,6 @@
     private static final int STATE_PRINTER_UNAVAILABLE = 6;
     private static final int STATE_UPDATE_SLOW = 7;
     private static final int STATE_PRINT_COMPLETED = 8;
-    private static final int STATE_FINISHING = 9;
 
     private static final int UI_STATE_PREVIEW = 0;
     private static final int UI_STATE_ERROR = 1;
@@ -255,6 +254,9 @@
     /** Whether at least one print services is enabled or not */
     private boolean mArePrintServicesEnabled;
 
+    /** Is doFinish() already in progress */
+    private boolean mIsFinishing;
+
     @Override
     public void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
@@ -2035,11 +2037,11 @@
             return;
         }
 
-        if (mState == STATE_FINISHING) {
+        if (mIsFinishing) {
             return;
         }
 
-        mState = STATE_FINISHING;
+        mIsFinishing = true;
 
         if (mPrinterRegistry != null) {
             mPrinterRegistry.setTrackedPrinter(null);
diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml
index 1aee490..a2a3fd3 100644
--- a/packages/SettingsLib/res/values/strings.xml
+++ b/packages/SettingsLib/res/values/strings.xml
@@ -793,4 +793,23 @@
     <!-- Label for length of time until battery is charged [CHAR LIMIT=20] -->
     <string name="remaining_length_format"><xliff:g name="time" example="3 hours">%1$s</xliff:g> left</string>
 
+    <!-- Hint text for the IP address -->
+    <string name="wifi_ip_address_hint" translatable="false">192.168.1.128</string>
+    <!-- Hint text for DNS -->
+    <string name="wifi_dns1_hint" translatable="false">8.8.8.8</string>
+    <!-- Hint text for DNS -->
+    <string name="wifi_dns2_hint" translatable="false">8.8.4.4</string>
+    <!-- Hint text for the gateway -->
+    <string name="wifi_gateway_hint" translatable="false">192.168.1.1</string>
+    <!-- Hint text for network prefix length -->
+    <string name="wifi_network_prefix_length_hint" translatable="false">24</string>
+    <!-- HTTP proxy settings. The hint text field for port. -->
+    <string name="proxy_port_hint" translatable="false">8080</string>
+    <!-- HTTP proxy settings. Hint for Proxy-Auto Config URL. -->
+    <string name="proxy_url_hint" translatable="false">https://www.example.com/proxy.pac</string>
+    <!-- HTTP proxy settings. The hint text for proxy exclusion list. -->
+    <string name="proxy_exclusionlist_hint" translatable="false">example.com,mycomp.test.com,localhost</string>
+    <!-- HTTP proxy settings. The hint text field for the hostname. -->
+    <string name="proxy_hostname_hint" translatable="false">proxy.example.com</string>
+
 </resources>
diff --git a/packages/Shell/src/com/android/shell/BugreportProgressService.java b/packages/Shell/src/com/android/shell/BugreportProgressService.java
index 7fae0ee..c9582ea 100644
--- a/packages/Shell/src/com/android/shell/BugreportProgressService.java
+++ b/packages/Shell/src/com/android/shell/BugreportProgressService.java
@@ -149,6 +149,10 @@
     // Passed to Message.obtain() when msg.arg2 is not used.
     private static final int UNUSED_ARG2 = -2;
 
+    // Maximum progress displayed (like 99.00%).
+    private static final int CAPPED_PROGRESS = 9900;
+    private static final int CAPPED_MAX = 10000;
+
     /**
      * Delay before a screenshot is taken.
      * <p>
@@ -427,7 +431,7 @@
         final NumberFormat nf = NumberFormat.getPercentInstance();
         nf.setMinimumFractionDigits(2);
         nf.setMaximumFractionDigits(2);
-        final String percentText = nf.format((double) info.progress / info.max);
+        final String percentageText = nf.format((double) info.progress / info.max);
         final Action cancelAction = new Action.Builder(null, mContext.getString(
                 com.android.internal.R.string.cancel), newCancelIntent(mContext, info)).build();
         final Intent infoIntent = new Intent(mContext, BugreportProgressService.class);
@@ -458,7 +462,7 @@
                 .setContentTitle(title)
                 .setTicker(title)
                 .setContentText(name)
-                .setContentInfo(percentText)
+                .setContentInfo(percentageText)
                 .setProgress(info.max, info.progress, false)
                 .setOngoing(true)
                 .setContentIntent(infoPendingIntent)
@@ -472,7 +476,7 @@
         }
         if (DEBUG) {
             Log.d(TAG, "Sending 'Progress' notification for id " + info.id + "(pid " + info.pid
-                    + "): " + percentText);
+                    + "): " + percentageText);
         }
         NotificationManager.from(mContext).notify(TAG, info.id, notification);
     }
@@ -545,25 +549,47 @@
             }
             activeProcesses++;
             final String progressKey = DUMPSTATE_PREFIX + pid + PROGRESS_SUFFIX;
-            final int progress = SystemProperties.getInt(progressKey, 0);
-            if (progress == 0) {
+            info.realProgress = SystemProperties.getInt(progressKey, 0);
+            if (info.realProgress == 0) {
                 Log.v(TAG, "System property " + progressKey + " is not set yet");
             }
-            final int max = SystemProperties.getInt(DUMPSTATE_PREFIX + pid + MAX_SUFFIX, 0);
-            final boolean maxChanged = max > 0 && max != info.max;
-            final boolean progressChanged = progress > 0 && progress != info.progress;
+            final String maxKey = DUMPSTATE_PREFIX + pid + MAX_SUFFIX;
+            info.realMax = SystemProperties.getInt(maxKey, info.max);
+            if (info.realMax <= 0 ) {
+                Log.w(TAG, "Property " + maxKey + " is not positive: " + info.max);
+                continue;
+            }
+            /*
+             * Checks whether the progress changed in a way that should be displayed to the user:
+             * - info.progress / info.max represents the displayed progress
+             * - info.realProgress / info.realMax represents the real progress
+             * - since the real progress can decrease, the displayed progress is only updated if it
+             *   increases
+             * - the displayed progress is capped at a maximum (like 99%)
+             */
+            final int oldPercentage = (CAPPED_MAX * info.progress) / info.max;
+            int newPercentage = (CAPPED_MAX * info.realProgress) / info.realMax;
+            int max = info.realMax;
+            int progress = info.realProgress;
 
-            if (progressChanged || maxChanged) {
-                if (progressChanged) {
-                    if (DEBUG) Log.v(TAG, "Updating progress for PID " + pid + "(id: " + id
-                            + ") from " + info.progress + " to " + progress);
-                    info.progress = progress;
+            if (newPercentage > CAPPED_PROGRESS) {
+                progress = newPercentage = CAPPED_PROGRESS;
+                max = CAPPED_MAX;
+            }
+
+            if (newPercentage > oldPercentage) {
+                if (DEBUG) {
+                    if (progress != info.progress) {
+                        Log.v(TAG, "Updating progress for PID " + pid + "(id: " + id + ") from "
+                                + info.progress + " to " + progress);
+                    }
+                    if (max != info.max) {
+                        Log.v(TAG, "Updating max progress for PID " + pid + "(id: " + id + ") from "
+                                + info.max + " to " + max);
+                    }
                 }
-                if (maxChanged) {
-                    Log.i(TAG, "Updating max progress for PID " + pid + "(id: " + id
-                            + ") from " + info.max + " to " + max);
-                    info.max = max;
-                }
+                info.progress = progress;
+                info.max = max;
                 info.lastUpdate = System.currentTimeMillis();
                 updateProgress(info);
             } else {
@@ -1450,16 +1476,26 @@
         String description;
 
         /**
-         * Maximum progress of the bugreport generation.
+         * Maximum progress of the bugreport generation as displayed by the UI.
          */
         int max;
 
         /**
-         * Current progress of the bugreport generation.
+         * Current progress of the bugreport generation as displayed by the UI.
          */
         int progress;
 
         /**
+         * Maximum progress of the bugreport generation as reported by dumpstate.
+         */
+        int realMax;
+
+        /**
+         * Current progress of the bugreport generation as reported by dumpstate.
+         */
+        int realProgress;
+
+        /**
          * Time of the last progress update.
          */
         long lastUpdate = System.currentTimeMillis();
@@ -1568,10 +1604,12 @@
         @Override
         public String toString() {
             final float percent = ((float) progress * 100 / max);
+            final float realPercent = ((float) realProgress * 100 / realMax);
             return "id: " + id + ", pid: " + pid + ", name: " + name + ", finished: " + finished
                     + "\n\ttitle: " + title + "\n\tdescription: " + description
                     + "\n\tfile: " + bugreportFile + "\n\tscreenshots: " + screenshotFiles
                     + "\n\tprogress: " + progress + "/" + max + " (" + percent + ")"
+                    + "\n\treal progress: " + realProgress + "/" + realMax + " (" + realPercent + ")"
                     + "\n\tlast_update: " + getFormattedLastUpdate()
                     + "\naddingDetailsToZip: " + addingDetailsToZip
                     + " addedDetailsToZip: " + addedDetailsToZip;
@@ -1587,6 +1625,8 @@
             description = in.readString();
             max = in.readInt();
             progress = in.readInt();
+            realMax = in.readInt();
+            realProgress = in.readInt();
             lastUpdate = in.readLong();
             formattedLastUpdate = in.readString();
             bugreportFile = readFile(in);
@@ -1609,6 +1649,8 @@
             dest.writeString(description);
             dest.writeInt(max);
             dest.writeInt(progress);
+            dest.writeInt(realMax);
+            dest.writeInt(realProgress);
             dest.writeLong(lastUpdate);
             dest.writeString(getFormattedLastUpdate());
             writeFile(dest, bugreportFile);
diff --git a/packages/Shell/src/com/android/shell/BugreportStorageProvider.java b/packages/Shell/src/com/android/shell/BugreportStorageProvider.java
index 49759c5..814aa8c 100644
--- a/packages/Shell/src/com/android/shell/BugreportStorageProvider.java
+++ b/packages/Shell/src/com/android/shell/BugreportStorageProvider.java
@@ -56,7 +56,7 @@
         final MatrixCursor result = new MatrixCursor(resolveRootProjection(projection));
         final RowBuilder row = result.newRow();
         row.add(Root.COLUMN_ROOT_ID, DOC_ID_ROOT);
-        row.add(Root.COLUMN_FLAGS, Root.FLAG_LOCAL_ONLY);
+        row.add(Root.COLUMN_FLAGS, Root.FLAG_LOCAL_ONLY | Root.FLAG_ADVANCED);
         row.add(Root.COLUMN_ICON, android.R.mipmap.sym_def_app_icon);
         row.add(Root.COLUMN_TITLE, getContext().getString(R.string.bugreport_storage_title));
         row.add(Root.COLUMN_DOCUMENT_ID, DOC_ID_ROOT);
diff --git a/packages/Shell/tests/src/com/android/shell/BugreportReceiverTest.java b/packages/Shell/tests/src/com/android/shell/BugreportReceiverTest.java
index 17f6f6b..e1e0c3b 100644
--- a/packages/Shell/tests/src/com/android/shell/BugreportReceiverTest.java
+++ b/packages/Shell/tests/src/com/android/shell/BugreportReceiverTest.java
@@ -26,6 +26,7 @@
 import static com.android.shell.BugreportProgressService.EXTRA_SCREENSHOT;
 import static com.android.shell.BugreportProgressService.INTENT_BUGREPORT_FINISHED;
 import static com.android.shell.BugreportProgressService.INTENT_BUGREPORT_STARTED;
+import static com.android.shell.BugreportProgressService.POLLING_FREQUENCY;
 import static com.android.shell.BugreportProgressService.SCREENSHOT_DELAY_SECONDS;
 
 import java.io.BufferedOutputStream;
@@ -92,7 +93,7 @@
     private static final String TAG = "BugreportReceiverTest";
 
     // Timeout for UI operations, in milliseconds.
-    private static final int TIMEOUT = (int) BugreportProgressService.POLLING_FREQUENCY * 4;
+    private static final int TIMEOUT = (int) POLLING_FREQUENCY * 4;
 
     // Timeout for when waiting for a screenshot to finish.
     private static final int SAFE_SCREENSHOT_DELAY = SCREENSHOT_DELAY_SECONDS + 10;
@@ -190,8 +191,30 @@
         SystemProperties.set(PROGRESS_PROPERTY, "500");
         assertProgressNotification(NAME, nf.format(0.50));
 
+        SystemProperties.set(PROGRESS_PROPERTY, "950");
+        assertProgressNotification(NAME, nf.format(0.95));
+
+        // Make sure progress never goes back...
         SystemProperties.set(MAX_PROPERTY, "2000");
-        assertProgressNotification(NAME, nf.format(0.25));
+        Thread.sleep(POLLING_FREQUENCY + DateUtils.SECOND_IN_MILLIS);
+        assertProgressNotification(NAME, nf.format(0.95));
+
+        SystemProperties.set(PROGRESS_PROPERTY, "1000");
+        assertProgressNotification(NAME, nf.format(0.95));
+
+        // ...only forward...
+        SystemProperties.set(PROGRESS_PROPERTY, "1902");
+        assertProgressNotification(NAME, nf.format(0.9510));
+
+        SystemProperties.set(PROGRESS_PROPERTY, "1960");
+        assertProgressNotification(NAME, nf.format(0.98));
+
+        // ...but never more than the capped value.
+        SystemProperties.set(PROGRESS_PROPERTY, "2000");
+        assertProgressNotification(NAME, nf.format(0.99));
+
+        SystemProperties.set(PROGRESS_PROPERTY, "3000");
+        assertProgressNotification(NAME, nf.format(0.99));
 
         Bundle extras =
                 sendBugreportFinishedAndGetSharedIntent(ID, mPlainTextPath, mScreenshotPath);
diff --git a/packages/Shell/tests/src/com/android/shell/UiBot.java b/packages/Shell/tests/src/com/android/shell/UiBot.java
index 384c3da..5bfe1a0 100644
--- a/packages/Shell/tests/src/com/android/shell/UiBot.java
+++ b/packages/Shell/tests/src/com/android/shell/UiBot.java
@@ -32,7 +32,7 @@
 final class UiBot {
 
     private static final String TAG = "UiBot";
-    private static final String SYSTEMUI_PACKAGED = "com.android.systemui";
+    private static final String SYSTEMUI_PACKAGE = "com.android.systemui";
 
     private final UiDevice mDevice;
     private final int mTimeout;
@@ -51,8 +51,8 @@
     public UiObject getNotification(String text) {
         boolean opened = mDevice.openNotification();
         Log.v(TAG, "openNotification(): " + opened);
-        boolean gotIt = mDevice.wait(Until.hasObject(By.pkg(SYSTEMUI_PACKAGED)), mTimeout);
-        assertTrue("could not get system ui (" + SYSTEMUI_PACKAGED + ")", gotIt);
+        boolean gotIt = mDevice.wait(Until.hasObject(By.pkg(SYSTEMUI_PACKAGE)), mTimeout);
+        assertTrue("could not get system ui (" + SYSTEMUI_PACKAGE + ")", gotIt);
 
         return getObject(text);
     }
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index 073cf14..589eac6 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -156,6 +156,9 @@
     <!-- TV picture-in-picture -->
     <uses-permission android:name="android.permission.RECEIVE_MEDIA_RESOURCE_USAGE" />
 
+    <!-- DND access -->
+    <uses-permission android:name="android.permission.MANAGE_NOTIFICATIONS" />
+
     <application
         android:name=".SystemUIApplication"
         android:persistent="true"
diff --git a/packages/SystemUI/res/drawable-hdpi/ic_cancel_white_24dp.png b/packages/SystemUI/res/drawable-hdpi/ic_cancel_white_24dp.png
new file mode 100644
index 0000000..73f5116
--- /dev/null
+++ b/packages/SystemUI/res/drawable-hdpi/ic_cancel_white_24dp.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/ic_cancel_white_24dp.png b/packages/SystemUI/res/drawable-mdpi/ic_cancel_white_24dp.png
new file mode 100644
index 0000000..787e259
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/ic_cancel_white_24dp.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/ic_cancel_white_24dp.png b/packages/SystemUI/res/drawable-xhdpi/ic_cancel_white_24dp.png
new file mode 100644
index 0000000..6ebbc83
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/ic_cancel_white_24dp.png
Binary files differ
diff --git a/packages/SystemUI/res/layout/hybrid_notification.xml b/packages/SystemUI/res/layout/hybrid_notification.xml
index f667859..476f52b 100644
--- a/packages/SystemUI/res/layout/hybrid_notification.xml
+++ b/packages/SystemUI/res/layout/hybrid_notification.xml
@@ -21,7 +21,7 @@
     android:layout_height="wrap_content"
     android:paddingStart="@*android:dimen/notification_content_margin_start"
     android:paddingEnd="12dp"
-    android:gravity="bottom">
+    android:gravity="bottom|start">
     <TextView
         android:id="@+id/notification_title"
         android:layout_width="wrap_content"
diff --git a/packages/SystemUI/res/layout/hybrid_overflow_number.xml b/packages/SystemUI/res/layout/hybrid_overflow_number.xml
new file mode 100644
index 0000000..f3dde8d
--- /dev/null
+++ b/packages/SystemUI/res/layout/hybrid_overflow_number.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2016 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
+  -->
+<TextView
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/notification_text"
+    android:layout_width="wrap_content"
+    android:layout_height="wrap_content"
+    android:textAppearance="@*android:style/TextAppearance.Material.Notification"
+    android:paddingEnd="@*android:dimen/notification_content_margin_end"
+    android:gravity="end"
+    android:singleLine="true"
+    />
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/notification_guts.xml b/packages/SystemUI/res/layout/notification_guts.xml
index 1ab6bf9..062ae35 100644
--- a/packages/SystemUI/res/layout/notification_guts.xml
+++ b/packages/SystemUI/res/layout/notification_guts.xml
@@ -31,12 +31,12 @@
     <!-- header -->
     <LinearLayout
             android:layout_width="match_parent"
-            android:layout_height="30dp"
-            android:paddingTop="9dp"
+            android:layout_height="wrap_content"
+            android:paddingTop="14dp"
             android:paddingEnd="8dp"
             android:id="@+id/notification_guts_header"
             android:orientation="horizontal"
-            android:layout_gravity="center_vertical|start">
+            android:layout_gravity="start">
 
         <ImageView
                 android:id="@+id/app_icon"
@@ -64,7 +64,6 @@
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
             android:paddingTop="4dp"
-            android:paddingBottom="16dip"
             android:paddingEnd="8dp" >
         <RadioButton
                 android:id="@+id/silent_importance"
@@ -99,7 +98,6 @@
             android:orientation="vertical"
             android:clickable="false"
             android:focusable="false"
-            android:paddingBottom="8dip"
             android:paddingEnd="8dp"
             android:visibility="gone">
         <TextView
@@ -166,13 +164,14 @@
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
             android:gravity="end"
+            android:paddingTop="16dp"
             android:paddingBottom="8dp" >
 
         <TextView
             android:id="@+id/more_settings"
             android:text="@string/notification_more_settings"
             android:layout_width="wrap_content"
-            android:layout_height="48dp"
+            android:layout_height="36dp"
             style="@style/TextAppearance.NotificationGuts.Button"
             android:background="@drawable/btn_borderless_rect"
             android:gravity="center"
@@ -184,7 +183,7 @@
             android:id="@+id/done"
             android:text="@string/notification_done"
             android:layout_width="wrap_content"
-            android:layout_height="48dp"
+            android:layout_height="36dp"
             style="@style/TextAppearance.NotificationGuts.Button"
             android:background="@drawable/btn_borderless_rect"
             android:gravity="center"
diff --git a/packages/SystemUI/res/layout/recents_on_tv.xml b/packages/SystemUI/res/layout/recents_on_tv.xml
index 567e009..f0bfebe 100644
--- a/packages/SystemUI/res/layout/recents_on_tv.xml
+++ b/packages/SystemUI/res/layout/recents_on_tv.xml
@@ -28,26 +28,21 @@
         android:clipChildren="false"
         android:clipToPadding="false"
         android:descendantFocusability="beforeDescendants"
-        android:layout_gravity="center"
-        android:gravity="center"
-        android:paddingStart="@dimen/recents_tv_grid_row_padding"
-        android:paddingEnd="@dimen/recents_tv_grid_row_padding"
+        android:layout_marginTop="@dimen/recents_tv_gird_row_top_margin"
         android:focusable="true" />
-
     <View
         android:id="@+id/pip_shade"
         android:layout_width="match_parent"
         android:layout_height="match_parent"
-        android:visibility="invisible"
+        android:visibility="gone"
         android:background="#76000000"/>
 
     <!-- Placeholder view to handle key events for PIP when it's focused.
-         Size and positions will be adjusted to comply with
-         config_pictureInPictureBoundsInRecents -->
+         Size and positions will be adjusted to comply with the PIP bounds -->
     <View
         android:id="@+id/pip"
         android:layout_width="0dp"
         android:layout_height="0dp"
-        android:visibility="invisible"
+        android:visibility="gone"
         android:focusable="true" />
 </com.android.systemui.recents.tv.views.RecentsTvView>
diff --git a/packages/SystemUI/res/layout/recents_tv_task_card_view.xml b/packages/SystemUI/res/layout/recents_tv_task_card_view.xml
index 54e97da..766ef60 100644
--- a/packages/SystemUI/res/layout/recents_tv_task_card_view.xml
+++ b/packages/SystemUI/res/layout/recents_tv_task_card_view.xml
@@ -21,9 +21,11 @@
     android:focusableInTouchMode="true"
     android:layout_gravity="center"
     android:layout_centerInParent="true"
+    android:orientation="vertical"
     android:layoutDirection="ltr">
 
     <LinearLayout
+            android:id="@+id/recents_tv_card"
             android:layout_width="@dimen/recents_tv_card_width"
             android:layout_height="wrap_content"
             android:layout_centerInParent="true"
@@ -66,4 +68,30 @@
                 android:layout_centerHorizontal="true"
                 android:layout_below="@id/card_title_text" />
     </LinearLayout>
+    <LinearLayout
+            android:id="@+id/card_dismiss"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:orientation="vertical"
+            android:layout_gravity="center_horizontal"
+            android:layout_below="@id/recents_tv_card"
+            android:alpha="0.0">
+        <ImageView
+                android:id="@+id/card_dismiss_icon"
+                android:layout_width="@dimen/recents_tv_dismiss_icon_size"
+                android:layout_height="@dimen/recents_tv_dismiss_icon_size"
+                android:layout_gravity="center_horizontal"
+                android:layout_marginTop="@dimen/recents_tv_dismiss_icon_top_margin"
+                android:layout_marginBottom="@dimen/recents_tv_dismiss_icon_bottom_margin"
+                android:src="@drawable/ic_cancel_white_24dp" />
+        <TextView
+                android:id="@+id/card_dismiss_text"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:textSize="@dimen/recents_tv_dismiss_text_size"
+                android:fontFamily="@string/font_roboto_light"
+                android:textColor="@color/recents_tv_dismiss_text_color"
+                android:text="@string/recents_tv_dismiss"
+                android:layout_gravity="center_horizontal" />
+    </LinearLayout>
 </com.android.systemui.recents.tv.views.TaskCardView>
\ No newline at end of file
diff --git a/packages/SystemUI/res/values/colors_tv.xml b/packages/SystemUI/res/values/colors_tv.xml
index af99aae..4126d3c 100644
--- a/packages/SystemUI/res/values/colors_tv.xml
+++ b/packages/SystemUI/res/values/colors_tv.xml
@@ -19,4 +19,5 @@
 <resources>
     <color name="recents_tv_card_background_color">#FF37474F</color>
     <color name="recents_tv_card_title_text_color">#CCEEEEEE</color>
+    <color name="recents_tv_dismiss_text_color">#7FEEEEEE</color>
 </resources>
\ No newline at end of file
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 8b433f9..8cd2167 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -397,9 +397,6 @@
     <!-- The padding on top of the first notification to the children container -->
     <dimen name="notification_children_container_top_padding">8dp</dimen>
 
-    <!-- The vertical distance from which the notification appear when children are expanded -->
-    <dimen name="notification_appear_distance">140dp</dimen>
-
     <!-- end margin for multi user switch in expanded quick settings -->
     <dimen name="multi_user_switch_expanded_margin">8dp</dimen>
 
diff --git a/packages/SystemUI/res/values/dimens_tv.xml b/packages/SystemUI/res/values/dimens_tv.xml
index 6b153d1..953dd65 100644
--- a/packages/SystemUI/res/values/dimens_tv.xml
+++ b/packages/SystemUI/res/values/dimens_tv.xml
@@ -18,8 +18,8 @@
 -->
 <resources>
     <!-- Dimens for recents card in the recents view on tv -->
-    <dimen name="recents_tv_card_width">268dip</dimen>
-    <dimen name="recents_tv_screenshot_height">151dip</dimen>
+    <dimen name="recents_tv_card_width">240dip</dimen>
+    <dimen name="recents_tv_screenshot_height">135dip</dimen>
     <dimen name="recents_tv_card_extra_badge_size">20dip</dimen>
     <dimen name="recents_tv_banner_width">114dip</dimen>
     <dimen name="recents_tv_banner_height">64dip</dimen>
@@ -29,10 +29,10 @@
     <dimen name="recents_tv_text_padding_bottom">12dip</dimen>
 
     <!-- Padding for grid view in recents view on tv -->
-    <dimen name="recents_tv_grid_row_padding">56dip</dimen>
-    <dimen name="recents_tv_gird_row_top_padding">57dip</dimen>
+    <dimen name="recents_tv_gird_row_top_margin">215dip</dimen>
     <dimen name="recents_tv_grid_max_row_height">268dip</dimen>
-    <dimen name="recents_tv_gird_card_spacing">20dip</dimen>
+    <dimen name="recents_tv_gird_card_spacing">8dip</dimen>
+    <dimen name="recents_tv_gird_focused_card_delta">44dip</dimen>
 
     <!-- Values for focus animation -->
     <dimen name="recents_tv_unselected_item_z">6dp</dimen>
@@ -43,4 +43,13 @@
 
     <!-- Values for text on recents cards on tv -->
     <dimen name="recents_tv_title_text_size">12sp</dimen>
+
+    <!-- Values for card dismiss state -->
+    <dimen name="recents_tv_dismiss_shift_down">48dip</dimen>
+    <dimen name="recents_tv_dismiss_top_margin">356dip</dimen>
+    <dimen name="recents_tv_dismiss_icon_size">24dip</dimen>
+    <dimen name="recents_tv_dismiss_icon_top_margin">38dip</dimen>
+    <dimen name="recents_tv_dismiss_icon_bottom_margin">1dip</dimen>
+    <dimen name="recents_tv_dismiss_text_size">12sp</dimen>
+
 </resources>
diff --git a/packages/SystemUI/res/values/integers_tv.xml b/packages/SystemUI/res/values/integers_tv.xml
index bfd8f8b..c60c245 100644
--- a/packages/SystemUI/res/values/integers_tv.xml
+++ b/packages/SystemUI/res/values/integers_tv.xml
@@ -15,4 +15,6 @@
 -->
 <resources>
     <integer name="item_scale_anim_duration">150</integer>
+    <integer name="dismiss_short_duration">200</integer>
+    <integer name="dismiss_long_duration">400</integer>
 </resources>
\ No newline at end of file
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 5295ccb..2bde141 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -570,6 +570,9 @@
     <!-- Content description of the clear button in the notification panel for accessibility (not shown on the screen). [CHAR LIMIT=NONE] -->
     <string name="accessibility_clear_all">Clear all notifications.</string>
 
+    <!-- The overflow indicator shown when a group has more notification inside the group than the visible ones. An example is "+ 3" [CHAR LIMIT=5] -->
+    <string name="notification_group_overflow_indicator">+ <xliff:g id="number" example="3">%s</xliff:g></string>
+
     <!-- Content description of button in notification inspector for system settings relating to
          notifications from this application [CHAR LIMIT=NONE] -->
     <string name="status_bar_notification_inspect_item_title">Notification settings</string>
diff --git a/packages/SystemUI/res/values/strings_tv.xml b/packages/SystemUI/res/values/strings_tv.xml
index 0e1fe8f..52aba0d 100644
--- a/packages/SystemUI/res/values/strings_tv.xml
+++ b/packages/SystemUI/res/values/strings_tv.xml
@@ -19,13 +19,13 @@
 <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <!-- Picture-in-Picture (PIP) menu -->
     <eat-comment />
-    <!-- Button to close picture-in-picture (PIP) in PIP menu [CHAR LIMIT=16] -->
+    <!-- Button to close picture-in-picture (PIP) in PIP menu [CHAR LIMIT=30] -->
     <string name="pip_close">Close PIP</string>
-    <!-- Button to move picture-in-picture (PIP) screen to the fullscreen in PIP menu [CHAR LIMIT=16] -->
+    <!-- Button to move picture-in-picture (PIP) screen to the fullscreen in PIP menu [CHAR LIMIT=30] -->
     <string name="pip_fullscreen">Full screen</string>
-    <!-- Button to play the current media on picture-in-picture (PIP) [CHAR LIMIT=16] -->
+    <!-- Button to play the current media on picture-in-picture (PIP) [CHAR LIMIT=30] -->
     <string name="pip_play">Play</string>
-    <!-- Button to pause the current media on picture-in-picture (PIP) [CHAR LIMIT=16] -->
+    <!-- Button to pause the current media on picture-in-picture (PIP) [CHAR LIMIT=30] -->
     <string name="pip_pause">Pause</string>
     <!-- Overlay text on picture-in-picture (PIP) to indicate that longpress HOME key to control PIP [CHAR LIMIT=52] -->
     <string name="pip_hold_home">Hold <b>HOME</b> to control PIP</string>
@@ -35,7 +35,11 @@
     <string name="pip_onboarding_description">Press and hold the HOME button to control PIP</string>
     <!-- Button to close picture-in-picture (PIP) onboarding screen. -->
     <string name="pip_onboarding_button">Got it</string>
+    <!-- Dismiss icon description -->
+    <string name="recents_tv_dismiss">Dismiss</string>
     <!-- Font for Recents -->
     <!-- DO NOT TRANSLATE -->
     <string name="font_roboto_regular" translatable="false">sans-serif</string>
+    <!-- DO NOT TRANSLATE -->
+    <string name="font_roboto_light" translatable="false">sans-serif-light</string>
 </resources>
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index b0c1e95..21ad216 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -305,7 +305,7 @@
 
     <style name="TextAppearance.NotificationGuts">
         <item name="android:textSize">14sp</item>
-        <item name="android:fontFamily">sans-serif-medium</item>
+        <item name="android:fontFamily">roboto-regular</item>
         <item name="android:textColor">@android:color/black</item>
     </style>
 
diff --git a/packages/SystemUI/res/values/values_tv.xml b/packages/SystemUI/res/values/values_tv.xml
index 6a72e54..bd72c51 100644
--- a/packages/SystemUI/res/values/values_tv.xml
+++ b/packages/SystemUI/res/values/values_tv.xml
@@ -15,5 +15,5 @@
 -->
 <resources xmlns:android="http://schemas.android.com/apk/res/android">
     <item format="float" type="integer" name="unselected_scale">1.0</item>
-    <item format="float" type="integer" name="selected_scale">1.1</item>
+    <item format="float" type="integer" name="selected_scale">1.259</item>
 </resources>
diff --git a/packages/SystemUI/src/com/android/systemui/recents/Recents.java b/packages/SystemUI/src/com/android/systemui/recents/Recents.java
index 2b6ed44..da07aec 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/Recents.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/Recents.java
@@ -17,6 +17,7 @@
 package com.android.systemui.recents;
 
 import android.app.ActivityManager;
+import android.app.UiModeManager;
 import android.content.ComponentName;
 import android.content.ContentResolver;
 import android.content.Context;
@@ -53,6 +54,7 @@
 import com.android.systemui.recents.events.ui.RecentsDrawnEvent;
 import com.android.systemui.recents.misc.SystemServicesProxy;
 import com.android.systemui.recents.model.RecentsTaskLoader;
+import com.android.systemui.recents.tv.RecentsTvImpl;
 
 import java.util.ArrayList;
 
@@ -182,7 +184,13 @@
         sTaskLoader = new RecentsTaskLoader(mContext);
         sConfiguration = new RecentsConfiguration(mContext);
         mHandler = new Handler();
-        mImpl = new RecentsImpl(mContext);
+        UiModeManager uiModeManager = (UiModeManager) mContext.
+                getSystemService(Context.UI_MODE_SERVICE);
+        if (uiModeManager.getCurrentModeType() == Configuration.UI_MODE_TYPE_TELEVISION) {
+            mImpl = new RecentsTvImpl(mContext);
+        } else {
+            mImpl = new RecentsImpl(mContext);
+        }
 
         // Check if there is a recents override package
         if ("userdebug".equals(Build.TYPE) || "eng".equals(Build.TYPE)) {
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
index 9be24de..880fe10 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
@@ -21,12 +21,10 @@
 import android.app.ActivityManager;
 import android.app.ActivityOptions;
 import android.app.ITaskStackListener;
-import android.app.UiModeManager;
 import android.appwidget.AppWidgetProviderInfo;
 import android.content.ActivityNotFoundException;
 import android.content.Context;
 import android.content.Intent;
-import android.content.res.Configuration;
 import android.content.res.Resources;
 import android.graphics.Bitmap;
 import android.graphics.Canvas;
@@ -66,7 +64,6 @@
 import com.android.systemui.recents.model.Task;
 import com.android.systemui.recents.model.TaskGrouping;
 import com.android.systemui.recents.model.TaskStack;
-import com.android.systemui.recents.tv.views.TaskCardView;
 import com.android.systemui.recents.views.TaskStackLayoutAlgorithm;
 import com.android.systemui.recents.views.TaskStackView;
 import com.android.systemui.recents.views.TaskStackViewScroller;
@@ -96,10 +93,6 @@
 
     public final static String RECENTS_PACKAGE = "com.android.systemui";
     public final static String RECENTS_ACTIVITY = "com.android.systemui.recents.RecentsActivity";
-    public final static String RECENTS_TV_ACTIVITY = "com.android.systemui.recents.tv.RecentsTvActivity";
-
-    //Used to store tv or non-tv activty for use in creating intents.
-    private final String mRecentsIntentActivityName;
 
     /**
      * An implementation of ITaskStackListener, that allows us to listen for changes to the system
@@ -158,16 +151,15 @@
         }
     }
 
-    private static RecentsTaskLoadPlan sInstanceLoadPlan;
+    protected static RecentsTaskLoadPlan sInstanceLoadPlan;
 
-    Context mContext;
-    Handler mHandler;
+    protected Context mContext;
+    protected Handler mHandler;
     TaskStackListenerImpl mTaskStackListener;
     RecentsAppWidgetHost mAppWidgetHost;
-    boolean mCanReuseTaskStackViews = true;
+    protected boolean mCanReuseTaskStackViews = true;
     boolean mDraggingInRecents;
     boolean mLaunchedWhileDocking;
-    private boolean mIsRunningOnTv;
 
     // Task launching
     Rect mSearchBarBounds = new Rect();
@@ -182,11 +174,11 @@
     // Header (for transition)
     TaskViewHeader mHeaderBar;
     final Object mHeaderBarLock = new Object();
-    TaskStackView mDummyStackView;
+    protected TaskStackView mDummyStackView;
 
     // Variables to keep track of if we need to start recents after binding
-    boolean mTriggeredFromAltTab;
-    long mLastToggleTime;
+    protected boolean mTriggeredFromAltTab;
+    protected long mLastToggleTime;
     DozeTrigger mFastAltTabTrigger = new DozeTrigger(FAST_ALT_TAB_DELAY_MS, new Runnable() {
         @Override
         public void run() {
@@ -197,7 +189,7 @@
         }
     });
 
-    Bitmap mThumbnailTransitionBitmapCache;
+    protected Bitmap mThumbnailTransitionBitmapCache;
     Task mThumbnailTransitionBitmapCacheKey;
 
     public RecentsImpl(Context context) {
@@ -227,16 +219,6 @@
         launchOpts.numVisibleTaskThumbnails = loader.getThumbnailCacheSize();
         launchOpts.onlyLoadForCache = true;
         loader.loadTasks(mContext, plan, launchOpts);
-
-        //Manager used to determine if we are running on tv or not
-        UiModeManager uiModeManager = (UiModeManager) mContext.getSystemService(Context.UI_MODE_SERVICE);
-        if (uiModeManager.getCurrentModeType() == Configuration.UI_MODE_TYPE_TELEVISION) {
-            mRecentsIntentActivityName = RECENTS_TV_ACTIVITY;
-            mIsRunningOnTv = true;
-        } else {
-            mRecentsIntentActivityName = RECENTS_ACTIVITY;
-            mIsRunningOnTv = false;
-        }
     }
 
     public void onBootCompleted() {
@@ -729,7 +711,7 @@
     /**
      * Creates the activity options for a unknown state->recents transition.
      */
-    private ActivityOptions getUnknownTransitionActivityOptions() {
+    protected ActivityOptions getUnknownTransitionActivityOptions() {
         return ActivityOptions.makeCustomAnimation(mContext,
                 R.anim.recents_from_unknown_enter,
                 R.anim.recents_from_unknown_exit,
@@ -739,7 +721,7 @@
     /**
      * Creates the activity options for a home->recents transition.
      */
-    private ActivityOptions getHomeTransitionActivityOptions(boolean fromSearchHome) {
+    protected ActivityOptions getHomeTransitionActivityOptions(boolean fromSearchHome) {
         if (fromSearchHome) {
             return ActivityOptions.makeCustomAnimation(mContext,
                     R.anim.recents_from_search_launcher_enter,
@@ -797,22 +779,6 @@
         }
     }
 
-    /**
-     * Creates the activity options for an app->recents transition on TV.
-     */
-    private ActivityOptions getThumbnailTransitionActivityOptionsForTV(
-            ActivityManager.RunningTaskInfo topTask) {
-        Bitmap thumbnail = mThumbnailTransitionBitmapCache;
-        Rect rect = TaskCardView.getStartingCardThumbnailRect(mContext);
-        if (thumbnail != null) {
-            return ActivityOptions.makeThumbnailAspectScaleDownAnimation(mDummyStackView,
-                    null, (int) rect.left, (int) rect.top,
-                    (int) rect.width(), (int) rect.height(), mHandler, null);
-        }
-        // If both the screenshot and thumbnail fails, then just fall back to the default transition
-        return getUnknownTransitionActivityOptions();
-    }
-
     private Bitmap getThumbnailBitmap(ActivityManager.RunningTaskInfo topTask, Task toTask,
             TaskViewTransform toTransform) {
         Bitmap thumbnail;
@@ -888,15 +854,10 @@
     /**
      * Shows the recents activity
      */
-    private void startRecentsActivity(ActivityManager.RunningTaskInfo topTask,
+    protected void startRecentsActivity(ActivityManager.RunningTaskInfo topTask,
             boolean isTopTaskHome, boolean animate) {
         RecentsTaskLoader loader = Recents.getTaskLoader();
 
-        // If we are on TV, divert to a different helper method
-        if (mIsRunningOnTv) {
-            setUpAndStartTvRecents(topTask, isTopTaskHome, animate);
-            return;
-        }
         // In the case where alt-tab is triggered, we never get a preloadRecents() call, so we
         // should always preload the tasks now. If we are dragging in recents, reload them as
         // the stacks might have changed.
@@ -972,90 +933,6 @@
     }
 
     /**
-     * Used to set up the animations of Tv Recents, then start the Recents Activity.
-     * TODO: Add the Transitions for Home -> Recents TV
-     * TODO: Shift Transition code to separate class under /tv directory and access
-     *              from here
-     */
-    private void setUpAndStartTvRecents(ActivityManager.RunningTaskInfo topTask,
-                                      boolean isTopTaskHome, boolean animate) {
-        RecentsTaskLoader loader = Recents.getTaskLoader();
-
-        // In the case where alt-tab is triggered, we never get a preloadRecents() call, so we
-        // should always preload the tasks now. If we are dragging in recents, reload them as
-        // the stacks might have changed.
-        if (mLaunchedWhileDocking || mTriggeredFromAltTab || sInstanceLoadPlan == null) {
-            // Create a new load plan if preloadRecents() was never triggered
-            sInstanceLoadPlan = loader.createLoadPlan(mContext);
-        }
-        if (mLaunchedWhileDocking || mTriggeredFromAltTab || !sInstanceLoadPlan.hasTasks()) {
-            loader.preloadTasks(sInstanceLoadPlan, topTask.id, isTopTaskHome);
-        }
-        TaskStack stack = sInstanceLoadPlan.getTaskStack();
-
-        // Update the header bar if necessary
-        updateHeaderBarLayout(false /* tryAndBindSearchWidget */, stack);
-
-        // Prepare the dummy stack for the transition
-        TaskStackLayoutAlgorithm.VisibilityReport stackVr =
-                mDummyStackView.computeStackVisibilityReport();
-
-        if (!animate) {
-            ActivityOptions opts = ActivityOptions.makeCustomAnimation(mContext, -1, -1);
-            startRecentsActivity(topTask, opts, false /* fromHome */,
-                    false /* fromSearchHome */, false /* fromThumbnail*/, stackVr);
-            return;
-        }
-
-        boolean hasRecentTasks = stack.getTaskCount() > 0;
-        boolean useThumbnailTransition = (topTask != null) && !isTopTaskHome && hasRecentTasks;
-
-        if (useThumbnailTransition) {
-            // Try starting with a thumbnail transition
-            ActivityOptions opts = getThumbnailTransitionActivityOptionsForTV(topTask);
-            if (opts != null) {
-                startRecentsActivity(topTask, opts, false /* fromHome */,
-                        false /* fromSearchHome */, true /* fromThumbnail */, stackVr);
-            } else {
-                // Fall through below to the non-thumbnail transition
-                useThumbnailTransition = false;
-            }
-        }
-
-        if (!useThumbnailTransition) {
-            // If there is no thumbnail transition, but is launching from home into recents, then
-            // use a quick home transition and do the animation from home
-            if (hasRecentTasks) {
-                SystemServicesProxy ssp = Recents.getSystemServices();
-                String homeActivityPackage = ssp.getHomeActivityPackageName();
-                String searchWidgetPackage = null;
-                if (RecentsDebugFlags.Static.EnableSearchBar) {
-                    searchWidgetPackage = Prefs.getString(mContext,
-                            Prefs.Key.OVERVIEW_SEARCH_APP_WIDGET_PACKAGE, null);
-                } else {
-                    AppWidgetProviderInfo searchWidgetInfo = ssp.resolveSearchAppWidget();
-                    if (searchWidgetInfo != null) {
-                        searchWidgetPackage = searchWidgetInfo.provider.getPackageName();
-                    }
-                }
-
-                // Determine whether we are coming from a search owned home activity
-                boolean fromSearchHome = (homeActivityPackage != null) &&
-                        homeActivityPackage.equals(searchWidgetPackage);
-                ActivityOptions opts = getHomeTransitionActivityOptions(fromSearchHome);
-                startRecentsActivity(topTask, opts, true /* fromHome */, fromSearchHome,
-                        false /* fromThumbnail */, stackVr);
-            } else {
-                // Otherwise we do the normal fade from an unknown source
-                ActivityOptions opts = getUnknownTransitionActivityOptions();
-                startRecentsActivity(topTask, opts, true /* fromHome */,
-                        false /* fromSearchHome */, false /* fromThumbnail */, stackVr);
-            }
-        }
-        mLastToggleTime = SystemClock.elapsedRealtime();
-    }
-
-    /**
      * Starts the recents activity.
      */
     private void startRecentsActivity(ActivityManager.RunningTaskInfo topTask,
@@ -1078,7 +955,7 @@
         launchState.launchedWhileDocking = mLaunchedWhileDocking;
 
         Intent intent = new Intent();
-        intent.setClassName(RECENTS_PACKAGE, mRecentsIntentActivityName);
+        intent.setClassName(RECENTS_PACKAGE, RECENTS_ACTIVITY);
         intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
                 | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS
                 | Intent.FLAG_ACTIVITY_TASK_ON_HOME);
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 532e796..330d138 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
@@ -47,7 +47,6 @@
 import android.graphics.drawable.BitmapDrawable;
 import android.graphics.drawable.ColorDrawable;
 import android.graphics.drawable.Drawable;
-import android.hardware.display.DisplayManager;
 import android.os.Bundle;
 import android.os.ParcelFileDescriptor;
 import android.os.RemoteException;
@@ -73,6 +72,7 @@
 import com.android.systemui.R;
 import com.android.systemui.recents.RecentsDebugFlags;
 import com.android.systemui.recents.RecentsImpl;
+import com.android.systemui.recents.tv.RecentsTvImpl;
 
 import java.io.IOException;
 import java.util.ArrayList;
@@ -94,12 +94,18 @@
     final static String TAG = "SystemServicesProxy";
 
     final static BitmapFactory.Options sBitmapOptions;
-
     static {
         sBitmapOptions = new BitmapFactory.Options();
         sBitmapOptions.inMutable = true;
     }
 
+    final static List<String> sRecentsBlacklist;
+    static {
+        sRecentsBlacklist = new ArrayList<>();
+        sRecentsBlacklist.add("com.android.systemui.tv.pip.PipOnboardingActivity");
+        sRecentsBlacklist.add("com.android.systemui.tv.pip.PipMenuActivity");
+    }
+
     AccessibilityManager mAccm;
     ActivityManager mAm;
     IActivityManager mIam;
@@ -227,12 +233,13 @@
 
             // Check the first non-recents task, include this task even if it is marked as excluded
             // from recents if we are currently in the app.  In other words, only remove excluded
-            // tasks if it is not the first active task.
+            // tasks if it is not the first active task, and not in the blacklist.
             boolean isExcluded = (t.baseIntent.getFlags() & Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS)
                     == Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS;
+            boolean isBlackListed = sRecentsBlacklist.contains(t.realActivity.getClassName());
             // Filter out recent tasks from managed profiles which are in quiet mode.
             isExcluded |= quietProfileIds.contains(t.userId);
-            if (isExcluded && (isTopTaskHome || !isFirstValidTask)) {
+            if (isBlackListed || (isExcluded && (isTopTaskHome || !isFirstValidTask))) {
                 iter.remove();
                 continue;
             }
@@ -280,7 +287,7 @@
             // Check if the front most activity is recents
             if ((topActivity.getPackageName().equals(RecentsImpl.RECENTS_PACKAGE) &&
                     (topActivity.getClassName().equals(RecentsImpl.RECENTS_ACTIVITY) ||
-                    topActivity.getClassName().equals(RecentsImpl.RECENTS_TV_ACTIVITY)))) {
+                    topActivity.getClassName().equals(RecentsTvImpl.RECENTS_TV_ACTIVITY)))) {
                 if (isHomeTopMost != null) {
                     isHomeTopMost.value = false;
                 }
diff --git a/packages/SystemUI/src/com/android/systemui/recents/tv/RecentsTvActivity.java b/packages/SystemUI/src/com/android/systemui/recents/tv/RecentsTvActivity.java
index dae522f..960bd8c 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/tv/RecentsTvActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/tv/RecentsTvActivity.java
@@ -85,9 +85,13 @@
     private PipManager mPipManager;
     private PipManager.Listener mPipListener = new PipManager.Listener() {
         @Override
+        public void onPipEntered() {
+            updatePipUI();
+        }
+
+        @Override
         public void onPipActivityClosed() {
-            mPipView.setVisibility(View.GONE);
-            mPipShadeView.setVisibility(View.GONE);
+            updatePipUI();
         }
 
         @Override
@@ -102,6 +106,7 @@
         @Override
         public void onMediaControllerChanged() { }
     };
+    private boolean mHasPip;
 
     /**
      * A common Runnable to finish Recents by launching Home with an animation depending on the
@@ -266,6 +271,10 @@
         homeIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK |
                 Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
         mFinishLaunchHomeRunnable = new FinishRecentsRunnable(homeIntent);
+
+        mHasPip = false;
+        updatePipUI();
+        mPipManager.addListener(mPipListener);
     }
 
     @Override
@@ -296,34 +305,6 @@
         SystemServicesProxy ssp = Recents.getSystemServices();
         EventBus.getDefault().send(new RecentsVisibilityChangedEvent(this, true));
 
-        if (mPipManager.isPipShown()) {
-            // Place mPipView at the PIP bounds for fine tuned focus handling.
-            Rect pipBounds = mPipManager.getPipBounds();
-            LayoutParams lp = (LayoutParams) mPipView.getLayoutParams();
-            lp.width = pipBounds.width();
-            lp.height = pipBounds.height();
-            lp.leftMargin = pipBounds.left;
-            lp.topMargin = pipBounds.top;
-            mPipView.setLayoutParams(lp);
-
-            mPipView.setVisibility(View.VISIBLE);
-            mPipView.setOnClickListener(new View.OnClickListener() {
-                @Override
-                public void onClick(View v) {
-                    mPipManager.resizePinnedStack(PipManager.STATE_PIP_MENU);
-                }
-            });
-            mPipView.setOnFocusChangeListener(new View.OnFocusChangeListener() {
-                @Override
-                public void onFocusChange(View v, boolean hasFocus) {
-                    mPipManager.onPipViewFocusChangedInRecents(hasFocus);
-                    mPipShadeView.setVisibility(hasFocus ? View.VISIBLE : View.INVISIBLE);
-                }
-            });
-            mPipManager.addListener(mPipListener);
-        } else {
-            mPipView.setVisibility(View.GONE);
-        }
         mPipManager.onRecentsStarted();
         // Give focus to the recents row whenever its visible to an user.
         mRecentsView.requestFocus();
@@ -340,7 +321,6 @@
         super.onStop();
 
         mPipManager.onRecentsStopped();
-        mPipManager.removeListener(mPipListener);
         mIgnoreAltTabRelease = false;
         // Notify that recents is now hidden
         EventBus.getDefault().send(new RecentsVisibilityChangedEvent(this, false));
@@ -357,6 +337,7 @@
     protected void onDestroy() {
         super.onDestroy();
 
+        mPipManager.removeListener(mPipListener);
         // In the case that the activity finished on startup, just skip the unregistration below
         if (mFinishedOnStartup) {
             return;
@@ -480,4 +461,40 @@
         });
         return true;
     }
+
+    private void updatePipUI() {
+        if (mHasPip == mPipManager.isPipShown()) {
+            return;
+        }
+        mHasPip = mPipManager.isPipShown();
+        if (mHasPip) {
+            // Place mPipView at the PIP bounds for fine tuned focus handling.
+            Rect pipBounds = mPipManager.getPipBounds();
+            LayoutParams lp = (LayoutParams) mPipView.getLayoutParams();
+            lp.width = pipBounds.width();
+            lp.height = pipBounds.height();
+            lp.leftMargin = pipBounds.left;
+            lp.topMargin = pipBounds.top;
+            mPipView.setLayoutParams(lp);
+
+            mPipView.setVisibility(View.VISIBLE);
+            mPipView.setOnClickListener(new View.OnClickListener() {
+                @Override
+                public void onClick(View v) {
+                    mPipManager.resizePinnedStack(PipManager.STATE_PIP_MENU);
+                }
+            });
+            mPipView.setOnFocusChangeListener(new View.OnFocusChangeListener() {
+                @Override
+                public void onFocusChange(View v, boolean hasFocus) {
+                    mPipManager.onPipViewFocusChangedInRecents(hasFocus);
+                    mPipShadeView.setVisibility(hasFocus ? View.VISIBLE : View.INVISIBLE);
+                }
+            });
+            mPipShadeView.setVisibility(View.GONE);
+        } else {
+            mPipView.setVisibility(View.GONE);
+            mPipShadeView.setVisibility(View.GONE);
+        }
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/recents/tv/RecentsTvImpl.java b/packages/SystemUI/src/com/android/systemui/recents/tv/RecentsTvImpl.java
new file mode 100644
index 0000000..9fd5d55
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/recents/tv/RecentsTvImpl.java
@@ -0,0 +1,139 @@
+/*
+ * Copyright (C) 2016 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.tv;
+
+import android.app.ActivityManager;
+import android.app.ActivityOptions;
+import android.content.Context;
+import android.content.Intent;
+import android.graphics.Bitmap;
+import android.graphics.Rect;
+import android.os.SystemClock;
+import android.os.UserHandle;
+import com.android.systemui.recents.*;
+import com.android.systemui.recents.events.EventBus;
+import com.android.systemui.recents.events.activity.RecentsActivityStartingEvent;
+import com.android.systemui.recents.misc.SystemServicesProxy;
+import com.android.systemui.recents.model.RecentsTaskLoader;
+import com.android.systemui.recents.model.TaskStack;
+import com.android.systemui.recents.tv.views.TaskCardView;
+
+public class RecentsTvImpl extends RecentsImpl{
+    public final static String RECENTS_TV_ACTIVITY =
+            "com.android.systemui.recents.tv.RecentsTvActivity";
+
+    public RecentsTvImpl(Context context) {
+        super(context);
+    }
+
+    @Override
+    protected void startRecentsActivity(ActivityManager.RunningTaskInfo topTask,
+            boolean isTopTaskHome, boolean animate) {
+        RecentsTaskLoader loader = Recents.getTaskLoader();
+
+        // In the case where alt-tab is triggered, we never get a preloadRecents() call, so we
+        // should always preload the tasks now. If we are dragging in recents, reload them as
+        // the stacks might have changed.
+        if (mTriggeredFromAltTab || sInstanceLoadPlan == null) {
+            // Create a new load plan if preloadRecents() was never triggered
+            sInstanceLoadPlan = loader.createLoadPlan(mContext);
+        }
+        if (mTriggeredFromAltTab || !sInstanceLoadPlan.hasTasks()) {
+            loader.preloadTasks(sInstanceLoadPlan, topTask.id, isTopTaskHome);
+        }
+        TaskStack stack = sInstanceLoadPlan.getTaskStack();
+
+        if (!animate) {
+            ActivityOptions opts = ActivityOptions.makeCustomAnimation(mContext, -1, -1);
+            startRecentsActivity(topTask, opts, false /* fromHome */, false /* fromThumbnail*/);
+            return;
+        }
+
+        boolean hasRecentTasks = stack.getTaskCount() > 0;
+        boolean useThumbnailTransition = (topTask != null) && !isTopTaskHome && hasRecentTasks;
+
+        if (useThumbnailTransition) {
+            // Try starting with a thumbnail transition
+            ActivityOptions opts = getThumbnailTransitionActivityOptionsForTV(topTask);
+            if (opts != null) {
+                startRecentsActivity(topTask, opts, false /* fromHome */, true /* fromThumbnail */);
+            } else {
+                // Fall through below to the non-thumbnail transition
+                useThumbnailTransition = false;
+            }
+        }
+
+        if (!useThumbnailTransition) {
+            // If there is no thumbnail transition, but is launching from home into recents, then
+            // use a quick home transition and do the animation from home
+            if (hasRecentTasks) {
+                SystemServicesProxy ssp = Recents.getSystemServices();
+                ActivityOptions opts = getHomeTransitionActivityOptions(false);
+                startRecentsActivity(topTask, opts, true /* fromHome */, false /* fromThumbnail */);
+            } else {
+                // Otherwise we do the normal fade from an unknown source
+                ActivityOptions opts = getUnknownTransitionActivityOptions();
+                startRecentsActivity(topTask, opts, true /* fromHome */, false /* fromThumbnail */);
+            }
+        }
+        mLastToggleTime = SystemClock.elapsedRealtime();
+    }
+
+    protected void startRecentsActivity(ActivityManager.RunningTaskInfo topTask,
+            ActivityOptions opts, boolean fromHome, boolean fromThumbnail) {
+        // Update the configuration based on the launch options
+        RecentsConfiguration config = Recents.getConfiguration();
+        RecentsActivityLaunchState launchState = config.getLaunchState();
+        launchState.launchedFromHome = fromHome;
+        launchState.launchedFromSearchHome = false;
+        launchState.launchedFromApp = fromThumbnail;
+        launchState.launchedToTaskId = (topTask != null) ? topTask.id : -1;
+        launchState.launchedWithAltTab = mTriggeredFromAltTab;
+        launchState.launchedReuseTaskStackViews = mCanReuseTaskStackViews;
+        launchState.launchedHasConfigurationChanged = false;
+
+        Intent intent = new Intent();
+        intent.setClassName(RECENTS_PACKAGE, RECENTS_TV_ACTIVITY);
+        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
+                | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS
+                | Intent.FLAG_ACTIVITY_TASK_ON_HOME);
+
+        if (opts != null) {
+            mContext.startActivityAsUser(intent, opts.toBundle(), UserHandle.CURRENT);
+        } else {
+            mContext.startActivityAsUser(intent, UserHandle.CURRENT);
+        }
+        mCanReuseTaskStackViews = true;
+        EventBus.getDefault().send(new RecentsActivityStartingEvent());
+    }
+
+    /**
+     * Creates the activity options for an app->recents transition on TV.
+     */
+    private ActivityOptions getThumbnailTransitionActivityOptionsForTV(
+            ActivityManager.RunningTaskInfo topTask) {
+        Bitmap thumbnail = mThumbnailTransitionBitmapCache;
+        Rect rect = TaskCardView.getStartingCardThumbnailRect(mContext);
+        if (thumbnail != null) {
+            return ActivityOptions.makeThumbnailAspectScaleDownAnimation(mDummyStackView,
+                    null, (int) rect.left, (int) rect.top,
+                    (int) rect.width(), (int) rect.height(), mHandler, null);
+        }
+        // If both the screenshot and thumbnail fails, then just fall back to the default transition
+        return getUnknownTransitionActivityOptions();
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/tv/animations/DismissAnimationsHolder.java b/packages/SystemUI/src/com/android/systemui/recents/tv/animations/DismissAnimationsHolder.java
new file mode 100644
index 0000000..8996d0b
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/recents/tv/animations/DismissAnimationsHolder.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2016 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.tv.animations;
+
+
+import android.animation.Animator;
+import android.content.res.Resources;
+import android.support.v4.view.animation.FastOutSlowInInterpolator;
+import android.view.View;
+import android.widget.LinearLayout;
+import com.android.systemui.recents.tv.views.TaskCardView;
+
+import com.android.systemui.R;
+
+public class DismissAnimationsHolder {
+    private LinearLayout mDismissArea;
+    private LinearLayout mTaskCardView;
+    private FastOutSlowInInterpolator mFastOutSlowIn;
+    private int mCardYDelta;
+    private long mShortDuration;
+    private long mLongDuration;
+
+    public DismissAnimationsHolder(TaskCardView taskCardView) {
+        mTaskCardView = (LinearLayout) taskCardView.findViewById(R.id.recents_tv_card);
+        mDismissArea = (LinearLayout) taskCardView.findViewById(R.id.card_dismiss);
+        mFastOutSlowIn = new FastOutSlowInInterpolator();
+
+        Resources res = taskCardView.getResources();
+        mCardYDelta = res.getDimensionPixelOffset(R.dimen.recents_tv_dismiss_shift_down);
+        mShortDuration =  res.getInteger(R.integer.dismiss_short_duration);
+        mLongDuration =  res.getInteger(R.integer.dismiss_long_duration);
+    }
+
+    public void startEnterAnimation() {
+        mDismissArea.animate().setDuration(mShortDuration);
+        mDismissArea.animate().setInterpolator(mFastOutSlowIn);
+        mDismissArea.animate().alpha(1.0f);
+
+        mTaskCardView.animate().setDuration(mShortDuration);
+        mTaskCardView.animate().setInterpolator(mFastOutSlowIn);
+        mTaskCardView.animate().translationYBy(mCardYDelta);
+        mTaskCardView.animate().alpha(0.5f);
+    }
+
+    public void startExitAnimation() {
+        mDismissArea.animate().setDuration(mShortDuration);
+        mDismissArea.animate().setInterpolator(mFastOutSlowIn);
+        mDismissArea.animate().alpha(0.0f);
+
+        mTaskCardView.animate().setDuration(mShortDuration);
+        mTaskCardView.animate().setInterpolator(mFastOutSlowIn);
+        mTaskCardView.animate().translationYBy(-mCardYDelta);
+        mTaskCardView.animate().alpha(1.0f);
+    }
+
+    public void startDismissAnimation(Animator.AnimatorListener listener) {
+        mDismissArea.animate().setDuration(mShortDuration);
+        mDismissArea.animate().setInterpolator(mFastOutSlowIn);
+        mDismissArea.animate().alpha(0.0f);
+
+        mTaskCardView.animate().setDuration(mLongDuration);
+        mTaskCardView.animate().setInterpolator(mFastOutSlowIn);
+        mTaskCardView.animate().translationYBy(mCardYDelta);
+        mTaskCardView.animate().alpha(0.0f);
+        mTaskCardView.animate().setListener(listener);
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/tv/animations/ViewFocusAnimator.java b/packages/SystemUI/src/com/android/systemui/recents/tv/animations/ViewFocusAnimator.java
index 365b29d..888561c 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/tv/animations/ViewFocusAnimator.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/tv/animations/ViewFocusAnimator.java
@@ -33,6 +33,8 @@
     private final float mSelectedScaleDelta;
     private final float mUnselectedZ;
     private final float mSelectedZDelta;
+    private final float mUnselectedSpacing;
+    private final float mSelectedSpacingDelta;
     private final int mAnimDuration;
     private final Interpolator mFocusInterpolator;
 
@@ -57,6 +59,9 @@
         mUnselectedZ = res.getDimensionPixelOffset(R.dimen.recents_tv_unselected_item_z);
         mSelectedZDelta = res.getDimensionPixelOffset(R.dimen.recents_tv_selected_item_z_delta);
 
+        mUnselectedSpacing = res.getDimensionPixelOffset(R.dimen.recents_tv_gird_card_spacing);
+        mSelectedSpacingDelta = res.getDimensionPixelOffset(R.dimen.recents_tv_gird_focused_card_delta);
+
         mAnimDuration = res.getInteger(R.integer.item_scale_anim_duration);
 
         mFocusInterpolator = new AccelerateDecelerateInterpolator();
@@ -85,10 +90,14 @@
 
         float scale = mUnselectedScale + (level * mSelectedScaleDelta);
         float z = mUnselectedZ + (level * mSelectedZDelta);
+        float spacing = mUnselectedSpacing + (level * mSelectedSpacingDelta);
 
         mTargetView.setScaleX(scale);
         mTargetView.setScaleY(scale);
         mTargetView.setZ(z);
+
+        mTargetView.setPadding((int) spacing, mTargetView.getPaddingTop(),
+                (int) spacing, mTargetView.getPaddingBottom());
     }
 
     public float getFocusProgress() {
diff --git a/packages/SystemUI/src/com/android/systemui/recents/tv/views/TaskCardView.java b/packages/SystemUI/src/com/android/systemui/recents/tv/views/TaskCardView.java
index 5775b60..3343aec 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/tv/views/TaskCardView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/tv/views/TaskCardView.java
@@ -15,6 +15,7 @@
  */
 package com.android.systemui.recents.tv.views;
 
+import android.animation.Animator;
 import android.content.Context;
 import android.content.res.Resources;
 import android.graphics.Point;
@@ -22,12 +23,14 @@
 import android.util.AttributeSet;
 import android.util.TypedValue;
 import android.view.Display;
+import android.view.KeyEvent;
 import android.view.WindowManager;
 import android.widget.ImageView;
 import android.widget.LinearLayout;
 import android.widget.TextView;
 
 import com.android.systemui.R;
+import com.android.systemui.recents.tv.animations.DismissAnimationsHolder;
 import com.android.systemui.recents.tv.animations.ViewFocusAnimator;
 import com.android.systemui.recents.model.Task;
 
@@ -37,8 +40,10 @@
     private TextView mTitleTextView;
     private ImageView mBadgeView;
     private Task mTask;
+    private boolean mDismissState;
 
     private ViewFocusAnimator mViewFocusAnimator;
+    private DismissAnimationsHolder mDismissAnimationsHolder;
 
     public TaskCardView(Context context) {
         this(context, null);
@@ -51,6 +56,7 @@
     public TaskCardView(Context context, AttributeSet attrs, int defStyleAttr) {
         super(context, attrs, defStyleAttr);
         mViewFocusAnimator = new ViewFocusAnimator(this);
+        mDismissState = false;
     }
 
     @Override
@@ -58,6 +64,7 @@
         mThumbnailView = (ImageView) findViewById(R.id.card_view_thumbnail);
         mTitleTextView = (TextView) findViewById(R.id.card_title_text);
         mBadgeView = (ImageView) findViewById(R.id.card_extra_badge);
+        mDismissAnimationsHolder = new DismissAnimationsHolder(this);
     }
 
     public void init(Task task) {
@@ -98,13 +105,23 @@
 
         int width = res.getDimensionPixelOffset(R.dimen.recents_tv_card_width);
         int widthDelta = (int) (width * scale - width);
-        int height = (int) (res.getDimensionPixelOffset(
-                R.dimen.recents_tv_screenshot_height) * scale);
-        int padding = res.getDimensionPixelOffset(R.dimen.recents_tv_grid_row_padding);
+        int height = res.getDimensionPixelOffset(R.dimen.recents_tv_screenshot_height);
+        int heightDelta = (int) (height * scale - height);
+        int topMargin = res.getDimensionPixelOffset(R.dimen.recents_tv_gird_row_top_margin);
 
-        int headerHeight = (int) ((res.getDimensionPixelOffset(
-                R.dimen.recents_tv_card_extra_badge_size) +
-                res.getDimensionPixelOffset(R.dimen.recents_tv_icon_padding_bottom)) * scale);
+        int headerHeight = res.getDimensionPixelOffset(R.dimen.recents_tv_card_extra_badge_size) +
+                res.getDimensionPixelOffset(R.dimen.recents_tv_icon_padding_bottom);
+        int headerHeightDelta = (int) (headerHeight * scale - headerHeight);
+
+        int dismissAreaHeight =
+                res.getDimensionPixelOffset(R.dimen.recents_tv_dismiss_icon_top_margin) +
+                res.getDimensionPixelOffset(R.dimen.recents_tv_dismiss_icon_bottom_margin) +
+                res.getDimensionPixelOffset(R.dimen.recents_tv_dismiss_icon_size) +
+                res.getDimensionPixelOffset(R.dimen.recents_tv_dismiss_text_size);
+
+        int dismissAreaHeightDelta = (int) (dismissAreaHeight * scale - dismissAreaHeight);
+
+        int totalHeightDelta = heightDelta + headerHeightDelta + dismissAreaHeightDelta;
 
         WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
         Display display = wm.getDefaultDisplay();
@@ -113,9 +130,72 @@
         int screenWidth = size.x;
         int screenHeight = size.y;
 
-        return new Rect(screenWidth - width - padding - widthDelta / 2,
-                screenHeight / 2 - height / 2 + headerHeight / 2,
-                screenWidth - padding + widthDelta / 2,
-                screenHeight / 2 + height / 2 + headerHeight / 2);
+        return new Rect(screenWidth / 2 - width / 2 - widthDelta / 2,
+                topMargin - totalHeightDelta / 2 + (int) (headerHeight * scale),
+                screenWidth / 2 + width / 2 + widthDelta / 2,
+                topMargin - totalHeightDelta / 2 + (int) (headerHeight * scale) +
+                        (int) (height * scale));
+    }
+
+    @Override
+    public boolean onKeyDown(int keyCode, KeyEvent event) {
+        switch (keyCode) {
+            case KeyEvent.KEYCODE_DPAD_DOWN : {
+                if (!isInDismissState()) {
+                    setDismissState(true);
+                    return true;
+                }
+                break;
+            }
+            case KeyEvent.KEYCODE_DPAD_UP : {
+                if (isInDismissState()) {
+                    setDismissState(false);
+                    return true;
+                }
+                break;
+            }
+
+            //Eat right and left key presses when we are in dismiss state
+            case KeyEvent.KEYCODE_DPAD_LEFT : {
+                if (isInDismissState()) {
+                    return true;
+                }
+                break;
+            }
+            case KeyEvent.KEYCODE_DPAD_RIGHT : {
+                if (isInDismissState()) {
+                    return true;
+                }
+                break;
+            }
+            default:
+                break;
+        }
+        return super.onKeyDown(keyCode, event);
+    }
+
+    private void setDismissState(boolean dismissState) {
+        if (mDismissState != dismissState) {
+            mDismissState = dismissState;
+            if (dismissState) {
+                mDismissAnimationsHolder.startEnterAnimation();
+            } else {
+                mDismissAnimationsHolder.startExitAnimation();
+            }
+        }
+    }
+
+    public boolean isInDismissState() {
+        return mDismissState;
+    }
+
+    public void startDismissTaskAnimation(Animator.AnimatorListener listener) {
+        mDismissAnimationsHolder.startDismissAnimation(listener);
+    }
+
+    @Override
+    protected void onDetachedFromWindow() {
+        super.onDetachedFromWindow();
+        setDismissState(false);
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/recents/tv/views/TaskStackHorizontalGridView.java b/packages/SystemUI/src/com/android/systemui/recents/tv/views/TaskStackHorizontalGridView.java
index cf8c9bb..5c2de8e 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/tv/views/TaskStackHorizontalGridView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/tv/views/TaskStackHorizontalGridView.java
@@ -41,7 +41,6 @@
     private ArrayList<TaskCardView> mTaskViews = new ArrayList<>();
     private Task mFocusedTask;
 
-
     public TaskStackHorizontalGridView(Context context) {
         this(context, null);
     }
@@ -53,7 +52,7 @@
     @Override
     protected void onAttachedToWindow() {
         EventBus.getDefault().register(this, RecentsActivity.EVENT_BUS_PRIORITY + 1);
-        setItemMargin((int) getResources().getDimension(R.dimen.recents_tv_gird_card_spacing));
+        setWindowAlignment(WINDOW_ALIGN_NO_EDGE);
         setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_YES);
         super.onAttachedToWindow();
     }
@@ -109,6 +108,13 @@
     }
 
     /**
+     * @return - The focused task card view.
+     */
+    public TaskCardView getFocusedTaskCardView() {
+        return ((TaskCardView)findFocus());
+    }
+
+    /**
      * @param task
      * @return Child view for given task
      */
diff --git a/packages/SystemUI/src/com/android/systemui/recents/tv/views/TaskStackHorizontalViewAdapter.java b/packages/SystemUI/src/com/android/systemui/recents/tv/views/TaskStackHorizontalViewAdapter.java
index fba424e..3788719 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/tv/views/TaskStackHorizontalViewAdapter.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/tv/views/TaskStackHorizontalViewAdapter.java
@@ -15,6 +15,7 @@
  */
 package com.android.systemui.recents.tv.views;
 
+import android.animation.Animator;
 import android.app.Activity;
 import android.support.v7.widget.RecyclerView;
 import android.util.Log;
@@ -25,6 +26,7 @@
 import com.android.systemui.R;
 import com.android.systemui.recents.events.EventBus;
 import com.android.systemui.recents.events.activity.LaunchTvTaskEvent;
+import com.android.systemui.recents.events.ui.DeleteTaskDataEvent;
 import com.android.systemui.recents.model.Task;
 
 import java.util.ArrayList;
@@ -39,7 +41,7 @@
     private static final String TAG = "TaskStackViewAdapter";
     private List<Task> mTaskList;
 
-    static class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener{
+    public class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener{
         private TaskCardView mTaskCardView;
         private Task mTask;
         public ViewHolder(View v) {
@@ -58,9 +60,14 @@
         @Override
         public void onClick(View v) {
             try {
-                EventBus.getDefault().send(new LaunchTvTaskEvent(mTaskCardView, mTask,
-                        null, INVALID_STACK_ID));
-                ((Activity)(v.getContext())).finish();
+                if (mTaskCardView.isInDismissState()) {
+                    mTaskCardView.startDismissTaskAnimation(
+                            getRemoveAtListener(getAdapterPosition(), mTaskCardView));
+                } else {
+                    EventBus.getDefault().send(new LaunchTvTaskEvent(mTaskCardView, mTask,
+                            null, INVALID_STACK_ID));
+                    ((Activity) (v.getContext())).finish();
+                }
             } catch (Exception e) {
                 Log.e(TAG, v.getContext()
                         .getString(R.string.recents_launch_error_message, mTask.title), e);
@@ -97,4 +104,31 @@
     public int getItemCount() {
         return mTaskList.size();
     }
+
+    private Animator.AnimatorListener getRemoveAtListener(final int position,
+                                                          final TaskCardView taskCardView) {
+        return new Animator.AnimatorListener() {
+
+            @Override
+            public void onAnimationStart(Animator animation) { }
+
+            @Override
+            public void onAnimationEnd(Animator animation) {
+                removeAt(position);
+                EventBus.getDefault().send(new DeleteTaskDataEvent(taskCardView.getTask()));
+            }
+
+            @Override
+            public void onAnimationCancel(Animator animation) { }
+
+            @Override
+            public void onAnimationRepeat(Animator animation) { }
+        };
+
+    }
+
+    private void removeAt(int position) {
+        mTaskList.remove(position);
+        notifyItemRemoved(position);
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java
index 7f61e7a..132c09f 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java
@@ -83,18 +83,6 @@
     private static final int TASK_POSITION_SAME = Integer.MAX_VALUE;
 
     /**
-     * Fraction of the divider position between two snap targets to switch to the full-screen
-     * target.
-     */
-    private static final float SWITCH_FULLSCREEN_FRACTION = 0.12f;
-
-    /**
-     * Fraction of the divider position between two snap targets to switch to the larger target
-     * for the bottom/right app layout.
-     */
-    private static final float BOTTOM_RIGHT_SWITCH_BIGGER_FRACTION = 0.2f;
-
-    /**
      * How much the background gets scaled when we are in the minimized dock state.
      */
     private static final float MINIMIZE_DOCK_SCALE = 0.375f;
@@ -653,12 +641,6 @@
                     restrictDismissingTaskPosition(taskPosition, mDockSide, taskSnapTarget);
             int taskPositionOther =
                     restrictDismissingTaskPosition(taskPosition, dockSideInverted, taskSnapTarget);
-
-            taskPositionDocked = minimizeHoles(position, taskPositionDocked, mDockSide,
-                    taskSnapTarget);
-            taskPositionOther = minimizeHoles(position, taskPositionOther, dockSideInverted,
-                    taskSnapTarget);
-
             calculateBoundsForPosition(taskPositionDocked, mDockSide, mDockedTaskRect);
             calculateBoundsForPosition(taskPositionOther, dockSideInverted, mOtherTaskRect);
             mDisplayRect.set(0, 0, mDisplayWidth, mDisplayHeight);
@@ -724,51 +706,6 @@
     }
 
     /**
-     * Given the current split position and the task position calculated by dragging, this
-     * method calculates a "better" task position in a sense so holes get smaller while dragging.
-     *
-     * @return the new task position
-     */
-    private int minimizeHoles(int position, int taskPosition, int dockSide,
-            SnapTarget taskSnapTarget) {
-        if (dockSideTopLeft(dockSide)) {
-            if (position > taskPosition) {
-                SnapTarget nextTarget = mSnapAlgorithm.getNextTarget(taskSnapTarget);
-
-                // If the next target is the dismiss end target, switch earlier to make the hole
-                // smaller.
-                if (nextTarget != taskSnapTarget
-                        && nextTarget == mSnapAlgorithm.getDismissEndTarget()) {
-                    float t = (float) (position - taskPosition)
-                            / (nextTarget.position - taskPosition);
-                    if (t > SWITCH_FULLSCREEN_FRACTION) {
-                        return nextTarget.position;
-                    }
-                }
-            }
-        } else if (dockSideBottomRight(dockSide)) {
-            if (position < taskPosition) {
-                SnapTarget previousTarget = mSnapAlgorithm.getPreviousTarget(taskSnapTarget);
-                if (previousTarget != taskSnapTarget) {
-                    float t = (float) (taskPosition - position)
-                            / (taskPosition - previousTarget.position);
-
-                    // In general, switch a bit earlier (at 20% instead of 50%), but if we are
-                    // dismissing the top, switch really early.
-                    float threshold = previousTarget == mSnapAlgorithm.getDismissStartTarget()
-                            ? SWITCH_FULLSCREEN_FRACTION
-                            : BOTTOM_RIGHT_SWITCH_BIGGER_FRACTION;
-                    if (t > threshold) {
-                        return previousTarget.position;
-                    }
-
-                }
-            }
-        }
-        return taskPosition;
-    }
-
-    /**
      * When the snap target is dismissing one side, make sure that the dismissing side doesn't get
      * 0 size.
      */
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/WindowManagerProxy.java b/packages/SystemUI/src/com/android/systemui/stackdivider/WindowManagerProxy.java
index e312fa2..ef32f7e 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/WindowManagerProxy.java
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/WindowManagerProxy.java
@@ -100,8 +100,8 @@
         @Override
         public void run() {
             try {
-                ActivityManagerNative.getDefault().resizeStack(DOCKED_STACK_ID, null, true, true,
-                        false);
+                ActivityManagerNative.getDefault().resizeStack(
+                        DOCKED_STACK_ID, null, true, true, false, -1);
             } catch (RemoteException e) {
                 Log.w(TAG, "Failed to resize stack: " + e);
             }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
index bb4a771..7119559c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
@@ -2056,24 +2056,24 @@
         }
         for (int i = 0; i < N; i++) {
             NotificationData.Entry entry = activeNotifications.get(i);
+            boolean childNotification = mGroupManager.isChildInGroupWithSummary(entry.notification);
             if (onKeyguard) {
                 entry.row.setOnKeyguard(true);
             } else {
                 entry.row.setOnKeyguard(false);
-                boolean top = (i == 0);
-                entry.row.setSystemExpanded(top);
+                entry.row.setSystemExpanded(visibleNotifications == 0 && !childNotification);
             }
-            boolean childNotification = mGroupManager.isChildInGroupWithSummary(entry.notification);
+            boolean suppressedSummary = mGroupManager.isSummaryOfSuppressedGroup(entry.notification);
             boolean childWithVisibleSummary = childNotification
                     && mGroupManager.getGroupSummary(entry.notification).getVisibility()
                     == View.VISIBLE;
             boolean showOnKeyguard = shouldShowOnKeyguard(entry.notification);
-            if ((isLockscreenPublicMode() && !mShowLockscreenNotifications) ||
+            if (suppressedSummary || (isLockscreenPublicMode() && !mShowLockscreenNotifications) ||
                     (onKeyguard && (visibleNotifications >= maxNotifications
                             && !childWithVisibleSummary
                             || !showOnKeyguard))) {
                 entry.row.setVisibility(View.GONE);
-                if (onKeyguard && showOnKeyguard && !childNotification) {
+                if (onKeyguard && showOnKeyguard && !childNotification && !suppressedSummary) {
                     mKeyguardIconOverflowContainer.getIconsView().addNotification(entry);
                 }
             } else {
@@ -2082,7 +2082,8 @@
                 if (!childNotification) {
                     if (wasGone) {
                         // notify the scroller of a child addition
-                        mStackScroller.generateAddAnimation(entry.row, true /* fromMoreCard */);
+                        mStackScroller.generateAddAnimation(entry.row,
+                                !showOnKeyguard /* fromMoreCard */);
                     }
                     visibleNotifications++;
                 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
index 22bb8eb..2446535 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
@@ -43,6 +43,7 @@
 
 import com.android.systemui.R;
 import com.android.systemui.classifier.FalsingManager;
+import com.android.systemui.statusbar.notification.HybridNotificationView;
 import com.android.systemui.statusbar.notification.NotificationViewWrapper;
 import com.android.systemui.statusbar.phone.NotificationGroupManager;
 import com.android.systemui.statusbar.policy.HeadsUpManager;
@@ -227,6 +228,7 @@
         updateClearability();
         if (mIsSummaryWithChildren) {
             recreateNotificationHeader();
+            mChildrenContainer.onNotificationUpdated();
         }
         if (mIconAnimationRunning) {
             setIconAnimationRunning(true);
@@ -584,6 +586,29 @@
         mPublicLayout.closeRemoteInput();
     }
 
+    /**
+     * Set by how much the single line view should be indented.
+     */
+    public void setSingleLineWidthIndention(int indention) {
+        mPrivateLayout.setSingleLineWidthIndention(indention);
+    }
+
+    public int getNotificationColor() {
+        int color = getStatusBarNotification().getNotification().color;
+        if (color == Notification.COLOR_DEFAULT) {
+            return mContext.getColor(com.android.internal.R.color.notification_icon_default_color);
+        }
+        return color;
+    }
+
+    public HybridNotificationView getSingleLineView() {
+        return mPrivateLayout.getSingleLineView();
+    }
+
+    public boolean isOnKeyguard() {
+        return mOnKeyguard;
+    }
+
     public interface ExpansionLogger {
         public void logNotificationExpansion(String key, boolean userAction, boolean expanded);
     }
@@ -677,6 +702,7 @@
             public void onInflate(ViewStub stub, View inflated) {
                 mChildrenContainer = (NotificationChildrenContainer) inflated;
                 mChildrenContainer.setNotificationParent(ExpandableNotificationRow.this);
+                mChildrenContainer.onNotificationUpdated();
                 mTranslateableViews.add(mChildrenContainer);
             }
         });
@@ -1026,11 +1052,8 @@
 
     private void onChildrenCountChanged() {
         mIsSummaryWithChildren = BaseStatusBar.ENABLE_CHILD_NOTIFICATIONS
-                && mGroupManager.hasGroupChildren(mStatusBarNotification);
+                && mChildrenContainer != null && mChildrenContainer.getChildCount() > 0;
         if (mIsSummaryWithChildren) {
-            if (mChildrenContainer == null) {
-                mChildrenContainerStub.inflate();
-            }
             if (mNotificationHeader == null) {
                 recreateNotificationHeader();
             }
@@ -1270,7 +1293,7 @@
     @Override
     public int getMinExpandHeight() {
         if (mIsSummaryWithChildren && !mShowingPublic) {
-            return mChildrenContainer.getMinExpandHeight(mOnKeyguard);
+            return mChildrenContainer.getMinExpandHeight();
         }
         return getMinHeight();
     }
@@ -1357,7 +1380,7 @@
             if (isGroupExpanded()) {
                 return 1.0f;
             } else if (isUserLocked()) {
-                return mChildrenContainer.getChildExpandFraction();
+                return mChildrenContainer.getGroupExpandFraction();
             }
         }
         return 0.0f;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java
index c2df292..0a41e42 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java
@@ -31,7 +31,7 @@
 
 import com.android.systemui.R;
 import com.android.systemui.statusbar.notification.HybridNotificationView;
-import com.android.systemui.statusbar.notification.HybridNotificationViewManager;
+import com.android.systemui.statusbar.notification.HybridGroupManager;
 import com.android.systemui.statusbar.notification.NotificationCustomViewWrapper;
 import com.android.systemui.statusbar.notification.NotificationUtils;
 import com.android.systemui.statusbar.notification.NotificationViewWrapper;
@@ -75,7 +75,7 @@
     private NotificationViewWrapper mContractedWrapper;
     private NotificationViewWrapper mExpandedWrapper;
     private NotificationViewWrapper mHeadsUpWrapper;
-    private HybridNotificationViewManager mHybridViewManager;
+    private HybridGroupManager mHybridGroupManager;
     private int mClipTopAmount;
     private int mContentHeight;
     private int mUnrestrictedContentHeight;
@@ -116,10 +116,11 @@
     private ExpandableNotificationRow mContainingNotification;
     private int mTransformationStartVisibleType;
     private boolean mUserExpanding;
+    private int mSingleLineWidthIndention;
 
     public NotificationContentView(Context context, AttributeSet attrs) {
         super(context, attrs);
-        mHybridViewManager = new HybridNotificationViewManager(getContext(), this);
+        mHybridGroupManager = new HybridGroupManager(getContext(), this);
         mMinContractedHeight = getResources().getDimensionPixelSize(
                 R.dimen.min_notification_layout_height);
         mNotificationContentMarginEnd = getResources().getDimensionPixelSize(
@@ -139,6 +140,7 @@
         boolean hasFixedHeight = heightMode == MeasureSpec.EXACTLY;
         boolean isHeightLimited = heightMode == MeasureSpec.AT_MOST;
         int maxSize = Integer.MAX_VALUE;
+        int width = MeasureSpec.getSize(widthMeasureSpec);
         if (hasFixedHeight || isHeightLimited) {
             maxSize = MeasureSpec.getSize(heightMeasureSpec);
         }
@@ -187,12 +189,18 @@
             maxChildHeight = Math.max(maxChildHeight, mHeadsUpChild.getMeasuredHeight());
         }
         if (mSingleLineView != null) {
-            mSingleLineView.measure(widthMeasureSpec,
+            int singleLineWidthSpec = widthMeasureSpec;
+            if (mSingleLineWidthIndention != 0
+                    && MeasureSpec.getMode(widthMeasureSpec) != MeasureSpec.UNSPECIFIED) {
+                singleLineWidthSpec = MeasureSpec.makeMeasureSpec(
+                        width - mSingleLineWidthIndention + mSingleLineView.getPaddingEnd(),
+                        MeasureSpec.AT_MOST);
+            }
+            mSingleLineView.measure(singleLineWidthSpec,
                     MeasureSpec.makeMeasureSpec(maxSize, MeasureSpec.AT_MOST));
             maxChildHeight = Math.max(maxChildHeight, mSingleLineView.getMeasuredHeight());
         }
         int ownHeight = Math.min(maxChildHeight, maxSize);
-        int width = MeasureSpec.getSize(widthMeasureSpec);
         setMeasuredDimension(width, ownHeight);
     }
 
@@ -715,7 +723,7 @@
 
     private void updateSingleLineView() {
         if (mIsChildInGroup) {
-            mSingleLineView = mHybridViewManager.bindFromNotification(
+            mSingleLineView = mHybridGroupManager.bindFromNotification(
                     mSingleLineView, mStatusBarNotification.getNotification());
         } else if (mSingleLineView != null) {
             removeView(mSingleLineView);
@@ -878,4 +886,20 @@
             updateBackgroundColor(false);
         }
     }
+
+    /**
+     * Set by how much the single line view should be indented. Used when a overflow indicator is
+     * present and only during measuring
+     */
+    public void setSingleLineWidthIndention(int singleLineWidthIndention) {
+        if (singleLineWidthIndention != mSingleLineWidthIndention) {
+            mSingleLineWidthIndention = singleLineWidthIndention;
+            mContainingNotification.forceLayout();
+            forceLayout();
+        }
+    }
+
+    public HybridNotificationView getSingleLineView() {
+        return mSingleLineView;
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/HybridNotificationViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/HybridGroupManager.java
similarity index 64%
rename from packages/SystemUI/src/com/android/systemui/statusbar/notification/HybridNotificationViewManager.java
rename to packages/SystemUI/src/com/android/systemui/statusbar/notification/HybridGroupManager.java
index 28bb66f..8f2c81f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/HybridNotificationViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/HybridGroupManager.java
@@ -26,6 +26,7 @@
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
+import android.widget.TextView;
 
 import com.android.systemui.R;
 import com.android.systemui.statusbar.ExpandableNotificationRow;
@@ -33,18 +34,18 @@
 import java.util.List;
 
 /**
- * A class managing {@link HybridNotificationView} views
+ * A class managing hybrid groups that include {@link HybridNotificationView} and the notification
+ * group overflow.
  */
-public class HybridNotificationViewManager {
+public class HybridGroupManager {
 
     private final Context mContext;
     private ViewGroup mParent;
-    private String mDivider;
+    private int mOverflowNumberColor;
 
-    public HybridNotificationViewManager(Context ctx, ViewGroup parent) {
+    public HybridGroupManager(Context ctx, ViewGroup parent) {
         mContext = ctx;
         mParent = parent;
-        mDivider = " • ";
     }
 
     private HybridNotificationView inflateHybridView() {
@@ -55,6 +56,26 @@
         return hybrid;
     }
 
+    private TextView inflateOverflowNumber() {
+        LayoutInflater inflater = mContext.getSystemService(LayoutInflater.class);
+        TextView numberView = (TextView) inflater.inflate(
+                R.layout.hybrid_overflow_number, mParent, false);
+        mParent.addView(numberView);
+        updateOverFlowNumberColor(numberView);
+        return numberView;
+    }
+
+    private void updateOverFlowNumberColor(TextView numberView) {
+        numberView.setTextColor(mOverflowNumberColor);
+    }
+
+    public void setOverflowNumberColor(TextView numberView, int overflowNumberColor) {
+        mOverflowNumberColor = overflowNumberColor;
+        if (numberView != null) {
+            updateOverFlowNumberColor(numberView);
+        }
+    }
+
     public HybridNotificationView bindFromNotification(HybridNotificationView reusableView,
             Notification notification) {
         if (reusableView == null) {
@@ -82,33 +103,15 @@
         return titleText;
     }
 
-    public HybridNotificationView bindFromNotificationGroup(
-            HybridNotificationView reusableView,
-            List<ExpandableNotificationRow> group, int startIndex) {
+    public TextView bindOverflowNumber(TextView reusableView, int number) {
         if (reusableView == null) {
-            reusableView = inflateHybridView();
+            reusableView = inflateOverflowNumber();
         }
-        SpannableStringBuilder summary = new SpannableStringBuilder();
-        int childCount = group.size();
-        for (int i = startIndex; i < childCount; i++) {
-            ExpandableNotificationRow child = group.get(i);
-            CharSequence titleText = resolveTitle(
-                    child.getStatusBarNotification().getNotification());
-            if (titleText == null) {
-                continue;
-            }
-            if (!TextUtils.isEmpty(summary)) {
-                summary.append(mDivider,
-                        new TextAppearanceSpan(mContext, R.style.
-                                TextAppearance_Material_Notification_HybridNotificationDivider),
-                        Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
-            }
-            summary.append(BidiFormatter.getInstance().unicodeWrap(titleText));
+        String text = mContext.getResources().getString(
+                R.string.notification_group_overflow_indicator, number);
+        if (!text.equals(reusableView.getText())) {
+            reusableView.setText(text);
         }
-        // We want to force the same orientation as the layout RTL mode
-        BidiFormatter formater = BidiFormatter.getInstance(
-                reusableView.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL);
-        reusableView.bind(formater.unicodeWrap(summary));
         return reusableView;
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/HybridNotificationView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/HybridNotificationView.java
index c80cad8..0a1795f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/HybridNotificationView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/HybridNotificationView.java
@@ -60,6 +60,14 @@
         super(context, attrs, defStyleAttr, defStyleRes);
     }
 
+    public TextView getTitleView() {
+        return mTitleView;
+    }
+
+    public TextView getTextView() {
+        return mTextView;
+    }
+
     @Override
     protected void onFinishInflate() {
         super.onFinishInflate();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationUtils.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationUtils.java
index 6ef61ec..844a2c4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationUtils.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationUtils.java
@@ -17,15 +17,19 @@
 package com.android.systemui.statusbar.notification;
 
 import android.graphics.Color;
+import android.view.View;
 import android.widget.ImageView;
 
 import com.android.internal.util.NotificationColorUtil;
 import com.android.systemui.R;
+import com.android.systemui.statusbar.stack.NotificationChildrenContainer;
 
 /**
  * A util class for various reusable functions
  */
 public class NotificationUtils {
+    private static final int[] sLocationBase = new int[2];
+    private static final int[] sLocationOffset = new int[2];
     public static boolean isGrayscale(ImageView v, NotificationColorUtil colorUtil) {
         Object isGrayscale = v.getTag(R.id.icon_is_grayscale);
         if (isGrayscale != null) {
@@ -47,4 +51,10 @@
                 (int) interpolate(Color.green(startColor), Color.green(endColor), amount),
                 (int) interpolate(Color.blue(startColor), Color.blue(endColor), amount));
     }
+
+    public static float getRelativeYOffset(View offsetView, View baseView) {
+        baseView.getLocationOnScreen(sLocationBase);
+        offsetView.getLocationOnScreen(sLocationOffset);
+        return sLocationOffset[1] - sLocationBase[1];
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java
index d3681b7..f7a6b271 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java
@@ -17,7 +17,7 @@
 package com.android.systemui.statusbar.phone;
 
 import android.service.notification.StatusBarNotification;
-import android.util.ArraySet;
+import android.support.annotation.Nullable;
 
 import com.android.systemui.statusbar.ExpandableNotificationRow;
 import com.android.systemui.statusbar.NotificationData;
@@ -35,7 +35,7 @@
     private final HashMap<String, NotificationGroup> mGroupMap = new HashMap<>();
     private OnGroupChangeListener mListener;
     private int mBarState = -1;
-    private ArraySet<String> mHeadsUpedEntries = new ArraySet<>();
+    private HashMap<String, StatusBarNotification> mIsolatedEntries = new HashMap<>();
 
     public void setOnGroupChangeListener(OnGroupChangeListener listener) {
         mListener = listener;
@@ -91,6 +91,7 @@
         } else {
             group.summary = null;
         }
+        updateSuppression(group);
         if (group.children.isEmpty()) {
             if (group.summary == null) {
                 mGroupMap.remove(groupKey);
@@ -109,43 +110,81 @@
         }
         if (isGroupChild) {
             group.children.add(added);
+            updateSuppression(group);
         } else {
             group.summary = added;
             group.expanded = added.row.areChildrenExpanded();
+            updateSuppression(group);
             if (!group.children.isEmpty()) {
                 mListener.onGroupCreatedFromChildren(group);
             }
         }
     }
 
+    private void updateSuppression(NotificationGroup group) {
+        if (group == null) {
+            return;
+        }
+        boolean prevSuppressed = group.suppressed;
+        group.suppressed = group.summary != null && !group.expanded
+                && (group.children.size() == 1
+                || (group.children.size() == 0
+                        && !group.summary.notification.getNotification().isGroupChild()
+                        && hasIsolatedChildren(group)));
+        if (prevSuppressed != group.suppressed) {
+            mListener.onGroupsChanged();
+        }
+    }
+
+    private boolean hasIsolatedChildren(NotificationGroup group) {
+        return getNumberOfIsolatedChildren(group.summary.notification.getGroupKey()) != 0;
+    }
+
+    private int getNumberOfIsolatedChildren(String groupKey) {
+        int count = 0;
+        for (StatusBarNotification sbn : mIsolatedEntries.values()) {
+            if (sbn.getGroupKey().equals(groupKey) && isIsolated(sbn)) {
+                count++;
+            }
+        }
+        return count;
+    }
+
     public void onEntryUpdated(NotificationData.Entry entry,
             StatusBarNotification oldNotification) {
         if (mGroupMap.get(getGroupKey(oldNotification)) != null) {
             onEntryRemovedInternal(entry, oldNotification);
         }
         onEntryAdded(entry);
+        if (isIsolated(entry.notification)) {
+            mIsolatedEntries.put(entry.key, entry.notification);
+            String oldKey = oldNotification.getGroupKey();
+            String newKey = entry.notification.getGroupKey();
+            if (!oldKey.equals(newKey)) {
+                updateSuppression(mGroupMap.get(oldKey));
+                updateSuppression(mGroupMap.get(newKey));
+            }
+        }
     }
 
-    public boolean isVisible(StatusBarNotification sbn) {
-        if (!isGroupChild(sbn)) {
-            return true;
-        }
-        NotificationGroup group = mGroupMap.get(getGroupKey(sbn));
-        if (group != null && (group.expanded || group.summary == null)) {
-            return true;
-        }
-        return false;
+    public boolean isSummaryOfSuppressedGroup(StatusBarNotification sbn) {
+        return isGroupSuppressed(getGroupKey(sbn)) && sbn.getNotification().isGroupSummary();
     }
 
-    public boolean hasGroupChildren(StatusBarNotification sbn) {
-        if (!isGroupSummary(sbn)) {
-            return false;
-        }
-        NotificationGroup group = mGroupMap.get(getGroupKey(sbn));
-        if (group == null) {
-            return false;
-        }
-        return !group.children.isEmpty();
+    public boolean isOnlyChildInSuppressedGroup(StatusBarNotification sbn) {
+        return isGroupSuppressed(sbn.getGroupKey())
+                && sbn.getNotification().isGroupChild()
+                && getTotalNumberOfChildren(sbn) == 1;
+    }
+
+    private int getTotalNumberOfChildren(StatusBarNotification sbn) {
+        return getNumberOfIsolatedChildren(sbn.getGroupKey())
+                + mGroupMap.get(sbn.getGroupKey()).children.size();
+    }
+
+    private boolean isGroupSuppressed(String groupKey) {
+        NotificationGroup group = mGroupMap.get(groupKey);
+        return group != null && group.suppressed;
     }
 
     public void setStatusBarState(int newState) {
@@ -163,6 +202,7 @@
             if (group.expanded) {
                 setGroupExpanded(group, false);
             }
+            updateSuppression(group);
         }
     }
 
@@ -174,7 +214,7 @@
             return false;
         }
         NotificationGroup group = mGroupMap.get(getGroupKey(sbn));
-        if (group == null || group.summary == null) {
+        if (group == null || group.summary == null || group.suppressed) {
             return false;
         }
         return true;
@@ -194,11 +234,30 @@
         return !group.children.isEmpty();
     }
 
+    /**
+     * Get the summary of a specified status bar notification. For isolated notification this return
+     * itself.
+     */
     public ExpandableNotificationRow getGroupSummary(StatusBarNotification sbn) {
-        NotificationGroup group = mGroupMap.get(getGroupKey(sbn));
+        return getGroupSummary(getGroupKey(sbn));
+    }
+
+    /**
+     * Similar to {@link #getGroupSummary(StatusBarNotification)} but doesn't get the visual summary
+     * but the logical summary, i.e when a child is isolated, it still returns the summary as if
+     * it wasn't isolated.
+     */
+    public ExpandableNotificationRow getLogicalGroupSummary(
+            StatusBarNotification sbn) {
+        return getGroupSummary(sbn.getGroupKey());
+    }
+
+    @Nullable
+    private ExpandableNotificationRow getGroupSummary(String groupKey) {
+        NotificationGroup group = mGroupMap.get(groupKey);
         return group == null ? null
                 : group.summary == null ? null
-                : group.summary.row;
+                        : group.summary.row;
     }
 
     public void toggleGroupExpansion(StatusBarNotification sbn) {
@@ -210,7 +269,7 @@
     }
 
     private boolean isIsolated(StatusBarNotification sbn) {
-        return mHeadsUpedEntries.contains(sbn.getKey()) && sbn.getNotification().isGroupChild();
+        return mIsolatedEntries.containsKey(sbn.getKey());
     }
 
     private boolean isGroupSummary(StatusBarNotification sbn) {
@@ -249,38 +308,55 @@
     public void onHeadsUpStateChanged(NotificationData.Entry entry, boolean isHeadsUp) {
         final StatusBarNotification sbn = entry.notification;
         if (entry.row.isHeadsUp()) {
-            if (!mHeadsUpedEntries.contains(sbn.getKey())) {
-                final boolean groupChild = sbn.getNotification().isGroupChild();
-                if (groupChild) {
-                    // We will be isolated now, so lets update the groups
-                    onEntryRemovedInternal(entry, entry.notification);
-                }
-                mHeadsUpedEntries.add(sbn.getKey());
-                if (groupChild) {
-                    onEntryAdded(entry);
-                    mListener.onChildIsolationChanged();
-                }
+            if (shouldIsolate(sbn)) {
+                // We will be isolated now, so lets update the groups
+                onEntryRemovedInternal(entry, entry.notification);
+
+                mIsolatedEntries.put(sbn.getKey(), sbn);
+
+                onEntryAdded(entry);
+                // We also need to update the suppression of the old group, because this call comes
+                // even before the groupManager knows about the notification at all.
+                // When the notification gets added afterwards it is already isolated and therefore
+                // it doesn't lead to an update.
+                updateSuppression(mGroupMap.get(entry.notification.getGroupKey()));
+                mListener.onGroupsChanged();
             }
         } else {
-            if (mHeadsUpedEntries.contains(sbn.getKey())) {
-                boolean isolatedBefore = isIsolated(sbn);
-                if (isolatedBefore) {
-                    // not isolated anymore, we need to update the groups
-                    onEntryRemovedInternal(entry, entry.notification);
-                }
-                mHeadsUpedEntries.remove(sbn.getKey());
-                if (isolatedBefore) {
-                    onEntryAdded(entry);
-                    mListener.onChildIsolationChanged();
-                }
+            if (mIsolatedEntries.containsKey(sbn.getKey())) {
+                // not isolated anymore, we need to update the groups
+                onEntryRemovedInternal(entry, entry.notification);
+                mIsolatedEntries.remove(sbn.getKey());
+                onEntryAdded(entry);
+                mListener.onGroupsChanged();
             }
         }
     }
 
+    private boolean shouldIsolate(StatusBarNotification sbn) {
+        NotificationGroup notificationGroup = mGroupMap.get(sbn.getGroupKey());
+        return sbn.getNotification().isGroupChild()
+                && (sbn.getNotification().fullScreenIntent != null
+                        || notificationGroup == null
+                        || !notificationGroup.expanded
+                        || isGroupNotFullyVisible(notificationGroup));
+    }
+
+    private boolean isGroupNotFullyVisible(NotificationGroup notificationGroup) {
+        return notificationGroup.summary == null
+                || notificationGroup.summary.row.getClipTopOptimization() > 0
+                || notificationGroup.summary.row.getClipTopAmount() > 0
+                || notificationGroup.summary.row.getTranslationY() < 0;
+    }
+
     public static class NotificationGroup {
         public final HashSet<NotificationData.Entry> children = new HashSet<>();
         public NotificationData.Entry summary;
         public boolean expanded;
+        /**
+         * Is this notification group suppressed, i.e its summary is hidden
+         */
+        public boolean suppressed;
     }
 
     public interface OnGroupChangeListener {
@@ -301,8 +377,9 @@
         void onGroupCreatedFromChildren(NotificationGroup group);
 
         /**
-         * The isolation of a child has changed i.e it's group changes.
+         * The groups have changed. This can happen if the isolation of a child has changes or if a
+         * group became suppressed / unsuppressed
          */
-        void onChildIsolationChanged();
+        void onGroupsChanged();
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java
index 6e345f0..a605a81 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java
@@ -114,6 +114,9 @@
             if (!PhoneStatusBar.isTopLevelChild(ent)) {
                 continue;
             }
+            if (ent.row.getVisibility() == View.GONE) {
+                continue;
+            }
             toShow.add(ent.icon);
         }
 
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 bf58611..e84d8fc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -132,6 +132,7 @@
 import com.android.systemui.statusbar.DragDownHelper;
 import com.android.systemui.statusbar.EmptyShadeView;
 import com.android.systemui.statusbar.ExpandableNotificationRow;
+import com.android.systemui.statusbar.ExpandableView;
 import com.android.systemui.statusbar.GestureRecorder;
 import com.android.systemui.statusbar.KeyguardIndicationController;
 import com.android.systemui.statusbar.NotificationData;
@@ -1632,16 +1633,14 @@
     private void updateSpeedbump() {
         int speedbumpIndex = -1;
         int currentIndex = 0;
-        ArrayList<Entry> activeNotifications = mNotificationData.getActiveNotifications();
-        final int N = activeNotifications.size();
+        final int N = mStackScroller.getChildCount();
         for (int i = 0; i < N; i++) {
-            Entry entry = activeNotifications.get(i);
-            boolean isChild = !isTopLevelChild(entry);
-            if (isChild) {
+            View view = mStackScroller.getChildAt(i);
+            if (view.getVisibility() == View.GONE || !(view instanceof ExpandableNotificationRow)) {
                 continue;
             }
-            if (entry.row.getVisibility() != View.GONE &&
-                    mNotificationData.isAmbient(entry.key)) {
+            ExpandableNotificationRow row = (ExpandableNotificationRow) view;
+            if (mNotificationData.isAmbient(row.getStatusBarNotification().getKey())) {
                 speedbumpIndex = currentIndex;
                 break;
             }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationChildrenContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationChildrenContainer.java
index 676ff2e..dc567fc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationChildrenContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationChildrenContainer.java
@@ -22,13 +22,14 @@
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
+import android.widget.TextView;
 
 import com.android.systemui.R;
 import com.android.systemui.ViewInvertHelper;
 import com.android.systemui.statusbar.CrossFadeHelper;
 import com.android.systemui.statusbar.ExpandableNotificationRow;
+import com.android.systemui.statusbar.notification.HybridGroupManager;
 import com.android.systemui.statusbar.notification.HybridNotificationView;
-import com.android.systemui.statusbar.notification.HybridNotificationViewManager;
 import com.android.systemui.statusbar.notification.NotificationUtils;
 import com.android.systemui.statusbar.phone.NotificationPanelView;
 
@@ -46,23 +47,22 @@
 
     private final List<View> mDividers = new ArrayList<>();
     private final List<ExpandableNotificationRow> mChildren = new ArrayList<>();
-    private final HybridNotificationViewManager mHybridViewManager;
+    private final HybridGroupManager mHybridGroupManager;
     private int mChildPadding;
     private int mDividerHeight;
     private int mMaxNotificationHeight;
     private int mNotificationHeaderHeight;
-    private int mNotificationAppearDistance;
     private int mNotificatonTopPadding;
     private float mCollapsedBottompadding;
     private ViewInvertHelper mOverflowInvertHelper;
     private boolean mChildrenExpanded;
     private ExpandableNotificationRow mNotificationParent;
-    private HybridNotificationView mGroupOverflowContainer;
+    private TextView mOverflowNumber;
     private ViewState mGroupOverFlowState;
     private int mRealHeight;
-    private int mLayoutDirection = LAYOUT_DIRECTION_UNDEFINED;
     private boolean mUserLocked;
     private int mActualHeight;
+    private boolean mNeverAppliedGroupState;
 
     public NotificationChildrenContainer(Context context) {
         this(context, null);
@@ -80,7 +80,7 @@
             int defStyleRes) {
         super(context, attrs, defStyleAttr, defStyleRes);
         initDimens();
-        mHybridViewManager = new HybridNotificationViewManager(getContext(), this);
+        mHybridGroupManager = new HybridGroupManager(getContext(), this);
     }
 
     private void initDimens() {
@@ -90,8 +90,6 @@
                 R.dimen.notification_divider_height));
         mMaxNotificationHeight = getResources().getDimensionPixelSize(
                 R.dimen.notification_max_height);
-        mNotificationAppearDistance = getResources().getDimensionPixelSize(
-                R.dimen.notification_appear_distance);
         mNotificationHeaderHeight = getResources().getDimensionPixelSize(
                 com.android.internal.R.dimen.notification_content_margin_top);
         mNotificatonTopPadding = getResources().getDimensionPixelSize(
@@ -108,12 +106,12 @@
             if (child.getVisibility() == View.GONE) {
                 continue;
             }
-            child.layout(0, 0, getWidth(), child.getMeasuredHeight());
+            child.layout(0, 0, child.getMeasuredWidth(), child.getMeasuredHeight());
             mDividers.get(i).layout(0, 0, getWidth(), mDividerHeight);
         }
-        if (mGroupOverflowContainer != null) {
-            mGroupOverflowContainer.layout(0, 0, getWidth(),
-                    mGroupOverflowContainer.getMeasuredHeight());
+        if (mOverflowNumber != null) {
+            mOverflowNumber.layout(getWidth() - mOverflowNumber.getMeasuredWidth(), 0, getWidth(),
+                    mOverflowNumber.getMeasuredHeight());
         }
     }
 
@@ -128,11 +126,20 @@
             ownMaxHeight = Math.min(ownMaxHeight, size);
         }
         int newHeightSpec = MeasureSpec.makeMeasureSpec(ownMaxHeight, MeasureSpec.AT_MOST);
+        int width = MeasureSpec.getSize(widthMeasureSpec);
+        if (mOverflowNumber != null) {
+            mOverflowNumber.measure(MeasureSpec.makeMeasureSpec(width, MeasureSpec.AT_MOST),
+                    newHeightSpec);
+        }
         int dividerHeightSpec = MeasureSpec.makeMeasureSpec(mDividerHeight, MeasureSpec.EXACTLY);
         int height = mNotificationHeaderHeight + mNotificatonTopPadding;
         int childCount = Math.min(mChildren.size(), NUMBER_OF_CHILDREN_WHEN_CHILDREN_EXPANDED);
+        int collapsedChildren = getMaxAllowedVisibleChildren(true /* likeCollapsed */);
+        int overflowIndex = childCount > collapsedChildren ? collapsedChildren - 1 : -1;
         for (int i = 0; i < childCount; i++) {
-            View child = mChildren.get(i);
+            ExpandableNotificationRow child = mChildren.get(i);
+            boolean isOverflow = i == overflowIndex;
+            child.setSingleLineWidthIndention(isOverflow ? mOverflowNumber.getMeasuredWidth() : 0);
             child.measure(widthMeasureSpec, newHeightSpec);
             height += child.getMeasuredHeight();
 
@@ -141,10 +148,6 @@
             divider.measure(widthMeasureSpec, dividerHeightSpec);
             height += mDividerHeight;
         }
-        int width = MeasureSpec.getSize(widthMeasureSpec);
-        if (mGroupOverflowContainer != null) {
-            mGroupOverflowContainer.measure(widthMeasureSpec, newHeightSpec);
-        }
         mRealHeight = height;
         if (heightMode != MeasureSpec.UNSPECIFIED) {
             height = Math.min(height, size);
@@ -200,22 +203,30 @@
     public void updateGroupOverflow() {
         int childCount = mChildren.size();
         int maxAllowedVisibleChildren = getMaxAllowedVisibleChildren(true /* likeCollapsed */);
-        boolean hasOverflow = childCount > maxAllowedVisibleChildren;
-        int lastVisibleIndex = hasOverflow ? maxAllowedVisibleChildren - 2
-                : maxAllowedVisibleChildren - 1;
-        if (hasOverflow) {
-            mGroupOverflowContainer = mHybridViewManager.bindFromNotificationGroup(
-                    mGroupOverflowContainer, mChildren, lastVisibleIndex + 1);
+        if (childCount > maxAllowedVisibleChildren) {
+            mOverflowNumber = mHybridGroupManager.bindOverflowNumber(
+                    mOverflowNumber, childCount - maxAllowedVisibleChildren);
             if (mOverflowInvertHelper == null) {
-                mOverflowInvertHelper= new ViewInvertHelper(mGroupOverflowContainer,
+                mOverflowInvertHelper= new ViewInvertHelper(mOverflowNumber,
                         NotificationPanelView.DOZE_ANIMATION_DURATION);
             }
             if (mGroupOverFlowState == null) {
                 mGroupOverFlowState = new ViewState();
+                mNeverAppliedGroupState = true;
             }
-        } else if (mGroupOverflowContainer != null) {
-            removeView(mGroupOverflowContainer);
-            mGroupOverflowContainer = null;
+        } else if (mOverflowNumber != null) {
+            removeView(mOverflowNumber);
+            if (isShown()) {
+                final View removedOverflowNumber = mOverflowNumber;
+                addTransientView(removedOverflowNumber, getTransientViewCount());
+                CrossFadeHelper.fadeOut(removedOverflowNumber, new Runnable() {
+                    @Override
+                    public void run() {
+                        removeTransientView(removedOverflowNumber);
+                    }
+                });
+            }
+            mOverflowNumber = null;
             mOverflowInvertHelper = null;
             mGroupOverFlowState = null;
         }
@@ -224,11 +235,7 @@
     @Override
     protected void onConfigurationChanged(Configuration newConfig) {
         super.onConfigurationChanged(newConfig);
-        int layoutDirection = getLayoutDirection();
-        if (layoutDirection != mLayoutDirection) {
-            updateGroupOverflow();
-            mLayoutDirection = layoutDirection;
-        }
+        updateGroupOverflow();
     }
 
     private View inflateDivider() {
@@ -296,7 +303,7 @@
         boolean firstChild = true;
         float expandFactor = 0;
         if (mUserLocked) {
-            expandFactor = getChildExpandFraction();
+            expandFactor = getGroupExpandFraction();
         }
         for (int i = 0; i < childCount; i++) {
             if (visibleChildren >= maxAllowedVisibleChildren) {
@@ -346,14 +353,12 @@
         int yPosition = mNotificationHeaderHeight;
         boolean firstChild = true;
         int maxAllowedVisibleChildren = getMaxAllowedVisibleChildren();
-        boolean hasOverflow = !mChildrenExpanded && childCount > maxAllowedVisibleChildren
-                && maxAllowedVisibleChildren != NUMBER_OF_CHILDREN_WHEN_CHILDREN_EXPANDED;
-        int lastVisibleIndex = hasOverflow
-                ? maxAllowedVisibleChildren - 2
-                : maxAllowedVisibleChildren - 1;
+        int lastVisibleIndex = maxAllowedVisibleChildren - 1;
+        int firstOverflowIndex = lastVisibleIndex + 1;
         float expandFactor = 0;
         if (mUserLocked) {
-            expandFactor = getChildExpandFraction();
+            expandFactor = getGroupExpandFraction();
+            firstOverflowIndex = getMaxAllowedVisibleChildren(true /* likeCollapsed */);
         }
         for (int i = 0; i < childCount; i++) {
             ExpandableNotificationRow child = mChildren.get(i);
@@ -386,19 +391,38 @@
             childState.belowSpeedBump = parentState.belowSpeedBump;
             childState.clipTopAmount = 0;
             childState.topOverLap = 0;
-            boolean visible = i <= lastVisibleIndex;
-            childState.alpha = visible ? 1 : 0;
+            childState.alpha = 0;
+            if (i < firstOverflowIndex) {
+                childState.alpha = 1;
+            } else if (expandFactor == 1.0f && i <= lastVisibleIndex) {
+                childState.alpha = (mActualHeight - childState.yTranslation) / childState.height;
+                childState.alpha = Math.max(0.0f, Math.min(1.0f, childState.alpha));
+            }
             childState.location = parentState.location;
             yPosition += intrinsicHeight;
         }
-        if (mGroupOverflowContainer != null) {
-            mGroupOverFlowState.initFrom(mGroupOverflowContainer);
-            if (hasOverflow) {
-                StackViewState firstOverflowState =
-                        resultState.getViewStateForView(mChildren.get(lastVisibleIndex + 1));
-                mGroupOverFlowState.yTranslation = firstOverflowState.yTranslation;
+        if (mOverflowNumber != null) {
+            ExpandableNotificationRow overflowView = mChildren.get(Math.min(
+                    getMaxAllowedVisibleChildren(true /* likeCollpased */), childCount) - 1);
+            mGroupOverFlowState.copyFrom(resultState.getViewStateForView(overflowView));
+            if (!mChildrenExpanded) {
+                if (mUserLocked) {
+                    HybridNotificationView singleLineView = overflowView.getSingleLineView();
+                    View mirrorView = singleLineView.getTextView();
+                    if (mirrorView.getVisibility() == GONE) {
+                        mirrorView = singleLineView.getTitleView();
+                    }
+                    if (mirrorView.getVisibility() == GONE) {
+                        mirrorView = singleLineView;
+                    }
+                    mGroupOverFlowState.yTranslation += NotificationUtils.getRelativeYOffset(
+                            mirrorView, overflowView);
+                    mGroupOverFlowState.alpha = mirrorView.getAlpha();
+                }
+            } else {
+                mGroupOverFlowState.yTranslation += mNotificationHeaderHeight;
+                mGroupOverFlowState.alpha = 0.0f;
             }
-            mGroupOverFlowState.alpha = mChildrenExpanded || !hasOverflow ? 0.0f : 1.0f;
         }
     }
 
@@ -410,7 +434,8 @@
         if (!likeCollapsed && (mChildrenExpanded || mNotificationParent.isUserLocked())) {
             return NUMBER_OF_CHILDREN_WHEN_CHILDREN_EXPANDED;
         }
-        if (mNotificationParent.isExpanded() || mNotificationParent.isHeadsUp()) {
+        if (!mNotificationParent.isOnKeyguard()
+                && (mNotificationParent.isExpanded() || mNotificationParent.isHeadsUp())) {
             return NUMBER_OF_CHILDREN_WHEN_SYSTEM_EXPANDED;
         }
         return NUMBER_OF_CHILDREN_WHEN_COLLAPSED;
@@ -419,7 +444,10 @@
     public void applyState(StackScrollState state) {
         int childCount = mChildren.size();
         ViewState tmpState = new ViewState();
-        float expandFraction = getChildExpandFraction();
+        float expandFraction = 0.0f;
+        if (mUserLocked) {
+            expandFraction = getGroupExpandFraction();
+        }
         for (int i = 0; i < childCount; i++) {
             ExpandableNotificationRow child = mChildren.get(i);
             StackViewState viewState = state.getViewStateForView(child);
@@ -431,13 +459,17 @@
             tmpState.yTranslation = viewState.yTranslation - mDividerHeight;
             float alpha = mChildrenExpanded && viewState.alpha != 0 ? 0.5f : 0;
             if (mUserLocked && viewState.alpha != 0) {
-                alpha = NotificationUtils.interpolate(0, 0.5f, expandFraction);
+                alpha = NotificationUtils.interpolate(0, 0.5f,
+                        Math.min(viewState.alpha, expandFraction));
             }
             tmpState.alpha = alpha;
             state.applyViewState(divider, tmpState);
+            // There is no fake shadow to be drawn on the children
+            child.setFakeShadowIntensity(0.0f, 0.0f, 0, 0);
         }
-        if (mGroupOverflowContainer != null) {
-            state.applyViewState(mGroupOverflowContainer, mGroupOverFlowState);
+        if (mOverflowNumber != null) {
+            state.applyViewState(mOverflowNumber, mGroupOverFlowState);
+            mNeverAppliedGroupState = false;
         }
     }
 
@@ -456,7 +488,7 @@
             long baseDelay, long duration) {
         int childCount = mChildren.size();
         ViewState tmpState = new ViewState();
-        float expandFraction = getChildExpandFraction();
+        float expandFraction = getGroupExpandFraction();
         for (int i = childCount - 1; i >= 0; i--) {
             ExpandableNotificationRow child = mChildren.get(i);
             StackViewState viewState = state.getViewStateForView(child);
@@ -468,13 +500,23 @@
             tmpState.yTranslation = viewState.yTranslation - mDividerHeight;
             float alpha = mChildrenExpanded && viewState.alpha != 0 ? 0.5f : 0;
             if (mUserLocked && viewState.alpha != 0) {
-                alpha = NotificationUtils.interpolate(0, 0.5f, expandFraction);
+                alpha = NotificationUtils.interpolate(0, 0.5f,
+                        Math.min(viewState.alpha, expandFraction));
             }
             tmpState.alpha = alpha;
             stateAnimator.startViewAnimations(divider, tmpState, baseDelay, duration);
+            // There is no fake shadow to be drawn on the children
+            child.setFakeShadowIntensity(0.0f, 0.0f, 0, 0);
         }
-        if (mGroupOverflowContainer != null) {
-            stateAnimator.startViewAnimations(mGroupOverflowContainer, mGroupOverFlowState,
+        if (mOverflowNumber != null) {
+            if (mNeverAppliedGroupState) {
+                float alpha = mGroupOverFlowState.alpha;
+                mGroupOverFlowState.alpha = 0;
+                state.applyViewState(mOverflowNumber, mGroupOverFlowState);
+                mGroupOverFlowState.alpha = alpha;
+                mNeverAppliedGroupState = false;
+            }
+            stateAnimator.startViewAnimations(mOverflowNumber, mGroupOverFlowState,
                     baseDelay, duration);
         }
     }
@@ -529,44 +571,49 @@
             return;
         }
         mActualHeight = actualHeight;
-        float fraction = getChildExpandFraction();
+        float fraction = getGroupExpandFraction();
+        int maxAllowedVisibleChildren = getMaxAllowedVisibleChildren(true /* forceCollapsed */);
         int childCount = mChildren.size();
         for (int i = 0; i < childCount; i++) {
             ExpandableNotificationRow child = mChildren.get(i);
             float childHeight = child.isExpanded(true /* allowOnKeyguard */)
                     ? child.getMaxExpandHeight()
                     : child.getShowingLayout().getMinHeight(true /* likeGroupExpanded */);
-            float singleLineHeight = child.getShowingLayout().getMinHeight(
-                    false /* likeGroupExpanded */);
-            child.setActualHeight((int) NotificationUtils.interpolate(singleLineHeight, childHeight,
-                    fraction), false);
+            if (i < maxAllowedVisibleChildren) {
+                float singleLineHeight = child.getShowingLayout().getMinHeight(
+                        false /* likeGroupExpanded */);
+                child.setActualHeight((int) NotificationUtils.interpolate(singleLineHeight,
+                        childHeight, fraction), false);
+            } else {
+                child.setActualHeight((int) childHeight, false);
+            }
         }
     }
 
-    public float getChildExpandFraction() {
-        int allChildrenVisibleHeight = getChildrenExpandStartHeight();
-        int maxContentHeight = getMaxContentHeight();
-        float factor = (mActualHeight - allChildrenVisibleHeight)
-                / (float) (maxContentHeight - allChildrenVisibleHeight);
+    public float getGroupExpandFraction() {
+        int visibleChildrenExpandedHeight = getVisibleChildrenExpandHeight();
+        int minExpandHeight = getMinExpandHeight();
+        float factor = (mActualHeight - minExpandHeight)
+                / (float) (visibleChildrenExpandedHeight - minExpandHeight);
         return Math.max(0.0f, Math.min(1.0f, factor));
     }
 
-    private int getChildrenExpandStartHeight() {
-        int intrinsicHeight = mNotificationHeaderHeight;
+    private int getVisibleChildrenExpandHeight() {
+        int intrinsicHeight = mNotificationHeaderHeight + mNotificatonTopPadding + mDividerHeight;
         int visibleChildren = 0;
         int childCount = mChildren.size();
+        int maxAllowedVisibleChildren = getMaxAllowedVisibleChildren(true /* forceCollapsed */);
         for (int i = 0; i < childCount; i++) {
-            if (visibleChildren >= NUMBER_OF_CHILDREN_WHEN_CHILDREN_EXPANDED) {
+            if (visibleChildren >= maxAllowedVisibleChildren) {
                 break;
             }
             ExpandableNotificationRow child = mChildren.get(i);
-            intrinsicHeight += child.getMinHeight();
+            float childHeight = child.isExpanded(true /* allowOnKeyguard */)
+                    ? child.getMaxExpandHeight()
+                    : child.getShowingLayout().getMinHeight(true /* likeGroupExpanded */);
+            intrinsicHeight += childHeight;
             visibleChildren++;
         }
-        if (visibleChildren > 0) {
-            intrinsicHeight += (visibleChildren - 1) * mChildPadding;
-        }
-        intrinsicHeight += mCollapsedBottompadding;
         return intrinsicHeight;
     }
 
@@ -574,9 +621,8 @@
         return getIntrinsicHeight(NUMBER_OF_CHILDREN_WHEN_COLLAPSED);
     }
 
-    public int getMinExpandHeight(boolean onKeyguard) {
-        int maxAllowedVisibleChildren = onKeyguard ? NUMBER_OF_CHILDREN_WHEN_COLLAPSED
-                : getMaxAllowedVisibleChildren(true /* forceCollapsed */);
+    public int getMinExpandHeight() {
+        int maxAllowedVisibleChildren = getMaxAllowedVisibleChildren(true /* forceCollapsed */);
         int minExpandHeight = mNotificationHeaderHeight;
         int visibleChildren = 0;
         boolean firstChild = true;
@@ -599,7 +645,7 @@
     }
 
     public void setDark(boolean dark, boolean fade, long delay) {
-        if (mGroupOverflowContainer != null) {
+        if (mOverflowNumber != null) {
             mOverflowInvertHelper.setInverted(dark, fade, delay);
         }
     }
@@ -624,4 +670,9 @@
             child.setUserLocked(userLocked);
         }
     }
+
+    public void onNotificationUpdated() {
+        mHybridGroupManager.setOverflowNumberColor(mOverflowNumber,
+                mNotificationParent.getNotificationColor());
+    }
 }
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 686a712..bd5dcc6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
@@ -694,11 +694,7 @@
                 mHeadsUpManager.addSwipedOutNotification(row.getStatusBarNotification().getKey());
             }
         }
-        final View veto = v.findViewById(R.id.veto);
-        if (veto != null && veto.getVisibility() != View.GONE) {
-            veto.performClick();
-        }
-        if (DEBUG) Log.v(TAG, "onChildDismissed: " + v);
+        performDismiss(v);
 
         mFalsingManager.onNotificationDismissed();
         if (mFalsingManager.shouldEnforceBouncer()) {
@@ -707,6 +703,24 @@
         }
     }
 
+    private void performDismiss(View v) {
+        if (v instanceof ExpandableNotificationRow) {
+            ExpandableNotificationRow row = (ExpandableNotificationRow) v;
+            if (mGroupManager.isOnlyChildInSuppressedGroup(row.getStatusBarNotification())) {
+                ExpandableNotificationRow groupSummary =
+                        mGroupManager.getLogicalGroupSummary(row.getStatusBarNotification());
+                if (groupSummary.isClearable()) {
+                    performDismiss(groupSummary);
+                }
+            }
+        }
+        final View veto = v.findViewById(R.id.veto);
+        if (veto != null && veto.getVisibility() != View.GONE) {
+            veto.performClick();
+        }
+        if (DEBUG) Log.v(TAG, "onChildDismissed: " + v);
+    }
+
     @Override
     public void onChildSnappedBack(View animView, float targetLeft) {
         mAmbientState.onDragFinished(animView);
@@ -3265,7 +3279,7 @@
     }
 
     @Override
-    public void onChildIsolationChanged() {
+    public void onGroupsChanged() {
         mPhoneStatusBar.requestNotificationUpdate();
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java
index cf4802d..dba5bbd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java
@@ -972,10 +972,10 @@
     }
 
     /**
-     * Get the end value of the height animation running on a view or the actualHeight
+     * Get the end value of the yTranslation animation running on a view or the yTranslation
      * if no animation is running.
      */
-    public static float getFinalTranslationY(ExpandableView view) {
+    public static float getFinalTranslationY(View view) {
         if (view == null) {
             return 0;
         }
diff --git a/packages/SystemUI/src/com/android/systemui/tv/pip/PipManager.java b/packages/SystemUI/src/com/android/systemui/tv/pip/PipManager.java
index ff7ea27..95cee4c 100644
--- a/packages/SystemUI/src/com/android/systemui/tv/pip/PipManager.java
+++ b/packages/SystemUI/src/com/android/systemui/tv/pip/PipManager.java
@@ -120,6 +120,14 @@
             mMediaSessionManager.addOnActiveSessionsChangedListener(
                     mActiveMediaSessionListener, null);
             updateMediaController(mMediaSessionManager.getActiveSessions(null));
+            if (mIsRecentsShown) {
+                // If an activity becomes PIPed again after the fullscreen, the Recents is shown
+                // behind so we need to resize the pinned stack and show the correct overlay.
+                resizePinnedStack(STATE_PIP_OVERLAY);
+            }
+            for (int i = mListeners.size() - 1; i >= 0; i--) {
+                mListeners.get(i).onPipEntered();
+            }
         }
     };
     private final Runnable mOnTaskStackChanged = new Runnable() {
@@ -152,6 +160,7 @@
     private final Runnable mOnPinnedActivityRestartAttempt = new Runnable() {
         @Override
         public void run() {
+            // If PIPed activity is launched again by Launcher or intent, make it fullscreen.
             movePipToFullscreen();
         }
     };
@@ -315,11 +324,7 @@
     private void showPipOverlay() {
         if (DEBUG) Log.d(TAG, "showPipOverlay()");
         mState = STATE_PIP_OVERLAY;
-        Intent intent = new Intent(mContext, PipOverlayActivity.class);
-        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
-        final ActivityOptions options = ActivityOptions.makeBasic();
-        options.setLaunchStackId(PINNED_STACK_ID);
-        mContext.startActivity(intent, options.toBundle());
+        PipOverlayActivity.showPipOverlay(mContext);
     }
 
     /**
@@ -385,9 +390,9 @@
                 break;
         }
         try {
-            mActivityManager.resizeStack(PINNED_STACK_ID, mCurrentPipBounds, true, true, true);
+            mActivityManager.resizeStack(PINNED_STACK_ID, mCurrentPipBounds, true, true, true, -1);
         } catch (RemoteException e) {
-            Log.e(TAG, "showPipMenu failed", e);
+            Log.e(TAG, "resizeStack failed", e);
         }
     }
 
@@ -485,17 +490,7 @@
      * Returns {@code true} if PIP is shown.
      */
     public boolean isPipShown() {
-        return hasPipTasks();
-    }
-
-    private boolean hasPipTasks() {
-        try {
-            StackInfo stackInfo = mActivityManager.getStackInfo(PINNED_STACK_ID);
-            return stackInfo != null;
-        } catch (RemoteException e) {
-            Log.e(TAG, "getStackInfo failed", e);
-            return false;
-        }
+        return mState != STATE_NO_PIP;
     }
 
     private void handleMediaResourceGranted(String[] packageNames) {
@@ -603,6 +598,13 @@
      * A listener interface to receive notification on changes in PIP.
      */
     public interface Listener {
+        /**
+         * Invoked when an activity is pinned and PIP manager is set corresponding information.
+         * Classes must use this instead of {@link android.app.ITaskStackListener.onActivityPinned}
+         * because there's no guarantee for the PIP manager be return relavent information
+         * correctly. (e.g. {@link isPipShown}, {@link getPipBounds})
+         */
+        void onPipEntered();
         /** Invoked when a PIPed activity is closed. */
         void onPipActivityClosed();
         /** Invoked when the PIP menu gets shown. */
diff --git a/packages/SystemUI/src/com/android/systemui/tv/pip/PipMenuActivity.java b/packages/SystemUI/src/com/android/systemui/tv/pip/PipMenuActivity.java
index 285dfd1..b8b837a 100644
--- a/packages/SystemUI/src/com/android/systemui/tv/pip/PipMenuActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/tv/pip/PipMenuActivity.java
@@ -198,6 +198,9 @@
     }
 
     @Override
+    public void onPipEntered() { }
+
+    @Override
     public void onPipActivityClosed() {
         finish();
     }
diff --git a/packages/SystemUI/src/com/android/systemui/tv/pip/PipOnboardingActivity.java b/packages/SystemUI/src/com/android/systemui/tv/pip/PipOnboardingActivity.java
index ad45625b..79daf3d 100644
--- a/packages/SystemUI/src/com/android/systemui/tv/pip/PipOnboardingActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/tv/pip/PipOnboardingActivity.java
@@ -67,6 +67,9 @@
     }
 
     @Override
+    public void onPipEntered() { }
+
+    @Override
     public void onPipActivityClosed() {
         finish();
     }
diff --git a/packages/SystemUI/src/com/android/systemui/tv/pip/PipOverlayActivity.java b/packages/SystemUI/src/com/android/systemui/tv/pip/PipOverlayActivity.java
index 95d655c..1de321d 100644
--- a/packages/SystemUI/src/com/android/systemui/tv/pip/PipOverlayActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/tv/pip/PipOverlayActivity.java
@@ -17,21 +17,31 @@
 package com.android.systemui.tv.pip;
 
 import android.app.Activity;
+import android.app.ActivityOptions;
+import android.content.Context;
+import android.content.Intent;
 import android.os.Bundle;
 import android.os.Handler;
 import android.view.View;
 
 import com.android.systemui.R;
 
+import static android.app.ActivityManager.StackId.PINNED_STACK_ID;
+
 /**
  * Activity to show an overlay on top of PIP activity to show how to pop up PIP menu.
  */
 public class PipOverlayActivity extends Activity implements PipManager.Listener {
-    private static final String TAG = "PipOverlayActivity";
-    private static final boolean DEBUG = false;
-
     private static final long SHOW_GUIDE_OVERLAY_VIEW_DURATION_MS = 4000;
 
+    /**
+     * The single instance of PipOverlayActivity to prevent it from restarting.
+     * Note that {@link PipManager} moves the PIPed activity to fullscreen if the activity is
+     * restarted. It's because the activity may be started by the Launcher or an intent again,
+     * but we don't want do so for the PipOverlayActivity.
+     */
+    private static PipOverlayActivity sPipOverlayActivity;
+
     private final PipManager mPipManager = PipManager.getInstance();
     private final Handler mHandler = new Handler();
     private View mGuideOverlayView;
@@ -42,6 +52,19 @@
         }
     };
 
+    /**
+     * Launches the PIP overlay. This should be only called on the main thread.
+     */
+    public static void showPipOverlay(Context context) {
+        if (sPipOverlayActivity == null) {
+            Intent intent = new Intent(context, PipOverlayActivity.class);
+            intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+            final ActivityOptions options = ActivityOptions.makeBasic();
+            options.setLaunchStackId(PINNED_STACK_ID);
+            context.startActivity(intent, options.toBundle());
+        }
+    }
+
     @Override
     protected void onCreate(Bundle bundle) {
         super.onCreate(bundle);
@@ -49,6 +72,8 @@
         mGuideOverlayView = findViewById(R.id.guide_overlay);
         mGuideButtonsView = findViewById(R.id.guide_buttons);
         mPipManager.addListener(this);
+
+        sPipOverlayActivity = this;
     }
 
     @Override
@@ -76,6 +101,7 @@
     @Override
     protected void onDestroy() {
         super.onDestroy();
+        sPipOverlayActivity = null;
         mHandler.removeCallbacksAndMessages(null);
         mPipManager.removeListener(this);
         mPipManager.resumePipResizing(
@@ -83,6 +109,9 @@
     }
 
     @Override
+    public void onPipEntered() { }
+
+    @Override
     public void onPipActivityClosed() {
         finish();
     }
@@ -105,6 +134,11 @@
     }
 
     @Override
-    public void onMediaControllerChanged() {
+    public void onMediaControllerChanged() { }
+
+    @Override
+    public void finish() {
+        sPipOverlayActivity = null;
+        super.finish();
     }
 }
diff --git a/rs/java/android/renderscript/RenderScript.java b/rs/java/android/renderscript/RenderScript.java
index 425569c..9beaba30 100644
--- a/rs/java/android/renderscript/RenderScript.java
+++ b/rs/java/android/renderscript/RenderScript.java
@@ -1387,6 +1387,27 @@
     }
 
     /**
+     * Name of the file that holds the object cache.
+     */
+    private static String mCachePath;
+
+    /**
+     * Gets the path to the code cache.
+     */
+    static synchronized String getCachePath() {
+        if (mCachePath == null) {
+            final String CACHE_PATH = "com.android.renderscript.cache";
+            if (RenderScriptCacheDir.mCacheDir == null) {
+                throw new RSRuntimeException("RenderScript code cache directory uninitialized.");
+            }
+            File f = new File(RenderScriptCacheDir.mCacheDir, CACHE_PATH);
+            mCachePath = f.getAbsolutePath();
+            f.mkdirs();
+        }
+        return mCachePath;
+    }
+
+    /**
      * Create a RenderScript context.
      *
      * @param ctx The context.
@@ -1415,11 +1436,7 @@
         }
 
         // set up cache directory for entire context
-        final String CACHE_PATH = "com.android.renderscript.cache";
-        File f = new File(RenderScriptCacheDir.mCacheDir, CACHE_PATH);
-        String mCachePath = f.getAbsolutePath();
-        f.mkdirs();
-        rs.nContextSetCacheDir(mCachePath);
+        rs.nContextSetCacheDir(RenderScript.getCachePath());
 
         rs.mMessageThread = new MessageThread(rs);
         rs.mMessageThread.start();
diff --git a/rs/java/android/renderscript/ScriptC.java b/rs/java/android/renderscript/ScriptC.java
index bf706c1..00ebe57 100644
--- a/rs/java/android/renderscript/ScriptC.java
+++ b/rs/java/android/renderscript/ScriptC.java
@@ -84,13 +84,6 @@
         setID(id);
     }
 
-    /**
-     * Name of the file that holds the object cache.
-     */
-    private static final String CACHE_PATH = "com.android.renderscript.cache";
-
-    static String mCachePath;
-
     private static synchronized long internalCreate(RenderScript rs, Resources resources, int resourceID) {
         byte[] pgm;
         int pgmLength;
@@ -122,26 +115,12 @@
 
         String resName = resources.getResourceEntryName(resourceID);
 
-        // Create the RS cache path if we haven't done so already.
-        if (mCachePath == null) {
-            File f = new File(RenderScriptCacheDir.mCacheDir, CACHE_PATH);
-            mCachePath = f.getAbsolutePath();
-            f.mkdirs();
-        }
         //        Log.v(TAG, "Create script for resource = " + resName);
-        return rs.nScriptCCreate(resName, mCachePath, pgm, pgmLength);
+        return rs.nScriptCCreate(resName, RenderScript.getCachePath(), pgm, pgmLength);
     }
 
     private static synchronized long internalStringCreate(RenderScript rs, String resName, byte[] bitcode) {
-        // Create the RS cache path if we haven't done so already.
-        if (mCachePath == null) {
-            File f = new File(RenderScriptCacheDir.mCacheDir, CACHE_PATH);
-            mCachePath = f.getAbsolutePath();
-            f.mkdirs();
-        }
         //        Log.v(TAG, "Create script for resource = " + resName);
-        return rs.nScriptCCreate(resName, mCachePath, bitcode, bitcode.length);
+        return rs.nScriptCCreate(resName, RenderScript.getCachePath(), bitcode, bitcode.length);
     }
-
-
 }
diff --git a/rs/java/android/renderscript/ScriptGroup.java b/rs/java/android/renderscript/ScriptGroup.java
index 9bbacbc..9357c3bb 100644
--- a/rs/java/android/renderscript/ScriptGroup.java
+++ b/rs/java/android/renderscript/ScriptGroup.java
@@ -396,7 +396,7 @@
         for (int i = 0; i < closureIDs.length; i++) {
             closureIDs[i] = closures.get(i).getID(rs);
         }
-        long id = rs.nScriptGroup2Create(name, ScriptC.mCachePath, closureIDs);
+        long id = rs.nScriptGroup2Create(name, RenderScript.getCachePath(), closureIDs);
         setID(id);
     }
 
diff --git a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
index 8febecc..215be4a 100644
--- a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
+++ b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
@@ -332,7 +332,12 @@
                 for (int i = N - 1; i >= 0; i--) {
                     Provider provider = installedProviders.get(i);
 
-                    ensureGroupStateLoadedLocked(provider.getUserId());
+                    final int userId = provider.getUserId();
+                    if (!mUserManager.isUserUnlocked(userId) ||
+                            isProfileWithLockedParent(userId)) {
+                        continue;
+                    }
+                    ensureGroupStateLoadedLocked(userId);
 
                     if (!removedProviders.contains(provider.id)) {
                         final boolean changed = updateProvidersForPackageLocked(
diff --git a/services/core/java/com/android/server/AppOpsService.java b/services/core/java/com/android/server/AppOpsService.java
index a94c8b8..e7db2a8 100644
--- a/services/core/java/com/android/server/AppOpsService.java
+++ b/services/core/java/com/android/server/AppOpsService.java
@@ -447,12 +447,8 @@
             int[] ops) {
         mContext.enforcePermission(android.Manifest.permission.GET_APP_OPS_STATS,
                 Binder.getCallingPid(), Binder.getCallingUid(), null);
-        String resolvedPackageName = resolvePackageName(uid, packageName);
-        if (resolvedPackageName == null) {
-            return Collections.emptyList();
-        }
         synchronized (this) {
-            Ops pkgOps = getOpsRawLocked(uid, resolvedPackageName, false);
+            Ops pkgOps = getOpsLocked(uid, packageName, false);
             if (pkgOps == null) {
                 return null;
             }
@@ -470,7 +466,7 @@
 
     private void pruneOp(Op op, int uid, String packageName) {
         if (op.time == 0 && op.rejectTime == 0) {
-            Ops ops = getOpsRawLocked(uid, packageName, false);
+            Ops ops = getOpsLocked(uid, packageName, false);
             if (ops != null) {
                 ops.remove(op.op);
                 if (ops.size() <= 0) {
@@ -884,12 +880,8 @@
     public int checkOperation(int code, int uid, String packageName) {
         verifyIncomingUid(uid);
         verifyIncomingOp(code);
-        String resolvedPackageName = resolvePackageName(uid, packageName);
-        if (resolvedPackageName == null) {
-            return AppOpsManager.MODE_IGNORED;
-        }
         synchronized (this) {
-            if (isOpRestricted(uid, code, resolvedPackageName)) {
+            if (isOpRestricted(uid, code, packageName)) {
                 return AppOpsManager.MODE_IGNORED;
             }
             code = AppOpsManager.opToSwitch(code);
@@ -900,7 +892,7 @@
                     return uidMode;
                 }
             }
-            Op op = getOpLocked(code, uid, resolvedPackageName, false);
+            Op op = getOpLocked(code, uid, packageName, false);
             if (op == null) {
                 return AppOpsManager.opToDefaultMode(code);
             }
@@ -976,7 +968,6 @@
 
     @Override
     public int checkPackage(int uid, String packageName) {
-        Preconditions.checkNotNull(packageName);
         synchronized (this) {
             if (getOpsRawLocked(uid, packageName, true) != null) {
                 return AppOpsManager.MODE_ALLOWED;
@@ -990,39 +981,26 @@
     public int noteProxyOperation(int code, String proxyPackageName,
             int proxiedUid, String proxiedPackageName) {
         verifyIncomingOp(code);
-        final int proxyUid = Binder.getCallingUid();
-        String resolveProxyPackageName = resolvePackageName(proxyUid, proxyPackageName);
-        if (resolveProxyPackageName == null) {
-            return AppOpsManager.MODE_IGNORED;
-        }
-        final int proxyMode = noteOperationUnchecked(code, proxyUid,
-                resolveProxyPackageName, -1, null);
+        final int proxyMode = noteOperationUnchecked(code, Binder.getCallingUid(),
+                proxyPackageName, -1, null);
         if (proxyMode != AppOpsManager.MODE_ALLOWED || Binder.getCallingUid() == proxiedUid) {
             return proxyMode;
         }
-        String resolveProxiedPackageName = resolvePackageName(proxiedUid, proxiedPackageName);
-        if (resolveProxiedPackageName == null) {
-            return AppOpsManager.MODE_IGNORED;
-        }
-        return noteOperationUnchecked(code, proxiedUid, resolveProxiedPackageName,
-                proxyMode, resolveProxyPackageName);
+        return noteOperationUnchecked(code, proxiedUid, proxiedPackageName,
+                Binder.getCallingUid(), proxyPackageName);
     }
 
     @Override
     public int noteOperation(int code, int uid, String packageName) {
         verifyIncomingUid(uid);
         verifyIncomingOp(code);
-        String resolvedPackageName = resolvePackageName(uid, packageName);
-        if (resolvedPackageName == null) {
-            return AppOpsManager.MODE_IGNORED;
-        }
-        return noteOperationUnchecked(code, uid, resolvedPackageName, 0, null);
+        return noteOperationUnchecked(code, uid, packageName, 0, null);
     }
 
     private int noteOperationUnchecked(int code, int uid, String packageName,
             int proxyUid, String proxyPackageName) {
         synchronized (this) {
-            Ops ops = getOpsRawLocked(uid, packageName, true);
+            Ops ops = getOpsLocked(uid, packageName, true);
             if (ops == null) {
                 if (DEBUG) Log.d(TAG, "noteOperation: no op for code " + code + " uid " + uid
                         + " package " + packageName);
@@ -1070,20 +1048,16 @@
     public int startOperation(IBinder token, int code, int uid, String packageName) {
         verifyIncomingUid(uid);
         verifyIncomingOp(code);
-        String resolvedPackageName = resolvePackageName(uid, packageName);
-        if (resolvedPackageName == null) {
-            return  AppOpsManager.MODE_IGNORED;
-        }
         ClientState client = (ClientState)token;
         synchronized (this) {
-            Ops ops = getOpsRawLocked(uid, resolvedPackageName, true);
+            Ops ops = getOpsLocked(uid, packageName, true);
             if (ops == null) {
                 if (DEBUG) Log.d(TAG, "startOperation: no op for code " + code + " uid " + uid
-                        + " package " + resolvedPackageName);
+                        + " package " + packageName);
                 return AppOpsManager.MODE_ERRORED;
             }
             Op op = getOpLocked(ops, code, true);
-            if (isOpRestricted(uid, code, resolvedPackageName)) {
+            if (isOpRestricted(uid, code, packageName)) {
                 return AppOpsManager.MODE_IGNORED;
             }
             final int switchCode = AppOpsManager.opToSwitch(code);
@@ -1093,7 +1067,7 @@
                 if (uidMode != AppOpsManager.MODE_ALLOWED) {
                     if (DEBUG) Log.d(TAG, "noteOperation: reject #" + op.mode + " for code "
                             + switchCode + " (" + code + ") uid " + uid + " package "
-                            + resolvedPackageName);
+                            + packageName);
                     op.rejectTime = System.currentTimeMillis();
                     return uidMode;
                 }
@@ -1101,13 +1075,12 @@
             final Op switchOp = switchCode != code ? getOpLocked(ops, switchCode, true) : op;
             if (switchOp.mode != AppOpsManager.MODE_ALLOWED) {
                 if (DEBUG) Log.d(TAG, "startOperation: reject #" + op.mode + " for code "
-                        + switchCode + " (" + code + ") uid " + uid + " package "
-                        + resolvedPackageName);
+                        + switchCode + " (" + code + ") uid " + uid + " package " + packageName);
                 op.rejectTime = System.currentTimeMillis();
                 return switchOp.mode;
             }
             if (DEBUG) Log.d(TAG, "startOperation: allowing code " + code + " uid " + uid
-                    + " package " + resolvedPackageName);
+                    + " package " + packageName);
             if (op.nesting == 0) {
                 op.time = System.currentTimeMillis();
                 op.rejectTime = 0;
@@ -1125,16 +1098,9 @@
     public void finishOperation(IBinder token, int code, int uid, String packageName) {
         verifyIncomingUid(uid);
         verifyIncomingOp(code);
-        String resolvedPackageName = resolvePackageName(uid, packageName);
-        if (resolvedPackageName == null) {
-            return;
-        }
-        if (!(token instanceof ClientState)) {
-            return;
-        }
-        ClientState client = (ClientState) token;
+        ClientState client = (ClientState)token;
         synchronized (this) {
-            Op op = getOpLocked(code, uid, resolvedPackageName, true);
+            Op op = getOpLocked(code, uid, packageName, true);
             if (op == null) {
                 return;
             }
@@ -1150,9 +1116,6 @@
 
     @Override
     public int permissionToOpCode(String permission) {
-        if (permission == null) {
-            return AppOpsManager.OP_NONE;
-        }
         return AppOpsManager.permissionToOpCode(permission);
     }
 
@@ -1202,6 +1165,15 @@
         return uidState;
     }
 
+    private Ops getOpsLocked(int uid, String packageName, boolean edit) {
+        if (uid == 0) {
+            packageName = "root";
+        } else if (uid == Process.SHELL_UID) {
+            packageName = "com.android.shell";
+        }
+        return getOpsRawLocked(uid, packageName, edit);
+    }
+
     private Ops getOpsRawLocked(int uid, String packageName, boolean edit) {
         UidState uidState = getUidStateLocked(uid, edit);
         if (uidState == null) {
@@ -1287,7 +1259,7 @@
     }
 
     private Op getOpLocked(int code, int uid, String packageName, boolean edit) {
-        Ops ops = getOpsRawLocked(uid, packageName, edit);
+        Ops ops = getOpsLocked(uid, packageName, edit);
         if (ops == null) {
             return null;
         }
@@ -1345,7 +1317,7 @@
                 if (AppOpsManager.opAllowSystemBypassRestriction(code)) {
                     // If we are the system, bypass user restrictions for certain codes
                     synchronized (this) {
-                        Ops ops = getOpsRawLocked(uid, packageName, true);
+                        Ops ops = getOpsLocked(uid, packageName, true);
                         if ((ops != null) && ops.isPrivileged) {
                             return false;
                         }
@@ -1610,7 +1582,7 @@
                         out.startTag(null, "uid");
                         out.attribute(null, "n", Integer.toString(pkg.getUid()));
                         synchronized (this) {
-                            Ops ops = getOpsRawLocked(pkg.getUid(), pkg.getPackageName(), false);
+                            Ops ops = getOpsLocked(pkg.getUid(), pkg.getPackageName(), false);
                             // Should always be present as the list of PackageOps is generated
                             // from Ops.
                             if (ops != null) {
@@ -2131,7 +2103,6 @@
     @Override
     public void setUserRestrictions(Bundle restrictions, IBinder token, int userHandle) {
         checkSystemUid("setUserRestrictions");
-        Preconditions.checkNotNull(restrictions);
         Preconditions.checkNotNull(token);
         final boolean[] opRestrictions = getOrCreateUserRestrictionsForToken(token, userHandle);
         for (int i = 0; i < opRestrictions.length; ++i) {
@@ -2346,15 +2317,6 @@
         }
     }
 
-    private static String resolvePackageName(int uid, String packageName)  {
-        if (uid == 0) {
-            return "root";
-        } else if (uid == Process.SHELL_UID) {
-            return "com.android.shell";
-        }
-        return packageName;
-    }
-
     private static String[] getPackagesForUid(int uid) {
         String[] packageNames = null;
         try {
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index b7fca1a..5b01062 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -3845,8 +3845,16 @@
     @Override
     public NetworkRequest requestNetwork(NetworkCapabilities networkCapabilities,
             Messenger messenger, int timeoutMs, IBinder binder, int legacyType) {
-        networkCapabilities = new NetworkCapabilities(networkCapabilities);
-        enforceNetworkRequestPermissions(networkCapabilities);
+        // If the requested networkCapabilities is null, take them instead from
+        // the default network request. This allows callers to keep track of
+        // the system default network.
+        if (networkCapabilities == null) {
+            networkCapabilities = new NetworkCapabilities(mDefaultRequest.networkCapabilities);
+            enforceAccessPermission();
+        } else {
+            networkCapabilities = new NetworkCapabilities(networkCapabilities);
+            enforceNetworkRequestPermissions(networkCapabilities);
+        }
         enforceMeteredApnPolicy(networkCapabilities);
         ensureRequestableCapabilities(networkCapabilities);
 
diff --git a/services/core/java/com/android/server/MountService.java b/services/core/java/com/android/server/MountService.java
index 65a22b9..ccca5ba 100644
--- a/services/core/java/com/android/server/MountService.java
+++ b/services/core/java/com/android/server/MountService.java
@@ -1946,6 +1946,10 @@
                 throw new IllegalStateException(
                         "Emulation not available on device with native FBE");
             }
+            if (mLockPatternUtils.isCredentialRequiredToDecrypt(false)) {
+                throw new IllegalStateException(
+                        "Emulation requires disabling 'Secure start-up' in Settings > Security");
+            }
 
             final long token = Binder.clearCallingIdentity();
             try {
diff --git a/services/core/java/com/android/server/NetworkManagementService.java b/services/core/java/com/android/server/NetworkManagementService.java
index 07c10b0..548b662 100644
--- a/services/core/java/com/android/server/NetworkManagementService.java
+++ b/services/core/java/com/android/server/NetworkManagementService.java
@@ -1149,81 +1149,6 @@
     }
 
     @Override
-    public RouteInfo[] getRoutes(String interfaceName) {
-        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
-        ArrayList<RouteInfo> routes = new ArrayList<RouteInfo>();
-
-        // v4 routes listed as:
-        // iface dest-addr gateway-addr flags refcnt use metric netmask mtu window IRTT
-        for (String s : readRouteList("/proc/net/route")) {
-            String[] fields = s.split("\t");
-
-            if (fields.length > 7) {
-                String iface = fields[0];
-
-                if (interfaceName.equals(iface)) {
-                    String dest = fields[1];
-                    String gate = fields[2];
-                    String flags = fields[3]; // future use?
-                    String mask = fields[7];
-                    try {
-                        // address stored as a hex string, ex: 0014A8C0
-                        InetAddress destAddr =
-                                NetworkUtils.intToInetAddress((int)Long.parseLong(dest, 16));
-                        int prefixLength =
-                                NetworkUtils.netmaskIntToPrefixLength(
-                                (int)Long.parseLong(mask, 16));
-                        LinkAddress linkAddress = new LinkAddress(destAddr, prefixLength);
-
-                        // address stored as a hex string, ex 0014A8C0
-                        InetAddress gatewayAddr =
-                                NetworkUtils.intToInetAddress((int)Long.parseLong(gate, 16));
-
-                        RouteInfo route = new RouteInfo(linkAddress, gatewayAddr);
-                        routes.add(route);
-                    } catch (Exception e) {
-                        Log.e(TAG, "Error parsing route " + s + " : " + e);
-                        continue;
-                    }
-                }
-            }
-        }
-
-        // v6 routes listed as:
-        // dest-addr prefixlength ?? ?? gateway-addr ?? ?? ?? ?? iface
-        for (String s : readRouteList("/proc/net/ipv6_route")) {
-            String[]fields = s.split("\\s+");
-            if (fields.length > 9) {
-                String iface = fields[9].trim();
-                if (interfaceName.equals(iface)) {
-                    String dest = fields[0];
-                    String prefix = fields[1];
-                    String gate = fields[4];
-
-                    try {
-                        // prefix length stored as a hex string, ex 40
-                        int prefixLength = Integer.parseInt(prefix, 16);
-
-                        // address stored as a 32 char hex string
-                        // ex fe800000000000000000000000000000
-                        InetAddress destAddr = NetworkUtils.hexToInet6Address(dest);
-                        LinkAddress linkAddress = new LinkAddress(destAddr, prefixLength);
-
-                        InetAddress gateAddr = NetworkUtils.hexToInet6Address(gate);
-
-                        RouteInfo route = new RouteInfo(linkAddress, gateAddr);
-                        routes.add(route);
-                    } catch (Exception e) {
-                        Log.e(TAG, "Error parsing route " + s + " : " + e);
-                        continue;
-                    }
-                }
-            }
-        }
-        return routes.toArray(new RouteInfo[routes.size()]);
-    }
-
-    @Override
     public void setMtu(String iface, int mtu) {
         mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
 
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index 63a0e87..f51fb6c 100755
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -811,10 +811,8 @@
             // Hacky kind of thing -- allow system stuff to tell us
             // what they are, so we can report this elsewhere for
             // others to know why certain services are running.
-            try {
-                clientIntent = service.getParcelableExtra(Intent.EXTRA_CLIENT_INTENT);
-            } catch (RuntimeException e) {
-            }
+            service.setDefusable(true);
+            clientIntent = service.getParcelableExtra(Intent.EXTRA_CLIENT_INTENT);
             if (clientIntent != null) {
                 clientLabel = service.getIntExtra(Intent.EXTRA_CLIENT_LABEL, 0);
                 if (clientLabel != 0) {
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 5aac43d..c1ec71c 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -6923,6 +6923,7 @@
                 intents[i].setDefusable(true);
             }
         }
+        Bundle.setDefusable(bOptions, true);
 
         final boolean noCreate = (flags&PendingIntent.FLAG_NO_CREATE) != 0;
         final boolean cancelCurrent = (flags&PendingIntent.FLAG_CANCEL_CURRENT) != 0;
@@ -9673,14 +9674,14 @@
 
     @Override
     public void resizeStack(int stackId, Rect bounds, boolean allowResizeInDockedMode,
-            boolean preserveWindows, boolean animate) {
+            boolean preserveWindows, boolean animate, int animationDuration) {
         enforceCallingPermission(MANAGE_ACTIVITY_STACKS, "resizeStack()");
         long ident = Binder.clearCallingIdentity();
         try {
             synchronized (this) {
                 if (animate) {
                     if (stackId == PINNED_STACK_ID) {
-                        mWindowManager.animateResizePinnedStack(bounds);
+                        mWindowManager.animateResizePinnedStack(bounds, animationDuration);
                     } else {
                         throw new IllegalArgumentException("Stack: " + stackId
                                 + " doesn't support animated resize.");
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index d364d85..7def1bd 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -2408,7 +2408,7 @@
         ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
         resumeFocusedStackTopActivityLocked();
 
-        mWindowManager.animateResizePinnedStack(bounds);
+        mWindowManager.animateResizePinnedStack(bounds, -1);
         mService.notifyActivityPinnedLocked();
     }
 
diff --git a/services/core/java/com/android/server/am/ActivityStarter.java b/services/core/java/com/android/server/am/ActivityStarter.java
index efa7420..af69c93 100644
--- a/services/core/java/com/android/server/am/ActivityStarter.java
+++ b/services/core/java/com/android/server/am/ActivityStarter.java
@@ -1483,7 +1483,8 @@
             if (mLaunchBounds != null) {
                 final int stackId = mTargetStack.mStackId;
                 if (StackId.resizeStackWithLaunchBounds(stackId)) {
-                    mService.resizeStack(stackId, mLaunchBounds, true, !PRESERVE_WINDOWS, ANIMATE);
+                    mService.resizeStack(
+                            stackId, mLaunchBounds, true, !PRESERVE_WINDOWS, ANIMATE, -1);
                 } else {
                     mStartActivity.task.updateOverrideConfiguration(mLaunchBounds);
                 }
@@ -1571,7 +1572,7 @@
                 stackId = stack.mStackId;
             }
             if (StackId.resizeStackWithLaunchBounds(stackId)) {
-                mService.resizeStack(stackId, mLaunchBounds, true, !PRESERVE_WINDOWS, ANIMATE);
+                mService.resizeStack(stackId, mLaunchBounds, true, !PRESERVE_WINDOWS, ANIMATE, -1);
             }
         }
         mTargetStack = mInTask.stack;
diff --git a/services/core/java/com/android/server/am/PendingIntentRecord.java b/services/core/java/com/android/server/am/PendingIntentRecord.java
index 8039072..b8f45bc 100644
--- a/services/core/java/com/android/server/am/PendingIntentRecord.java
+++ b/services/core/java/com/android/server/am/PendingIntentRecord.java
@@ -208,7 +208,10 @@
             String requiredPermission, IBinder resultTo, String resultWho, int requestCode,
             int flagsMask, int flagsValues, Bundle options, IActivityContainer container)
             throws TransactionTooLargeException {
-        synchronized(owner) {
+        if (intent != null) intent.setDefusable(true);
+        if (options != null) options.setDefusable(true);
+
+        synchronized (owner) {
             final ActivityContainer activityContainer = (ActivityContainer)container;
             if (activityContainer != null && activityContainer.mParentActivity != null &&
                     activityContainer.mParentActivity.state
diff --git a/services/core/java/com/android/server/connectivity/ApfFilter.java b/services/core/java/com/android/server/connectivity/ApfFilter.java
index d62a0b3..0014665 100644
--- a/services/core/java/com/android/server/connectivity/ApfFilter.java
+++ b/services/core/java/com/android/server/connectivity/ApfFilter.java
@@ -90,6 +90,7 @@
     }
 
     private static final String TAG = "ApfFilter";
+    private static final boolean DBG = true;
     private static final boolean VDBG = false;
 
     private final ConnectivityService mConnectivityService;
@@ -205,6 +206,10 @@
         // For debugging only. How many times this RA was seen.
         int seenCount = 0;
 
+        // For debugging only. Returns the hex representation of the last matching packet.
+        String getLastMatchingPacket() {
+            return HexDump.toHexString(mPacket.array(), 0, mPacket.capacity(), false /* lowercase */);
+        }
 
         private String IPv6AddresstoString(int pos) {
             try {
@@ -454,7 +459,7 @@
     private long mLastInstalledProgramMinLifetime;
 
     // For debugging only. The length in bytes of the last program.
-    private long mLastInstalledProgramLength;
+    private byte[] mLastInstalledProgram;
 
     private void installNewProgram() {
         if (mRas.size() == 0) return;
@@ -495,7 +500,7 @@
         }
         mLastTimeInstalledProgram = curTime();
         mLastInstalledProgramMinLifetime = programMinLifetime;
-        mLastInstalledProgramLength = program.length;
+        mLastInstalledProgram = program;
         if (VDBG) {
             hexDump("Installing filter: ", program, program.length);
         } else {
@@ -515,7 +520,7 @@
     }
 
     private void hexDump(String msg, byte[] packet, int length) {
-        log(msg + HexDump.toHexString(packet, 0, length));
+        log(msg + HexDump.toHexString(packet, 0, length, false /* lowercase */));
     }
 
     private void processRa(byte[] packet, int length) {
@@ -608,7 +613,7 @@
 
         pw.println(String.format(
                 "Last program length %d, installed %ds ago, lifetime %d",
-                mLastInstalledProgramLength, curTime() - mLastTimeInstalledProgram,
+                mLastInstalledProgram.length, curTime() - mLastTimeInstalledProgram,
                 mLastInstalledProgramMinLifetime));
 
         pw.println("RA filters:");
@@ -618,8 +623,22 @@
             pw.increaseIndent();
             pw.println(String.format(
                     "Seen: %d, last %ds ago", ra.seenCount, curTime() - ra.mLastSeen));
+            if (DBG) {
+                pw.println("Last match:");
+                pw.increaseIndent();
+                pw.println(ra.getLastMatchingPacket());
+                pw.decreaseIndent();
+            }
             pw.decreaseIndent();
         }
+
+        if (DBG) {
+            pw.println("Last program:");
+            pw.increaseIndent();
+            pw.println(HexDump.toHexString(mLastInstalledProgram, false /* lowercase */));
+            pw.decreaseIndent();
+        }
+
         pw.decreaseIndent();
     }
 }
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index 0b4f7f0..49ae293 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -28,13 +28,12 @@
 import static android.content.Intent.ACTION_USER_ADDED;
 import static android.content.Intent.ACTION_USER_REMOVED;
 import static android.content.Intent.EXTRA_UID;
-
 import static android.net.ConnectivityManager.CONNECTIVITY_ACTION;
-import static android.net.ConnectivityManager.TYPE_MOBILE;
-import static android.net.ConnectivityManager.TYPE_WIMAX;
 import static android.net.ConnectivityManager.RESTRICT_BACKGROUND_STATUS_DISABLED;
 import static android.net.ConnectivityManager.RESTRICT_BACKGROUND_STATUS_ENABLED;
 import static android.net.ConnectivityManager.RESTRICT_BACKGROUND_STATUS_WHITELISTED;
+import static android.net.ConnectivityManager.TYPE_MOBILE;
+import static android.net.ConnectivityManager.TYPE_WIMAX;
 import static android.net.ConnectivityManager.isNetworkTypeMobile;
 import static android.net.NetworkPolicy.CYCLE_NONE;
 import static android.net.NetworkPolicy.LIMIT_DISABLED;
@@ -47,11 +46,9 @@
 import static android.net.NetworkPolicyManager.FIREWALL_RULE_ALLOW;
 import static android.net.NetworkPolicyManager.FIREWALL_RULE_DEFAULT;
 import static android.net.NetworkPolicyManager.FIREWALL_RULE_DENY;
-import static android.net.NetworkPolicyManager.POLICY_ALLOW_BACKGROUND_BATTERY_SAVE;
 import static android.net.NetworkPolicyManager.POLICY_NONE;
 import static android.net.NetworkPolicyManager.POLICY_REJECT_METERED_BACKGROUND;
 import static android.net.NetworkPolicyManager.RULE_ALLOW_ALL;
-import static android.net.NetworkPolicyManager.RULE_REJECT_ALL;
 import static android.net.NetworkPolicyManager.RULE_REJECT_METERED;
 import static android.net.NetworkPolicyManager.RULE_UNKNOWN;
 import static android.net.NetworkPolicyManager.computeLastCycleBoundary;
@@ -80,6 +77,7 @@
 import static com.android.internal.util.XmlUtils.writeLongAttribute;
 import static com.android.server.NetworkManagementService.LIMIT_GLOBAL_ALERT;
 import static com.android.server.net.NetworkStatsService.ACTION_NETWORK_STATS_UPDATED;
+
 import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT;
 import static org.xmlpull.v1.XmlPullParser.END_TAG;
 import static org.xmlpull.v1.XmlPullParser.START_TAG;
@@ -130,12 +128,12 @@
 import android.os.INetworkManagementService;
 import android.os.IPowerManager;
 import android.os.Message;
-import android.os.ResultReceiver;
 import android.os.MessageQueue.IdleHandler;
 import android.os.PowerManager;
 import android.os.PowerManagerInternal;
 import android.os.RemoteCallbackList;
 import android.os.RemoteException;
+import android.os.ResultReceiver;
 import android.os.ServiceManager;
 import android.os.UserHandle;
 import android.os.UserManager;
@@ -157,8 +155,6 @@
 import android.util.TrustedTime;
 import android.util.Xml;
 
-import libcore.io.IoUtils;
-
 import com.android.internal.R;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.content.PackageMonitor;
@@ -169,6 +165,9 @@
 import com.android.server.EventLogTags;
 import com.android.server.LocalServices;
 import com.android.server.SystemConfig;
+
+import libcore.io.IoUtils;
+
 import com.google.android.collect.Lists;
 
 import org.xmlpull.v1.XmlPullParser;
@@ -1446,9 +1445,11 @@
 
                         final NetworkTemplate template = new NetworkTemplate(networkTemplate,
                                 subscriberId, networkId);
-                        mNetworkPolicy.put(template, new NetworkPolicy(template, cycleDay,
-                                cycleTimezone, warningBytes, limitBytes, lastWarningSnooze,
-                                lastLimitSnooze, metered, inferred));
+                        if (template.isPersistable()) {
+                            mNetworkPolicy.put(template, new NetworkPolicy(template, cycleDay,
+                                    cycleTimezone, warningBytes, limitBytes, lastWarningSnooze,
+                                    lastLimitSnooze, metered, inferred));
+                        }
 
                     } else if (TAG_UID_POLICY.equals(tag)) {
                         final int uid = readIntAttribute(in, ATTR_UID);
@@ -1535,6 +1536,7 @@
             for (int i = 0; i < mNetworkPolicy.size(); i++) {
                 final NetworkPolicy policy = mNetworkPolicy.valueAt(i);
                 final NetworkTemplate template = policy.template;
+                if (!template.isPersistable()) continue;
 
                 out.startTag(null, TAG_NETWORK_POLICY);
                 writeIntAttribute(out, ATTR_NETWORK_TEMPLATE, template.getMatchRule());
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 274a73f..0f23fde 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -1852,16 +1852,11 @@
         }
 
         private boolean checkPolicyAccess(String pkg) {
-            if (PackageManager.PERMISSION_GRANTED == getContext().checkCallingPermission(
-                    android.Manifest.permission.MANAGE_NOTIFICATIONS)) {
+            if (PackageManager.PERMISSION_GRANTED == ActivityManager.checkComponentPermission(
+                    android.Manifest.permission.MANAGE_NOTIFICATIONS, Binder.getCallingUid(),
+                    -1, true)) {
                 return true;
             }
-            if (mAudioManagerInternal != null) {
-                final int vcuid = mAudioManagerInternal.getVolumeControllerUid();
-                if (vcuid > 0 && Binder.getCallingUid() == vcuid) {
-                    return true;
-                }
-            }
             return checkPackagePolicyAccess(pkg) || mListeners.isComponentEnabledForPackage(pkg);
         }
 
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index c2e0992..9335116 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -10451,10 +10451,10 @@
 
     void startCleaningPackages() {
         // reader
+        if (!isExternalMediaAvailable()) {
+            return;
+        }
         synchronized (mPackages) {
-            if (!isExternalMediaAvailable()) {
-                return;
-            }
             if (mSettings.mPackagesToBeCleaned.isEmpty()) {
                 return;
             }
diff --git a/services/core/java/com/android/server/pm/ShortcutLauncher.java b/services/core/java/com/android/server/pm/ShortcutLauncher.java
new file mode 100644
index 0000000..f1920c7
--- /dev/null
+++ b/services/core/java/com/android/server/pm/ShortcutLauncher.java
@@ -0,0 +1,199 @@
+/*
+ * Copyright (C) 2016 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.pm;
+
+import android.annotation.NonNull;
+import android.annotation.UserIdInt;
+import android.content.pm.ShortcutInfo;
+import android.util.ArrayMap;
+import android.util.ArraySet;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlSerializer;
+
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.List;
+
+/**
+ * Launcher information used by {@link ShortcutService}.
+ */
+class ShortcutLauncher {
+    private static final String TAG = ShortcutService.TAG;
+
+    static final String TAG_ROOT = "launcher-pins";
+
+    private static final String TAG_PACKAGE = "package";
+    private static final String TAG_PIN = "pin";
+
+    private static final String ATTR_VALUE = "value";
+    private static final String ATTR_PACKAGE_NAME = "package-name";
+
+    @UserIdInt
+    final int mUserId;
+
+    @NonNull
+    final String mPackageName;
+
+    /**
+     * Package name -> IDs.
+     */
+    final private ArrayMap<String, ArraySet<String>> mPinnedShortcuts = new ArrayMap<>();
+
+    ShortcutLauncher(@UserIdInt int userId, @NonNull String packageName) {
+        mUserId = userId;
+        mPackageName = packageName;
+    }
+
+    public void pinShortcuts(@NonNull ShortcutService s, @NonNull String packageName,
+            @NonNull List<String> ids) {
+        final int idSize = ids.size();
+        if (idSize == 0) {
+            mPinnedShortcuts.remove(packageName);
+        } else {
+            final ArraySet<String> prevSet = mPinnedShortcuts.get(packageName);
+
+            // Pin shortcuts.  Make sure only pin the ones that were visible to the caller.
+            // i.e. a non-dynamic, pinned shortcut by *other launchers* shouldn't be pinned here.
+
+            final ShortcutPackage packageShortcuts =
+                    s.getPackageShortcutsLocked(packageName, mUserId);
+            final ArraySet<String> newSet = new ArraySet<>();
+
+            for (int i = 0; i < idSize; i++) {
+                final String id = ids.get(i);
+                final ShortcutInfo si = packageShortcuts.findShortcutById(id);
+                if (si == null) {
+                    continue;
+                }
+                if (si.isDynamic() || (prevSet != null && prevSet.contains(id))) {
+                    newSet.add(id);
+                }
+            }
+            mPinnedShortcuts.put(packageName, newSet);
+        }
+        s.getPackageShortcutsLocked(packageName, mUserId).refreshPinnedFlags(s);
+    }
+
+    /**
+     * Return the pinned shortcut IDs for the publisher package.
+     */
+    public ArraySet<String> getPinnedShortcutIds(@NonNull String packageName) {
+        return mPinnedShortcuts.get(packageName);
+    }
+
+    boolean cleanUpPackage(String packageName) {
+        return mPinnedShortcuts.remove(packageName) != null;
+    }
+
+    /**
+     * Persist.
+     */
+    public void saveToXml(XmlSerializer out) throws IOException {
+        final int size = mPinnedShortcuts.size();
+        if (size == 0) {
+            return; // Nothing to write.
+        }
+
+        out.startTag(null, TAG_ROOT);
+        ShortcutService.writeAttr(out, ATTR_PACKAGE_NAME,
+                mPackageName);
+
+        for (int i = 0; i < size; i++) {
+            out.startTag(null, TAG_PACKAGE);
+            ShortcutService.writeAttr(out, ATTR_PACKAGE_NAME,
+                    mPinnedShortcuts.keyAt(i));
+
+            final ArraySet<String> ids = mPinnedShortcuts.valueAt(i);
+            final int idSize = ids.size();
+            for (int j = 0; j < idSize; j++) {
+                ShortcutService.writeTagValue(out, TAG_PIN, ids.valueAt(j));
+            }
+            out.endTag(null, TAG_PACKAGE);
+        }
+
+        out.endTag(null, TAG_ROOT);
+    }
+
+    /**
+     * Load.
+     */
+    public static ShortcutLauncher loadFromXml(XmlPullParser parser, int userId)
+            throws IOException, XmlPullParserException {
+        final String launcherPackageName = ShortcutService.parseStringAttribute(parser,
+                ATTR_PACKAGE_NAME);
+
+        final ShortcutLauncher ret = new ShortcutLauncher(userId, launcherPackageName);
+
+        ArraySet<String> ids = null;
+        final int outerDepth = parser.getDepth();
+        int type;
+        while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+                && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
+            if (type != XmlPullParser.START_TAG) {
+                continue;
+            }
+            final int depth = parser.getDepth();
+            final String tag = parser.getName();
+            switch (tag) {
+                case TAG_PACKAGE: {
+                    final String packageName = ShortcutService.parseStringAttribute(parser,
+                            ATTR_PACKAGE_NAME);
+                    ids = new ArraySet<>();
+                    ret.mPinnedShortcuts.put(packageName, ids);
+                    continue;
+                }
+                case TAG_PIN: {
+                    ids.add(ShortcutService.parseStringAttribute(parser,
+                            ATTR_VALUE));
+                    continue;
+                }
+            }
+            throw ShortcutService.throwForInvalidTag(depth, tag);
+        }
+        return ret;
+    }
+
+    public void dump(@NonNull ShortcutService s, @NonNull PrintWriter pw, @NonNull String prefix) {
+        pw.println();
+
+        pw.print(prefix);
+        pw.print("Launcher: ");
+        pw.print(mPackageName);
+        pw.println();
+
+        final int size = mPinnedShortcuts.size();
+        for (int i = 0; i < size; i++) {
+            pw.println();
+
+            pw.print(prefix);
+            pw.print("  ");
+            pw.print("Package: ");
+            pw.println(mPinnedShortcuts.keyAt(i));
+
+            final ArraySet<String> ids = mPinnedShortcuts.valueAt(i);
+            final int idSize = ids.size();
+
+            for (int j = 0; j < idSize; j++) {
+                pw.print(prefix);
+                pw.print("    ");
+                pw.print(ids.valueAt(j));
+                pw.println();
+            }
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/pm/ShortcutPackage.java b/services/core/java/com/android/server/pm/ShortcutPackage.java
new file mode 100644
index 0000000..d614251
--- /dev/null
+++ b/services/core/java/com/android/server/pm/ShortcutPackage.java
@@ -0,0 +1,520 @@
+/*
+ * Copyright (C) 2016 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.pm;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.UserIdInt;
+import android.content.ComponentName;
+import android.content.Intent;
+import android.content.pm.ShortcutInfo;
+import android.os.PersistableBundle;
+import android.text.format.Formatter;
+import android.util.ArrayMap;
+import android.util.ArraySet;
+import android.util.Slog;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlSerializer;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.function.Predicate;
+
+/**
+ * Package information used by {@link ShortcutService}.
+ */
+class ShortcutPackage {
+    private static final String TAG = ShortcutService.TAG;
+
+    static final String TAG_ROOT = "package";
+    private static final String TAG_INTENT_EXTRAS = "intent-extras";
+    private static final String TAG_EXTRAS = "extras";
+    private static final String TAG_SHORTCUT = "shortcut";
+
+    private static final String ATTR_NAME = "name";
+    private static final String ATTR_DYNAMIC_COUNT = "dynamic-count";
+    private static final String ATTR_CALL_COUNT = "call-count";
+    private static final String ATTR_LAST_RESET = "last-reset";
+    private static final String ATTR_ID = "id";
+    private static final String ATTR_ACTIVITY = "activity";
+    private static final String ATTR_TITLE = "title";
+    private static final String ATTR_INTENT = "intent";
+    private static final String ATTR_WEIGHT = "weight";
+    private static final String ATTR_TIMESTAMP = "timestamp";
+    private static final String ATTR_FLAGS = "flags";
+    private static final String ATTR_ICON_RES = "icon-res";
+    private static final String ATTR_BITMAP_PATH = "bitmap-path";
+
+    @UserIdInt
+    final int mUserId;
+
+    @NonNull
+    final String mPackageName;
+
+    /**
+     * All the shortcuts from the package, keyed on IDs.
+     */
+    final private ArrayMap<String, ShortcutInfo> mShortcuts = new ArrayMap<>();
+
+    /**
+     * # of dynamic shortcuts.
+     */
+    private int mDynamicShortcutCount = 0;
+
+    /**
+     * # of times the package has called rate-limited APIs.
+     */
+    private int mApiCallCount;
+
+    /**
+     * When {@link #mApiCallCount} was reset last time.
+     */
+    private long mLastResetTime;
+
+    ShortcutPackage(int userId, String packageName) {
+        mUserId = userId;
+        mPackageName = packageName;
+    }
+
+    @Nullable
+    public ShortcutInfo findShortcutById(String id) {
+        return mShortcuts.get(id);
+    }
+
+    private ShortcutInfo deleteShortcut(@NonNull ShortcutService s,
+            @NonNull String id) {
+        final ShortcutInfo shortcut = mShortcuts.remove(id);
+        if (shortcut != null) {
+            s.removeIcon(mUserId, shortcut);
+            shortcut.clearFlags(ShortcutInfo.FLAG_DYNAMIC | ShortcutInfo.FLAG_PINNED);
+        }
+        return shortcut;
+    }
+
+    void addShortcut(@NonNull ShortcutService s, @NonNull ShortcutInfo newShortcut) {
+        deleteShortcut(s, newShortcut.getId());
+        s.saveIconAndFixUpShortcut(mUserId, newShortcut);
+        mShortcuts.put(newShortcut.getId(), newShortcut);
+    }
+
+    /**
+     * Add a shortcut, or update one with the same ID, with taking over existing flags.
+     *
+     * It checks the max number of dynamic shortcuts.
+     */
+    public void addDynamicShortcut(@NonNull ShortcutService s,
+            @NonNull ShortcutInfo newShortcut) {
+        newShortcut.addFlags(ShortcutInfo.FLAG_DYNAMIC);
+
+        final ShortcutInfo oldShortcut = mShortcuts.get(newShortcut.getId());
+
+        final boolean wasPinned;
+        final int newDynamicCount;
+
+        if (oldShortcut == null) {
+            wasPinned = false;
+            newDynamicCount = mDynamicShortcutCount + 1; // adding a dynamic shortcut.
+        } else {
+            wasPinned = oldShortcut.isPinned();
+            if (oldShortcut.isDynamic()) {
+                newDynamicCount = mDynamicShortcutCount; // not adding a dynamic shortcut.
+            } else {
+                newDynamicCount = mDynamicShortcutCount + 1; // adding a dynamic shortcut.
+            }
+        }
+
+        // Make sure there's still room.
+        s.enforceMaxDynamicShortcuts(newDynamicCount);
+
+        // Okay, make it dynamic and add.
+        if (wasPinned) {
+            newShortcut.addFlags(ShortcutInfo.FLAG_PINNED);
+        }
+
+        addShortcut(s, newShortcut);
+        mDynamicShortcutCount = newDynamicCount;
+    }
+
+    /**
+     * Remove all shortcuts that aren't pinned nor dynamic.
+     */
+    private void removeOrphans(@NonNull ShortcutService s) {
+        ArrayList<String> removeList = null; // Lazily initialize.
+
+        for (int i = mShortcuts.size() - 1; i >= 0; i--) {
+            final ShortcutInfo si = mShortcuts.valueAt(i);
+
+            if (si.isPinned() || si.isDynamic()) continue;
+
+            if (removeList == null) {
+                removeList = new ArrayList<>();
+            }
+            removeList.add(si.getId());
+        }
+        if (removeList != null) {
+            for (int i = removeList.size() - 1; i >= 0; i--) {
+                deleteShortcut(s, removeList.get(i));
+            }
+        }
+    }
+
+    /**
+     * Remove all dynamic shortcuts.
+     */
+    public void deleteAllDynamicShortcuts(@NonNull ShortcutService s) {
+        for (int i = mShortcuts.size() - 1; i >= 0; i--) {
+            mShortcuts.valueAt(i).clearFlags(ShortcutInfo.FLAG_DYNAMIC);
+        }
+        removeOrphans(s);
+        mDynamicShortcutCount = 0;
+    }
+
+    /**
+     * Remove a dynamic shortcut by ID.
+     */
+    public void deleteDynamicWithId(@NonNull ShortcutService s, @NonNull String shortcutId) {
+        final ShortcutInfo oldShortcut = mShortcuts.get(shortcutId);
+
+        if (oldShortcut == null) {
+            return;
+        }
+        if (oldShortcut.isDynamic()) {
+            mDynamicShortcutCount--;
+        }
+        if (oldShortcut.isPinned()) {
+            oldShortcut.clearFlags(ShortcutInfo.FLAG_DYNAMIC);
+        } else {
+            deleteShortcut(s, shortcutId);
+        }
+    }
+
+    /**
+     * Called after a launcher updates the pinned set.  For each shortcut in this package,
+     * set FLAG_PINNED if any launcher has pinned it.  Otherwise, clear it.
+     *
+     * <p>Then remove all shortcuts that are not dynamic and no longer pinned either.
+     */
+    public void refreshPinnedFlags(@NonNull ShortcutService s) {
+        // First, un-pin all shortcuts
+        for (int i = mShortcuts.size() - 1; i >= 0; i--) {
+            mShortcuts.valueAt(i).clearFlags(ShortcutInfo.FLAG_PINNED);
+        }
+
+        // Then, for the pinned set for each launcher, set the pin flag one by one.
+        final ArrayMap<String, ShortcutLauncher> launchers =
+                s.getUserShortcutsLocked(mUserId).getLaunchers();
+
+        for (int l = launchers.size() - 1; l >= 0; l--) {
+            final ShortcutLauncher launcherShortcuts = launchers.valueAt(l);
+            final ArraySet<String> pinned = launcherShortcuts.getPinnedShortcutIds(mPackageName);
+
+            if (pinned == null || pinned.size() == 0) {
+                continue;
+            }
+            for (int i = pinned.size() - 1; i >= 0; i--) {
+                final ShortcutInfo si = mShortcuts.get(pinned.valueAt(i));
+                if (si == null) {
+                    s.wtf("Shortcut not found");
+                } else {
+                    si.addFlags(ShortcutInfo.FLAG_PINNED);
+                }
+            }
+        }
+
+        // Lastly, remove the ones that are no longer pinned nor dynamic.
+        removeOrphans(s);
+    }
+
+    /**
+     * Number of calls that the caller has made, since the last reset.
+     */
+    public int getApiCallCount(@NonNull ShortcutService s) {
+        final long last = s.getLastResetTimeLocked();
+
+        final long now = s.injectCurrentTimeMillis();
+        if (ShortcutService.isClockValid(now) && mLastResetTime > now) {
+            Slog.w(TAG, "Clock rewound");
+            // Clock rewound.
+            mLastResetTime = now;
+            mApiCallCount = 0;
+            return mApiCallCount;
+        }
+
+        // If not reset yet, then reset.
+        if (mLastResetTime < last) {
+            if (ShortcutService.DEBUG) {
+                Slog.d(TAG, String.format("My last reset=%d, now=%d, last=%d: resetting",
+                        mLastResetTime, now, last));
+            }
+            mApiCallCount = 0;
+            mLastResetTime = last;
+        }
+        return mApiCallCount;
+    }
+
+    /**
+     * If the caller app hasn't been throttled yet, increment {@link #mApiCallCount}
+     * and return true.  Otherwise just return false.
+     */
+    public boolean tryApiCall(@NonNull ShortcutService s) {
+        if (getApiCallCount(s) >= s.mMaxDailyUpdates) {
+            return false;
+        }
+        mApiCallCount++;
+        return true;
+    }
+
+    public void resetRateLimitingForCommandLine() {
+        mApiCallCount = 0;
+        mLastResetTime = 0;
+    }
+
+    /**
+     * Find all shortcuts that match {@code query}.
+     */
+    public void findAll(@NonNull ShortcutService s, @NonNull List<ShortcutInfo> result,
+            @Nullable Predicate<ShortcutInfo> query, int cloneFlag,
+            @Nullable String callingLauncher) {
+
+        // Set of pinned shortcuts by the calling launcher.
+        final ArraySet<String> pinnedByCallerSet = (callingLauncher == null) ? null
+                : s.getLauncherShortcuts(callingLauncher, mUserId)
+                    .getPinnedShortcutIds(mPackageName);
+
+        for (int i = 0; i < mShortcuts.size(); i++) {
+            final ShortcutInfo si = mShortcuts.valueAt(i);
+
+            // If it's called by non-launcher (i.e. publisher, always include -> true.
+            // Otherwise, only include non-dynamic pinned one, if the calling launcher has pinned
+            // it.
+            final boolean isPinnedByCaller = (callingLauncher == null)
+                    || ((pinnedByCallerSet != null) && pinnedByCallerSet.contains(si.getId()));
+            if (!si.isDynamic()) {
+                if (!si.isPinned()) {
+                    s.wtf("Shortcut not pinned here");
+                    continue;
+                }
+                if (!isPinnedByCaller) {
+                    continue;
+                }
+            }
+            final ShortcutInfo clone = si.clone(cloneFlag);
+            // Fix up isPinned for the caller.  Note we need to do it before the "test" callback,
+            // since it may check isPinned.
+            if (!isPinnedByCaller) {
+                clone.clearFlags(ShortcutInfo.FLAG_PINNED);
+            }
+            if (query == null || query.test(clone)) {
+                result.add(clone);
+            }
+        }
+    }
+
+    public void resetThrottling() {
+        mApiCallCount = 0;
+    }
+
+    public void dump(@NonNull ShortcutService s, @NonNull PrintWriter pw, @NonNull String prefix) {
+        pw.println();
+
+        pw.print(prefix);
+        pw.print("Package: ");
+        pw.print(mPackageName);
+        pw.println();
+
+        pw.print(prefix);
+        pw.print("  ");
+        pw.print("Calls: ");
+        pw.print(getApiCallCount(s));
+        pw.println();
+
+        // This should be after getApiCallCount(), which may update it.
+        pw.print(prefix);
+        pw.print("  ");
+        pw.print("Last reset: [");
+        pw.print(mLastResetTime);
+        pw.print("] ");
+        pw.print(s.formatTime(mLastResetTime));
+        pw.println();
+
+        pw.println("      Shortcuts:");
+        long totalBitmapSize = 0;
+        final ArrayMap<String, ShortcutInfo> shortcuts = mShortcuts;
+        final int size = shortcuts.size();
+        for (int i = 0; i < size; i++) {
+            final ShortcutInfo si = shortcuts.valueAt(i);
+            pw.print("        ");
+            pw.println(si.toInsecureString());
+            if (si.getBitmapPath() != null) {
+                final long len = new File(si.getBitmapPath()).length();
+                pw.print("          ");
+                pw.print("bitmap size=");
+                pw.println(len);
+
+                totalBitmapSize += len;
+            }
+        }
+        pw.print(prefix);
+        pw.print("  ");
+        pw.print("Total bitmap size: ");
+        pw.print(totalBitmapSize);
+        pw.print(" (");
+        pw.print(Formatter.formatFileSize(s.mContext, totalBitmapSize));
+        pw.println(")");
+    }
+
+    public void saveToXml(@NonNull XmlSerializer out) throws IOException, XmlPullParserException {
+        final int size = mShortcuts.size();
+
+        if (size == 0 && mApiCallCount == 0) {
+            return; // nothing to write.
+        }
+
+        out.startTag(null, TAG_ROOT);
+
+        ShortcutService.writeAttr(out, ATTR_NAME, mPackageName);
+        ShortcutService.writeAttr(out, ATTR_DYNAMIC_COUNT, mDynamicShortcutCount);
+        ShortcutService.writeAttr(out, ATTR_CALL_COUNT, mApiCallCount);
+        ShortcutService.writeAttr(out, ATTR_LAST_RESET, mLastResetTime);
+
+        for (int j = 0; j < size; j++) {
+            saveShortcut(out, mShortcuts.valueAt(j));
+        }
+
+        out.endTag(null, TAG_ROOT);
+    }
+
+    private static void saveShortcut(XmlSerializer out, ShortcutInfo si)
+            throws IOException, XmlPullParserException {
+        out.startTag(null, TAG_SHORTCUT);
+        ShortcutService.writeAttr(out, ATTR_ID, si.getId());
+        // writeAttr(out, "package", si.getPackageName()); // not needed
+        ShortcutService.writeAttr(out, ATTR_ACTIVITY, si.getActivityComponent());
+        // writeAttr(out, "icon", si.getIcon());  // We don't save it.
+        ShortcutService.writeAttr(out, ATTR_TITLE, si.getTitle());
+        ShortcutService.writeAttr(out, ATTR_INTENT, si.getIntentNoExtras());
+        ShortcutService.writeAttr(out, ATTR_WEIGHT, si.getWeight());
+        ShortcutService.writeAttr(out, ATTR_TIMESTAMP,
+                si.getLastChangedTimestamp());
+        ShortcutService.writeAttr(out, ATTR_FLAGS, si.getFlags());
+        ShortcutService.writeAttr(out, ATTR_ICON_RES, si.getIconResourceId());
+        ShortcutService.writeAttr(out, ATTR_BITMAP_PATH, si.getBitmapPath());
+
+        ShortcutService.writeTagExtra(out, TAG_INTENT_EXTRAS,
+                si.getIntentPersistableExtras());
+        ShortcutService.writeTagExtra(out, TAG_EXTRAS, si.getExtras());
+
+        out.endTag(null, TAG_SHORTCUT);
+    }
+
+    public static ShortcutPackage loadFromXml(XmlPullParser parser, int userId)
+            throws IOException, XmlPullParserException {
+
+        final String packageName = ShortcutService.parseStringAttribute(parser,
+                ATTR_NAME);
+
+        final ShortcutPackage ret = new ShortcutPackage(userId, packageName);
+
+        ret.mDynamicShortcutCount =
+                ShortcutService.parseIntAttribute(parser, ATTR_DYNAMIC_COUNT);
+        ret.mApiCallCount =
+                ShortcutService.parseIntAttribute(parser, ATTR_CALL_COUNT);
+        ret.mLastResetTime =
+                ShortcutService.parseLongAttribute(parser, ATTR_LAST_RESET);
+
+        final int outerDepth = parser.getDepth();
+        int type;
+        while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+                && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
+            if (type != XmlPullParser.START_TAG) {
+                continue;
+            }
+            final int depth = parser.getDepth();
+            final String tag = parser.getName();
+            switch (tag) {
+                case TAG_SHORTCUT:
+                    final ShortcutInfo si = parseShortcut(parser, packageName);
+
+                    // Don't use addShortcut(), we don't need to save the icon.
+                    ret.mShortcuts.put(si.getId(), si);
+                    continue;
+            }
+            throw ShortcutService.throwForInvalidTag(depth, tag);
+        }
+        return ret;
+    }
+
+    private static ShortcutInfo parseShortcut(XmlPullParser parser, String packageName)
+            throws IOException, XmlPullParserException {
+        String id;
+        ComponentName activityComponent;
+        // Icon icon;
+        String title;
+        Intent intent;
+        PersistableBundle intentPersistableExtras = null;
+        int weight;
+        PersistableBundle extras = null;
+        long lastChangedTimestamp;
+        int flags;
+        int iconRes;
+        String bitmapPath;
+
+        id = ShortcutService.parseStringAttribute(parser, ATTR_ID);
+        activityComponent = ShortcutService.parseComponentNameAttribute(parser,
+                ATTR_ACTIVITY);
+        title = ShortcutService.parseStringAttribute(parser, ATTR_TITLE);
+        intent = ShortcutService.parseIntentAttribute(parser, ATTR_INTENT);
+        weight = (int) ShortcutService.parseLongAttribute(parser, ATTR_WEIGHT);
+        lastChangedTimestamp = (int) ShortcutService.parseLongAttribute(parser,
+                ATTR_TIMESTAMP);
+        flags = (int) ShortcutService.parseLongAttribute(parser, ATTR_FLAGS);
+        iconRes = (int) ShortcutService.parseLongAttribute(parser, ATTR_ICON_RES);
+        bitmapPath = ShortcutService.parseStringAttribute(parser, ATTR_BITMAP_PATH);
+
+        final int outerDepth = parser.getDepth();
+        int type;
+        while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+                && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
+            if (type != XmlPullParser.START_TAG) {
+                continue;
+            }
+            final int depth = parser.getDepth();
+            final String tag = parser.getName();
+            if (ShortcutService.DEBUG_LOAD) {
+                Slog.d(TAG, String.format("  depth=%d type=%d name=%s",
+                        depth, type, tag));
+            }
+            switch (tag) {
+                case TAG_INTENT_EXTRAS:
+                    intentPersistableExtras = PersistableBundle.restoreFromXml(parser);
+                    continue;
+                case TAG_EXTRAS:
+                    extras = PersistableBundle.restoreFromXml(parser);
+                    continue;
+            }
+            throw ShortcutService.throwForInvalidTag(depth, tag);
+        }
+        return new ShortcutInfo(
+                id, packageName, activityComponent, /* icon =*/ null, title, intent,
+                intentPersistableExtras, weight, extras, lastChangedTimestamp, flags,
+                iconRes, bitmapPath);
+    }
+}
diff --git a/services/core/java/com/android/server/pm/ShortcutService.java b/services/core/java/com/android/server/pm/ShortcutService.java
index e831bb1..42954f5 100644
--- a/services/core/java/com/android/server/pm/ShortcutService.java
+++ b/services/core/java/com/android/server/pm/ShortcutService.java
@@ -53,6 +53,7 @@
 import android.os.SELinux;
 import android.os.ShellCommand;
 import android.os.UserHandle;
+import android.os.UserManager;
 import android.text.TextUtils;
 import android.text.format.Formatter;
 import android.text.format.Time;
@@ -67,6 +68,7 @@
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.content.PackageMonitor;
 import com.android.internal.os.BackgroundThread;
 import com.android.internal.util.FastXmlSerializer;
 import com.android.internal.util.Preconditions;
@@ -99,6 +101,11 @@
  *
  * - Default launcher check does take a few ms.  Worth caching.
  *
+ * - Allow non-default launcher to start pinned shortcuts. (but not dynamic.)
+ *
+ * - Extract the user/package/launcher classes to their own files.  Maybe rename so they all have
+ *   the same "Shortcut" prefix.
+ *
  * - Listen to PACKAGE_*, remove orphan info, update timestamp for icon res
  *   -> Need to scan all packages when a user starts too.
  *   -> Clear data -> remove all dynamic?  but not the pinned?
@@ -215,7 +222,7 @@
      * User ID -> UserShortcuts
      */
     @GuardedBy("mLock")
-    private final SparseArray<UserShortcuts> mUsers = new SparseArray<>();
+    private final SparseArray<ShortcutUser> mUsers = new SparseArray<>();
 
     /**
      * Max number of dynamic shortcuts that each application can have at a time.
@@ -243,6 +250,7 @@
     private int mSaveDelayMillis;
 
     private final PackageManagerInternal mPackageManagerInternal;
+    private final UserManager mUserManager;
 
     @GuardedBy("mLock")
     private List<Integer> mDirtyUserIds = new ArrayList<>();
@@ -257,6 +265,9 @@
         LocalServices.addService(ShortcutServiceInternal.class, new LocalService());
         mHandler = new Handler(looper);
         mPackageManagerInternal = LocalServices.getService(PackageManagerInternal.class);
+        mUserManager = context.getSystemService(UserManager.class);
+
+        mPackageMonitor.register(context, looper, UserHandle.ALL, /* externalStorage= */ false);
     }
 
     /**
@@ -282,16 +293,12 @@
 
         @Override
         public void onCleanupUser(int userHandle) {
-            synchronized (mService.mLock) {
-                mService.onCleanupUserLocked(userHandle);
-            }
+            mService.handleCleanupUser(userHandle);
         }
 
         @Override
         public void onUnlockUser(int userId) {
-            synchronized (mService.mLock) {
-                mService.onStartUserLocked(userId);
-            }
+            mService.handleUnlockUser(userId);
         }
     }
 
@@ -308,13 +315,24 @@
     }
 
     /** lifecycle event */
-    void onStartUserLocked(int userId) {
-        // Preload
-        getUserShortcutsLocked(userId);
+    void handleUnlockUser(int userId) {
+        synchronized (mLock) {
+            // Preload
+            getUserShortcutsLocked(userId);
+        }
     }
 
     /** lifecycle event */
-    void onCleanupUserLocked(int userId) {
+    void handleCleanupUser(int userId) {
+        synchronized (mLock) {
+            unloadUserLocked(userId);
+        }
+    }
+
+    private void unloadUserLocked(int userId) {
+        if (DEBUG) {
+            Slog.d(TAG, "unloadUserLocked: user=" + userId);
+        }
         // Save all dirty information.
         saveDirtyInfo();
 
@@ -615,7 +633,7 @@
     }
 
     @Nullable
-    private UserShortcuts loadUserLocked(@UserIdInt int userId) {
+    private ShortcutUser loadUserLocked(@UserIdInt int userId) {
         final File path = new File(injectUserDataPath(userId), FILENAME_USER_PACKAGES);
         if (DEBUG) {
             Slog.d(TAG, "Loading from " + path);
@@ -631,7 +649,7 @@
             }
             return null;
         }
-        UserShortcuts ret = null;
+        ShortcutUser ret = null;
         try {
             XmlPullParser parser = Xml.newPullParser();
             parser.setInput(in, StandardCharsets.UTF_8.name());
@@ -648,8 +666,8 @@
                     Slog.d(TAG, String.format("depth=%d type=%d name=%s",
                             depth, type, tag));
                 }
-                if ((depth == 1) && UserShortcuts.TAG_ROOT.equals(tag)) {
-                    ret = UserShortcuts.loadFromXml(parser, userId);
+                if ((depth == 1) && ShortcutUser.TAG_ROOT.equals(tag)) {
+                    ret = ShortcutUser.loadFromXml(parser, userId);
                     continue;
                 }
                 throwForInvalidTag(depth, tag);
@@ -752,15 +770,21 @@
         }
     }
 
+    @GuardedBy("mLock")
+    @NonNull
+    boolean isUserLoadedLocked(@UserIdInt int userId) {
+        return mUsers.get(userId) != null;
+    }
+
     /** Return the per-user state. */
     @GuardedBy("mLock")
     @NonNull
-    UserShortcuts getUserShortcutsLocked(@UserIdInt int userId) {
-        UserShortcuts userPackages = mUsers.get(userId);
+    ShortcutUser getUserShortcutsLocked(@UserIdInt int userId) {
+        ShortcutUser userPackages = mUsers.get(userId);
         if (userPackages == null) {
             userPackages = loadUserLocked(userId);
             if (userPackages == null) {
-                userPackages = new UserShortcuts(userId);
+                userPackages = new ShortcutUser(userId);
             }
             mUsers.put(userId, userPackages);
         }
@@ -770,14 +794,14 @@
     /** Return the per-user per-package state. */
     @GuardedBy("mLock")
     @NonNull
-    PackageShortcuts getPackageShortcutsLocked(
+    ShortcutPackage getPackageShortcutsLocked(
             @NonNull String packageName, @UserIdInt int userId) {
         return getUserShortcutsLocked(userId).getPackageShortcuts(packageName);
     }
 
     @GuardedBy("mLock")
     @NonNull
-    LauncherShortcuts getLauncherShortcuts(
+    ShortcutLauncher getLauncherShortcuts(
             @NonNull String packageName, @UserIdInt int userId) {
         return getUserShortcutsLocked(userId).getLauncherShortcuts(packageName);
     }
@@ -1047,6 +1071,9 @@
     }
 
     private void notifyListeners(@NonNull String packageName, @UserIdInt int userId) {
+        if (!mUserManager.isUserRunning(userId)) {
+            return;
+        }
         postToHandler(() -> {
             final ArrayList<ShortcutChangeListener> copy;
             synchronized (mLock) {
@@ -1142,7 +1169,7 @@
         final int size = newShortcuts.size();
 
         synchronized (mLock) {
-            final PackageShortcuts ps = getPackageShortcutsLocked(packageName, userId);
+            final ShortcutPackage ps = getPackageShortcutsLocked(packageName, userId);
 
             // Throttling.
             if (!ps.tryApiCall(this)) {
@@ -1177,7 +1204,7 @@
         final int size = newShortcuts.size();
 
         synchronized (mLock) {
-            final PackageShortcuts ps = getPackageShortcutsLocked(packageName, userId);
+            final ShortcutPackage ps = getPackageShortcutsLocked(packageName, userId);
 
             // Throttling.
             if (!ps.tryApiCall(this)) {
@@ -1214,7 +1241,7 @@
         verifyCaller(packageName, userId);
 
         synchronized (mLock) {
-            final PackageShortcuts ps = getPackageShortcutsLocked(packageName, userId);
+            final ShortcutPackage ps = getPackageShortcutsLocked(packageName, userId);
 
             // Throttling.
             if (!ps.tryApiCall(this)) {
@@ -1354,7 +1381,7 @@
                 start = System.currentTimeMillis();
             }
 
-            final UserShortcuts user = getUserShortcutsLocked(userId);
+            final ShortcutUser user = getUserShortcutsLocked(userId);
 
             final List<ResolveInfo> allHomeCandidates = new ArrayList<>();
 
@@ -1420,6 +1447,44 @@
         }
     }
 
+    // === House keeping ===
+
+    @VisibleForTesting
+    void cleanUpPackageLocked(String packageName, int userId) {
+        final boolean wasUserLoaded = isUserLoadedLocked(userId);
+
+        final ShortcutUser mUser = getUserShortcutsLocked(userId);
+        boolean doNotify = false;
+
+        // First, remove the package from the package list (if the package is a publisher).
+        if (mUser.getPackages().remove(packageName) != null) {
+            doNotify = true;
+        }
+        // Also remove from the launcher list (if the package is a launcher).
+        mUser.getLaunchers().remove(packageName);
+
+        // Then remove pinned shortcuts from all launchers.
+        for (int i = mUser.getLaunchers().size() - 1; i >= 0; i--) {
+            mUser.getLaunchers().valueAt(i).cleanUpPackage(packageName);
+        }
+        // Now there may be orphan shortcuts because we removed pinned shortucts at the previous
+        // step.  Remove them too.
+        for (int i = mUser.getPackages().size() - 1; i >= 0; i--) {
+            mUser.getPackages().valueAt(i).refreshPinnedFlags(this);
+        }
+
+        scheduleSaveUser(userId);
+
+        if (doNotify) {
+            notifyListeners(packageName, userId);
+        }
+
+        if (!wasUserLoaded) {
+            // Note this will execute the scheduled save.
+            unloadUserLocked(userId);
+        }
+    }
+
     /**
      * Entry point from {@link LauncherApps}.
      */
@@ -1441,7 +1506,7 @@
                             callingPackage, packageName, changedSince,
                             componentName, queryFlags, userId, ret, cloneFlag);
                 } else {
-                    final ArrayMap<String, PackageShortcuts> packages =
+                    final ArrayMap<String, ShortcutPackage> packages =
                             getUserShortcutsLocked(userId).getPackages();
                     for (int i = packages.size() - 1; i >= 0; i--) {
                         getShortcutsInnerLocked(
@@ -1575,6 +1640,50 @@
         }
     }
 
+    private PackageMonitor mPackageMonitor = new PackageMonitor() {
+        @Override
+        public void onPackageUpdateFinished(String packageName, int uid) {
+            handlePackageUpdateFinished(packageName, getChangingUserId());
+        }
+
+        @Override
+        public void onPackageRemoved(String packageName, int uid) {
+            handlePackageRemoved(packageName, getChangingUserId());
+        }
+
+        @Override
+        public void onPackageRemovedAllUsers(String packageName, int uid) {
+            handlePackageRemovedAllUsers(packageName, getChangingUserId());
+        }
+    };
+
+    void handlePackageUpdateFinished(String packageName, @UserIdInt int userId) {
+        if (DEBUG) {
+            Slog.d(TAG, "onPackageUpdateFinished() userId=" + userId);
+        }
+        // TODO Update the version.
+    }
+
+    void handlePackageRemoved(String packageName, @UserIdInt int userId) {
+        if (DEBUG) {
+            Slog.d(TAG, "onPackageRemoved() userId=" + userId);
+        }
+        synchronized (mLock) {
+            cleanUpPackageLocked(packageName, userId);
+        }
+    }
+
+    void handlePackageRemovedAllUsers(String packageName, @UserIdInt int userId) {
+        if (DEBUG) {
+            Slog.d(TAG, "onPackageRemovedAllUsers() userId=" + userId);
+        }
+        synchronized (mLock) {
+            cleanUpPackageLocked(packageName, userId);
+        }
+
+        // TODO Remove from all users, which we can't if the user is locked.
+    }
+
     // === Dump ===
 
     @Override
@@ -1823,25 +1932,29 @@
     // === Unit test support ===
 
     // Injection point.
+    @VisibleForTesting
     long injectCurrentTimeMillis() {
         return System.currentTimeMillis();
     }
 
     // Injection point.
+    @VisibleForTesting
     int injectBinderCallingUid() {
         return getCallingUid();
     }
 
-    final int getCallingUserId() {
+    private int getCallingUserId() {
         return UserHandle.getUserId(injectBinderCallingUid());
     }
 
     // Injection point.
+    @VisibleForTesting
     long injectClearCallingIdentity() {
         return Binder.clearCallingIdentity();
     }
 
     // Injection point.
+    @VisibleForTesting
     void injectRestoreCallingIdentity(long token) {
         Binder.restoreCallingIdentity(token);
     }
@@ -1854,10 +1967,12 @@
         Slog.wtf(TAG, message, e);
     }
 
+    @VisibleForTesting
     File injectSystemDataPath() {
         return Environment.getDataSystemDirectory();
     }
 
+    @VisibleForTesting
     File injectUserDataPath(@UserIdInt int userId) {
         return new File(Environment.getDataSystemCeDirectory(userId), DIRECTORY_PER_USER);
     }
@@ -1867,16 +1982,18 @@
         return ActivityManager.isLowRamDeviceStatic();
     }
 
+    @VisibleForTesting
     PackageManagerInternal injectPackageManagerInternal() {
         return mPackageManagerInternal;
     }
 
+    @VisibleForTesting
     File getUserBitmapFilePath(@UserIdInt int userId) {
         return new File(injectUserDataPath(userId), DIRECTORY_BITMAPS);
     }
 
     @VisibleForTesting
-    SparseArray<UserShortcuts> getShortcutsForTest() {
+    SparseArray<ShortcutUser> getShortcutsForTest() {
         return mUsers;
     }
 
@@ -1913,790 +2030,13 @@
     @VisibleForTesting
     ShortcutInfo getPackageShortcutForTest(String packageName, String shortcutId, int userId) {
         synchronized (mLock) {
-            return getPackageShortcutsLocked(packageName, userId).findShortcutById(shortcutId);
+            final ShortcutUser user = mUsers.get(userId);
+            if (user == null) return null;
+
+            final ShortcutPackage pkg = user.getPackages().get(packageName);
+            if (pkg == null) return null;
+
+            return pkg.findShortcutById(shortcutId);
         }
     }
 }
-
-/**
- * Per-user information.
- */
-class UserShortcuts {
-    private static final String TAG = ShortcutService.TAG;
-
-    static final String TAG_ROOT = "user";
-    private static final String TAG_LAUNCHER = "launcher";
-
-    private static final String ATTR_VALUE = "value";
-
-    @UserIdInt
-    final int mUserId;
-
-    private final ArrayMap<String, PackageShortcuts> mPackages = new ArrayMap<>();
-
-    private final ArrayMap<String, LauncherShortcuts> mLaunchers = new ArrayMap<>();
-
-    private ComponentName mLauncherComponent;
-
-    public UserShortcuts(int userId) {
-        mUserId = userId;
-    }
-
-    public ArrayMap<String, PackageShortcuts> getPackages() {
-        return mPackages;
-    }
-
-    public ArrayMap<String, LauncherShortcuts> getLaunchers() {
-        return mLaunchers;
-    }
-
-    public PackageShortcuts getPackageShortcuts(@NonNull String packageName) {
-        PackageShortcuts ret = mPackages.get(packageName);
-        if (ret == null) {
-            ret = new PackageShortcuts(mUserId, packageName);
-            mPackages.put(packageName, ret);
-        }
-        return ret;
-    }
-
-    public LauncherShortcuts getLauncherShortcuts(@NonNull String packageName) {
-        LauncherShortcuts ret = mLaunchers.get(packageName);
-        if (ret == null) {
-            ret = new LauncherShortcuts(mUserId, packageName);
-            mLaunchers.put(packageName, ret);
-        }
-        return ret;
-    }
-
-    public void saveToXml(XmlSerializer out) throws IOException, XmlPullParserException {
-        out.startTag(null, TAG_ROOT);
-
-        ShortcutService.writeTagValue(out, TAG_LAUNCHER,
-                mLauncherComponent);
-
-        final int lsize = mLaunchers.size();
-        for (int i = 0; i < lsize; i++) {
-            mLaunchers.valueAt(i).saveToXml(out);
-        }
-
-        final int psize = mPackages.size();
-        for (int i = 0; i < psize; i++) {
-            mPackages.valueAt(i).saveToXml(out);
-        }
-
-        out.endTag(null, TAG_ROOT);
-    }
-
-    public static UserShortcuts loadFromXml(XmlPullParser parser, int userId)
-            throws IOException, XmlPullParserException {
-        final UserShortcuts ret = new UserShortcuts(userId);
-
-        final int outerDepth = parser.getDepth();
-        int type;
-        while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
-                && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
-            if (type != XmlPullParser.START_TAG) {
-                continue;
-            }
-            final int depth = parser.getDepth();
-            final String tag = parser.getName();
-            switch (tag) {
-                case TAG_LAUNCHER: {
-                    ret.mLauncherComponent = ShortcutService.parseComponentNameAttribute(
-                            parser, ATTR_VALUE);
-                    continue;
-                }
-                case PackageShortcuts.TAG_ROOT: {
-                    final PackageShortcuts shortcuts = PackageShortcuts.loadFromXml(parser, userId);
-
-                    // Don't use addShortcut(), we don't need to save the icon.
-                    ret.getPackages().put(shortcuts.mPackageName, shortcuts);
-                    continue;
-                }
-
-                case LauncherShortcuts.TAG_ROOT: {
-                    final LauncherShortcuts shortcuts =
-                            LauncherShortcuts.loadFromXml(parser, userId);
-
-                    ret.getLaunchers().put(shortcuts.mPackageName, shortcuts);
-                    continue;
-                }
-            }
-            throw ShortcutService.throwForInvalidTag(depth, tag);
-        }
-        return ret;
-    }
-
-    public ComponentName getLauncherComponent() {
-        return mLauncherComponent;
-    }
-
-    public void setLauncherComponent(ShortcutService s, ComponentName launcherComponent) {
-        if (Objects.equal(mLauncherComponent, launcherComponent)) {
-            return;
-        }
-        mLauncherComponent = launcherComponent;
-        s.scheduleSaveUser(mUserId);
-    }
-
-    public void resetThrottling() {
-        for (int i = mPackages.size() - 1; i >= 0; i--) {
-            mPackages.valueAt(i).resetThrottling();
-        }
-    }
-
-    public void dump(@NonNull ShortcutService s, @NonNull PrintWriter pw, @NonNull String prefix) {
-        pw.print(prefix);
-        pw.print("User: ");
-        pw.print(mUserId);
-        pw.println();
-
-        pw.print(prefix);
-        pw.print("  ");
-        pw.print("Default launcher: ");
-        pw.print(mLauncherComponent);
-        pw.println();
-
-        for (int i = 0; i < mLaunchers.size(); i++) {
-            mLaunchers.valueAt(i).dump(s, pw, prefix + "  ");
-        }
-
-        for (int i = 0; i < mPackages.size(); i++) {
-            mPackages.valueAt(i).dump(s, pw, prefix + "  ");
-        }
-    }
-}
-
-class LauncherShortcuts {
-    private static final String TAG = ShortcutService.TAG;
-
-    static final String TAG_ROOT = "launcher-pins";
-
-    private static final String TAG_PACKAGE = "package";
-    private static final String TAG_PIN = "pin";
-
-    private static final String ATTR_VALUE = "value";
-    private static final String ATTR_PACKAGE_NAME = "package-name";
-
-    @UserIdInt
-    final int mUserId;
-
-    @NonNull
-    final String mPackageName;
-
-    /**
-     * Package name -> IDs.
-     */
-    final private ArrayMap<String, ArraySet<String>> mPinnedShortcuts = new ArrayMap<>();
-
-    LauncherShortcuts(@UserIdInt int userId, @NonNull String packageName) {
-        mUserId = userId;
-        mPackageName = packageName;
-    }
-
-    public void pinShortcuts(@NonNull ShortcutService s, @NonNull String packageName,
-            @NonNull List<String> ids) {
-        final int idSize = ids.size();
-        if (idSize == 0) {
-            mPinnedShortcuts.remove(packageName);
-        } else {
-            final ArraySet<String> prevSet = mPinnedShortcuts.get(packageName);
-
-            // Pin shortcuts.  Make sure only pin the ones that were visible to the caller.
-            // i.e. a non-dynamic, pinned shortcut by *other launchers* shouldn't be pinned here.
-
-            final PackageShortcuts packageShortcuts =
-                    s.getPackageShortcutsLocked(packageName, mUserId);
-            final ArraySet<String> newSet = new ArraySet<>();
-
-            for (int i = 0; i < idSize; i++) {
-                final String id = ids.get(i);
-                final ShortcutInfo si = packageShortcuts.findShortcutById(id);
-                if (si == null) {
-                    continue;
-                }
-                if (si.isDynamic() || (prevSet != null && prevSet.contains(id))) {
-                    newSet.add(id);
-                }
-            }
-            mPinnedShortcuts.put(packageName, newSet);
-        }
-        s.getPackageShortcutsLocked(packageName, mUserId).refreshPinnedFlags(s);
-    }
-
-    /**
-     * Return the pinned shortcut IDs for the publisher package.
-     */
-    public ArraySet<String> getPinnedShortcutIds(@NonNull String packageName) {
-        return mPinnedShortcuts.get(packageName);
-    }
-
-    /**
-     * Persist.
-     */
-    public void saveToXml(XmlSerializer out) throws IOException {
-        out.startTag(null, TAG_ROOT);
-        ShortcutService.writeAttr(out, ATTR_PACKAGE_NAME,
-                mPackageName);
-
-        final int size = mPinnedShortcuts.size();
-        for (int i = 0; i < size; i++) {
-            out.startTag(null, TAG_PACKAGE);
-            ShortcutService.writeAttr(out, ATTR_PACKAGE_NAME,
-                    mPinnedShortcuts.keyAt(i));
-
-            final ArraySet<String> ids = mPinnedShortcuts.valueAt(i);
-            final int idSize = ids.size();
-            for (int j = 0; j < idSize; j++) {
-                ShortcutService.writeTagValue(out, TAG_PIN, ids.valueAt(j));
-            }
-            out.endTag(null, TAG_PACKAGE);
-        }
-
-        out.endTag(null, TAG_ROOT);
-    }
-
-    /**
-     * Load.
-     */
-    public static LauncherShortcuts loadFromXml(XmlPullParser parser, int userId)
-            throws IOException, XmlPullParserException {
-        final String launcherPackageName = ShortcutService.parseStringAttribute(parser,
-                ATTR_PACKAGE_NAME);
-
-        final LauncherShortcuts ret = new LauncherShortcuts(userId, launcherPackageName);
-
-        ArraySet<String> ids = null;
-        final int outerDepth = parser.getDepth();
-        int type;
-        while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
-                && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
-            if (type != XmlPullParser.START_TAG) {
-                continue;
-            }
-            final int depth = parser.getDepth();
-            final String tag = parser.getName();
-            switch (tag) {
-                case TAG_PACKAGE: {
-                    final String packageName = ShortcutService.parseStringAttribute(parser,
-                            ATTR_PACKAGE_NAME);
-                    ids = new ArraySet<>();
-                    ret.mPinnedShortcuts.put(packageName, ids);
-                    continue;
-                }
-                case TAG_PIN: {
-                    ids.add(ShortcutService.parseStringAttribute(parser,
-                            ATTR_VALUE));
-                    continue;
-                }
-            }
-            throw ShortcutService.throwForInvalidTag(depth, tag);
-        }
-        return ret;
-    }
-
-    public void dump(@NonNull ShortcutService s, @NonNull PrintWriter pw, @NonNull String prefix) {
-        pw.println();
-
-        pw.print(prefix);
-        pw.print("Launcher: ");
-        pw.print(mPackageName);
-        pw.println();
-
-        final int size = mPinnedShortcuts.size();
-        for (int i = 0; i < size; i++) {
-            pw.println();
-
-            pw.print(prefix);
-            pw.print("  ");
-            pw.print("Package: ");
-            pw.println(mPinnedShortcuts.keyAt(i));
-
-            final ArraySet<String> ids = mPinnedShortcuts.valueAt(i);
-            final int idSize = ids.size();
-
-            for (int j = 0; j < idSize; j++) {
-                pw.print(prefix);
-                pw.print("    ");
-                pw.print(ids.valueAt(j));
-                pw.println();
-            }
-        }
-    }
-}
-
-/**
- * All the information relevant to shortcuts from a single package (per-user).
- */
-class PackageShortcuts {
-    private static final String TAG = ShortcutService.TAG;
-
-    static final String TAG_ROOT = "package";
-    private static final String TAG_INTENT_EXTRAS = "intent-extras";
-    private static final String TAG_EXTRAS = "extras";
-    private static final String TAG_SHORTCUT = "shortcut";
-
-    private static final String ATTR_NAME = "name";
-    private static final String ATTR_DYNAMIC_COUNT = "dynamic-count";
-    private static final String ATTR_CALL_COUNT = "call-count";
-    private static final String ATTR_LAST_RESET = "last-reset";
-    private static final String ATTR_ID = "id";
-    private static final String ATTR_ACTIVITY = "activity";
-    private static final String ATTR_TITLE = "title";
-    private static final String ATTR_INTENT = "intent";
-    private static final String ATTR_WEIGHT = "weight";
-    private static final String ATTR_TIMESTAMP = "timestamp";
-    private static final String ATTR_FLAGS = "flags";
-    private static final String ATTR_ICON_RES = "icon-res";
-    private static final String ATTR_BITMAP_PATH = "bitmap-path";
-
-    @UserIdInt
-    final int mUserId;
-
-    @NonNull
-    final String mPackageName;
-
-    /**
-     * All the shortcuts from the package, keyed on IDs.
-     */
-    final private ArrayMap<String, ShortcutInfo> mShortcuts = new ArrayMap<>();
-
-    /**
-     * # of dynamic shortcuts.
-     */
-    private int mDynamicShortcutCount = 0;
-
-    /**
-     * # of times the package has called rate-limited APIs.
-     */
-    private int mApiCallCount;
-
-    /**
-     * When {@link #mApiCallCount} was reset last time.
-     */
-    private long mLastResetTime;
-
-    PackageShortcuts(int userId, String packageName) {
-        mUserId = userId;
-        mPackageName = packageName;
-    }
-
-    @Nullable
-    public ShortcutInfo findShortcutById(String id) {
-        return mShortcuts.get(id);
-    }
-
-    private ShortcutInfo deleteShortcut(@NonNull ShortcutService s,
-            @NonNull String id) {
-        final ShortcutInfo shortcut = mShortcuts.remove(id);
-        if (shortcut != null) {
-            s.removeIcon(mUserId, shortcut);
-            shortcut.clearFlags(ShortcutInfo.FLAG_DYNAMIC | ShortcutInfo.FLAG_PINNED);
-        }
-        return shortcut;
-    }
-
-    void addShortcut(@NonNull ShortcutService s, @NonNull ShortcutInfo newShortcut) {
-        deleteShortcut(s, newShortcut.getId());
-        s.saveIconAndFixUpShortcut(mUserId, newShortcut);
-        mShortcuts.put(newShortcut.getId(), newShortcut);
-    }
-
-    /**
-     * Add a shortcut, or update one with the same ID, with taking over existing flags.
-     *
-     * It checks the max number of dynamic shortcuts.
-     */
-    public void addDynamicShortcut(@NonNull ShortcutService s,
-            @NonNull ShortcutInfo newShortcut) {
-        newShortcut.addFlags(ShortcutInfo.FLAG_DYNAMIC);
-
-        final ShortcutInfo oldShortcut = mShortcuts.get(newShortcut.getId());
-
-        final boolean wasPinned;
-        final int newDynamicCount;
-
-        if (oldShortcut == null) {
-            wasPinned = false;
-            newDynamicCount = mDynamicShortcutCount + 1; // adding a dynamic shortcut.
-        } else {
-            wasPinned = oldShortcut.isPinned();
-            if (oldShortcut.isDynamic()) {
-                newDynamicCount = mDynamicShortcutCount; // not adding a dynamic shortcut.
-            } else {
-                newDynamicCount = mDynamicShortcutCount + 1; // adding a dynamic shortcut.
-            }
-        }
-
-        // Make sure there's still room.
-        s.enforceMaxDynamicShortcuts(newDynamicCount);
-
-        // Okay, make it dynamic and add.
-        if (wasPinned) {
-            newShortcut.addFlags(ShortcutInfo.FLAG_PINNED);
-        }
-
-        addShortcut(s, newShortcut);
-        mDynamicShortcutCount = newDynamicCount;
-    }
-
-    /**
-     * Remove all shortcuts that aren't pinned nor dynamic.
-     */
-    private void removeOrphans(@NonNull ShortcutService s) {
-        ArrayList<String> removeList = null; // Lazily initialize.
-
-        for (int i = mShortcuts.size() - 1; i >= 0; i--) {
-            final ShortcutInfo si = mShortcuts.valueAt(i);
-
-            if (si.isPinned() || si.isDynamic()) continue;
-
-            if (removeList == null) {
-                removeList = new ArrayList<>();
-            }
-            removeList.add(si.getId());
-        }
-        if (removeList != null) {
-            for (int i = removeList.size() - 1 ; i >= 0; i--) {
-                deleteShortcut(s, removeList.get(i));
-            }
-        }
-    }
-
-    /**
-     * Remove all dynamic shortcuts.
-     */
-    public void deleteAllDynamicShortcuts(@NonNull ShortcutService s) {
-        for (int i = mShortcuts.size() - 1; i >= 0; i--) {
-            mShortcuts.valueAt(i).clearFlags(ShortcutInfo.FLAG_DYNAMIC);
-        }
-        removeOrphans(s);
-        mDynamicShortcutCount = 0;
-    }
-
-    /**
-     * Remove a dynamic shortcut by ID.
-     */
-    public void deleteDynamicWithId(@NonNull ShortcutService s, @NonNull String shortcutId) {
-        final ShortcutInfo oldShortcut = mShortcuts.get(shortcutId);
-
-        if (oldShortcut == null) {
-            return;
-        }
-        if (oldShortcut.isDynamic()) {
-            mDynamicShortcutCount--;
-        }
-        if (oldShortcut.isPinned()) {
-            oldShortcut.clearFlags(ShortcutInfo.FLAG_DYNAMIC);
-        } else {
-            deleteShortcut(s, shortcutId);
-        }
-    }
-
-    /**
-     * Called after a launcher updates the pinned set.  For each shortcut in this package,
-     * set FLAG_PINNED if any launcher has pinned it.  Otherwise, clear it.
-     *
-     * <p>Then remove all shortcuts that are not dynamic and no longer pinned either.
-     */
-    public void refreshPinnedFlags(@NonNull ShortcutService s) {
-        // First, un-pin all shortcuts
-        for (int i = mShortcuts.size() - 1; i >= 0; i--) {
-            mShortcuts.valueAt(i).clearFlags(ShortcutInfo.FLAG_PINNED);
-        }
-
-        // Then, for the pinned set for each launcher, set the pin flag one by one.
-        final ArrayMap<String, LauncherShortcuts> launchers =
-                s.getUserShortcutsLocked(mUserId).getLaunchers();
-
-        for (int l = launchers.size() - 1; l >= 0; l--) {
-            final LauncherShortcuts launcherShortcuts = launchers.valueAt(l);
-            final ArraySet<String> pinned = launcherShortcuts.getPinnedShortcutIds(mPackageName);
-
-            if (pinned == null || pinned.size() == 0) {
-                continue;
-            }
-            for (int i = pinned.size() - 1; i >= 0; i--) {
-                final ShortcutInfo si = mShortcuts.get(pinned.valueAt(i));
-                if (si == null) {
-                    s.wtf("Shortcut not found");
-                } else {
-                    si.addFlags(ShortcutInfo.FLAG_PINNED);
-                }
-            }
-        }
-
-        // Lastly, remove the ones that are no longer pinned nor dynamic.
-        removeOrphans(s);
-    }
-
-    /**
-     * Number of calls that the caller has made, since the last reset.
-     */
-    public int getApiCallCount(@NonNull ShortcutService s) {
-        final long last = s.getLastResetTimeLocked();
-
-        final long now = s.injectCurrentTimeMillis();
-        if (ShortcutService.isClockValid(now) && mLastResetTime > now) {
-            Slog.w(TAG, "Clock rewound");
-            // Clock rewound.
-            mLastResetTime = now;
-            mApiCallCount = 0;
-            return mApiCallCount;
-        }
-
-        // If not reset yet, then reset.
-        if (mLastResetTime < last) {
-            if (ShortcutService.DEBUG) {
-                Slog.d(TAG, String.format("My last reset=%d, now=%d, last=%d: resetting",
-                        mLastResetTime, now, last));
-            }
-            mApiCallCount = 0;
-            mLastResetTime = last;
-        }
-        return mApiCallCount;
-    }
-
-    /**
-     * If the caller app hasn't been throttled yet, increment {@link #mApiCallCount}
-     * and return true.  Otherwise just return false.
-     */
-    public boolean tryApiCall(@NonNull ShortcutService s) {
-        if (getApiCallCount(s) >= s.mMaxDailyUpdates) {
-            return false;
-        }
-        mApiCallCount++;
-        return true;
-    }
-
-    public void resetRateLimitingForCommandLine() {
-        mApiCallCount = 0;
-        mLastResetTime = 0;
-    }
-
-    /**
-     * Find all shortcuts that match {@code query}.
-     */
-    public void findAll(@NonNull ShortcutService s, @NonNull List<ShortcutInfo> result,
-            @Nullable Predicate<ShortcutInfo> query, int cloneFlag,
-            @Nullable String callingLauncher) {
-
-        // Set of pinned shortcuts by the calling launcher.
-        final ArraySet<String> pinnedByCallerSet = (callingLauncher == null) ? null
-                : s.getLauncherShortcuts(callingLauncher, mUserId)
-                    .getPinnedShortcutIds(mPackageName);
-
-        for (int i = 0; i < mShortcuts.size(); i++) {
-            final ShortcutInfo si = mShortcuts.valueAt(i);
-
-            // If it's called by non-launcher (i.e. publisher, always include -> true.
-            // Otherwise, only include non-dynamic pinned one, if the calling launcher has pinned
-            // it.
-            final boolean isPinnedByCaller = (callingLauncher == null)
-                    || ((pinnedByCallerSet != null) && pinnedByCallerSet.contains(si.getId()));
-            if (!si.isDynamic()) {
-                if (!si.isPinned()) {
-                    s.wtf("Shortcut not pinned here");
-                    continue;
-                }
-                if (!isPinnedByCaller) {
-                    continue;
-                }
-            }
-            final ShortcutInfo clone = si.clone(cloneFlag);
-            // Fix up isPinned for the caller.  Note we need to do it before the "test" callback,
-            // since it may check isPinned.
-            if (!isPinnedByCaller) {
-                clone.clearFlags(ShortcutInfo.FLAG_PINNED);
-            }
-            if (query == null || query.test(clone)) {
-                result.add(clone);
-            }
-        }
-    }
-
-    public void resetThrottling() {
-        mApiCallCount = 0;
-    }
-
-    public void dump(@NonNull ShortcutService s, @NonNull PrintWriter pw, @NonNull String prefix) {
-        pw.println();
-
-        pw.print(prefix);
-        pw.print("Package: ");
-        pw.print(mPackageName);
-        pw.println();
-
-        pw.print(prefix);
-        pw.print("  ");
-        pw.print("Calls: ");
-        pw.print(getApiCallCount(s));
-        pw.println();
-
-        // This should be after getApiCallCount(), which may update it.
-        pw.print(prefix);
-        pw.print("  ");
-        pw.print("Last reset: [");
-        pw.print(mLastResetTime);
-        pw.print("] ");
-        pw.print(s.formatTime(mLastResetTime));
-        pw.println();
-
-        pw.println("      Shortcuts:");
-        long totalBitmapSize = 0;
-        final ArrayMap<String, ShortcutInfo> shortcuts = mShortcuts;
-        final int size = shortcuts.size();
-        for (int i = 0; i < size; i++) {
-            final ShortcutInfo si = shortcuts.valueAt(i);
-            pw.print("        ");
-            pw.println(si.toInsecureString());
-            if (si.getBitmapPath() != null) {
-                final long len = new File(si.getBitmapPath()).length();
-                pw.print("          ");
-                pw.print("bitmap size=");
-                pw.println(len);
-
-                totalBitmapSize += len;
-            }
-        }
-        pw.print(prefix);
-        pw.print("  ");
-        pw.print("Total bitmap size: ");
-        pw.print(totalBitmapSize);
-        pw.print(" (");
-        pw.print(Formatter.formatFileSize(s.mContext, totalBitmapSize));
-        pw.println(")");
-    }
-
-    public void saveToXml(@NonNull XmlSerializer out) throws IOException, XmlPullParserException {
-        out.startTag(null, TAG_ROOT);
-
-        ShortcutService.writeAttr(out, ATTR_NAME, mPackageName);
-        ShortcutService.writeAttr(out, ATTR_DYNAMIC_COUNT, mDynamicShortcutCount);
-        ShortcutService.writeAttr(out, ATTR_CALL_COUNT, mApiCallCount);
-        ShortcutService.writeAttr(out, ATTR_LAST_RESET, mLastResetTime);
-
-        final int size = mShortcuts.size();
-        for (int j = 0; j < size; j++) {
-            saveShortcut(out, mShortcuts.valueAt(j));
-        }
-
-        out.endTag(null, TAG_ROOT);
-    }
-
-    private static void saveShortcut(XmlSerializer out, ShortcutInfo si)
-            throws IOException, XmlPullParserException {
-        out.startTag(null, TAG_SHORTCUT);
-        ShortcutService.writeAttr(out, ATTR_ID, si.getId());
-        // writeAttr(out, "package", si.getPackageName()); // not needed
-        ShortcutService.writeAttr(out, ATTR_ACTIVITY, si.getActivityComponent());
-        // writeAttr(out, "icon", si.getIcon());  // We don't save it.
-        ShortcutService.writeAttr(out, ATTR_TITLE, si.getTitle());
-        ShortcutService.writeAttr(out, ATTR_INTENT, si.getIntentNoExtras());
-        ShortcutService.writeAttr(out, ATTR_WEIGHT, si.getWeight());
-        ShortcutService.writeAttr(out, ATTR_TIMESTAMP,
-                si.getLastChangedTimestamp());
-        ShortcutService.writeAttr(out, ATTR_FLAGS, si.getFlags());
-        ShortcutService.writeAttr(out, ATTR_ICON_RES, si.getIconResourceId());
-        ShortcutService.writeAttr(out, ATTR_BITMAP_PATH, si.getBitmapPath());
-
-        ShortcutService.writeTagExtra(out, TAG_INTENT_EXTRAS,
-                si.getIntentPersistableExtras());
-        ShortcutService.writeTagExtra(out, TAG_EXTRAS, si.getExtras());
-
-        out.endTag(null, TAG_SHORTCUT);
-    }
-
-    public static PackageShortcuts loadFromXml(XmlPullParser parser, int userId)
-            throws IOException, XmlPullParserException {
-
-        final String packageName = ShortcutService.parseStringAttribute(parser,
-                ATTR_NAME);
-
-        final PackageShortcuts ret = new PackageShortcuts(userId, packageName);
-
-        ret.mDynamicShortcutCount =
-                ShortcutService.parseIntAttribute(parser, ATTR_DYNAMIC_COUNT);
-        ret.mApiCallCount =
-                ShortcutService.parseIntAttribute(parser, ATTR_CALL_COUNT);
-        ret.mLastResetTime =
-                ShortcutService.parseLongAttribute(parser, ATTR_LAST_RESET);
-
-        final int outerDepth = parser.getDepth();
-        int type;
-        while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
-                && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
-            if (type != XmlPullParser.START_TAG) {
-                continue;
-            }
-            final int depth = parser.getDepth();
-            final String tag = parser.getName();
-            switch (tag) {
-                case TAG_SHORTCUT:
-                    final ShortcutInfo si = parseShortcut(parser, packageName);
-
-                    // Don't use addShortcut(), we don't need to save the icon.
-                    ret.mShortcuts.put(si.getId(), si);
-                    continue;
-            }
-            throw ShortcutService.throwForInvalidTag(depth, tag);
-        }
-        return ret;
-    }
-
-    private static ShortcutInfo parseShortcut(XmlPullParser parser, String packageName)
-            throws IOException, XmlPullParserException {
-        String id;
-        ComponentName activityComponent;
-        // Icon icon;
-        String title;
-        Intent intent;
-        PersistableBundle intentPersistableExtras = null;
-        int weight;
-        PersistableBundle extras = null;
-        long lastChangedTimestamp;
-        int flags;
-        int iconRes;
-        String bitmapPath;
-
-        id = ShortcutService.parseStringAttribute(parser, ATTR_ID);
-        activityComponent = ShortcutService.parseComponentNameAttribute(parser,
-                ATTR_ACTIVITY);
-        title = ShortcutService.parseStringAttribute(parser, ATTR_TITLE);
-        intent = ShortcutService.parseIntentAttribute(parser, ATTR_INTENT);
-        weight = (int) ShortcutService.parseLongAttribute(parser, ATTR_WEIGHT);
-        lastChangedTimestamp = (int) ShortcutService.parseLongAttribute(parser,
-                ATTR_TIMESTAMP);
-        flags = (int) ShortcutService.parseLongAttribute(parser, ATTR_FLAGS);
-        iconRes = (int) ShortcutService.parseLongAttribute(parser, ATTR_ICON_RES);
-        bitmapPath = ShortcutService.parseStringAttribute(parser, ATTR_BITMAP_PATH);
-
-        final int outerDepth = parser.getDepth();
-        int type;
-        while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
-                && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
-            if (type != XmlPullParser.START_TAG) {
-                continue;
-            }
-            final int depth = parser.getDepth();
-            final String tag = parser.getName();
-            if (ShortcutService.DEBUG_LOAD) {
-                Slog.d(TAG, String.format("  depth=%d type=%d name=%s",
-                        depth, type, tag));
-            }
-            switch (tag) {
-                case TAG_INTENT_EXTRAS:
-                    intentPersistableExtras = PersistableBundle.restoreFromXml(parser);
-                    continue;
-                case TAG_EXTRAS:
-                    extras = PersistableBundle.restoreFromXml(parser);
-                    continue;
-            }
-            throw ShortcutService.throwForInvalidTag(depth, tag);
-        }
-        return new ShortcutInfo(
-                id, packageName, activityComponent, /* icon =*/ null, title, intent,
-                intentPersistableExtras, weight, extras, lastChangedTimestamp, flags,
-                iconRes, bitmapPath);
-    }
-}
diff --git a/services/core/java/com/android/server/pm/ShortcutUser.java b/services/core/java/com/android/server/pm/ShortcutUser.java
new file mode 100644
index 0000000..4a6b1e4
--- /dev/null
+++ b/services/core/java/com/android/server/pm/ShortcutUser.java
@@ -0,0 +1,179 @@
+/*
+ * Copyright (C) 2016 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.pm;
+
+import android.annotation.NonNull;
+import android.annotation.UserIdInt;
+import android.content.ComponentName;
+import android.util.ArrayMap;
+
+import libcore.util.Objects;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlSerializer;
+
+import java.io.IOException;
+import java.io.PrintWriter;
+
+/**
+ * User information used by {@link ShortcutService}.
+ */
+class ShortcutUser {
+    private static final String TAG = ShortcutService.TAG;
+
+    static final String TAG_ROOT = "user";
+    private static final String TAG_LAUNCHER = "launcher";
+
+    private static final String ATTR_VALUE = "value";
+
+    @UserIdInt
+    final int mUserId;
+
+    private final ArrayMap<String, ShortcutPackage> mPackages = new ArrayMap<>();
+
+    private final ArrayMap<String, ShortcutLauncher> mLaunchers = new ArrayMap<>();
+
+    private ComponentName mLauncherComponent;
+
+    public ShortcutUser(int userId) {
+        mUserId = userId;
+    }
+
+    public ArrayMap<String, ShortcutPackage> getPackages() {
+        return mPackages;
+    }
+
+    public ArrayMap<String, ShortcutLauncher> getLaunchers() {
+        return mLaunchers;
+    }
+
+    public ShortcutPackage getPackageShortcuts(@NonNull String packageName) {
+        ShortcutPackage ret = mPackages.get(packageName);
+        if (ret == null) {
+            ret = new ShortcutPackage(mUserId, packageName);
+            mPackages.put(packageName, ret);
+        }
+        return ret;
+    }
+
+    public ShortcutLauncher getLauncherShortcuts(@NonNull String packageName) {
+        ShortcutLauncher ret = mLaunchers.get(packageName);
+        if (ret == null) {
+            ret = new ShortcutLauncher(mUserId, packageName);
+            mLaunchers.put(packageName, ret);
+        }
+        return ret;
+    }
+
+    public void saveToXml(XmlSerializer out) throws IOException, XmlPullParserException {
+        out.startTag(null, TAG_ROOT);
+
+        ShortcutService.writeTagValue(out, TAG_LAUNCHER,
+                mLauncherComponent);
+
+        final int lsize = mLaunchers.size();
+        for (int i = 0; i < lsize; i++) {
+            mLaunchers.valueAt(i).saveToXml(out);
+        }
+
+        final int psize = mPackages.size();
+        for (int i = 0; i < psize; i++) {
+            mPackages.valueAt(i).saveToXml(out);
+        }
+
+        out.endTag(null, TAG_ROOT);
+    }
+
+    public static ShortcutUser loadFromXml(XmlPullParser parser, int userId)
+            throws IOException, XmlPullParserException {
+        final ShortcutUser ret = new ShortcutUser(userId);
+
+        final int outerDepth = parser.getDepth();
+        int type;
+        while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+                && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
+            if (type != XmlPullParser.START_TAG) {
+                continue;
+            }
+            final int depth = parser.getDepth();
+            final String tag = parser.getName();
+            switch (tag) {
+                case TAG_LAUNCHER: {
+                    ret.mLauncherComponent = ShortcutService.parseComponentNameAttribute(
+                            parser, ATTR_VALUE);
+                    continue;
+                }
+                case ShortcutPackage.TAG_ROOT: {
+                    final ShortcutPackage shortcuts = ShortcutPackage.loadFromXml(parser, userId);
+
+                    // Don't use addShortcut(), we don't need to save the icon.
+                    ret.getPackages().put(shortcuts.mPackageName, shortcuts);
+                    continue;
+                }
+
+                case ShortcutLauncher.TAG_ROOT: {
+                    final ShortcutLauncher shortcuts =
+                            ShortcutLauncher.loadFromXml(parser, userId);
+
+                    ret.getLaunchers().put(shortcuts.mPackageName, shortcuts);
+                    continue;
+                }
+            }
+            throw ShortcutService.throwForInvalidTag(depth, tag);
+        }
+        return ret;
+    }
+
+    public ComponentName getLauncherComponent() {
+        return mLauncherComponent;
+    }
+
+    public void setLauncherComponent(ShortcutService s, ComponentName launcherComponent) {
+        if (Objects.equal(mLauncherComponent, launcherComponent)) {
+            return;
+        }
+        mLauncherComponent = launcherComponent;
+        s.scheduleSaveUser(mUserId);
+    }
+
+    public void resetThrottling() {
+        for (int i = mPackages.size() - 1; i >= 0; i--) {
+            mPackages.valueAt(i).resetThrottling();
+        }
+    }
+
+    public void dump(@NonNull ShortcutService s, @NonNull PrintWriter pw, @NonNull String prefix) {
+        pw.print(prefix);
+        pw.print("User: ");
+        pw.print(mUserId);
+        pw.println();
+
+        pw.print(prefix);
+        pw.print("  ");
+        pw.print("Default launcher: ");
+        pw.print(mLauncherComponent);
+        pw.println();
+
+        for (int i = 0; i < mLaunchers.size(); i++) {
+            mLaunchers.valueAt(i).dump(s, pw, prefix + "  ");
+        }
+
+        for (int i = 0; i < mPackages.size(); i++) {
+            mPackages.valueAt(i).dump(s, pw, prefix + "  ");
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index a3622b5..715f1e5 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -1,3 +1,4 @@
+
 /*
  * Copyright (C) 2011 The Android Open Source Project
  *
@@ -650,18 +651,27 @@
 
     @Override
     public UserInfo getUserInfo(int userId) {
+        checkManageUsersPermission("query user");
+        synchronized (mUsersLock) {
+            return getUserInfoLU(userId);
+        }
+    }
+
+    @Override
+    public boolean isManagedProfile(int userId) {
         int callingUserId = UserHandle.getCallingUserId();
         if (callingUserId != userId && !hasManageUsersPermission()) {
             synchronized (mPackagesLock) {
                 if (!isSameProfileGroupLP(callingUserId, userId)) {
                     throw new SecurityException(
-                            "You need MANAGE_USERS permission to: query users outside profile" +
-                                    " group");
+                            "You need MANAGE_USERS permission to: check if specified user a " +
+                            "managed profile outside your profile group");
                 }
             }
         }
         synchronized (mUsersLock) {
-            return getUserInfoLU(userId);
+            UserInfo userInfo =  getUserInfoLU(userId);
+            return userInfo != null && userInfo.isManagedProfile();
         }
     }
 
diff --git a/services/core/java/com/android/server/policy/ShortcutManager.java b/services/core/java/com/android/server/policy/ShortcutManager.java
index a47f250..a14c614 100644
--- a/services/core/java/com/android/server/policy/ShortcutManager.java
+++ b/services/core/java/com/android/server/policy/ShortcutManager.java
@@ -78,7 +78,7 @@
     public Intent getIntent(KeyCharacterMap kcm, int keyCode, int metaState) {
         ShortcutInfo shortcut = null;
 
-        // If the Shift key is preesed, then search for the shift shortcuts.
+        // If the Shift key is pressed, then search for the shift shortcuts.
         boolean isShiftOn = (metaState & KeyEvent.META_SHIFT_ON) == KeyEvent.META_SHIFT_ON;
         SparseArray<ShortcutInfo> shortcutMap = isShiftOn ? mShiftShortcuts : mShortcuts;
 
diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java
index cbd77d4..ff5a0f9 100644
--- a/services/core/java/com/android/server/power/PowerManagerService.java
+++ b/services/core/java/com/android/server/power/PowerManagerService.java
@@ -71,6 +71,8 @@
 import com.android.server.am.BatteryStatsService;
 import com.android.server.lights.Light;
 import com.android.server.lights.LightsManager;
+import com.android.server.vr.VrManagerInternal;
+import com.android.server.vr.VrStateListener;
 import libcore.util.Objects;
 
 import java.io.FileDescriptor;
@@ -158,6 +160,7 @@
     // Power hints defined in hardware/libhardware/include/hardware/power.h.
     private static final int POWER_HINT_LOW_POWER = 5;
     private static final int POWER_HINT_SUSTAINED_PERFORMANCE = 6;
+    private static final int POWER_HINT_VR_MODE = 7;
 
     // Power features defined in hardware/libhardware/include/hardware/power.h.
     private static final int POWER_FEATURE_DOUBLE_TAP_TO_WAKE = 1;
@@ -654,6 +657,7 @@
             resolver.registerContentObserver(Settings.Secure.getUriFor(
                     Secure.BRIGHTNESS_USE_TWILIGHT),
                     false, mSettingsObserver, UserHandle.USER_ALL);
+            getLocalService(VrManagerInternal.class).registerListener(mVrStateListener);
             // Go.
             readConfigurationLocked();
             updateSettingsLocked();
@@ -3002,6 +3006,13 @@
         }
     }
 
+    private final VrStateListener mVrStateListener = new VrStateListener() {
+        @Override
+        public void onVrStateChanged(boolean enabled) {
+            powerHintInternal(POWER_HINT_VR_MODE, enabled ? 1 : 0);
+        }
+    };
+
     /**
      * Handler for asynchronous operations performed by the power manager.
      */
diff --git a/services/core/java/com/android/server/vr/VrManagerService.java b/services/core/java/com/android/server/vr/VrManagerService.java
index 0aa3052..6bf949c 100644
--- a/services/core/java/com/android/server/vr/VrManagerService.java
+++ b/services/core/java/com/android/server/vr/VrManagerService.java
@@ -158,7 +158,7 @@
 
     @Override
     public void onBootPhase(int phase) {
-        if (phase == SystemService.PHASE_THIRD_PARTY_APPS_CAN_START) {
+        if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) {
             synchronized (mLock) {
                 Looper looper = Looper.getMainLooper();
                 Handler handler = new Handler(looper);
diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
index 3cf9590..fb9b1ce 100644
--- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
+++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
@@ -20,6 +20,7 @@
 import static android.app.WallpaperManager.FLAG_SET_LOCK;
 import static android.os.ParcelFileDescriptor.*;
 
+import android.app.ActivityManager;
 import android.app.ActivityManagerNative;
 import android.app.AppGlobals;
 import android.app.AppOpsManager;
@@ -875,12 +876,8 @@
         if (!isWallpaperSupported(callingPackage) || !isWallpaperSettingAllowed(callingPackage)) {
             return;
         }
-        if (userId != UserHandle.getCallingUserId()) {
-            // cross-user call
-            mContext.enforceCallingOrSelfPermission(
-                    android.Manifest.permission.INTERACT_ACROSS_USERS_FULL,
-                    "WallpaperManagerService");
-        }
+        userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(),
+                Binder.getCallingUid(), userId, false, true, "clearWallpaper", null);
 
         synchronized (mLock) {
             clearWallpaperLocked(false, which, userId, null);
@@ -1103,12 +1100,8 @@
     @Override
     public ParcelFileDescriptor getWallpaper(IWallpaperManagerCallback cb, final int which,
             Bundle outParams, int wallpaperUserId) {
-        if (wallpaperUserId != UserHandle.getCallingUserId()) {
-            // cross-user call
-            mContext.enforceCallingOrSelfPermission(
-                    android.Manifest.permission.INTERACT_ACROSS_USERS_FULL,
-                    "WallpaperManagerService");
-        }
+        wallpaperUserId = ActivityManager.handleIncomingUser(Binder.getCallingPid(),
+                Binder.getCallingUid(), wallpaperUserId, false, true, "getWallpaper", null);
 
         if (which != FLAG_SET_SYSTEM && which != FLAG_SET_LOCK) {
             throw new IllegalArgumentException("Must specify exactly one kind of wallpaper to read");
@@ -1147,6 +1140,7 @@
         }
     }
 
+    @Override
     public WallpaperInfo getWallpaperInfo() {
         int userId = UserHandle.getCallingUserId();
         synchronized (mLock) {
@@ -1159,6 +1153,26 @@
     }
 
     @Override
+    public int getWallpaperIdForUser(int which, int userId) {
+        userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(),
+                Binder.getCallingUid(), userId, false, true, "getWallpaperIdForUser", null);
+
+        if (which != FLAG_SET_SYSTEM && which != FLAG_SET_LOCK) {
+            throw new IllegalArgumentException("Must specify exactly one kind of wallpaper");
+        }
+
+        final SparseArray<WallpaperData> map =
+                (which == FLAG_SET_LOCK) ? mLockWallpaperMap : mWallpaperMap;
+        synchronized (mLock) {
+            WallpaperData wallpaper = map.get(userId);
+            if (wallpaper != null) {
+                return wallpaper.wallpaperId;
+            }
+        }
+        return -1;
+    }
+
+    @Override
     public boolean setLockWallpaperCallback(IWallpaperManagerCallback cb) {
         checkPermission(android.Manifest.permission.INTERNAL_SYSTEM_WINDOW);
         synchronized (mLock) {
diff --git a/services/core/java/com/android/server/wm/BoundsAnimationController.java b/services/core/java/com/android/server/wm/BoundsAnimationController.java
index 79d3d84..b7d6062 100644
--- a/services/core/java/com/android/server/wm/BoundsAnimationController.java
+++ b/services/core/java/com/android/server/wm/BoundsAnimationController.java
@@ -214,7 +214,7 @@
         void getFullScreenBounds(Rect bounds);
     }
 
-    void animateBounds(final AnimateBoundsUser target, Rect from, Rect to) {
+    void animateBounds(final AnimateBoundsUser target, Rect from, Rect to, int animationDuration) {
         boolean moveToFullscreen = false;
         if (to == null) {
             to = new Rect();
@@ -242,7 +242,8 @@
                 new BoundsAnimator(target, from, to, moveToFullscreen, replacing);
         mRunningAnimations.put(target, animator);
         animator.setFloatValues(0f, 1f);
-        animator.setDuration(DEFAULT_APP_TRANSITION_DURATION * DEBUG_ANIMATION_SLOW_DOWN_FACTOR);
+        animator.setDuration((animationDuration != -1 ? animationDuration
+                : DEFAULT_APP_TRANSITION_DURATION) * DEBUG_ANIMATION_SLOW_DOWN_FACTOR);
         animator.setInterpolator(new LinearInterpolator());
         animator.start();
     }
diff --git a/services/core/java/com/android/server/wm/Session.java b/services/core/java/com/android/server/wm/Session.java
index a589f89..c0c1ed8 100644
--- a/services/core/java/com/android/server/wm/Session.java
+++ b/services/core/java/com/android/server/wm/Session.java
@@ -195,8 +195,10 @@
 
     @Override
     public void repositionChild(IWindow window, int left, int top, int right, int bottom,
-             long deferTransactionUntilFrame, Rect outFrame) {
+            int requestedWidth, int requestedHeight,
+            long deferTransactionUntilFrame, Rect outFrame) {
         mService.repositionChild(this, window, left, top, right, bottom,
+                requestedWidth, requestedHeight,
                 deferTransactionUntilFrame, outFrame);
     }
 
diff --git a/services/core/java/com/android/server/wm/TaskStack.java b/services/core/java/com/android/server/wm/TaskStack.java
index 60b2e4a..c667767 100644
--- a/services/core/java/com/android/server/wm/TaskStack.java
+++ b/services/core/java/com/android/server/wm/TaskStack.java
@@ -1089,7 +1089,7 @@
             }
         }
         try {
-            mService.mActivityManager.resizeStack(mStackId, bounds, false, true, false);
+            mService.mActivityManager.resizeStack(mStackId, bounds, false, true, false, -1);
         } catch (RemoteException e) {
         }
         return true;
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 607a3e9..14291ca 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -2522,6 +2522,7 @@
 
     void repositionChild(Session session, IWindow client,
             int left, int top, int right, int bottom,
+            int requestedWidth, int requestedHeight,
             long deferTransactionUntilFrame, Rect outFrame) {
         Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "repositionChild");
         long origId = Binder.clearCallingIdentity();
@@ -2537,6 +2538,7 @@
                             "repositionChild called but window is not"
                             + "attached to a parent win=" + win);
                 }
+                win.setRequestedSize(requestedWidth, requestedHeight);
 
                 win.mAttrs.x = left;
                 win.mAttrs.y = top;
@@ -2593,7 +2595,8 @@
                         == PackageManager.PERMISSION_GRANTED;
 
         long origId = Binder.clearCallingIdentity();
-
+        final boolean preserveGeometry = (attrs != null) && (attrs.privateFlags &
+                WindowManager.LayoutParams.PRIVATE_FLAG_PRESERVE_GEOMETRY) != 0;
         synchronized(mWindowMap) {
             WindowState win = windowForClientLocked(session, client, false);
             if (win == null) {
@@ -2601,7 +2604,7 @@
             }
 
             WindowStateAnimator winAnimator = win.mWinAnimator;
-            if (viewVisibility != View.GONE) {
+            if (!preserveGeometry && viewVisibility != View.GONE) {
                 win.setRequestedSize(requestedWidth, requestedHeight);
             }
 
@@ -2650,7 +2653,9 @@
             if ((attrChanges & WindowManager.LayoutParams.ALPHA_CHANGED) != 0) {
                 winAnimator.mAlpha = attrs.alpha;
             }
-            win.setWindowScale(requestedWidth, requestedHeight);
+            if (!preserveGeometry) {
+                win.setWindowScale(win.mRequestedWidth, win.mRequestedHeight);
+            }
 
             boolean imMayMove = (flagChanges & (FLAG_ALT_FOCUSABLE_IM | FLAG_NOT_FOCUSABLE)) != 0;
             final boolean isDefaultDisplay = win.isDefaultDisplay();
@@ -8241,8 +8246,8 @@
                 break;
                 case RESIZE_STACK: {
                     try {
-                        mActivityManager.resizeStack(msg.arg1, (Rect) msg.obj, msg.arg2 == 1, false,
-                                false);
+                        mActivityManager.resizeStack(
+                                msg.arg1, (Rect) msg.obj, msg.arg2 == 1, false, false, -1);
                     } catch (RemoteException e) {
                         // This will not happen since we are in the same process.
                     }
@@ -10460,7 +10465,7 @@
         }
     }
 
-    public void animateResizePinnedStack(final Rect bounds) {
+    public void animateResizePinnedStack(final Rect bounds, final int animationDuration) {
         synchronized (mWindowMap) {
             final TaskStack stack = mStackIdToStack.get(PINNED_STACK_ID);
             if (stack == null) {
@@ -10472,7 +10477,8 @@
             UiThread.getHandler().post(new Runnable() {
                 @Override
                 public void run() {
-                    mBoundsAnimationController.animateBounds(stack, originalBounds, bounds);
+                    mBoundsAnimationController.animateBounds(
+                            stack, originalBounds, bounds, animationDuration);
                 }
             });
         }
diff --git a/services/tests/servicestests/src/com/android/server/ConnectivityServiceTest.java b/services/tests/servicestests/src/com/android/server/ConnectivityServiceTest.java
index 34f2e2e..8e11511 100644
--- a/services/tests/servicestests/src/com/android/server/ConnectivityServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/ConnectivityServiceTest.java
@@ -1008,29 +1008,41 @@
     private class TestNetworkCallback extends NetworkCallback {
         private final ConditionVariable mConditionVariable = new ConditionVariable();
         private CallbackState mLastCallback = CallbackState.NONE;
+        private Network mLastNetwork;
 
         public void onAvailable(Network network) {
             assertEquals(CallbackState.NONE, mLastCallback);
             mLastCallback = CallbackState.AVAILABLE;
+            mLastNetwork = network;
             mConditionVariable.open();
         }
 
         public void onLosing(Network network, int maxMsToLive) {
             assertEquals(CallbackState.NONE, mLastCallback);
             mLastCallback = CallbackState.LOSING;
+            mLastNetwork = network;
             mConditionVariable.open();
         }
 
         public void onLost(Network network) {
             assertEquals(CallbackState.NONE, mLastCallback);
             mLastCallback = CallbackState.LOST;
+            mLastNetwork = network;
             mConditionVariable.open();
         }
 
         void expectCallback(CallbackState state) {
+            expectCallback(state, null);
+        }
+
+        void expectCallback(CallbackState state, MockNetworkAgent mockAgent) {
             waitFor(mConditionVariable);
             assertEquals(state, mLastCallback);
+            if (mockAgent != null) {
+                assertEquals(mockAgent.getNetwork(), mLastNetwork);
+            }
             mLastCallback = CallbackState.NONE;
+            mLastNetwork = null;
             mConditionVariable.close();
         }
 
@@ -1389,6 +1401,55 @@
                 execptionCalled);
     }
 
+    @LargeTest
+    public void testRegisterDefaultNetworkCallback() throws Exception {
+        final TestNetworkCallback defaultNetworkCallback = new TestNetworkCallback();
+        mCm.registerDefaultNetworkCallback(defaultNetworkCallback);
+        defaultNetworkCallback.assertNoCallback();
+
+        // Create a TRANSPORT_CELLULAR request to keep the mobile interface up
+        // whenever Wi-Fi is up. Without this, the mobile network agent is
+        // reaped before any other activity can take place.
+        final TestNetworkCallback cellNetworkCallback = new TestNetworkCallback();
+        final NetworkRequest cellRequest = new NetworkRequest.Builder()
+                .addTransportType(TRANSPORT_CELLULAR).build();
+        mCm.requestNetwork(cellRequest, cellNetworkCallback);
+        cellNetworkCallback.assertNoCallback();
+
+        // Bring up cell and expect CALLBACK_AVAILABLE.
+        mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
+        mCellNetworkAgent.connect(true);
+        cellNetworkCallback.expectCallback(CallbackState.AVAILABLE, mCellNetworkAgent);
+        defaultNetworkCallback.expectCallback(CallbackState.AVAILABLE, mCellNetworkAgent);
+
+        // Bring up wifi and expect CALLBACK_AVAILABLE.
+        mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
+        mWiFiNetworkAgent.connect(true);
+        cellNetworkCallback.assertNoCallback();
+        defaultNetworkCallback.expectCallback(CallbackState.AVAILABLE, mWiFiNetworkAgent);
+
+        // Bring down cell. Expect no default network callback, since it wasn't the default.
+        mCellNetworkAgent.disconnect();
+        cellNetworkCallback.expectCallback(CallbackState.LOST, mCellNetworkAgent);
+        defaultNetworkCallback.assertNoCallback();
+
+        // Bring up cell. Expect no default network callback, since it won't be the default.
+        mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
+        mCellNetworkAgent.connect(true);
+        cellNetworkCallback.expectCallback(CallbackState.AVAILABLE, mCellNetworkAgent);
+        defaultNetworkCallback.assertNoCallback();
+
+        // Bring down wifi. Expect the default network callback to notified of LOST wifi
+        // followed by AVAILABLE cell.
+        mWiFiNetworkAgent.disconnect();
+        cellNetworkCallback.assertNoCallback();
+        defaultNetworkCallback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent);
+        defaultNetworkCallback.expectCallback(CallbackState.AVAILABLE, mCellNetworkAgent);
+        mCellNetworkAgent.disconnect();
+        cellNetworkCallback.expectCallback(CallbackState.LOST, mCellNetworkAgent);
+        defaultNetworkCallback.expectCallback(CallbackState.LOST, mCellNetworkAgent);
+    }
+
     private static class TestKeepaliveCallback extends PacketKeepaliveCallback {
 
         public static enum CallbackType { ON_STARTED, ON_STOPPED, ON_ERROR };
diff --git a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest.java b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest.java
index ad86fd0..28966ca 100644
--- a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest.java
@@ -22,6 +22,7 @@
 import static org.mockito.Mockito.reset;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
 
 import android.annotation.NonNull;
 import android.annotation.UserIdInt;
@@ -56,6 +57,7 @@
 import android.test.InstrumentationTestCase;
 import android.test.mock.MockContext;
 import android.test.suitebuilder.annotation.SmallTest;
+import android.util.ArraySet;
 import android.util.Log;
 import android.util.SparseArray;
 
@@ -124,6 +126,11 @@
         }
 
         @Override
+        public String getSystemServiceName(Class<?> serviceClass) {
+            return getTestContext().getSystemServiceName(serviceClass);
+        }
+
+        @Override
         public PackageManager getPackageManager() {
             return mMockPackageManager;
         }
@@ -231,7 +238,7 @@
 
         @Override
         boolean injectIsLowRamDevice() {
-            return mInjectdIsLowRamDevice;
+            return mInjectedIsLowRamDevice;
         }
 
         @Override
@@ -343,7 +350,7 @@
 
     private long mInjectedCurrentTimeLillis;
 
-    private boolean mInjectdIsLowRamDevice;
+    private boolean mInjectedIsLowRamDevice;
 
     private int mInjectedCallingUid;
     private String mInjectedClientPackage;
@@ -657,6 +664,14 @@
         return new ComponentName(mClientContext, clazz);
     }
 
+    private <T> Set<T> makeSet(T... values) {
+        final HashSet<T> ret = new HashSet<>();
+        for (T s : values) {
+            ret.add(s);
+        }
+        return ret;
+    }
+
     @NonNull
     private ShortcutInfo findById(List<ShortcutInfo> list, String id) {
         for (ShortcutInfo s : list) {
@@ -841,6 +856,14 @@
         return mService.getPackageShortcutForTest(packageName, shortcutId, userId);
     }
 
+    private void assertShortcutExists(String packageName, String shortcutId, int userId) {
+        assertTrue(getPackageShortcut(packageName, shortcutId, userId) != null);
+    }
+
+    private void assertShortcutNotExists(String packageName, String shortcutId, int userId) {
+        assertTrue(getPackageShortcut(packageName, shortcutId, userId) == null);
+    }
+
     private ShortcutInfo getPackageShortcut(String packageName, String shortcutId) {
         return getPackageShortcut(packageName, shortcutId, getCallingUserId());
     }
@@ -849,6 +872,27 @@
         return getPackageShortcut(getCallingPackage(), shortcutId, getCallingUserId());
     }
 
+    private List<ShortcutInfo> getLauncherShortcuts(String launcher, int userId, int queryFlags) {
+        final List<ShortcutInfo>[] ret = new List[1];
+        runWithCaller(launcher, userId, () -> {
+            final ShortcutQuery q = new ShortcutQuery();
+            q.setQueryFlags(queryFlags);
+            ret[0] = mLauncherApps.getShortcuts(q, UserHandle.of(userId));
+        });
+        return ret[0];
+    }
+
+    private List<ShortcutInfo> getLauncherPinnedShortcuts(String launcher, int userId) {
+        return getLauncherShortcuts(launcher, userId, ShortcutQuery.FLAG_GET_PINNED);
+    }
+
+    /**
+     * Wrap a set in an ArraySet just to get a better toString.
+     */
+    private <T> Set<T> set(Set<T> in) {
+        return new ArraySet<T>(in);
+    }
+
     /**
      * Test for the first launch path, no settings file available.
      */
@@ -857,7 +901,8 @@
     }
 
     /**
-     * Test for {@link ShortcutService#updateTimes()}
+     * Test for {@link ShortcutService#getLastResetTimeLocked()} and
+     * {@link ShortcutService#getNextResetTimeLocked()}.
      */
     public void testUpdateAndGetNextResetTimeLocked() {
         assertResetTimes(START_TIME, START_TIME + INTERVAL);
@@ -928,7 +973,7 @@
         assertEquals(CompressFormat.WEBP, mService.getIconPersistFormatForTest());
         assertEquals(75, mService.getIconPersistQualityForTest());
 
-        mInjectdIsLowRamDevice = true;
+        mInjectedIsLowRamDevice = true;
         mService.updateConfigurationLocked(
                 ConfigConstants.KEY_MAX_ICON_DIMENSION_DP + "=100,"
                         + ConfigConstants.KEY_MAX_ICON_DIMENSION_DP_LOWRAM + "=50,"
@@ -2205,6 +2250,8 @@
         // TODO Add "multi" version -- run the test with two launchers and make sure the callback
         // argument only contains the ones that are actually visible to each launcher.
 
+        when(mMockUserManager.isUserRunning(eq(USER_0))).thenReturn(true);
+
         LauncherApps.Callback c0 = mock(LauncherApps.Callback.class);
 
         // Set listeners
@@ -2320,6 +2367,20 @@
                 eq(UserHandle.of(USER_0))
         );
         assertEquals(0, shortcuts.getValue().size());
+
+        // Remove CALLING_PACKAGE_2
+        reset(c0);
+        mService.cleanUpPackageLocked(CALLING_PACKAGE_2, USER_0);
+
+        // Should get a callback with an empty list.
+        waitOnMainThread();
+        shortcuts = ArgumentCaptor.forClass(List.class);
+        verify(c0).onShortcutsChanged(
+                eq(CALLING_PACKAGE_2),
+                shortcuts.capture(),
+                eq(UserHandle.of(USER_0))
+        );
+        assertEquals(0, shortcuts.getValue().size());
     }
 
     // === Test for persisting ===
@@ -2436,7 +2497,7 @@
         assertEquals(0, mService.getShortcutsForTest().size());
 
         // this will pre-load the per-user info.
-        mService.onStartUserLocked(UserHandle.USER_SYSTEM);
+        mService.handleUnlockUser(UserHandle.USER_SYSTEM);
 
         // Now it's loaded.
         assertEquals(1, mService.getShortcutsForTest().size());
@@ -2462,7 +2523,7 @@
                 .getLauncherComponent().getPackageName());
 
         // Start another user
-        mService.onStartUserLocked(USER_10);
+        mService.handleUnlockUser(USER_10);
 
         // Now the size is 2.
         assertEquals(2, mService.getShortcutsForTest().size());
@@ -2478,7 +2539,7 @@
         assertNull(mService.getShortcutsForTest().get(USER_10).getLauncherComponent());
 
         // Try stopping the user
-        mService.onCleanupUserLocked(USER_10);
+        mService.handleCleanupUser(USER_10);
 
         // Now it's unloaded.
         assertEquals(1, mService.getShortcutsForTest().size());
@@ -2486,6 +2547,248 @@
         // TODO Check all other fields
     }
 
+    public void testCleanupPackage() {
+        runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+            assertTrue(mManager.setDynamicShortcuts(Arrays.asList(
+                    makeShortcut("s0_1"))));
+        });
+        runWithCaller(CALLING_PACKAGE_2, USER_0, () -> {
+            assertTrue(mManager.setDynamicShortcuts(Arrays.asList(
+                    makeShortcut("s0_2"))));
+        });
+        runWithCaller(LAUNCHER_1, USER_0, () -> {
+            mLauncherApps.pinShortcuts(CALLING_PACKAGE_1, Arrays.asList("s0_1"),
+                    UserHandle.of(USER_0));
+            mLauncherApps.pinShortcuts(CALLING_PACKAGE_2, Arrays.asList("s0_2"),
+                    UserHandle.of(USER_0));
+        });
+        runWithCaller(LAUNCHER_2, USER_0, () -> {
+            mLauncherApps.pinShortcuts(CALLING_PACKAGE_1, Arrays.asList("s0_1"),
+                    UserHandle.of(USER_0));
+            mLauncherApps.pinShortcuts(CALLING_PACKAGE_2, Arrays.asList("s0_2"),
+                    UserHandle.of(USER_0));
+        });
+
+        runWithCaller(CALLING_PACKAGE_1, USER_10, () -> {
+            assertTrue(mManager.setDynamicShortcuts(Arrays.asList(
+                    makeShortcut("s10_1"))));
+        });
+        runWithCaller(CALLING_PACKAGE_2, USER_10, () -> {
+            assertTrue(mManager.setDynamicShortcuts(Arrays.asList(
+                    makeShortcut("s10_2"))));
+        });
+        runWithCaller(LAUNCHER_1, USER_10, () -> {
+            mLauncherApps.pinShortcuts(CALLING_PACKAGE_1, Arrays.asList("s10_1"),
+                    UserHandle.of(USER_10));
+            mLauncherApps.pinShortcuts(CALLING_PACKAGE_2, Arrays.asList("s10_2"),
+                    UserHandle.of(USER_10));
+        });
+        runWithCaller(LAUNCHER_2, USER_10, () -> {
+            mLauncherApps.pinShortcuts(CALLING_PACKAGE_1, Arrays.asList("s10_1"),
+                    UserHandle.of(USER_10));
+            mLauncherApps.pinShortcuts(CALLING_PACKAGE_2, Arrays.asList("s10_2"),
+                    UserHandle.of(USER_10));
+        });
+
+        // Remove all dynamic shortcuts; now all shortcuts are just pinned.
+        runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+            mManager.deleteAllDynamicShortcuts();
+        });
+        runWithCaller(CALLING_PACKAGE_2, USER_0, () -> {
+            mManager.deleteAllDynamicShortcuts();
+        });
+        runWithCaller(CALLING_PACKAGE_1, USER_10, () -> {
+            mManager.deleteAllDynamicShortcuts();
+        });
+        runWithCaller(CALLING_PACKAGE_2, USER_10, () -> {
+            mManager.deleteAllDynamicShortcuts();
+        });
+
+
+        final SparseArray<ShortcutUser> users =  mService.getShortcutsForTest();
+        assertEquals(2, users.size());
+        assertEquals(USER_0, users.keyAt(0));
+        assertEquals(USER_10, users.keyAt(1));
+
+        final ShortcutUser user0 =  users.get(USER_0);
+        final ShortcutUser user10 =  users.get(USER_10);
+
+
+        // Check the registered packages.
+
+        assertEquals(makeSet(CALLING_PACKAGE_1, CALLING_PACKAGE_2),
+                set(user0.getPackages().keySet()));
+        assertEquals(makeSet(CALLING_PACKAGE_1, CALLING_PACKAGE_2),
+                set(user10.getPackages().keySet()));
+        assertEquals(makeSet(LAUNCHER_1, LAUNCHER_2),
+                set(user0.getLaunchers().keySet()));
+        assertEquals(makeSet(LAUNCHER_1, LAUNCHER_2),
+                set(user10.getLaunchers().keySet()));
+        assertShortcutIds(getLauncherPinnedShortcuts(LAUNCHER_1, USER_0),
+                "s0_1", "s0_2");
+        assertShortcutIds(getLauncherPinnedShortcuts(LAUNCHER_2, USER_0),
+                "s0_1", "s0_2");
+        assertShortcutIds(getLauncherPinnedShortcuts(LAUNCHER_1, USER_10),
+                "s10_1", "s10_2");
+        assertShortcutIds(getLauncherPinnedShortcuts(LAUNCHER_2, USER_10),
+                "s10_1", "s10_2");
+        assertShortcutExists(CALLING_PACKAGE_1, "s0_1", USER_0);
+        assertShortcutExists(CALLING_PACKAGE_2, "s0_2", USER_0);
+        assertShortcutExists(CALLING_PACKAGE_1, "s10_1", USER_10);
+        assertShortcutExists(CALLING_PACKAGE_2, "s10_2", USER_10);
+
+        mService.saveDirtyInfo();
+
+        // Nonexistent package.
+        mService.cleanUpPackageLocked("abc", USER_0);
+
+        // No changes.
+        assertEquals(makeSet(CALLING_PACKAGE_1, CALLING_PACKAGE_2),
+                set(user0.getPackages().keySet()));
+        assertEquals(makeSet(CALLING_PACKAGE_1, CALLING_PACKAGE_2),
+                set(user10.getPackages().keySet()));
+        assertEquals(makeSet(LAUNCHER_1, LAUNCHER_2),
+                set(user0.getLaunchers().keySet()));
+        assertEquals(makeSet(LAUNCHER_1, LAUNCHER_2),
+                set(user10.getLaunchers().keySet()));
+        assertShortcutIds(getLauncherPinnedShortcuts(LAUNCHER_1, USER_0),
+                "s0_1", "s0_2");
+        assertShortcutIds(getLauncherPinnedShortcuts(LAUNCHER_2, USER_0),
+                "s0_1", "s0_2");
+        assertShortcutIds(getLauncherPinnedShortcuts(LAUNCHER_1, USER_10),
+                "s10_1", "s10_2");
+        assertShortcutIds(getLauncherPinnedShortcuts(LAUNCHER_2, USER_10),
+                "s10_1", "s10_2");
+        assertShortcutExists(CALLING_PACKAGE_1, "s0_1", USER_0);
+        assertShortcutExists(CALLING_PACKAGE_2, "s0_2", USER_0);
+        assertShortcutExists(CALLING_PACKAGE_1, "s10_1", USER_10);
+        assertShortcutExists(CALLING_PACKAGE_2, "s10_2", USER_10);
+
+        mService.saveDirtyInfo();
+
+        // Remove a package.
+        mService.cleanUpPackageLocked(CALLING_PACKAGE_1, USER_0);
+
+        assertEquals(makeSet(CALLING_PACKAGE_2),
+                set(user0.getPackages().keySet()));
+        assertEquals(makeSet(CALLING_PACKAGE_1, CALLING_PACKAGE_2),
+                set(user10.getPackages().keySet()));
+        assertEquals(makeSet(LAUNCHER_1, LAUNCHER_2),
+                set(user0.getLaunchers().keySet()));
+        assertEquals(makeSet(LAUNCHER_1, LAUNCHER_2),
+                set(user10.getLaunchers().keySet()));
+        assertShortcutIds(getLauncherPinnedShortcuts(LAUNCHER_1, USER_0),
+                "s0_2");
+        assertShortcutIds(getLauncherPinnedShortcuts(LAUNCHER_2, USER_0),
+                "s0_2");
+        assertShortcutIds(getLauncherPinnedShortcuts(LAUNCHER_1, USER_10),
+                "s10_1", "s10_2");
+        assertShortcutIds(getLauncherPinnedShortcuts(LAUNCHER_2, USER_10),
+                "s10_1", "s10_2");
+        assertShortcutNotExists(CALLING_PACKAGE_1, "s0_1", USER_0);
+        assertShortcutExists(CALLING_PACKAGE_2, "s0_2", USER_0);
+        assertShortcutExists(CALLING_PACKAGE_1, "s10_1", USER_10);
+        assertShortcutExists(CALLING_PACKAGE_2, "s10_2", USER_10);
+
+        mService.saveDirtyInfo();
+
+        // Remove a launcher.
+        mService.cleanUpPackageLocked(LAUNCHER_1, USER_10);
+
+        assertEquals(makeSet(CALLING_PACKAGE_2),
+                set(user0.getPackages().keySet()));
+        assertEquals(makeSet(CALLING_PACKAGE_1, CALLING_PACKAGE_2),
+                set(user10.getPackages().keySet()));
+        assertEquals(makeSet(LAUNCHER_1, LAUNCHER_2),
+                set(user0.getLaunchers().keySet()));
+        assertEquals(makeSet(LAUNCHER_2),
+                set(user10.getLaunchers().keySet()));
+        assertShortcutIds(getLauncherPinnedShortcuts(LAUNCHER_1, USER_0),
+                "s0_2");
+        assertShortcutIds(getLauncherPinnedShortcuts(LAUNCHER_2, USER_0),
+                "s0_2");
+        assertShortcutIds(getLauncherPinnedShortcuts(LAUNCHER_2, USER_10),
+                "s10_1", "s10_2");
+        assertShortcutNotExists(CALLING_PACKAGE_1, "s0_1", USER_0);
+        assertShortcutExists(CALLING_PACKAGE_2, "s0_2", USER_0);
+        assertShortcutExists(CALLING_PACKAGE_1, "s10_1", USER_10);
+        assertShortcutExists(CALLING_PACKAGE_2, "s10_2", USER_10);
+
+        mService.saveDirtyInfo();
+
+        // Remove a package.
+        mService.cleanUpPackageLocked(CALLING_PACKAGE_2, USER_10);
+
+        assertEquals(makeSet(CALLING_PACKAGE_2),
+                set(user0.getPackages().keySet()));
+        assertEquals(makeSet(CALLING_PACKAGE_1),
+                set(user10.getPackages().keySet()));
+        assertEquals(makeSet(LAUNCHER_1, LAUNCHER_2),
+                set(user0.getLaunchers().keySet()));
+        assertEquals(makeSet(LAUNCHER_2),
+                set(user10.getLaunchers().keySet()));
+        assertShortcutIds(getLauncherPinnedShortcuts(LAUNCHER_1, USER_0),
+                "s0_2");
+        assertShortcutIds(getLauncherPinnedShortcuts(LAUNCHER_2, USER_0),
+                "s0_2");
+        assertShortcutIds(getLauncherPinnedShortcuts(LAUNCHER_2, USER_10),
+                "s10_1");
+        assertShortcutNotExists(CALLING_PACKAGE_1, "s0_1", USER_0);
+        assertShortcutExists(CALLING_PACKAGE_2, "s0_2", USER_0);
+        assertShortcutExists(CALLING_PACKAGE_1, "s10_1", USER_10);
+        assertShortcutNotExists(CALLING_PACKAGE_2, "s10_2", USER_10);
+
+        mService.saveDirtyInfo();
+
+        // Remove the other launcher from user 10 too.
+        mService.cleanUpPackageLocked(LAUNCHER_2, USER_10);
+
+        assertEquals(makeSet(CALLING_PACKAGE_2),
+                set(user0.getPackages().keySet()));
+        assertEquals(makeSet(CALLING_PACKAGE_1),
+                set(user10.getPackages().keySet()));
+        assertEquals(makeSet(LAUNCHER_1, LAUNCHER_2),
+                set(user0.getLaunchers().keySet()));
+        assertEquals(makeSet(),
+                set(user10.getLaunchers().keySet()));
+        assertShortcutIds(getLauncherPinnedShortcuts(LAUNCHER_1, USER_0),
+                "s0_2");
+        assertShortcutIds(getLauncherPinnedShortcuts(LAUNCHER_2, USER_0),
+                "s0_2");
+
+        // Note the pinned shortcuts on user-10 no longer referred, so they should both be removed.
+        assertShortcutNotExists(CALLING_PACKAGE_1, "s0_1", USER_0);
+        assertShortcutExists(CALLING_PACKAGE_2, "s0_2", USER_0);
+        assertShortcutNotExists(CALLING_PACKAGE_1, "s10_1", USER_10);
+        assertShortcutNotExists(CALLING_PACKAGE_2, "s10_2", USER_10);
+
+        mService.saveDirtyInfo();
+
+        // More remove.
+        mService.cleanUpPackageLocked(CALLING_PACKAGE_1, USER_10);
+
+        assertEquals(makeSet(CALLING_PACKAGE_2),
+                set(user0.getPackages().keySet()));
+        assertEquals(makeSet(),
+                set(user10.getPackages().keySet()));
+        assertEquals(makeSet(LAUNCHER_1, LAUNCHER_2),
+                set(user0.getLaunchers().keySet()));
+        assertEquals(makeSet(),
+                set(user10.getLaunchers().keySet()));
+        assertShortcutIds(getLauncherPinnedShortcuts(LAUNCHER_1, USER_0),
+                "s0_2");
+        assertShortcutIds(getLauncherPinnedShortcuts(LAUNCHER_2, USER_0),
+                "s0_2");
+
+        // Note the pinned shortcuts on user-10 no longer referred, so they should both be removed.
+        assertShortcutNotExists(CALLING_PACKAGE_1, "s0_1", USER_0);
+        assertShortcutExists(CALLING_PACKAGE_2, "s0_2", USER_0);
+        assertShortcutNotExists(CALLING_PACKAGE_1, "s10_1", USER_10);
+        assertShortcutNotExists(CALLING_PACKAGE_2, "s10_2", USER_10);
+
+        mService.saveDirtyInfo();
+    }
+
     // TODO Detailed test for hasShortcutPermissionInner().
 
     // TODO Add tests for the command line functions too.
diff --git a/services/usb/java/com/android/server/usb/UsbDeviceManager.java b/services/usb/java/com/android/server/usb/UsbDeviceManager.java
index 35a0464..5fe944c 100644
--- a/services/usb/java/com/android/server/usb/UsbDeviceManager.java
+++ b/services/usb/java/com/android/server/usb/UsbDeviceManager.java
@@ -608,6 +608,7 @@
             intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING
                     | Intent.FLAG_RECEIVER_FOREGROUND);
             intent.putExtra(UsbManager.USB_CONNECTED, mConnected);
+            intent.putExtra(UsbManager.USB_HOST_CONNECTED, mHostConnected);
             intent.putExtra(UsbManager.USB_CONFIGURED, mConfigured);
             intent.putExtra(UsbManager.USB_DATA_UNLOCKED, isUsbTransferAllowed() && mUsbDataUnlocked);
 
@@ -717,6 +718,9 @@
                 case MSG_UPDATE_HOST_STATE:
                     mHostConnected = (msg.arg1 == 1);
                     updateUsbNotification();
+                    if (mBootCompleted) {
+                        updateUsbStateBroadcastIfNeeded();
+                    }
                     break;
                 case MSG_ENABLE_ADB:
                     setAdbEnabled(msg.arg1 == 1);
diff --git a/telecomm/java/android/telecom/ParcelableConnection.java b/telecomm/java/android/telecom/ParcelableConnection.java
index fe0a4d8..ce51c96 100644
--- a/telecomm/java/android/telecom/ParcelableConnection.java
+++ b/telecomm/java/android/telecom/ParcelableConnection.java
@@ -188,7 +188,7 @@
             DisconnectCause disconnectCause = source.readParcelable(classLoader);
             List<String> conferenceableConnectionIds = new ArrayList<>();
             source.readStringList(conferenceableConnectionIds);
-            Bundle extras = source.readBundle(classLoader);
+            Bundle extras = Bundle.setDefusable(source.readBundle(classLoader), true);
 
             return new ParcelableConnection(
                     phoneAccount,
diff --git a/tools/layoutlib/bridge/src/android/graphics/PorterDuffColorFilter_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/PorterDuffColorFilter_Delegate.java
index 1ca94dc..ff3f19f 100644
--- a/tools/layoutlib/bridge/src/android/graphics/PorterDuffColorFilter_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/graphics/PorterDuffColorFilter_Delegate.java
@@ -48,7 +48,7 @@
 
     // ---- delegate data ----
 
-    private final int mSrcColor;
+    private final java.awt.Color mSrcColor;
     private final Mode mMode;
 
 
@@ -66,9 +66,9 @@
 
     @Override
     public void applyFilter(Graphics2D g, int width, int height) {
-        BufferedImage image = createFilterImage(width, height);
         g.setComposite(getComposite(mMode, 0xFF));
-        g.drawImage(image, 0, 0, null);
+        g.setColor(mSrcColor);
+        g.fillRect(0, 0, width, height);
     }
 
     // ---- native methods ----
@@ -84,22 +84,10 @@
     // ---- Private delegate/helper methods ----
 
     private PorterDuffColorFilter_Delegate(int srcColor, int mode) {
-        mSrcColor = srcColor;
+        mSrcColor = new java.awt.Color(srcColor, true /* hasAlpha */);
         mMode = getCompatibleMode(getPorterDuffMode(mode));
     }
 
-    private BufferedImage createFilterImage(int width, int height) {
-        BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
-        Graphics2D graphics = image.createGraphics();
-        try {
-            graphics.setColor(new java.awt.Color(mSrcColor, true /* hasAlpha */));
-            graphics.fillRect(0, 0, width, height);
-        } finally {
-            graphics.dispose();
-        }
-        return image;
-    }
-
     // For filtering the colors, the src image should contain the "color" only for pixel values
     // which are not transparent in the target image. But, we are using a simple rectangular image
     // completely filled with color. Hence some Composite rules do not apply as intended. However,
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindowSession.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindowSession.java
index fe05b0e..53adb41 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindowSession.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindowSession.java
@@ -96,7 +96,8 @@
     }
 
     @Override
-    public void repositionChild(IWindow childWindow, int x, int y, int width, int height,
+    public void repositionChild(IWindow window, int left, int top, int right, int bottom,
+            int requestedWidth, int requestedHeight,
             long deferTransactionUntilFrame, Rect outFrame) {
         // pass for now.
         return;
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/RenderParamsFlags.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/RenderParamsFlags.java
index b98f96f..bd17a2f 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/RenderParamsFlags.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/RenderParamsFlags.java
@@ -16,6 +16,7 @@
 
 package com.android.layoutlib.bridge.android;
 
+import com.android.ide.common.rendering.api.LayoutlibCallback;
 import com.android.ide.common.rendering.api.RenderParams;
 import com.android.ide.common.rendering.api.SessionParams.Key;
 
@@ -42,11 +43,16 @@
     public static final Key<Boolean> FLAG_KEY_RECYCLER_VIEW_SUPPORT =
             new Key<Boolean>("recyclerViewSupport", Boolean.class);
     /**
-     * The application package name. Used via
-     * {@link com.android.ide.common.rendering.api.LayoutlibCallback#getFlag(Key)}
+     * The application package name. Used via {@link LayoutlibCallback#getFlag(Key)}
      */
     public static final Key<String> FLAG_KEY_APPLICATION_PACKAGE =
             new Key<String>("applicationPackage", String.class);
+    /**
+     * To tell LayoutLib that IDE supports providing XML Parser for a file (useful for getting in
+     * memory contents of the file). Used via {@link LayoutlibCallback#getFlag(Key)}
+     */
+    public static final Key<Boolean> FLAG_KEY_XML_FILE_PARSER_SUPPORT =
+            new Key<Boolean>("xmlFileParser", Boolean.class);
 
     // Disallow instances.
     private RenderParamsFlags() {}
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/ResourceHelper.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/ResourceHelper.java
index 494b3d2..a21de56 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/ResourceHelper.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/ResourceHelper.java
@@ -25,6 +25,7 @@
 import com.android.layoutlib.bridge.Bridge;
 import com.android.layoutlib.bridge.android.BridgeContext;
 import com.android.layoutlib.bridge.android.BridgeXmlBlockParser;
+import com.android.layoutlib.bridge.android.RenderParamsFlags;
 import com.android.ninepatch.NinePatch;
 import com.android.ninepatch.NinePatchChunk;
 import com.android.resources.Density;
@@ -142,8 +143,13 @@
             return null;
         }
 
+        XmlPullParser parser = null;
         // first check if the value is a file (xml most likely)
-        XmlPullParser parser = context.getLayoutlibCallback().getXmlFileParser(value);
+        Boolean psiParserSupport = context.getLayoutlibCallback().getFlag(
+                RenderParamsFlags.FLAG_KEY_XML_FILE_PARSER_SUPPORT);
+        if (psiParserSupport != null && psiParserSupport) {
+            parser = context.getLayoutlibCallback().getXmlFileParser(value);
+        }
         if (parser == null) {
             File f = new File(value);
             if (f.isFile()) {
diff --git a/wifi/java/android/net/wifi/WifiEnterpriseConfig.java b/wifi/java/android/net/wifi/WifiEnterpriseConfig.java
index 58e8761..9e15d60 100644
--- a/wifi/java/android/net/wifi/WifiEnterpriseConfig.java
+++ b/wifi/java/android/net/wifi/WifiEnterpriseConfig.java
@@ -1047,7 +1047,7 @@
         StringBuffer sb = new StringBuffer();
         for (String key : mFields.keySet()) {
             // Don't display password in toString().
-            String value = (key == PASSWORD_KEY) ? "<removed>" : mFields.get(key);
+            String value = PASSWORD_KEY.equals(key) ? "<removed>" : mFields.get(key);
             sb.append(key).append(" ").append(value).append("\n");
         }
         return sb.toString();
diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java
index 823fd26..c040b06 100644
--- a/wifi/java/android/net/wifi/WifiManager.java
+++ b/wifi/java/android/net/wifi/WifiManager.java
@@ -689,7 +689,6 @@
         mContext = context;
         mService = service;
         mTargetSdkVersion = context.getApplicationInfo().targetSdkVersion;
-        init();
     }
 
     /**
@@ -1478,8 +1477,7 @@
      * @hide for CTS test only
      */
     public void getTxPacketCount(TxPacketCountListener listener) {
-        validateChannel();
-        mAsyncChannel.sendMessage(RSSI_PKTCNT_FETCH, 0, putListener(listener));
+        getChannel().sendMessage(RSSI_PKTCNT_FETCH, 0, putListener(listener));
     }
 
     /**
@@ -1972,30 +1970,28 @@
         }
     }
 
-    private void init() {
-        Messenger messenger = getWifiServiceMessenger();
-        if (messenger == null) {
-            mAsyncChannel = null;
-            return;
+    private synchronized AsyncChannel getChannel() {
+        if (mAsyncChannel == null) {
+            Messenger messenger = getWifiServiceMessenger();
+            if (messenger == null) {
+                throw new IllegalStateException(
+                        "getWifiServiceMessenger() returned null!  This is invalid.");
+            }
+
+            mHandlerThread = new HandlerThread("WifiManager");
+            mAsyncChannel = new AsyncChannel();
+            mConnected = new CountDownLatch(1);
+
+            mHandlerThread.start();
+            Handler handler = new ServiceHandler(mHandlerThread.getLooper());
+            mAsyncChannel.connect(mContext, handler, messenger);
+            try {
+                mConnected.await();
+            } catch (InterruptedException e) {
+                Log.e(TAG, "interrupted wait at init");
+            }
         }
-
-        mHandlerThread = new HandlerThread("WifiManager");
-        mAsyncChannel = new AsyncChannel();
-        mConnected = new CountDownLatch(1);
-
-        mHandlerThread.start();
-        Handler handler = new ServiceHandler(mHandlerThread.getLooper());
-        mAsyncChannel.connect(mContext, handler, messenger);
-        try {
-            mConnected.await();
-        } catch (InterruptedException e) {
-            Log.e(TAG, "interrupted wait at init");
-        }
-    }
-
-    private void validateChannel() {
-        if (mAsyncChannel == null) throw new IllegalStateException(
-                "No permission to access and change wifi or a bad initialization");
+        return mAsyncChannel;
     }
 
     /**
@@ -2016,10 +2012,9 @@
      */
     public void connect(WifiConfiguration config, ActionListener listener) {
         if (config == null) throw new IllegalArgumentException("config cannot be null");
-        validateChannel();
         // Use INVALID_NETWORK_ID for arg1 when passing a config object
         // arg1 is used to pass network id when the network already exists
-        mAsyncChannel.sendMessage(CONNECT_NETWORK, WifiConfiguration.INVALID_NETWORK_ID,
+        getChannel().sendMessage(CONNECT_NETWORK, WifiConfiguration.INVALID_NETWORK_ID,
                 putListener(listener), config);
     }
 
@@ -2038,8 +2033,7 @@
      */
     public void connect(int networkId, ActionListener listener) {
         if (networkId < 0) throw new IllegalArgumentException("Network id cannot be negative");
-        validateChannel();
-        mAsyncChannel.sendMessage(CONNECT_NETWORK, networkId, putListener(listener));
+        getChannel().sendMessage(CONNECT_NETWORK, networkId, putListener(listener));
     }
 
     /**
@@ -2062,8 +2056,7 @@
      */
     public void save(WifiConfiguration config, ActionListener listener) {
         if (config == null) throw new IllegalArgumentException("config cannot be null");
-        validateChannel();
-        mAsyncChannel.sendMessage(SAVE_NETWORK, 0, putListener(listener), config);
+        getChannel().sendMessage(SAVE_NETWORK, 0, putListener(listener), config);
     }
 
     /**
@@ -2081,8 +2074,7 @@
      */
     public void forget(int netId, ActionListener listener) {
         if (netId < 0) throw new IllegalArgumentException("Network id cannot be negative");
-        validateChannel();
-        mAsyncChannel.sendMessage(FORGET_NETWORK, netId, putListener(listener));
+        getChannel().sendMessage(FORGET_NETWORK, netId, putListener(listener));
     }
 
     /**
@@ -2096,8 +2088,7 @@
      */
     public void disable(int netId, ActionListener listener) {
         if (netId < 0) throw new IllegalArgumentException("Network id cannot be negative");
-        validateChannel();
-        mAsyncChannel.sendMessage(DISABLE_NETWORK, netId, putListener(listener));
+        getChannel().sendMessage(DISABLE_NETWORK, netId, putListener(listener));
     }
 
     /**
@@ -2125,8 +2116,7 @@
      */
     public void startWps(WpsInfo config, WpsCallback listener) {
         if (config == null) throw new IllegalArgumentException("config cannot be null");
-        validateChannel();
-        mAsyncChannel.sendMessage(START_WPS, 0, putListener(listener), config);
+        getChannel().sendMessage(START_WPS, 0, putListener(listener), config);
     }
 
     /**
@@ -2137,8 +2127,7 @@
      * initialized again
      */
     public void cancelWps(WpsCallback listener) {
-        validateChannel();
-        mAsyncChannel.sendMessage(CANCEL_WPS, 0, putListener(listener));
+        getChannel().sendMessage(CANCEL_WPS, 0, putListener(listener));
     }
 
     /**
@@ -2153,8 +2142,6 @@
             return mService.getWifiServiceMessenger();
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
-        } catch (SecurityException e) {
-            return null;
         }
     }