Merge "Add a new WakeupMessage class and use it in two places."
diff --git a/api/current.txt b/api/current.txt
index 007ed13..732a322 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -5802,6 +5802,7 @@
     method public void lockNow();
     method public void removeActiveAdmin(android.content.ComponentName);
     method public boolean removeCrossProfileWidgetProvider(android.content.ComponentName, java.lang.String);
+    method public boolean removeKeyPair(android.content.ComponentName, java.lang.String);
     method public boolean removeUser(android.content.ComponentName, android.os.UserHandle);
     method public boolean resetPassword(java.lang.String, int);
     method public void setAccountManagementDisabled(android.content.ComponentName, java.lang.String, boolean);
@@ -7779,6 +7780,8 @@
     method public abstract int checkUriPermission(android.net.Uri, java.lang.String, java.lang.String, int, int, int);
     method public abstract deprecated void clearWallpaper() throws java.io.IOException;
     method public abstract android.content.Context createConfigurationContext(android.content.res.Configuration);
+    method public abstract android.content.Context createCredentialEncryptedContext(android.content.Context);
+    method public abstract android.content.Context createDeviceEncryptedContext(android.content.Context);
     method public abstract android.content.Context createDisplayContext(android.view.Display);
     method public abstract android.content.Context createPackageContext(java.lang.String, int) throws android.content.pm.PackageManager.NameNotFoundException;
     method public abstract java.lang.String[] databaseList();
@@ -7802,7 +7805,6 @@
     method public final android.content.res.ColorStateList getColorStateList(int);
     method public abstract android.content.ContentResolver getContentResolver();
     method public abstract java.io.File getDatabasePath(java.lang.String);
-    method public abstract java.io.File getDeviceEncryptedFilesDir();
     method public abstract java.io.File getDir(java.lang.String, int);
     method public final android.graphics.drawable.Drawable getDrawable(int);
     method public abstract java.io.File getExternalCacheDir();
@@ -7835,6 +7837,8 @@
     method public abstract deprecated int getWallpaperDesiredMinimumHeight();
     method public abstract deprecated int getWallpaperDesiredMinimumWidth();
     method public abstract void grantUriPermission(java.lang.String, android.net.Uri, int);
+    method public abstract boolean isCredentialEncrypted();
+    method public abstract boolean isDeviceEncrypted();
     method public boolean isRestricted();
     method public final android.content.res.TypedArray obtainStyledAttributes(int[]);
     method public final android.content.res.TypedArray obtainStyledAttributes(int, int[]) throws android.content.res.Resources.NotFoundException;
@@ -7966,6 +7970,8 @@
     method public int checkUriPermission(android.net.Uri, java.lang.String, java.lang.String, int, int, int);
     method public deprecated void clearWallpaper() throws java.io.IOException;
     method public android.content.Context createConfigurationContext(android.content.res.Configuration);
+    method public android.content.Context createCredentialEncryptedContext(android.content.Context);
+    method public android.content.Context createDeviceEncryptedContext(android.content.Context);
     method public android.content.Context createDisplayContext(android.view.Display);
     method public android.content.Context createPackageContext(java.lang.String, int) throws android.content.pm.PackageManager.NameNotFoundException;
     method public java.lang.String[] databaseList();
@@ -7988,7 +7994,6 @@
     method public java.io.File getCodeCacheDir();
     method public android.content.ContentResolver getContentResolver();
     method public java.io.File getDatabasePath(java.lang.String);
-    method public java.io.File getDeviceEncryptedFilesDir();
     method public java.io.File getDir(java.lang.String, int);
     method public java.io.File getExternalCacheDir();
     method public java.io.File[] getExternalCacheDirs();
@@ -8016,6 +8021,8 @@
     method public deprecated int getWallpaperDesiredMinimumHeight();
     method public deprecated int getWallpaperDesiredMinimumWidth();
     method public void grantUriPermission(java.lang.String, android.net.Uri, int);
+    method public boolean isCredentialEncrypted();
+    method public boolean isDeviceEncrypted();
     method public java.io.FileInputStream openFileInput(java.lang.String) throws java.io.FileNotFoundException;
     method public java.io.FileOutputStream openFileOutput(java.lang.String, int) throws java.io.FileNotFoundException;
     method public android.database.sqlite.SQLiteDatabase openOrCreateDatabase(java.lang.String, int, android.database.sqlite.SQLiteDatabase.CursorFactory);
@@ -9097,8 +9104,10 @@
     field public java.lang.String backupAgentName;
     field public java.lang.String className;
     field public int compatibleWidthLimitDp;
+    field public java.lang.String credentialEncryptedDataDir;
     field public java.lang.String dataDir;
     field public int descriptionRes;
+    field public java.lang.String deviceEncryptedDataDir;
     field public boolean enabled;
     field public int flags;
     field public int largestWidthLimitDp;
@@ -9420,6 +9429,8 @@
     method public abstract int getComponentEnabledSetting(android.content.ComponentName);
     method public abstract android.graphics.drawable.Drawable getDefaultActivityIcon();
     method public abstract android.graphics.drawable.Drawable getDrawable(java.lang.String, int, android.content.pm.ApplicationInfo);
+    method public abstract byte[] getEphemeralCookie();
+    method public abstract int getEphemeralCookieMaxSizeBytes();
     method public abstract java.util.List<android.content.pm.ApplicationInfo> getInstalledApplications(int);
     method public abstract java.util.List<android.content.pm.PackageInfo> getInstalledPackages(int);
     method public abstract java.lang.String getInstallerPackageName(java.lang.String);
@@ -9451,6 +9462,7 @@
     method public abstract java.lang.CharSequence getUserBadgedLabel(java.lang.CharSequence, android.os.UserHandle);
     method public abstract android.content.res.XmlResourceParser getXml(java.lang.String, int, android.content.pm.ApplicationInfo);
     method public abstract boolean hasSystemFeature(java.lang.String);
+    method public abstract boolean isEphemeralApplication();
     method public abstract boolean isPermissionRevokedByPolicy(java.lang.String, java.lang.String);
     method public abstract boolean isSafeMode();
     method public abstract java.util.List<android.content.pm.ResolveInfo> queryBroadcastReceivers(android.content.Intent, int);
@@ -9468,6 +9480,7 @@
     method public abstract android.content.pm.ResolveInfo resolveService(android.content.Intent, int);
     method public abstract void setApplicationEnabledSetting(java.lang.String, int, int);
     method public abstract void setComponentEnabledSetting(android.content.ComponentName, int, int);
+    method public abstract boolean setEphemeralCookie(byte[]);
     method public abstract void setInstallerPackageName(java.lang.String, java.lang.String);
     method public abstract void verifyPendingInstall(int, int);
     field public static final int COMPONENT_ENABLED_STATE_DEFAULT = 0; // 0x0
@@ -29683,6 +29696,7 @@
     field public static final java.lang.String CONTENT_TYPE = "vnd.android.cursor.dir/email_v2";
     field public static final android.net.Uri CONTENT_URI;
     field public static final java.lang.String DISPLAY_NAME = "data4";
+    field public static final android.net.Uri ENTERPRISE_CONTENT_FILTER_URI;
     field public static final android.net.Uri ENTERPRISE_CONTENT_LOOKUP_URI;
     field public static final java.lang.String EXTRA_ADDRESS_BOOK_INDEX = "android.provider.extra.ADDRESS_BOOK_INDEX";
     field public static final java.lang.String EXTRA_ADDRESS_BOOK_INDEX_COUNTS = "android.provider.extra.ADDRESS_BOOK_INDEX_COUNTS";
@@ -36234,6 +36248,8 @@
     method public int checkUriPermission(android.net.Uri, java.lang.String, java.lang.String, int, int, int);
     method public void clearWallpaper();
     method public android.content.Context createConfigurationContext(android.content.res.Configuration);
+    method public android.content.Context createCredentialEncryptedContext(android.content.Context);
+    method public android.content.Context createDeviceEncryptedContext(android.content.Context);
     method public android.content.Context createDisplayContext(android.view.Display);
     method public android.content.Context createPackageContext(java.lang.String, int) throws android.content.pm.PackageManager.NameNotFoundException;
     method public java.lang.String[] databaseList();
@@ -36255,7 +36271,6 @@
     method public java.io.File getCodeCacheDir();
     method public android.content.ContentResolver getContentResolver();
     method public java.io.File getDatabasePath(java.lang.String);
-    method public java.io.File getDeviceEncryptedFilesDir();
     method public java.io.File getDir(java.lang.String, int);
     method public java.io.File getExternalCacheDir();
     method public java.io.File[] getExternalCacheDirs();
@@ -36283,6 +36298,8 @@
     method public int getWallpaperDesiredMinimumHeight();
     method public int getWallpaperDesiredMinimumWidth();
     method public void grantUriPermission(java.lang.String, android.net.Uri, int);
+    method public boolean isCredentialEncrypted();
+    method public boolean isDeviceEncrypted();
     method public java.io.FileInputStream openFileInput(java.lang.String) throws java.io.FileNotFoundException;
     method public java.io.FileOutputStream openFileOutput(java.lang.String, int) throws java.io.FileNotFoundException;
     method public android.database.sqlite.SQLiteDatabase openOrCreateDatabase(java.lang.String, int, android.database.sqlite.SQLiteDatabase.CursorFactory);
@@ -36406,6 +36423,8 @@
     method public android.graphics.drawable.Drawable getDefaultActivityIcon();
     method public java.lang.String getDefaultBrowserPackageName(int);
     method public android.graphics.drawable.Drawable getDrawable(java.lang.String, int, android.content.pm.ApplicationInfo);
+    method public byte[] getEphemeralCookie();
+    method public int getEphemeralCookieMaxSizeBytes();
     method public java.util.List<android.content.pm.ApplicationInfo> getInstalledApplications(int);
     method public java.util.List<android.content.pm.PackageInfo> getInstalledPackages(int);
     method public java.lang.String getInstallerPackageName(java.lang.String);
@@ -36436,6 +36455,7 @@
     method public java.lang.CharSequence getUserBadgedLabel(java.lang.CharSequence, android.os.UserHandle);
     method public android.content.res.XmlResourceParser getXml(java.lang.String, int, android.content.pm.ApplicationInfo);
     method public boolean hasSystemFeature(java.lang.String);
+    method public boolean isEphemeralApplication();
     method public boolean isPermissionRevokedByPolicy(java.lang.String, java.lang.String);
     method public boolean isSafeMode();
     method public java.util.List<android.content.pm.ResolveInfo> queryBroadcastReceivers(android.content.Intent, int);
@@ -36454,6 +36474,7 @@
     method public void setApplicationEnabledSetting(java.lang.String, int, int);
     method public void setComponentEnabledSetting(android.content.ComponentName, int, int);
     method public boolean setDefaultBrowserPackageName(java.lang.String, int);
+    method public boolean setEphemeralCookie(byte[]);
     method public void setInstallerPackageName(java.lang.String, java.lang.String);
     method public void verifyPendingInstall(int, int);
   }
@@ -41823,8 +41844,6 @@
     method public abstract void setContentView(int);
     method public abstract void setContentView(android.view.View);
     method public abstract void setContentView(android.view.View, android.view.ViewGroup.LayoutParams);
-    method public abstract void setDecorView(android.view.View);
-    method public abstract void setDecorView(int);
     method protected void setDefaultWindowFormat(int);
     method public void setDimAmount(float);
     method public void setElevation(float);
diff --git a/api/system-current.txt b/api/system-current.txt
index 1a05551..40347cb 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -5935,6 +5935,7 @@
     method public void notifyPendingSystemUpdate(long);
     method public void removeActiveAdmin(android.content.ComponentName);
     method public boolean removeCrossProfileWidgetProvider(android.content.ComponentName, java.lang.String);
+    method public boolean removeKeyPair(android.content.ComponentName, java.lang.String);
     method public boolean removeUser(android.content.ComponentName, android.os.UserHandle);
     method public boolean resetPassword(java.lang.String, int);
     method public void setAccountManagementDisabled(android.content.ComponentName, java.lang.String, boolean);
@@ -8023,6 +8024,8 @@
     method public abstract int checkUriPermission(android.net.Uri, java.lang.String, java.lang.String, int, int, int);
     method public abstract deprecated void clearWallpaper() throws java.io.IOException;
     method public abstract android.content.Context createConfigurationContext(android.content.res.Configuration);
+    method public abstract android.content.Context createCredentialEncryptedContext(android.content.Context);
+    method public abstract android.content.Context createDeviceEncryptedContext(android.content.Context);
     method public abstract android.content.Context createDisplayContext(android.view.Display);
     method public abstract android.content.Context createPackageContext(java.lang.String, int) throws android.content.pm.PackageManager.NameNotFoundException;
     method public abstract java.lang.String[] databaseList();
@@ -8045,9 +8048,7 @@
     method public final int getColor(int);
     method public final android.content.res.ColorStateList getColorStateList(int);
     method public abstract android.content.ContentResolver getContentResolver();
-    method public abstract java.io.File getCredentialEncryptedFilesDir();
     method public abstract java.io.File getDatabasePath(java.lang.String);
-    method public abstract java.io.File getDeviceEncryptedFilesDir();
     method public abstract java.io.File getDir(java.lang.String, int);
     method public final android.graphics.drawable.Drawable getDrawable(int);
     method public abstract java.io.File getExternalCacheDir();
@@ -8080,6 +8081,8 @@
     method public abstract deprecated int getWallpaperDesiredMinimumHeight();
     method public abstract deprecated int getWallpaperDesiredMinimumWidth();
     method public abstract void grantUriPermission(java.lang.String, android.net.Uri, int);
+    method public abstract boolean isCredentialEncrypted();
+    method public abstract boolean isDeviceEncrypted();
     method public boolean isRestricted();
     method public final android.content.res.TypedArray obtainStyledAttributes(int[]);
     method public final android.content.res.TypedArray obtainStyledAttributes(int, int[]) throws android.content.res.Resources.NotFoundException;
@@ -8219,6 +8222,8 @@
     method public int checkUriPermission(android.net.Uri, java.lang.String, java.lang.String, int, int, int);
     method public deprecated void clearWallpaper() throws java.io.IOException;
     method public android.content.Context createConfigurationContext(android.content.res.Configuration);
+    method public android.content.Context createCredentialEncryptedContext(android.content.Context);
+    method public android.content.Context createDeviceEncryptedContext(android.content.Context);
     method public android.content.Context createDisplayContext(android.view.Display);
     method public android.content.Context createPackageContext(java.lang.String, int) throws android.content.pm.PackageManager.NameNotFoundException;
     method public java.lang.String[] databaseList();
@@ -8240,9 +8245,7 @@
     method public java.lang.ClassLoader getClassLoader();
     method public java.io.File getCodeCacheDir();
     method public android.content.ContentResolver getContentResolver();
-    method public java.io.File getCredentialEncryptedFilesDir();
     method public java.io.File getDatabasePath(java.lang.String);
-    method public java.io.File getDeviceEncryptedFilesDir();
     method public java.io.File getDir(java.lang.String, int);
     method public java.io.File getExternalCacheDir();
     method public java.io.File[] getExternalCacheDirs();
@@ -8270,6 +8273,8 @@
     method public deprecated int getWallpaperDesiredMinimumHeight();
     method public deprecated int getWallpaperDesiredMinimumWidth();
     method public void grantUriPermission(java.lang.String, android.net.Uri, int);
+    method public boolean isCredentialEncrypted();
+    method public boolean isDeviceEncrypted();
     method public java.io.FileInputStream openFileInput(java.lang.String) throws java.io.FileNotFoundException;
     method public java.io.FileOutputStream openFileOutput(java.lang.String, int) throws java.io.FileNotFoundException;
     method public android.database.sqlite.SQLiteDatabase openOrCreateDatabase(java.lang.String, int, android.database.sqlite.SQLiteDatabase.CursorFactory);
@@ -9361,8 +9366,10 @@
     field public java.lang.String backupAgentName;
     field public java.lang.String className;
     field public int compatibleWidthLimitDp;
+    field public java.lang.String credentialEncryptedDataDir;
     field public java.lang.String dataDir;
     field public int descriptionRes;
+    field public java.lang.String deviceEncryptedDataDir;
     field public boolean enabled;
     field public int flags;
     field public int largestWidthLimitDp;
@@ -9712,6 +9719,8 @@
     method public abstract int getComponentEnabledSetting(android.content.ComponentName);
     method public abstract android.graphics.drawable.Drawable getDefaultActivityIcon();
     method public abstract android.graphics.drawable.Drawable getDrawable(java.lang.String, int, android.content.pm.ApplicationInfo);
+    method public abstract byte[] getEphemeralCookie();
+    method public abstract int getEphemeralCookieMaxSizeBytes();
     method public abstract java.util.List<android.content.pm.ApplicationInfo> getInstalledApplications(int);
     method public abstract java.util.List<android.content.pm.PackageInfo> getInstalledPackages(int);
     method public abstract java.lang.String getInstallerPackageName(java.lang.String);
@@ -9745,6 +9754,7 @@
     method public abstract android.content.res.XmlResourceParser getXml(java.lang.String, int, android.content.pm.ApplicationInfo);
     method public abstract void grantRuntimePermission(java.lang.String, java.lang.String, android.os.UserHandle);
     method public abstract boolean hasSystemFeature(java.lang.String);
+    method public abstract boolean isEphemeralApplication();
     method public abstract boolean isPermissionRevokedByPolicy(java.lang.String, java.lang.String);
     method public abstract boolean isSafeMode();
     method public abstract java.util.List<android.content.pm.ResolveInfo> queryBroadcastReceivers(android.content.Intent, int);
@@ -9764,6 +9774,7 @@
     method public abstract void revokeRuntimePermission(java.lang.String, java.lang.String, android.os.UserHandle);
     method public abstract void setApplicationEnabledSetting(java.lang.String, int, int);
     method public abstract void setComponentEnabledSetting(android.content.ComponentName, int, int);
+    method public abstract boolean setEphemeralCookie(byte[]);
     method public abstract void setInstallerPackageName(java.lang.String, java.lang.String);
     method public abstract void updatePermissionFlags(java.lang.String, java.lang.String, int, int, android.os.UserHandle);
     method public abstract void verifyIntentFilter(int, int, java.util.List<java.lang.String>);
@@ -31676,6 +31687,7 @@
     field public static final java.lang.String CONTENT_TYPE = "vnd.android.cursor.dir/email_v2";
     field public static final android.net.Uri CONTENT_URI;
     field public static final java.lang.String DISPLAY_NAME = "data4";
+    field public static final android.net.Uri ENTERPRISE_CONTENT_FILTER_URI;
     field public static final android.net.Uri ENTERPRISE_CONTENT_LOOKUP_URI;
     field public static final java.lang.String EXTRA_ADDRESS_BOOK_INDEX = "android.provider.extra.ADDRESS_BOOK_INDEX";
     field public static final java.lang.String EXTRA_ADDRESS_BOOK_INDEX_COUNTS = "android.provider.extra.ADDRESS_BOOK_INDEX_COUNTS";
@@ -38560,6 +38572,8 @@
     method public int checkUriPermission(android.net.Uri, java.lang.String, java.lang.String, int, int, int);
     method public void clearWallpaper();
     method public android.content.Context createConfigurationContext(android.content.res.Configuration);
+    method public android.content.Context createCredentialEncryptedContext(android.content.Context);
+    method public android.content.Context createDeviceEncryptedContext(android.content.Context);
     method public android.content.Context createDisplayContext(android.view.Display);
     method public android.content.Context createPackageContext(java.lang.String, int) throws android.content.pm.PackageManager.NameNotFoundException;
     method public java.lang.String[] databaseList();
@@ -38580,9 +38594,7 @@
     method public java.lang.ClassLoader getClassLoader();
     method public java.io.File getCodeCacheDir();
     method public android.content.ContentResolver getContentResolver();
-    method public java.io.File getCredentialEncryptedFilesDir();
     method public java.io.File getDatabasePath(java.lang.String);
-    method public java.io.File getDeviceEncryptedFilesDir();
     method public java.io.File getDir(java.lang.String, int);
     method public java.io.File getExternalCacheDir();
     method public java.io.File[] getExternalCacheDirs();
@@ -38610,6 +38622,8 @@
     method public int getWallpaperDesiredMinimumHeight();
     method public int getWallpaperDesiredMinimumWidth();
     method public void grantUriPermission(java.lang.String, android.net.Uri, int);
+    method public boolean isCredentialEncrypted();
+    method public boolean isDeviceEncrypted();
     method public java.io.FileInputStream openFileInput(java.lang.String) throws java.io.FileNotFoundException;
     method public java.io.FileOutputStream openFileOutput(java.lang.String, int) throws java.io.FileNotFoundException;
     method public android.database.sqlite.SQLiteDatabase openOrCreateDatabase(java.lang.String, int, android.database.sqlite.SQLiteDatabase.CursorFactory);
@@ -38736,6 +38750,8 @@
     method public android.graphics.drawable.Drawable getDefaultActivityIcon();
     method public java.lang.String getDefaultBrowserPackageName(int);
     method public android.graphics.drawable.Drawable getDrawable(java.lang.String, int, android.content.pm.ApplicationInfo);
+    method public byte[] getEphemeralCookie();
+    method public int getEphemeralCookieMaxSizeBytes();
     method public java.util.List<android.content.pm.ApplicationInfo> getInstalledApplications(int);
     method public java.util.List<android.content.pm.PackageInfo> getInstalledPackages(int);
     method public java.lang.String getInstallerPackageName(java.lang.String);
@@ -38768,6 +38784,7 @@
     method public android.content.res.XmlResourceParser getXml(java.lang.String, int, android.content.pm.ApplicationInfo);
     method public void grantRuntimePermission(java.lang.String, java.lang.String, android.os.UserHandle);
     method public boolean hasSystemFeature(java.lang.String);
+    method public boolean isEphemeralApplication();
     method public boolean isPermissionRevokedByPolicy(java.lang.String, java.lang.String);
     method public boolean isSafeMode();
     method public java.util.List<android.content.pm.ResolveInfo> queryBroadcastReceivers(android.content.Intent, int);
@@ -38788,6 +38805,7 @@
     method public void setApplicationEnabledSetting(java.lang.String, int, int);
     method public void setComponentEnabledSetting(android.content.ComponentName, int, int);
     method public boolean setDefaultBrowserPackageName(java.lang.String, int);
+    method public boolean setEphemeralCookie(byte[]);
     method public void setInstallerPackageName(java.lang.String, java.lang.String);
     method public void updatePermissionFlags(java.lang.String, java.lang.String, int, int, android.os.UserHandle);
     method public void verifyIntentFilter(int, int, java.util.List<java.lang.String>);
@@ -44159,8 +44177,6 @@
     method public abstract void setContentView(int);
     method public abstract void setContentView(android.view.View);
     method public abstract void setContentView(android.view.View, android.view.ViewGroup.LayoutParams);
-    method public abstract void setDecorView(android.view.View);
-    method public abstract void setDecorView(int);
     method protected void setDefaultWindowFormat(int);
     method public void setDimAmount(float);
     method public void setDisableWallpaperTouchEvents(boolean);
diff --git a/api/test-current.txt b/api/test-current.txt
index 7be966e..8a47196 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -5802,6 +5802,7 @@
     method public void lockNow();
     method public void removeActiveAdmin(android.content.ComponentName);
     method public boolean removeCrossProfileWidgetProvider(android.content.ComponentName, java.lang.String);
+    method public boolean removeKeyPair(android.content.ComponentName, java.lang.String);
     method public boolean removeUser(android.content.ComponentName, android.os.UserHandle);
     method public boolean resetPassword(java.lang.String, int);
     method public void setAccountManagementDisabled(android.content.ComponentName, java.lang.String, boolean);
@@ -7779,6 +7780,8 @@
     method public abstract int checkUriPermission(android.net.Uri, java.lang.String, java.lang.String, int, int, int);
     method public abstract deprecated void clearWallpaper() throws java.io.IOException;
     method public abstract android.content.Context createConfigurationContext(android.content.res.Configuration);
+    method public abstract android.content.Context createCredentialEncryptedContext(android.content.Context);
+    method public abstract android.content.Context createDeviceEncryptedContext(android.content.Context);
     method public abstract android.content.Context createDisplayContext(android.view.Display);
     method public abstract android.content.Context createPackageContext(java.lang.String, int) throws android.content.pm.PackageManager.NameNotFoundException;
     method public abstract java.lang.String[] databaseList();
@@ -7802,7 +7805,6 @@
     method public final android.content.res.ColorStateList getColorStateList(int);
     method public abstract android.content.ContentResolver getContentResolver();
     method public abstract java.io.File getDatabasePath(java.lang.String);
-    method public abstract java.io.File getDeviceEncryptedFilesDir();
     method public abstract java.io.File getDir(java.lang.String, int);
     method public final android.graphics.drawable.Drawable getDrawable(int);
     method public abstract java.io.File getExternalCacheDir();
@@ -7835,6 +7837,8 @@
     method public abstract deprecated int getWallpaperDesiredMinimumHeight();
     method public abstract deprecated int getWallpaperDesiredMinimumWidth();
     method public abstract void grantUriPermission(java.lang.String, android.net.Uri, int);
+    method public abstract boolean isCredentialEncrypted();
+    method public abstract boolean isDeviceEncrypted();
     method public boolean isRestricted();
     method public final android.content.res.TypedArray obtainStyledAttributes(int[]);
     method public final android.content.res.TypedArray obtainStyledAttributes(int, int[]) throws android.content.res.Resources.NotFoundException;
@@ -7966,6 +7970,8 @@
     method public int checkUriPermission(android.net.Uri, java.lang.String, java.lang.String, int, int, int);
     method public deprecated void clearWallpaper() throws java.io.IOException;
     method public android.content.Context createConfigurationContext(android.content.res.Configuration);
+    method public android.content.Context createCredentialEncryptedContext(android.content.Context);
+    method public android.content.Context createDeviceEncryptedContext(android.content.Context);
     method public android.content.Context createDisplayContext(android.view.Display);
     method public android.content.Context createPackageContext(java.lang.String, int) throws android.content.pm.PackageManager.NameNotFoundException;
     method public java.lang.String[] databaseList();
@@ -7988,7 +7994,6 @@
     method public java.io.File getCodeCacheDir();
     method public android.content.ContentResolver getContentResolver();
     method public java.io.File getDatabasePath(java.lang.String);
-    method public java.io.File getDeviceEncryptedFilesDir();
     method public java.io.File getDir(java.lang.String, int);
     method public java.io.File getExternalCacheDir();
     method public java.io.File[] getExternalCacheDirs();
@@ -8016,6 +8021,8 @@
     method public deprecated int getWallpaperDesiredMinimumHeight();
     method public deprecated int getWallpaperDesiredMinimumWidth();
     method public void grantUriPermission(java.lang.String, android.net.Uri, int);
+    method public boolean isCredentialEncrypted();
+    method public boolean isDeviceEncrypted();
     method public java.io.FileInputStream openFileInput(java.lang.String) throws java.io.FileNotFoundException;
     method public java.io.FileOutputStream openFileOutput(java.lang.String, int) throws java.io.FileNotFoundException;
     method public android.database.sqlite.SQLiteDatabase openOrCreateDatabase(java.lang.String, int, android.database.sqlite.SQLiteDatabase.CursorFactory);
@@ -9097,8 +9104,10 @@
     field public java.lang.String backupAgentName;
     field public java.lang.String className;
     field public int compatibleWidthLimitDp;
+    field public java.lang.String credentialEncryptedDataDir;
     field public java.lang.String dataDir;
     field public int descriptionRes;
+    field public java.lang.String deviceEncryptedDataDir;
     field public boolean enabled;
     field public int flags;
     field public int largestWidthLimitDp;
@@ -9420,6 +9429,8 @@
     method public abstract int getComponentEnabledSetting(android.content.ComponentName);
     method public abstract android.graphics.drawable.Drawable getDefaultActivityIcon();
     method public abstract android.graphics.drawable.Drawable getDrawable(java.lang.String, int, android.content.pm.ApplicationInfo);
+    method public abstract byte[] getEphemeralCookie();
+    method public abstract int getEphemeralCookieMaxSizeBytes();
     method public abstract java.util.List<android.content.pm.ApplicationInfo> getInstalledApplications(int);
     method public abstract java.util.List<android.content.pm.PackageInfo> getInstalledPackages(int);
     method public abstract java.lang.String getInstallerPackageName(java.lang.String);
@@ -9451,6 +9462,7 @@
     method public abstract java.lang.CharSequence getUserBadgedLabel(java.lang.CharSequence, android.os.UserHandle);
     method public abstract android.content.res.XmlResourceParser getXml(java.lang.String, int, android.content.pm.ApplicationInfo);
     method public abstract boolean hasSystemFeature(java.lang.String);
+    method public abstract boolean isEphemeralApplication();
     method public abstract boolean isPermissionRevokedByPolicy(java.lang.String, java.lang.String);
     method public abstract boolean isSafeMode();
     method public abstract java.util.List<android.content.pm.ResolveInfo> queryBroadcastReceivers(android.content.Intent, int);
@@ -9468,6 +9480,7 @@
     method public abstract android.content.pm.ResolveInfo resolveService(android.content.Intent, int);
     method public abstract void setApplicationEnabledSetting(java.lang.String, int, int);
     method public abstract void setComponentEnabledSetting(android.content.ComponentName, int, int);
+    method public abstract boolean setEphemeralCookie(byte[]);
     method public abstract void setInstallerPackageName(java.lang.String, java.lang.String);
     method public abstract void verifyPendingInstall(int, int);
     field public static final int COMPONENT_ENABLED_STATE_DEFAULT = 0; // 0x0
@@ -29685,6 +29698,7 @@
     field public static final java.lang.String CONTENT_TYPE = "vnd.android.cursor.dir/email_v2";
     field public static final android.net.Uri CONTENT_URI;
     field public static final java.lang.String DISPLAY_NAME = "data4";
+    field public static final android.net.Uri ENTERPRISE_CONTENT_FILTER_URI;
     field public static final android.net.Uri ENTERPRISE_CONTENT_LOOKUP_URI;
     field public static final java.lang.String EXTRA_ADDRESS_BOOK_INDEX = "android.provider.extra.ADDRESS_BOOK_INDEX";
     field public static final java.lang.String EXTRA_ADDRESS_BOOK_INDEX_COUNTS = "android.provider.extra.ADDRESS_BOOK_INDEX_COUNTS";
@@ -36236,6 +36250,8 @@
     method public int checkUriPermission(android.net.Uri, java.lang.String, java.lang.String, int, int, int);
     method public void clearWallpaper();
     method public android.content.Context createConfigurationContext(android.content.res.Configuration);
+    method public android.content.Context createCredentialEncryptedContext(android.content.Context);
+    method public android.content.Context createDeviceEncryptedContext(android.content.Context);
     method public android.content.Context createDisplayContext(android.view.Display);
     method public android.content.Context createPackageContext(java.lang.String, int) throws android.content.pm.PackageManager.NameNotFoundException;
     method public java.lang.String[] databaseList();
@@ -36257,7 +36273,6 @@
     method public java.io.File getCodeCacheDir();
     method public android.content.ContentResolver getContentResolver();
     method public java.io.File getDatabasePath(java.lang.String);
-    method public java.io.File getDeviceEncryptedFilesDir();
     method public java.io.File getDir(java.lang.String, int);
     method public java.io.File getExternalCacheDir();
     method public java.io.File[] getExternalCacheDirs();
@@ -36285,6 +36300,8 @@
     method public int getWallpaperDesiredMinimumHeight();
     method public int getWallpaperDesiredMinimumWidth();
     method public void grantUriPermission(java.lang.String, android.net.Uri, int);
+    method public boolean isCredentialEncrypted();
+    method public boolean isDeviceEncrypted();
     method public java.io.FileInputStream openFileInput(java.lang.String) throws java.io.FileNotFoundException;
     method public java.io.FileOutputStream openFileOutput(java.lang.String, int) throws java.io.FileNotFoundException;
     method public android.database.sqlite.SQLiteDatabase openOrCreateDatabase(java.lang.String, int, android.database.sqlite.SQLiteDatabase.CursorFactory);
@@ -36408,6 +36425,8 @@
     method public android.graphics.drawable.Drawable getDefaultActivityIcon();
     method public java.lang.String getDefaultBrowserPackageName(int);
     method public android.graphics.drawable.Drawable getDrawable(java.lang.String, int, android.content.pm.ApplicationInfo);
+    method public byte[] getEphemeralCookie();
+    method public int getEphemeralCookieMaxSizeBytes();
     method public java.util.List<android.content.pm.ApplicationInfo> getInstalledApplications(int);
     method public java.util.List<android.content.pm.PackageInfo> getInstalledPackages(int);
     method public java.lang.String getInstallerPackageName(java.lang.String);
@@ -36438,6 +36457,7 @@
     method public java.lang.CharSequence getUserBadgedLabel(java.lang.CharSequence, android.os.UserHandle);
     method public android.content.res.XmlResourceParser getXml(java.lang.String, int, android.content.pm.ApplicationInfo);
     method public boolean hasSystemFeature(java.lang.String);
+    method public boolean isEphemeralApplication();
     method public boolean isPermissionRevokedByPolicy(java.lang.String, java.lang.String);
     method public boolean isSafeMode();
     method public java.util.List<android.content.pm.ResolveInfo> queryBroadcastReceivers(android.content.Intent, int);
@@ -36456,6 +36476,7 @@
     method public void setApplicationEnabledSetting(java.lang.String, int, int);
     method public void setComponentEnabledSetting(android.content.ComponentName, int, int);
     method public boolean setDefaultBrowserPackageName(java.lang.String, int);
+    method public boolean setEphemeralCookie(byte[]);
     method public void setInstallerPackageName(java.lang.String, java.lang.String);
     method public void verifyPendingInstall(int, int);
   }
@@ -41825,8 +41846,6 @@
     method public abstract void setContentView(int);
     method public abstract void setContentView(android.view.View);
     method public abstract void setContentView(android.view.View, android.view.ViewGroup.LayoutParams);
-    method public abstract void setDecorView(android.view.View);
-    method public abstract void setDecorView(int);
     method protected void setDefaultWindowFormat(int);
     method public void setDimAmount(float);
     method public void setElevation(float);
diff --git a/cmds/pm/src/com/android/commands/pm/Pm.java b/cmds/pm/src/com/android/commands/pm/Pm.java
index 544fd6a..ad1b3b5 100644
--- a/cmds/pm/src/com/android/commands/pm/Pm.java
+++ b/cmds/pm/src/com/android/commands/pm/Pm.java
@@ -16,11 +16,11 @@
 
 package com.android.commands.pm;
 
-import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED;
-import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ASK;
 import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS;
 import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS_ASK;
+import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ASK;
 import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER;
+import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED;
 
 import android.accounts.IAccountManager;
 import android.app.ActivityManager;
@@ -139,19 +139,19 @@
         }
 
         if ("install-create".equals(op)) {
-            return runInstallSession();
+            return runInstallCreate();
         }
 
         if ("install-write".equals(op)) {
-            return runInstallSession();
+            return runInstallWrite();
         }
 
         if ("install-commit".equals(op)) {
-            return runInstallSession();
+            return runInstallCommit();
         }
 
         if ("install-abandon".equals(op) || "install-destroy".equals(op)) {
-            return runInstallSession();
+            return runInstallAbandon();
         }
 
         if ("set-installer".equals(op)) {
@@ -346,21 +346,203 @@
         throw new IllegalArgumentException("ABI " + abi + " not supported on this device");
     }
 
-    private void writeSessionFile(int sessionId, String path, long sizeBytes) throws RemoteException {
+    /*
+     * Keep this around to support existing users of the "pm install" command that may not be
+     * able to be updated [or, at least informed the API has changed] such as ddmlib.
+     *
+     * Moving the implementation of "pm install" to "cmd package install" changes the executing
+     * context. Instead of being a stand alone process, "cmd package install" runs in the
+     * system_server process. Due to SELinux rules, system_server cannot access many directories;
+     * one of which being the package install staging directory [/data/local/tmp].
+     *
+     * The use of "adb install" or "cmd package install" over "pm install" is highly encouraged.
+     */
+    private int runInstall() throws RemoteException {
+        final InstallParams params = makeInstallParams();
+        final int sessionId = doCreateSession(params.sessionParams,
+                params.installerPackageName, params.userId);
+
+        try {
+            final String inPath = nextArg();
+            if (inPath == null && params.sessionParams.sizeBytes == 0) {
+                System.err.println("Error: must either specify a package size or an APK file");
+                return 1;
+            }
+            if (doWriteSession(sessionId, inPath, params.sessionParams.sizeBytes, "base.apk",
+                    false /*logSuccess*/) != PackageInstaller.STATUS_SUCCESS) {
+                return 1;
+            }
+            if (doCommitSession(sessionId, false /*logSuccess*/)
+                    != PackageInstaller.STATUS_SUCCESS) {
+                return 1;
+            }
+            return 0;
+        } finally {
+            try {
+                mInstaller.abandonSession(sessionId);
+            } catch (Exception ignore) {
+            }
+        }
+    }
+
+    private int runInstallAbandon() throws RemoteException {
+        final int sessionId = Integer.parseInt(nextArg());
+        return doAbandonSession(sessionId, true /*logSuccess*/);
+    }
+
+    private int runInstallCommit() throws RemoteException {
+        final int sessionId = Integer.parseInt(nextArg());
+        return doCommitSession(sessionId, true /*logSuccess*/);
+    }
+
+    private int runInstallCreate() throws RemoteException {
+        final InstallParams installParams = makeInstallParams();
+        final int sessionId = doCreateSession(installParams.sessionParams,
+                installParams.installerPackageName, installParams.userId);
+
+        // NOTE: adb depends on parsing this string
+        System.out.println("Success: created install session [" + sessionId + "]");
+        return PackageInstaller.STATUS_SUCCESS;
+    }
+
+    private int runInstallWrite() throws RemoteException {
+        long sizeBytes = -1;
+
+        String opt;
+        while ((opt = nextOption()) != null) {
+            if (opt.equals("-S")) {
+                sizeBytes = Long.parseLong(nextArg());
+            } else {
+                throw new IllegalArgumentException("Unknown option: " + opt);
+            }
+        }
+
+        final int sessionId = Integer.parseInt(nextArg());
+        final String splitName = nextArg();
+        final String path = nextArg();
+        return doWriteSession(sessionId, path, sizeBytes, splitName, true /*logSuccess*/);
+    }
+
+    private static class InstallParams {
+        SessionParams sessionParams;
+        String installerPackageName;
+        int userId = UserHandle.USER_ALL;
+    }
+
+    private InstallParams makeInstallParams() {
+        final SessionParams sessionParams = new SessionParams(SessionParams.MODE_FULL_INSTALL);
+        final InstallParams params = new InstallParams();
+        params.sessionParams = sessionParams;
+        String opt;
+        while ((opt = nextOption()) != null) {
+            switch (opt) {
+                case "-l":
+                    sessionParams.installFlags |= PackageManager.INSTALL_FORWARD_LOCK;
+                    break;
+                case "-r":
+                    sessionParams.installFlags |= PackageManager.INSTALL_REPLACE_EXISTING;
+                    break;
+                case "-i":
+                    params.installerPackageName = nextArg();
+                    if (params.installerPackageName == null) {
+                        throw new IllegalArgumentException("Missing installer package");
+                    }
+                    break;
+                case "-t":
+                    sessionParams.installFlags |= PackageManager.INSTALL_ALLOW_TEST;
+                    break;
+                case "-s":
+                    sessionParams.installFlags |= PackageManager.INSTALL_EXTERNAL;
+                    break;
+                case "-f":
+                    sessionParams.installFlags |= PackageManager.INSTALL_INTERNAL;
+                    break;
+                case "-d":
+                    sessionParams.installFlags |= PackageManager.INSTALL_ALLOW_DOWNGRADE;
+                    break;
+                case "-g":
+                    sessionParams.installFlags |= PackageManager.INSTALL_GRANT_RUNTIME_PERMISSIONS;
+                    break;
+                case "--originating-uri":
+                    sessionParams.originatingUri = Uri.parse(nextOptionData());
+                    break;
+                case "--referrer":
+                    sessionParams.referrerUri = Uri.parse(nextOptionData());
+                    break;
+                case "-p":
+                    sessionParams.mode = SessionParams.MODE_INHERIT_EXISTING;
+                    sessionParams.appPackageName = nextOptionData();
+                    if (sessionParams.appPackageName == null) {
+                        throw new IllegalArgumentException("Missing inherit package name");
+                    }
+                    break;
+                case "-S":
+                    sessionParams.setSize(Long.parseLong(nextOptionData()));
+                    break;
+                case "--abi":
+                    sessionParams.abiOverride = checkAbiArgument(nextOptionData());
+                    break;
+                case "--ephemeral":
+                    sessionParams.installFlags |= PackageManager.INSTALL_EPHEMERAL;
+                    break;
+                case "--user":
+                    params.userId = UserHandle.parseUserArg(nextOptionData());
+                    break;
+                case "--install-location":
+                    sessionParams.installLocation = Integer.parseInt(nextOptionData());
+                    break;
+                case "--force-uuid":
+                    sessionParams.installFlags |= PackageManager.INSTALL_FORCE_VOLUME_UUID;
+                    sessionParams.volumeUuid = nextOptionData();
+                    if ("internal".equals(sessionParams.volumeUuid)) {
+                        sessionParams.volumeUuid = null;
+                    }
+                    break;
+                default:
+                    throw new IllegalArgumentException("Unknown option " + opt);
+            }
+        }
+        return params;
+    }
+
+    private int doCreateSession(SessionParams params, String installerPackageName, int userId)
+            throws RemoteException {
+        userId = translateUserId(userId, "runInstallCreate");
+        if (userId == UserHandle.USER_ALL) {
+            userId = UserHandle.USER_SYSTEM;
+            params.installFlags |= PackageManager.INSTALL_ALL_USERS;
+        }
+
+        final int sessionId = mInstaller.createSession(params, installerPackageName, userId);
+        return sessionId;
+    }
+
+    private int doWriteSession(int sessionId, String inPath, long sizeBytes, String splitName,
+            boolean logSuccess) throws RemoteException {
+        if ("-".equals(inPath)) {
+            inPath = null;
+        } else if (inPath != null) {
+            final File file = new File(inPath);
+            if (file.isFile()) {
+                sizeBytes = file.length();
+            }
+        }
+
         final SessionInfo info = mInstaller.getSessionInfo(sessionId);
 
         PackageInstaller.Session session = null;
         InputStream in = null;
         OutputStream out = null;
         try {
-            session = new PackageInstaller.Session(mInstaller.openSession(sessionId));
+            session = new PackageInstaller.Session(
+                    mInstaller.openSession(sessionId));
 
-            if (path == null) {
-                in = new SizedInputStream(System.in, sizeBytes);
+            if (inPath != null) {
+                in = new FileInputStream(inPath);
             } else {
-                in = new FileInputStream(path);
+                in = new SizedInputStream(System.in, sizeBytes);
             }
-            out = session.openWrite("base.apk", 0, sizeBytes);
+            out = session.openWrite(splitName, 0, sizeBytes);
 
             int total = 0;
             byte[] buffer = new byte[65536];
@@ -375,7 +557,14 @@
                 }
             }
             session.fsync(out);
-        } catch (IOException ignore) {
+
+            if (logSuccess) {
+                System.out.println("Success: streamed " + total + " bytes");
+            }
+            return PackageInstaller.STATUS_SUCCESS;
+        } catch (IOException e) {
+            System.err.println("Error: failed to write; " + e.getMessage());
+            return PackageInstaller.STATUS_FAILURE;
         } finally {
             IoUtils.closeQuietly(out);
             IoUtils.closeQuietly(in);
@@ -383,10 +572,11 @@
         }
     }
 
-    private int commitSessionFile(int sessionId) throws RemoteException {
+    private int doCommitSession(int sessionId, boolean logSuccess) throws RemoteException {
         PackageInstaller.Session session = null;
         try {
-            session = new PackageInstaller.Session(mInstaller.openSession(sessionId));
+            session = new PackageInstaller.Session(
+                    mInstaller.openSession(sessionId));
 
             final LocalIntentReceiver receiver = new LocalIntentReceiver();
             session.commit(receiver.getIntentSender());
@@ -395,10 +585,13 @@
             final int status = result.getIntExtra(PackageInstaller.EXTRA_STATUS,
                     PackageInstaller.STATUS_FAILURE);
             if (status == PackageInstaller.STATUS_SUCCESS) {
-                System.out.println("Success");
+                if (logSuccess) {
+                    System.out.println("Success");
+                }
             } else {
                 System.err.println("Failure ["
                         + result.getStringExtra(PackageInstaller.EXTRA_STATUS_MESSAGE) + "]");
+                System.err.println("Failure details: " + result.getExtras());
             }
             return status;
         } finally {
@@ -406,100 +599,18 @@
         }
     }
 
-    /*
-     * Keep this around to support existing users of the "pm install" command that may not be
-     * able to be updated [or, at least informed the API has changed] such as ddmlib.
-     *
-     * Moving the implementation of "pm install" to "cmd package install" changes the executing
-     * context. Instead of being a stand alone process, "cmd package install" runs in the
-     * system_server process. Due to SELinux rules, system_server cannot access many directories;
-     * one of which being the package install staging directory [/data/local/tmp].
-     *
-     * The use of "adb install" or "cmd package install" over "pm install" is highly encouraged.
-     */
-    private int runInstall() throws RemoteException {
-        int userId = UserHandle.USER_ALL;
-        String installerPackageName = null;
-
-        final SessionParams params = new SessionParams(SessionParams.MODE_FULL_INSTALL);
-
-        String opt;
-        while ((opt = nextOption()) != null) {
-            if (opt.equals("-l")) {
-                params.installFlags |= PackageManager.INSTALL_FORWARD_LOCK;
-            } else if (opt.equals("-r")) {
-                params.installFlags |= PackageManager.INSTALL_REPLACE_EXISTING;
-            } else if (opt.equals("-i")) {
-                installerPackageName = nextArg();
-                if (installerPackageName == null) {
-                    throw new IllegalArgumentException("Missing installer package");
-                }
-            } else if (opt.equals("-t")) {
-                params.installFlags |= PackageManager.INSTALL_ALLOW_TEST;
-            } else if (opt.equals("-s")) {
-                params.installFlags |= PackageManager.INSTALL_EXTERNAL;
-            } else if (opt.equals("-f")) {
-                params.installFlags |= PackageManager.INSTALL_INTERNAL;
-            } else if (opt.equals("-d")) {
-                params.installFlags |= PackageManager.INSTALL_ALLOW_DOWNGRADE;
-            } else if (opt.equals("-g")) {
-                params.installFlags |= PackageManager.INSTALL_GRANT_RUNTIME_PERMISSIONS;
-            } else if (opt.equals("--originating-uri")) {
-                params.originatingUri = Uri.parse(nextOptionData());
-            } else if (opt.equals("--referrer")) {
-                params.referrerUri = Uri.parse(nextOptionData());
-            } else if (opt.equals("-p")) {
-                params.mode = SessionParams.MODE_INHERIT_EXISTING;
-                params.appPackageName = nextOptionData();
-                if (params.appPackageName == null) {
-                    throw new IllegalArgumentException("Missing inherit package name");
-                }
-            } else if (opt.equals("-S")) {
-                params.setSize(Long.parseLong(nextOptionData()));
-            } else if (opt.equals("--abi")) {
-                params.abiOverride = checkAbiArgument(nextOptionData());
-            } else if (opt.equals("--ephemeral")) {
-                params.installFlags |= PackageManager.INSTALL_EPHEMERAL;
-            } else if (opt.equals("--user")) {
-                userId = Integer.parseInt(nextOptionData());
-            } else if (opt.equals("--install-location")) {
-                params.installLocation = Integer.parseInt(nextOptionData());
-            } else if (opt.equals("--force-uuid")) {
-                params.installFlags |= PackageManager.INSTALL_FORCE_VOLUME_UUID;
-                params.volumeUuid = nextOptionData();
-                if ("internal".equals(params.volumeUuid)) {
-                    params.volumeUuid = null;
-                }
-            } else {
-                throw new IllegalArgumentException("Unknown option " + opt);
+    private int doAbandonSession(int sessionId, boolean logSuccess) throws RemoteException {
+        PackageInstaller.Session session = null;
+        try {
+            session = new PackageInstaller.Session(mInstaller.openSession(sessionId));
+            session.abandon();
+            if (logSuccess) {
+                System.out.println("Success");
             }
+            return PackageInstaller.STATUS_SUCCESS;
+        } finally {
+            IoUtils.closeQuietly(session);
         }
-
-        userId = translateUserId(userId, "runInstallCreate");
-        if (userId == UserHandle.USER_ALL) {
-            userId = UserHandle.USER_SYSTEM;
-            params.installFlags |= PackageManager.INSTALL_ALL_USERS;
-        }
-
-        long sizeBytes = params.sizeBytes;
-        String path = nextArg();
-        if ("-".equals(path)) {
-            path = null;
-        } else if (path != null) {
-            final File file = new File(path);
-            if (file.isFile()) {
-                sizeBytes = file.length();
-            }
-        }
-
-        final int sessionId = mInstaller.createSession(params, installerPackageName, userId);
-
-        writeSessionFile(sessionId, path, sizeBytes);
-        return commitSessionFile(sessionId);
-    }
-
-    private int runInstallSession() {
-        return runShellCommand("package", mArgs);
     }
 
     /**
@@ -798,6 +909,10 @@
                 flags |= UserInfo.FLAG_MANAGED_PROFILE;
             } else if ("--restricted".equals(opt)) {
                 flags |= UserInfo.FLAG_RESTRICTED;
+            } else if ("--ephemeral".equals(opt)) {
+                flags |= UserInfo.FLAG_EPHEMERAL;
+            } else if ("--guest".equals(opt)) {
+                flags |= UserInfo.FLAG_GUEST;
             } else {
                 System.err.println("Error: unknown option " + opt);
                 return showUsage();
@@ -1356,7 +1471,7 @@
         System.err.println("       pm get-install-location");
         System.err.println("       pm set-permission-enforced PERMISSION [true|false]");
         System.err.println("       pm trim-caches DESIRED_FREE_SPACE [internal|UUID]");
-        System.err.println("       pm create-user [--profileOf USER_ID] [--managed] [--restricted] USER_NAME");
+        System.err.println("       pm create-user [--profileOf USER_ID] [--managed] [--restricted] [--ephemeral] [--guest] USER_NAME");
         System.err.println("       pm remove-user USER_ID");
         System.err.println("       pm get-max-users");
         System.err.println("");
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index e500e15..460e68c 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -30,6 +30,7 @@
 import android.content.pm.ApplicationInfo;
 import android.content.pm.ComponentInfo;
 import android.content.pm.ContainerEncryptionParams;
+import android.content.pm.EphemeralApplicationInfo;
 import android.content.pm.FeatureInfo;
 import android.content.pm.IOnPermissionsChangeListener;
 import android.content.pm.IPackageDataObserver;
@@ -85,9 +86,11 @@
 import com.android.internal.os.SomeArgs;
 import com.android.internal.util.Preconditions;
 import com.android.internal.util.UserIcons;
+import libcore.util.EmptyArray;
 
 import java.lang.ref.WeakReference;
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
@@ -98,6 +101,8 @@
     private static final String TAG = "ApplicationPackageManager";
     private final static boolean DEBUG_ICONS = false;
 
+    private static final int DEFAULT_EPHEMERAL_COOKIE_MAX_SIZE_BYTES = 16384; // 16KB
+
     // Default flags to use with PackageManager when no flags are given.
     private final static int sDefaultFlags = PackageManager.GET_SHARED_LIBRARY_FILES;
 
@@ -626,6 +631,80 @@
         }
     }
 
+    /** @hide */
+    @SuppressWarnings("unchecked")
+    @Override
+    public List<EphemeralApplicationInfo> getEphemeralApplications() {
+        try {
+            ParceledListSlice<EphemeralApplicationInfo> slice =
+                    mPM.getEphemeralApplications(mContext.getUserId());
+            if (slice != null) {
+                return slice.getList();
+            }
+            return Collections.emptyList();
+        } catch (RemoteException e) {
+            throw new RuntimeException("Package manager has died", e);
+        }
+    }
+
+    /** @hide */
+    @Override
+    public Drawable getEphemeralApplicationIcon(String packageName) {
+        try {
+            Bitmap bitmap = mPM.getEphemeralApplicationIcon(
+                    packageName, mContext.getUserId());
+            if (bitmap != null) {
+                return new BitmapDrawable(null, bitmap);
+            }
+            return null;
+        } catch (RemoteException e) {
+            throw new RuntimeException("Package manager has died", e);
+        }
+    }
+
+    @Override
+    public boolean isEphemeralApplication() {
+        try {
+            return mPM.isEphemeralApplication(
+                    mContext.getPackageName(), mContext.getUserId());
+        } catch (RemoteException e) {
+            Log.e(TAG, "System server is dead", e);
+        }
+        return false;
+    }
+
+    @Override
+    public int getEphemeralCookieMaxSizeBytes() {
+        return Settings.Global.getInt(mContext.getContentResolver(),
+                Settings.Global.EPHEMERAL_COOKIE_MAX_SIZE_BYTES,
+                DEFAULT_EPHEMERAL_COOKIE_MAX_SIZE_BYTES);
+    }
+
+    @Override
+    public @NonNull byte[] getEphemeralCookie() {
+        try {
+            final byte[] cookie = mPM.getEphemeralApplicationCookie(
+                    mContext.getPackageName(), mContext.getUserId());
+            if (cookie != null) {
+                return cookie;
+            }
+        } catch (RemoteException e) {
+            Log.e(TAG, "System server is dead", e);
+        }
+        return EmptyArray.BYTE;
+    }
+
+    @Override
+    public boolean setEphemeralCookie(@NonNull  byte[] cookie) {
+        try {
+            return mPM.setEphemeralApplicationCookie(
+                    mContext.getPackageName(), cookie, mContext.getUserId());
+        } catch (RemoteException e) {
+            Log.e(TAG, "System server is dead", e);
+        }
+        return false;
+    }
+
     @Override
     public ResolveInfo resolveActivity(Intent intent, int flags) {
         return resolveActivityAsUser(intent, flags, mContext.getUserId());
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index a703c53..36e98f9 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -74,6 +74,7 @@
 import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
+import java.util.Objects;
 
 class ReceiverRestrictedContext extends ContextWrapper {
     ReceiverRestrictedContext(Context base) {
@@ -148,7 +149,7 @@
     private final Display mDisplay; // may be null if default display
     private final DisplayAdjustments mDisplayAdjustments = new DisplayAdjustments();
 
-    private final boolean mRestricted;
+    private final int mFlags;
 
     private Context mOuterContext;
     private int mThemeResource = 0;
@@ -443,22 +444,6 @@
     }
 
     @Override
-    public File getDeviceEncryptedFilesDir() {
-        if (mPackageInfo != null) {
-            return mPackageInfo.getDeviceEncryptedDataDirFile();
-        }
-        throw new RuntimeException("Not supported in system context");
-    }
-
-    @Override
-    public File getCredentialEncryptedFilesDir() {
-        if (mPackageInfo != null) {
-            return mPackageInfo.getCredentialEncryptedDataDirFile();
-        }
-        throw new RuntimeException("Not supported in system context");
-    }
-
-    @Override
     public File getNoBackupFilesDir() {
         synchronized (mSync) {
             if (mNoBackupFilesDir == null) {
@@ -1684,9 +1669,8 @@
         LoadedApk pi = mMainThread.getPackageInfo(application, mResources.getCompatibilityInfo(),
                 flags | CONTEXT_REGISTER_PACKAGE);
         if (pi != null) {
-            final boolean restricted = (flags & CONTEXT_RESTRICTED) == CONTEXT_RESTRICTED;
             ContextImpl c = new ContextImpl(this, mMainThread, pi, mActivityToken,
-                    new UserHandle(UserHandle.getUserId(application.uid)), restricted,
+                    new UserHandle(UserHandle.getUserId(application.uid)), flags,
                     mDisplay, null, Display.INVALID_DISPLAY);
             if (c.mResources != null) {
                 return c;
@@ -1707,17 +1691,16 @@
     @Override
     public Context createPackageContextAsUser(String packageName, int flags, UserHandle user)
             throws NameNotFoundException {
-        final boolean restricted = (flags & CONTEXT_RESTRICTED) == CONTEXT_RESTRICTED;
         if (packageName.equals("system") || packageName.equals("android")) {
             return new ContextImpl(this, mMainThread, mPackageInfo, mActivityToken,
-                    user, restricted, mDisplay, null, Display.INVALID_DISPLAY);
+                    user, flags, mDisplay, null, Display.INVALID_DISPLAY);
         }
 
         LoadedApk pi = mMainThread.getPackageInfo(packageName, mResources.getCompatibilityInfo(),
                 flags | CONTEXT_REGISTER_PACKAGE, user.getIdentifier());
         if (pi != null) {
             ContextImpl c = new ContextImpl(this, mMainThread, pi, mActivityToken,
-                    user, restricted, mDisplay, null, Display.INVALID_DISPLAY);
+                    user, flags, mDisplay, null, Display.INVALID_DISPLAY);
             if (c.mResources != null) {
                 return c;
             }
@@ -1735,7 +1718,7 @@
         }
 
         return new ContextImpl(this, mMainThread, mPackageInfo, mActivityToken,
-                mUser, mRestricted, mDisplay, overrideConfiguration, Display.INVALID_DISPLAY);
+                mUser, mFlags, mDisplay, overrideConfiguration, Display.INVALID_DISPLAY);
     }
 
     @Override
@@ -1745,7 +1728,7 @@
         }
 
         return new ContextImpl(this, mMainThread, mPackageInfo, mActivityToken,
-                mUser, mRestricted, display, null, Display.INVALID_DISPLAY);
+                mUser, mFlags, display, null, Display.INVALID_DISPLAY);
     }
 
     Display getDisplay() {
@@ -1761,8 +1744,34 @@
     }
 
     @Override
+    public Context createDeviceEncryptedContext(Context context) {
+        final int flags = (mFlags & ~Context.CONTEXT_STORAGE_CREDENTIAL_ENCRYPTED)
+                | Context.CONTEXT_STORAGE_DEVICE_ENCRYPTED;
+        return new ContextImpl(this, mMainThread, mPackageInfo, mActivityToken,
+                mUser, flags, mDisplay, null, Display.INVALID_DISPLAY);
+    }
+
+    @Override
+    public Context createCredentialEncryptedContext(Context context) {
+        final int flags = (mFlags & ~Context.CONTEXT_STORAGE_DEVICE_ENCRYPTED)
+                | Context.CONTEXT_STORAGE_CREDENTIAL_ENCRYPTED;
+        return new ContextImpl(this, mMainThread, mPackageInfo, mActivityToken,
+                mUser, flags, mDisplay, null, Display.INVALID_DISPLAY);
+    }
+
+    @Override
     public boolean isRestricted() {
-        return mRestricted;
+        return (mFlags & Context.CONTEXT_RESTRICTED) != 0;
+    }
+
+    @Override
+    public boolean isDeviceEncrypted() {
+        return (mFlags & Context.CONTEXT_STORAGE_DEVICE_ENCRYPTED) != 0;
+    }
+
+    @Override
+    public boolean isCredentialEncrypted() {
+        return (mFlags & Context.CONTEXT_STORAGE_CREDENTIAL_ENCRYPTED) != 0;
     }
 
     @Override
@@ -1772,7 +1781,14 @@
 
     private File getDataDirFile() {
         if (mPackageInfo != null) {
-            return mPackageInfo.getDataDirFile();
+            if (isCredentialEncrypted()) {
+                return mPackageInfo.getCredentialEncryptedDataDirFile();
+            } else if (isDeviceEncrypted()) {
+                return mPackageInfo.getDeviceEncryptedDataDirFile();
+            } else {
+                throw new RuntimeException(
+                        "Storage location is neither credential nor device encrypted");
+            }
         }
         throw new RuntimeException("Not supported in system context");
     }
@@ -1798,7 +1814,7 @@
     static ContextImpl createSystemContext(ActivityThread mainThread) {
         LoadedApk packageInfo = new LoadedApk(mainThread);
         ContextImpl context = new ContextImpl(null, mainThread,
-                packageInfo, null, null, false, null, null, Display.INVALID_DISPLAY);
+                packageInfo, null, null, 0, null, null, Display.INVALID_DISPLAY);
         context.mResources.updateConfiguration(context.mResourcesManager.getConfiguration(),
                 context.mResourcesManager.getDisplayMetricsLocked());
         return context;
@@ -1807,24 +1823,38 @@
     static ContextImpl createAppContext(ActivityThread mainThread, LoadedApk packageInfo) {
         if (packageInfo == null) throw new IllegalArgumentException("packageInfo");
         return new ContextImpl(null, mainThread,
-                packageInfo, null, null, false, null, null, Display.INVALID_DISPLAY);
+                packageInfo, null, null, 0, null, null, Display.INVALID_DISPLAY);
     }
 
     static ContextImpl createActivityContext(ActivityThread mainThread,
             LoadedApk packageInfo, int displayId, Configuration overrideConfiguration) {
         if (packageInfo == null) throw new IllegalArgumentException("packageInfo");
-        return new ContextImpl(null, mainThread, packageInfo, null, null, false,
+        return new ContextImpl(null, mainThread, packageInfo, null, null, 0,
                 null, overrideConfiguration, displayId);
     }
 
     private ContextImpl(ContextImpl container, ActivityThread mainThread,
-            LoadedApk packageInfo, IBinder activityToken, UserHandle user, boolean restricted,
+            LoadedApk packageInfo, IBinder activityToken, UserHandle user, int flags,
             Display display, Configuration overrideConfiguration, int createDisplayWithId) {
         mOuterContext = this;
 
+        // If creator didn't specify which storage to use, use the default
+        // location for application.
+        if ((flags & Context.CONTEXT_STORAGE_MASK) == 0) {
+            final File dataDir = packageInfo.getDataDirFile();
+            if (Objects.equals(dataDir, packageInfo.getCredentialEncryptedDataDirFile())) {
+                flags |= Context.CONTEXT_STORAGE_CREDENTIAL_ENCRYPTED;
+            } else if (Objects.equals(dataDir, packageInfo.getDeviceEncryptedDataDirFile())) {
+                flags |= Context.CONTEXT_STORAGE_DEVICE_ENCRYPTED;
+            } else {
+                throw new IllegalStateException("Storage location " + dataDir
+                        + " doesn't match either credential or device encrypted storage");
+            }
+        }
+
         mMainThread = mainThread;
         mActivityToken = activityToken;
-        mRestricted = restricted;
+        mFlags = flags;
 
         if (user == null) {
             user = Process.myUserHandle();
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 6c0c3e8..099a5fe 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -48,6 +48,7 @@
 import android.util.Log;
 import android.util.TypedValue;
 import android.view.Gravity;
+import android.view.NotificationHeaderView;
 import android.view.View;
 import android.widget.ProgressBar;
 import android.widget.RemoteViews;
@@ -3000,6 +3001,7 @@
          */
         private void resetNotificationHeader(RemoteViews contentView) {
             contentView.setImageViewResource(R.id.icon, 0);
+            contentView.setBoolean(R.id.notification_header, "setExpanded", false);
             contentView.setTextViewText(R.id.app_name_text, null);
             contentView.setViewVisibility(R.id.chronometer, View.GONE);
             contentView.setViewVisibility(R.id.header_sub_text, View.GONE);
@@ -3134,6 +3136,8 @@
         private void bindExpandButton(RemoteViews contentView) {
             contentView.setDrawableParameters(R.id.expand_button, false, -1, resolveColor(),
                     PorterDuff.Mode.SRC_ATOP, -1);
+            contentView.setInt(R.id.notification_header, "setOriginalNotificationColor",
+                    resolveColor());
         }
 
         private void bindHeaderChronometerAndTime(RemoteViews contentView) {
@@ -3197,7 +3201,6 @@
 
         private void resetStandardTemplateWithActions(RemoteViews big) {
             big.setViewVisibility(R.id.actions, View.GONE);
-            big.setViewVisibility(R.id.action_divider, View.GONE);
             big.removeAllViews(R.id.actions);
         }
 
@@ -3209,7 +3212,6 @@
             int N = mActions.size();
             if (N > 0) {
                 big.setViewVisibility(R.id.actions, View.VISIBLE);
-                big.setViewVisibility(R.id.action_divider, View.VISIBLE);
                 if (N>MAX_ACTION_BUTTONS) N=MAX_ACTION_BUTTONS;
                 for (int i=0; i<N; i++) {
                     final RemoteViews button = generateActionButton(mActions.get(i));
@@ -3276,11 +3278,7 @@
         }
 
         private void adaptNotificationHeaderForBigContentView(RemoteViews result) {
-            // We have to set the collapse button instead
-            result.setImageViewResource(R.id.expand_button, R.drawable.ic_arrow_up_14dp);
-            // Apply the color again
-            result.setDrawableParameters(R.id.expand_button, false, -1, resolveColor(),
-                    PorterDuff.Mode.SRC_ATOP, -1);
+            result.setBoolean(R.id.notification_header, "setExpanded", true);
         }
 
         /**
@@ -3352,10 +3350,14 @@
          * Apply any necessariy colors to the small icon
          */
         private void processSmallIconColor(Icon smallIcon, RemoteViews contentView) {
-            if (!isLegacy() || getColorUtil().isGrayscaleIcon(mContext, smallIcon)) {
+            boolean colorable = !isLegacy() || getColorUtil().isGrayscaleIcon(mContext, smallIcon);
+            if (colorable) {
                 contentView.setDrawableParameters(R.id.icon, false, -1, resolveColor(),
                         PorterDuff.Mode.SRC_ATOP, -1);
+
             }
+            contentView.setInt(R.id.notification_header, "setOriginalIconColor",
+                    colorable ? resolveColor() : NotificationHeaderView.NO_COLOR);
         }
 
         /**
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 471750e..6b900a8 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -2325,8 +2325,8 @@
      * with that alias already exists, it will be overwritten.
      * @return {@code true} if the keys were installed, {@code false} otherwise.
      */
-    public boolean installKeyPair(@Nullable ComponentName admin, PrivateKey privKey, Certificate cert,
-            String alias) {
+    public boolean installKeyPair(@Nullable ComponentName admin, @NonNull PrivateKey privKey,
+            @NonNull Certificate cert, @NonNull String alias) {
         try {
             final byte[] pemCert = Credentials.convertToPem(cert);
             final byte[] pkcs8Key = KeyFactory.getInstance(privKey.getAlgorithm())
@@ -2343,6 +2343,24 @@
     }
 
     /**
+     * Called by a device or profile owner to remove all user credentials installed under a given
+     * alias.
+     *
+     * @param admin Which {@link DeviceAdminReceiver} this request is associated with, or
+     *            {@code null} if calling from a delegated certificate installer.
+     * @param alias The private key alias under which the certificate is installed.
+     * @return {@code true} if the keys were both removed, {@code false} otherwise.
+     */
+    public boolean removeKeyPair(@Nullable ComponentName admin, @NonNull String alias) {
+        try {
+            return mService.removeKeyPair(admin, alias);
+        } catch (RemoteException e) {
+            Log.w(TAG, "Failed talking with device policy service", e);
+        }
+        return false;
+    }
+
+    /**
      * @return the alias of a given CA certificate in the certificate store, or {@code null} if it
      * doesn't exist.
      */
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index 6b4567c..1708ee3 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -135,6 +135,7 @@
     void enforceCanManageCaCerts(in ComponentName admin);
 
     boolean installKeyPair(in ComponentName who, in byte[] privKeyBuffer, in byte[] certBuffer, String alias);
+    boolean removeKeyPair(in ComponentName who, String alias);
     void choosePrivateKeyAlias(int uid, in Uri uri, in String alias, IBinder aliasCallback);
 
     void setCertInstallerPackage(in ComponentName who, String installerPackage);
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 4c7e853..1f7fd9d 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -771,27 +771,6 @@
     public abstract File getFilesDir();
 
     /**
-     * Return the filesystem directory for storing device-encrypted private app
-     * data. Files stored in this location are typically encrypted with a key
-     * tied to the physical device, and they can be accessed whenever the device
-     * has booted successfully, both <em>before and after</em> the user has
-     * entered their credentials (such as a lock pattern or PIN).
-     */
-    public abstract File getDeviceEncryptedFilesDir();
-
-    /**
-     * Return the filesystem directory for storing credential-encrypted private
-     * app data. Files stored in this location are typically encrypted with a
-     * key tied to user credentials, and they can be accessed
-     * <em>only after</em> the user has entered their credentials (such as a
-     * lock pattern or PIN).
-     *
-     * @hide
-     */
-    @SystemApi
-    public abstract File getCredentialEncryptedFilesDir();
-
-    /**
      * Returns the absolute path to the directory on the filesystem similar to
      * {@link #getFilesDir()}. The difference is that files placed under this
      * directory will be excluded from automatic backup to remote storage. See
@@ -3886,6 +3865,26 @@
     public static final int CONTEXT_RESTRICTED = 0x00000004;
 
     /**
+     * Flag for use with {@link #createPackageContext}: point all file APIs at
+     * device-encrypted storage.
+     *
+     * @hide
+     */
+    public static final int CONTEXT_STORAGE_DEVICE_ENCRYPTED = 0x00000008;
+
+    /**
+     * Flag for use with {@link #createPackageContext}: point all file APIs at
+     * credential-encrypted storage.
+     *
+     * @hide
+     */
+    public static final int CONTEXT_STORAGE_CREDENTIAL_ENCRYPTED = 0x00000010;
+
+    /** {@hide} */
+    public static final int CONTEXT_STORAGE_MASK = CONTEXT_STORAGE_DEVICE_ENCRYPTED
+            | CONTEXT_STORAGE_CREDENTIAL_ENCRYPTED;
+
+    /**
      * @hide Used to indicate we should tell the activity manager about the process
      * loading this code.
      */
@@ -3985,6 +3984,42 @@
     public abstract Context createDisplayContext(@NonNull Display display);
 
     /**
+     * Return a new Context object for the current Context but whose storage
+     * APIs are backed by device-encrypted storage.
+     * <p>
+     * Data stored in device-encrypted storage is typically encrypted with a
+     * key tied to the physical device, and they can be accessed whenever the
+     * device has booted successfully, both <em>before and after</em> the user
+     * has entered their credentials (such as a lock pattern or PIN).
+     * <p>
+     * Each call to this method returns a new instance of a Context object;
+     * Context objects are not shared, however common state (ClassLoader, other
+     * Resources for the same configuration) may be so the Context itself can be
+     * fairly lightweight.
+     *
+     * @see #isDeviceEncrypted()
+     */
+    public abstract Context createDeviceEncryptedContext(Context context);
+
+    /**
+     * Return a new Context object for the current Context but whose storage
+     * APIs are backed by credential-encrypted storage.
+     * <p>
+     * Data stored in credential-encrypted storage is typically encrypted with a
+     * key tied to user credentials, and they can be accessed
+     * <em>only after</em> the user has entered their credentials (such as a
+     * lock pattern or PIN).
+     * <p>
+     * Each call to this method returns a new instance of a Context object;
+     * Context objects are not shared, however common state (ClassLoader, other
+     * Resources for the same configuration) may be so the Context itself can be
+     * fairly lightweight.
+     *
+     * @see #isCredentialEncrypted()
+     */
+    public abstract Context createCredentialEncryptedContext(Context context);
+
+    /**
      * Gets the display adjustments holder for this context.  This information
      * is provided on a per-application or activity basis and is used to simulate lower density
      * display metrics for legacy applications and restricted screen sizes.
@@ -4005,4 +4040,20 @@
     public boolean isRestricted() {
         return false;
     }
+
+    /**
+     * Indicates if the storage APIs of this Context are backed by
+     * device-encrypted storage.
+     *
+     * @see #createDeviceEncryptedContext(Context)
+     */
+    public abstract boolean isDeviceEncrypted();
+
+    /**
+     * Indicates if the storage APIs of this Context are backed by
+     * credential-encrypted storage.
+     *
+     * @see #createCredentialEncryptedContext(Context)
+     */
+    public abstract boolean isCredentialEncrypted();
 }
diff --git a/core/java/android/content/ContextWrapper.java b/core/java/android/content/ContextWrapper.java
index f5e9159..73d0ddc 100644
--- a/core/java/android/content/ContextWrapper.java
+++ b/core/java/android/content/ContextWrapper.java
@@ -208,18 +208,6 @@
     }
 
     @Override
-    public File getDeviceEncryptedFilesDir() {
-        return mBase.getDeviceEncryptedFilesDir();
-    }
-
-    /** {@hide} */
-    @SystemApi
-    @Override
-    public File getCredentialEncryptedFilesDir() {
-        return mBase.getCredentialEncryptedFilesDir();
-    }
-
-    @Override
     public File getNoBackupFilesDir() {
         return mBase.getNoBackupFilesDir();
     }
@@ -802,4 +790,24 @@
     public DisplayAdjustments getDisplayAdjustments(int displayId) {
         return mBase.getDisplayAdjustments(displayId);
     }
+
+    @Override
+    public Context createDeviceEncryptedContext(Context context) {
+        return mBase.createDeviceEncryptedContext(context);
+    }
+
+    @Override
+    public Context createCredentialEncryptedContext(Context context) {
+        return mBase.createCredentialEncryptedContext(context);
+    }
+
+    @Override
+    public boolean isDeviceEncrypted() {
+        return mBase.isDeviceEncrypted();
+    }
+
+    @Override
+    public boolean isCredentialEncrypted() {
+        return mBase.isCredentialEncrypted();
+    }
 }
diff --git a/core/java/android/content/pm/ApplicationInfo.aidl b/core/java/android/content/pm/ApplicationInfo.aidl
index 006d1bd..59b0a89 100644
--- a/core/java/android/content/pm/ApplicationInfo.aidl
+++ b/core/java/android/content/pm/ApplicationInfo.aidl
@@ -1,17 +1,17 @@
-/* //device/java/android/android/view/WindowManager.aidl
+/*
 **
 ** Copyright 2007, 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 
+** 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 
+**     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 
+** 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.
 */
 
diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java
index 545478c..4c5e766 100644
--- a/core/java/android/content/pm/ApplicationInfo.java
+++ b/core/java/android/content/pm/ApplicationInfo.java
@@ -22,7 +22,6 @@
 import android.os.Environment;
 import android.os.Parcel;
 import android.os.Parcelable;
-import android.os.SystemProperties;
 import android.os.UserHandle;
 import android.os.storage.StorageManager;
 import android.text.TextUtils;
@@ -383,13 +382,6 @@
     public static final int FLAG_HARDWARE_ACCELERATED = 1<<29;
 
     /**
-     * Value for {@link #flags}: {@code true} if the application is blocked via restrictions
-     * and for most purposes is considered as not installed.
-     * {@hide}
-     */
-    public static final int FLAG_EPHEMERAL = 1<<30;
-
-    /**
      * Value for {@link #flags}: true if code from this application will need to be
      * loaded into other applications' processes. On devices that support multiple
      * instruction sets, this implies the code might be loaded into a process that's
@@ -462,8 +454,8 @@
     public static final int PRIVATE_FLAG_PRIVILEGED = 1<<3;
 
     /**
-     * Value for {@link #flags}: {@code true} if the application has any IntentFiler with some
-     * data URI using HTTP or HTTPS with an associated VIEW action.
+     * Value for {@link #privateFlags}: {@code true} if the application has any IntentFiler
+     * with some data URI using HTTP or HTTPS with an associated VIEW action.
      *
      * {@hide}
      */
@@ -494,6 +486,13 @@
     public static final int PRIVATE_FLAG_AUTOPLAY = 1 << 7;
 
     /**
+     * Value for {@link #flags}: {@code true} if the application is blocked via restrictions
+     * and for most purposes is considered as not installed.
+     * {@hide}
+     */
+    public static final int PRIVATE_FLAG_EPHEMERAL = 1<<8;
+
+    /**
      * When set, at least one component inside this application is encryption aware.
      *
      * @hide
@@ -591,13 +590,21 @@
     public String[] sharedLibraryFiles;
     
     /**
-     * Full path to a directory assigned to the package for its persistent data.
+     * Full path to the default directory assigned to the package for its
+     * persistent data.
      */
     public String dataDir;
 
-    /** {@hide} */
+    /**
+     * Full path to the device-encrypted directory assigned to the package for
+     * its persistent data.
+     */
     public String deviceEncryptedDataDir;
-    /** {@hide} */
+
+    /**
+     * Full path to the credential-encrypted directory assigned to the package
+     * for its persistent data.
+     */
     public String credentialEncryptedDataDir;
 
     /**
@@ -1083,6 +1090,13 @@
     /**
      * @hide
      */
+    public boolean isEphemeralApp() {
+        return (privateFlags & ApplicationInfo.PRIVATE_FLAG_EPHEMERAL) != 0;
+    }
+
+    /**
+     * @hide
+     */
     @Override protected ApplicationInfo getApplicationInfo() {
         return this;
     }
diff --git a/core/java/android/content/pm/EphemeralApplicationInfo.aidl b/core/java/android/content/pm/EphemeralApplicationInfo.aidl
new file mode 100644
index 0000000..5aaae78
--- /dev/null
+++ b/core/java/android/content/pm/EphemeralApplicationInfo.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.content.pm;
+
+parcelable EphemeralApplicationInfo;
diff --git a/core/java/android/content/pm/EphemeralApplicationInfo.java b/core/java/android/content/pm/EphemeralApplicationInfo.java
new file mode 100644
index 0000000..87663f1
--- /dev/null
+++ b/core/java/android/content/pm/EphemeralApplicationInfo.java
@@ -0,0 +1,120 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.content.pm;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.graphics.drawable.Drawable;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * This class represents the state of an ephemeral app.
+ *
+ * @hide
+ */
+public final class EphemeralApplicationInfo implements Parcelable {
+    private final ApplicationInfo mApplicationInfo;
+
+    private final String mPackageName;
+    private final CharSequence mLabelText;
+
+    private final String[] mRequestedPermissions;
+    private final String[] mGrantedPermissions;
+
+    public EphemeralApplicationInfo(ApplicationInfo appInfo,
+            String[] requestedPermissions, String[] grantedPermissions) {
+        mApplicationInfo = appInfo;
+        mPackageName = null;
+        mLabelText = null;
+        mRequestedPermissions = requestedPermissions;
+        mGrantedPermissions = grantedPermissions;
+    }
+
+    public EphemeralApplicationInfo(String packageName, CharSequence label,
+            String[] requestedPermissions, String[] grantedPermissions) {
+        mApplicationInfo = null;
+        mPackageName = packageName;
+        mLabelText = label;
+        mRequestedPermissions = requestedPermissions;
+        mGrantedPermissions = grantedPermissions;
+    }
+
+    private EphemeralApplicationInfo(Parcel parcel) {
+        mPackageName = parcel.readString();
+        mLabelText = parcel.readCharSequence();
+        mRequestedPermissions = parcel.readStringArray();
+        mGrantedPermissions = parcel.createStringArray();
+        mApplicationInfo = parcel.readParcelable(null);
+    }
+
+    public @NonNull String getPackageName() {
+        if (mApplicationInfo != null) {
+            return mApplicationInfo.packageName;
+        }
+        return mPackageName;
+    }
+
+    public @NonNull CharSequence loadLabel(@NonNull PackageManager packageManager) {
+        if (mApplicationInfo != null) {
+            return mApplicationInfo.loadLabel(packageManager);
+        }
+        return mLabelText;
+    }
+
+    public @NonNull Drawable loadIcon(@NonNull PackageManager packageManager) {
+        if (mApplicationInfo != null) {
+            return mApplicationInfo.loadIcon(packageManager);
+        }
+        return packageManager.getEphemeralApplicationIcon(mPackageName);
+    }
+
+    public @Nullable String[] getRequestedPermissions() {
+        return mRequestedPermissions;
+    }
+
+    public @Nullable String[] getGrantedPermissions() {
+        return mGrantedPermissions;
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel parcel, int flags) {
+        parcel.writeString(mPackageName);
+        parcel.writeCharSequence(mLabelText);
+        parcel.writeStringArray(mRequestedPermissions);
+        parcel.writeStringArray(mGrantedPermissions);
+        parcel.writeParcelable(mApplicationInfo, flags);
+    }
+
+    public static final Creator<EphemeralApplicationInfo> CREATOR =
+            new Creator<EphemeralApplicationInfo>() {
+        @Override
+        public EphemeralApplicationInfo createFromParcel(Parcel parcel) {
+            return new EphemeralApplicationInfo(parcel);
+        }
+
+        @Override
+        public EphemeralApplicationInfo[] newArray(int size) {
+            return new EphemeralApplicationInfo[0];
+        }
+    };
+}
diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index b9a42eb..b947a2b 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -23,6 +23,7 @@
 import android.content.pm.ActivityInfo;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.ContainerEncryptionParams;
+import android.content.pm.EphemeralApplicationInfo;
 import android.content.pm.FeatureInfo;
 import android.content.pm.IPackageInstallObserver2;
 import android.content.pm.IPackageInstaller;
@@ -47,6 +48,7 @@
 import android.content.pm.UserInfo;
 import android.content.pm.VerificationParams;
 import android.content.pm.VerifierDeviceIdentity;
+import android.graphics.Bitmap;
 import android.net.Uri;
 import android.os.Bundle;
 import android.os.ParcelFileDescriptor;
@@ -513,4 +515,10 @@
     boolean isPermissionRevokedByPolicy(String permission, String packageName, int userId);
 
     String getPermissionControllerPackageName();
+
+    ParceledListSlice getEphemeralApplications(int userId);
+    byte[] getEphemeralApplicationCookie(String packageName, int userId);
+    boolean setEphemeralApplicationCookie(String packageName, in byte[] cookie, int userId);
+    Bitmap getEphemeralApplicationIcon(String packageName, int userId);
+    boolean isEphemeralApplication(String packageName, int userId);
 }
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 9b80a16..a822150 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -47,8 +47,10 @@
 import android.os.RemoteException;
 import android.os.UserHandle;
 import android.os.storage.VolumeInfo;
+import android.provider.Settings;
 import android.util.AndroidException;
 
+import android.util.Log;
 import com.android.internal.util.ArrayUtils;
 
 import java.io.File;
@@ -2860,6 +2862,83 @@
     public abstract List<ApplicationInfo> getInstalledApplications(int flags);
 
     /**
+     * Gets the ephemeral applications the user recently used. Requires
+     * holding "android.permission.ACCESS_EPHEMERAL_APPS".
+     *
+     * @return The ephemeral app list.
+     *
+     * @hide
+     */
+    @RequiresPermission(Manifest.permission.ACCESS_EPHEMERAL_APPS)
+    public abstract List<EphemeralApplicationInfo> getEphemeralApplications();
+
+    /**
+     * Gets the icon for an ephemeral application.
+     *
+     * @param packageName The app package name.
+     *
+     * @hide
+     */
+    public abstract Drawable getEphemeralApplicationIcon(String packageName);
+
+    /**
+     * Gets whether the caller is an ephemeral app.
+     *
+     * @return Whether caller is an ephemeral app.
+     *
+     * @see #setEphemeralCookie(byte[])
+     * @see #getEphemeralCookie()
+     * @see #getEphemeralCookieMaxSizeBytes()
+     */
+    public abstract boolean isEphemeralApplication();
+
+    /**
+     * Gets the maximum size in bytes of the cookie data an ephemeral app
+     * can store on the device.
+     *
+     * @return The max cookie size in bytes.
+     *
+     * @see #isEphemeralApplication()
+     * @see #setEphemeralCookie(byte[])
+     * @see #getEphemeralCookie()
+     */
+    public abstract int getEphemeralCookieMaxSizeBytes();
+
+    /**
+     * Gets the ephemeral application cookie for this app. Non
+     * ephemeral apps and apps that were ephemeral but were upgraded
+     * to non-ephemeral can still access this API. For ephemeral apps
+     * this cooke is cached for some time after uninstall while for
+     * normal apps the cookie is deleted after the app is uninstalled.
+     * The cookie is always present while the app is installed.
+     *
+     * @return The cookie.
+     *
+     * @see #isEphemeralApplication()
+     * @see #setEphemeralCookie(byte[])
+     * @see #getEphemeralCookieMaxSizeBytes()
+     */
+    public abstract @NonNull byte[] getEphemeralCookie();
+
+    /**
+     * Sets the ephemeral application cookie for the calling app. Non
+     * ephemeral apps and apps that were ephemeral but were upgraded
+     * to non-ephemeral can still access this API. For ephemeral apps
+     * this cooke is cached for some time after uninstall while for
+     * normal apps the cookie is deleted after the app is uninstalled.
+     * The cookie is always present while the app is installed. The
+     * cookie size is limited by {@link #getEphemeralCookieMaxSizeBytes()}.
+     *
+     * @param cookie The cookie data.
+     * @return True if the cookie was set.
+     *
+     * @see #isEphemeralApplication()
+     * @see #getEphemeralCookieMaxSizeBytes()
+     * @see #getEphemeralCookie();
+     */
+    public abstract boolean setEphemeralCookie(@NonNull  byte[] cookie);
+
+    /**
      * Get a list of shared libraries that are available on the
      * system.
      *
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index 0307108..b79b6b6 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -1470,6 +1470,10 @@
             pkg.applicationInfo.flags |= ApplicationInfo.FLAG_EXTERNAL_STORAGE;
         }
 
+        if ((flags & PARSE_IS_EPHEMERAL) != 0) {
+            pkg.applicationInfo.privateFlags |= ApplicationInfo.PRIVATE_FLAG_EPHEMERAL;
+        }
+
         // Resource boolean are -1, so 1 means we don't know the value.
         int supportsSmallScreens = 1;
         int supportsNormalScreens = 1;
diff --git a/core/java/android/content/pm/UserInfo.java b/core/java/android/content/pm/UserInfo.java
index 6e09595..e3050fe 100644
--- a/core/java/android/content/pm/UserInfo.java
+++ b/core/java/android/content/pm/UserInfo.java
@@ -78,6 +78,12 @@
 
     public static final int FLAG_QUIET_MODE = 0x00000080;
 
+    /**
+     * Indicates that this user is ephemeral. I.e. the user will be removed after leaving
+     * the foreground.
+     */
+    public static final int FLAG_EPHEMERAL = 0x00000100;
+
     public static final int NO_PROFILE_GROUP_ID = UserHandle.USER_NULL;
 
     public int id;
@@ -134,6 +140,11 @@
     public boolean isQuietModeEnabled() {
         return (flags & FLAG_QUIET_MODE) == FLAG_QUIET_MODE;
     }
+
+    public boolean isEphemeral() {
+        return (flags & FLAG_EPHEMERAL) == FLAG_EPHEMERAL;
+    }
+
     /**
      * Returns true if the user is a split system user.
      * <p>If {@link UserManager#isSplitSystemUser split system user mode} is not enabled,
diff --git a/core/java/android/content/res/Configuration.java b/core/java/android/content/res/Configuration.java
index 1a19a58..7db5a08 100644
--- a/core/java/android/content/res/Configuration.java
+++ b/core/java/android/content/res/Configuration.java
@@ -724,9 +724,7 @@
         fontScale = o.fontScale;
         mcc = o.mcc;
         mnc = o.mnc;
-        if (o.locale != null) {
-            locale = (Locale) o.locale.clone();
-        }
+        locale = o.locale == null ? null : (Locale) o.locale.clone();
         o.fixUpLocaleList();
         mLocaleList = o.mLocaleList;
         userSetLocale = o.userSetLocale;
diff --git a/core/java/android/content/res/Resources.java b/core/java/android/content/res/Resources.java
index 7b3dde4..cdca8698 100644
--- a/core/java/android/content/res/Resources.java
+++ b/core/java/android/content/res/Resources.java
@@ -1988,7 +1988,7 @@
         }
         synchronized (sSync) {
             if (mPluralRule != null) {
-                mPluralRule = PluralRules.forLocale(config.getLocales().getPrimary());
+                mPluralRule = PluralRules.forLocale(mConfiguration.getLocales().getPrimary());
             }
         }
     }
@@ -2421,75 +2421,93 @@
 
     @Nullable
     Drawable loadDrawable(TypedValue value, int id, Theme theme) throws NotFoundException {
-        if (TRACE_FOR_PRELOAD) {
-            // Log only framework resources
-            if ((id >>> 24) == 0x1) {
-                final String name = getResourceName(id);
-                if (name != null) {
-                    Log.d("PreloadDrawable", name);
+        try {
+            if (TRACE_FOR_PRELOAD) {
+                // Log only framework resources
+                if ((id >>> 24) == 0x1) {
+                    final String name = getResourceName(id);
+                    if (name != null) {
+                        Log.d("PreloadDrawable", name);
+                    }
                 }
             }
-        }
 
-        final boolean isColorDrawable;
-        final DrawableCache caches;
-        final long key;
-        if (value.type >= TypedValue.TYPE_FIRST_COLOR_INT
-                && value.type <= TypedValue.TYPE_LAST_COLOR_INT) {
-            isColorDrawable = true;
-            caches = mColorDrawableCache;
-            key = value.data;
-        } else {
-            isColorDrawable = false;
-            caches = mDrawableCache;
-            key = (((long) value.assetCookie) << 32) | value.data;
-        }
-
-        // First, check whether we have a cached version of this drawable
-        // that was inflated against the specified theme.
-        if (!mPreloading) {
-            final Drawable cachedDrawable = caches.getInstance(key, theme);
-            if (cachedDrawable != null) {
-                return cachedDrawable;
+            final boolean isColorDrawable;
+            final DrawableCache caches;
+            final long key;
+            if (value.type >= TypedValue.TYPE_FIRST_COLOR_INT
+                    && value.type <= TypedValue.TYPE_LAST_COLOR_INT) {
+                isColorDrawable = true;
+                caches = mColorDrawableCache;
+                key = value.data;
+            } else {
+                isColorDrawable = false;
+                caches = mDrawableCache;
+                key = (((long) value.assetCookie) << 32) | value.data;
             }
-        }
 
-        // Next, check preloaded drawables. These may contain unresolved theme
-        // attributes.
-        final ConstantState cs;
-        if (isColorDrawable) {
-            cs = sPreloadedColorDrawables.get(key);
-        } else {
-            cs = sPreloadedDrawables[mConfiguration.getLayoutDirection()].get(key);
-        }
+            // First, check whether we have a cached version of this drawable
+            // that was inflated against the specified theme.
+            if (!mPreloading) {
+                final Drawable cachedDrawable = caches.getInstance(key, theme);
+                if (cachedDrawable != null) {
+                    return cachedDrawable;
+                }
+            }
 
-        Drawable dr;
-        if (cs != null) {
-            dr = cs.newDrawable(this);
-        } else if (isColorDrawable) {
-            dr = new ColorDrawable(value.data);
-        } else {
-            dr = loadDrawableForCookie(value, id, null);
-        }
+            // Next, check preloaded drawables. These may contain unresolved theme
+            // attributes.
+            final ConstantState cs;
+            if (isColorDrawable) {
+                cs = sPreloadedColorDrawables.get(key);
+            } else {
+                cs = sPreloadedDrawables[mConfiguration.getLayoutDirection()].get(key);
+            }
 
-        // Determine if the drawable has unresolved theme attributes. If it
-        // does, we'll need to apply a theme and store it in a theme-specific
-        // cache.
-        final boolean canApplyTheme = dr != null && dr.canApplyTheme();
-        if (canApplyTheme && theme != null) {
-            dr = dr.mutate();
-            dr.applyTheme(theme);
-            dr.clearMutated();
-        }
+            Drawable dr;
+            if (cs != null) {
+                dr = cs.newDrawable(this);
+            } else if (isColorDrawable) {
+                dr = new ColorDrawable(value.data);
+            } else {
+                dr = loadDrawableForCookie(value, id, null);
+            }
 
-        // If we were able to obtain a drawable, store it in the appropriate
-        // cache: preload, not themed, null theme, or theme-specific.
-        if (dr != null) {
-            dr.setChangingConfigurations(value.changingConfigurations);
-            cacheDrawable(value, isColorDrawable, caches, theme, canApplyTheme, key, dr);
-        }
+            // Determine if the drawable has unresolved theme attributes. If it
+            // does, we'll need to apply a theme and store it in a theme-specific
+            // cache.
+            final boolean canApplyTheme = dr != null && dr.canApplyTheme();
+            if (canApplyTheme && theme != null) {
+                dr = dr.mutate();
+                dr.applyTheme(theme);
+                dr.clearMutated();
+            }
 
-        return dr;
+            // If we were able to obtain a drawable, store it in the appropriate
+            // cache: preload, not themed, null theme, or theme-specific.
+            if (dr != null) {
+                dr.setChangingConfigurations(value.changingConfigurations);
+                cacheDrawable(value, isColorDrawable, caches, theme, canApplyTheme, key, dr);
+            }
+
+            return dr;
+        } catch (Exception e) {
+            String name;
+            try {
+                name = getResourceName(id);
+            } catch (NotFoundException e2) {
+                name = "(missing name)";
+            }
+
+            // The target drawable might fail to load for any number of
+            // reasons, but we always want to include the resource name.
+            // Since the client already expects this method to throw a
+            // NotFoundException, just throw one of those.
+            final NotFoundException nfe = new NotFoundException("Drawable " + name
+                    + " with resource ID #0x" + Integer.toHexString(id), e);
+            nfe.setStackTrace(new StackTraceElement[0]);
+            throw nfe;
+        }
     }
 
     private void cacheDrawable(TypedValue value, boolean isColorDrawable, DrawableCache caches,
diff --git a/core/java/android/hardware/usb/UsbManager.java b/core/java/android/hardware/usb/UsbManager.java
index a23a6cb..1e3bcd0 100644
--- a/core/java/android/hardware/usb/UsbManager.java
+++ b/core/java/android/hardware/usb/UsbManager.java
@@ -23,6 +23,7 @@
 import android.content.Context;
 import android.os.Bundle;
 import android.os.ParcelFileDescriptor;
+import android.os.Process;
 import android.os.RemoteException;
 import android.util.Log;
 
@@ -455,6 +456,21 @@
     }
 
     /**
+     * Grants permission for USB device without showing system dialog.
+     * Only system components can call this function.
+     * @param device to request permissions for
+     *
+     * {@hide}
+     */
+    public void grantPermission(UsbDevice device) {
+        try {
+            mService.grantDevicePermission(device, Process.myUid());
+        } catch (RemoteException e) {
+            Log.e(TAG, "RemoteException in grantPermission", e);
+        }
+    }
+
+    /**
      * Returns true if the specified USB function is currently enabled when in device mode.
      * <p>
      * USB functions represent interfaces which are published to the host to access
diff --git a/core/java/android/os/IUserManager.aidl b/core/java/android/os/IUserManager.aidl
index 4af89c9..9ee6228 100644
--- a/core/java/android/os/IUserManager.aidl
+++ b/core/java/android/os/IUserManager.aidl
@@ -48,12 +48,15 @@
     UserInfo getProfileParent(int userHandle);
     boolean isSameProfileGroup(int userId, int otherUserId);
     UserInfo getUserInfo(int userHandle);
+    String getUserAccount(int userId);
+    void setUserAccount(int userId, String accountName);
     long getUserCreationTime(int userHandle);
     boolean isRestricted();
     boolean canHaveRestrictedProfile(int userId);
     int getUserSerialNumber(int userHandle);
     int getUserHandle(int userSerialNumber);
     Bundle getUserRestrictions(int userHandle);
+    boolean hasBaseUserRestriction(String restrictionKey, int userHandle);
     boolean hasUserRestriction(in String restrictionKey, int userHandle);
     void setUserRestriction(String key, boolean value, int userId);
     void setApplicationRestrictions(in String packageName, in Bundle restrictions,
diff --git a/core/java/android/os/Parcel.java b/core/java/android/os/Parcel.java
index 8d035b7..9b68f90 100644
--- a/core/java/android/os/Parcel.java
+++ b/core/java/android/os/Parcel.java
@@ -236,6 +236,7 @@
     private static final int VAL_DOUBLEARRAY = 28;
 
     // The initial int32 in a Binder call's reply Parcel header:
+    // Keep these in sync with libbinder's binder/Status.h.
     private static final int EX_SECURITY = -1;
     private static final int EX_BAD_PARCELABLE = -2;
     private static final int EX_ILLEGAL_ARGUMENT = -3;
@@ -243,7 +244,11 @@
     private static final int EX_ILLEGAL_STATE = -5;
     private static final int EX_NETWORK_MAIN_THREAD = -6;
     private static final int EX_UNSUPPORTED_OPERATION = -7;
+    private static final int EX_SERVICE_SPECIFIC = -8;
     private static final int EX_HAS_REPLY_HEADER = -128;  // special; see below
+    // EX_TRANSACTION_FAILED is used exclusively in native code.
+    // see libbinder's binder/Status.h
+    private static final int EX_TRANSACTION_FAILED = -129;
 
     private static native int nativeDataSize(long nativePtr);
     private static native int nativeDataAvail(long nativePtr);
@@ -1540,6 +1545,8 @@
             code = EX_NETWORK_MAIN_THREAD;
         } else if (e instanceof UnsupportedOperationException) {
             code = EX_UNSUPPORTED_OPERATION;
+        } else if (e instanceof ServiceSpecificException) {
+            code = EX_SERVICE_SPECIFIC;
         }
         writeInt(code);
         StrictMode.clearGatheredViolations();
@@ -1550,6 +1557,9 @@
             throw new RuntimeException(e);
         }
         writeString(e.getMessage());
+        if (e instanceof ServiceSpecificException) {
+            writeInt(((ServiceSpecificException)e).errorCode);
+        }
     }
 
     /**
@@ -1660,6 +1670,8 @@
                 throw new NetworkOnMainThreadException();
             case EX_UNSUPPORTED_OPERATION:
                 throw new UnsupportedOperationException(msg);
+            case EX_SERVICE_SPECIFIC:
+                throw new ServiceSpecificException(readInt(), msg);
         }
         throw new RuntimeException("Unknown exception code: " + code
                 + " msg " + msg);
diff --git a/core/java/android/os/ServiceSpecificException.java b/core/java/android/os/ServiceSpecificException.java
new file mode 100644
index 0000000..20f237a5
--- /dev/null
+++ b/core/java/android/os/ServiceSpecificException.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.os;
+
+/**
+ * An exception specific to a service.
+ *
+ * <p>This exception includes an error code specific to the throwing
+ * service.  This is mostly used by system services to indicate
+ * domain specific error conditions.
+ *
+ * @hide
+ */
+public class ServiceSpecificException extends RuntimeException {
+    public final int errorCode;
+
+    ServiceSpecificException(int errorCode, String message) {
+        super(message);
+        this.errorCode = errorCode;
+    }
+
+    ServiceSpecificException(int errorCode) {
+        this.errorCode = errorCode;
+    }
+}
+
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index 4781a6c..1346a39 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -15,8 +15,10 @@
  */
 package android.os;
 
+import android.Manifest;
 import android.accounts.AccountManager;
 import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
 import android.annotation.SystemApi;
 import android.app.ActivityManager;
 import android.app.ActivityManagerNative;
@@ -721,6 +723,25 @@
     }
 
     /**
+     * Checks if the calling app is running as an ephemeral user.
+     *
+     * @return whether the caller is an ephemeral user.
+     * @hide
+     */
+    public boolean isEphemeralUser() {
+        return isUserEphemeral(UserHandle.myUserId());
+    }
+
+    /**
+     * Returns whether the specified user is ephemeral.
+     * @hide
+     */
+    public boolean isUserEphemeral(int userId) {
+        final UserInfo user = getUserInfo(userId);
+        return user != null && user.isEphemeral();
+    }
+
+    /**
      * Return whether the given user is actively running.  This means that
      * the user is in the "started" state, not "stopped" -- it is currently
      * allowed to run code through scheduled alarms, receiving broadcasts,
@@ -826,6 +847,24 @@
         }
     }
 
+     /**
+     * @hide
+     * Returns whether the given user has been disallowed from performing certain actions
+     * or setting certain settings through UserManager. This method disregards restrictions
+     * set by device policy.
+     * @param restrictionKey the string key representing the restriction
+     * @param userHandle the UserHandle of the user for whom to retrieve the restrictions.
+     */
+    public boolean hasBaseUserRestriction(String restrictionKey, UserHandle userHandle) {
+        try {
+            return mService.hasBaseUserRestriction(restrictionKey, userHandle.getIdentifier());
+        } catch (RemoteException re) {
+            Log.w(TAG, "Could not get base user restrictions for user " +
+                    userHandle.getIdentifier(), re);
+            return false;
+        }
+    }
+
     /**
      * This will no longer work.  Device owners and profile owners should use
      * {@link DevicePolicyManager#addUserRestriction(ComponentName, String)} instead.
@@ -1084,6 +1123,39 @@
     }
 
     /**
+     * @return the user's account name, null if not found.
+     * @hide
+     */
+    @RequiresPermission( allOf = {
+            Manifest.permission.INTERACT_ACROSS_USERS_FULL,
+            Manifest.permission.MANAGE_USERS
+    })
+    public @Nullable String getUserAccount(int userHandle) {
+        try {
+            return mService.getUserAccount(userHandle);
+        } catch (RemoteException re) {
+            Log.w(TAG, "Could not get user account", re);
+            return null;
+        }
+    }
+
+    /**
+     * Set account name for the given user.
+     * @hide
+     */
+    @RequiresPermission( allOf = {
+            Manifest.permission.INTERACT_ACROSS_USERS_FULL,
+            Manifest.permission.MANAGE_USERS
+    })
+    public void setUserAccount(int userHandle, @Nullable String accountName) {
+        try {
+            mService.setUserAccount(userHandle, accountName);
+        } catch (RemoteException re) {
+            Log.w(TAG, "Could not set user account", re);
+        }
+    }
+
+    /**
      * Returns information for Primary user.
      * Requires {@link android.Manifest.permission#MANAGE_USERS} permission.
      *
diff --git a/core/java/android/os/storage/IMountService.java b/core/java/android/os/storage/IMountService.java
index c6510f0..9b3f02d 100644
--- a/core/java/android/os/storage/IMountService.java
+++ b/core/java/android/os/storage/IMountService.java
@@ -25,8 +25,6 @@
 import android.os.Parcelable;
 import android.os.RemoteException;
 
-import java.io.FileDescriptor;
-
 /**
  * WARNING! Update IMountService.h and IMountService.cpp if you change this
  * file. In particular, the ordering of the methods below must match the
@@ -1202,13 +1200,15 @@
             }
 
             @Override
-            public void createUserKey(int userId, int serialNumber) throws RemoteException {
+            public void createUserKey(int userId, int serialNumber, boolean ephemeral)
+                    throws RemoteException {
                 Parcel _data = Parcel.obtain();
                 Parcel _reply = Parcel.obtain();
                 try {
                     _data.writeInterfaceToken(DESCRIPTOR);
                     _data.writeInt(userId);
                     _data.writeInt(serialNumber);
+                    _data.writeInt(ephemeral ? 1 : 0);
                     mRemote.transact(Stub.TRANSACTION_createUserKey, _data, _reply, 0);
                     _reply.readException();
                 } finally {
@@ -1283,7 +1283,8 @@
             }
 
             @Override
-            public void prepareUserStorage(String volumeUuid, int userId, int serialNumber)
+            public void prepareUserStorage(
+                    String volumeUuid, int userId, int serialNumber, boolean ephemeral)
                     throws RemoteException {
                 Parcel _data = Parcel.obtain();
                 Parcel _reply = Parcel.obtain();
@@ -1292,6 +1293,7 @@
                     _data.writeString(volumeUuid);
                     _data.writeInt(userId);
                     _data.writeInt(serialNumber);
+                    _data.writeInt(ephemeral ? 1 : 0);
                     mRemote.transact(Stub.TRANSACTION_prepareUserStorage, _data, _reply, 0);
                     _reply.readException();
                 } finally {
@@ -2012,7 +2014,8 @@
                     data.enforceInterface(DESCRIPTOR);
                     int userId = data.readInt();
                     int serialNumber = data.readInt();
-                    createUserKey(userId, serialNumber);
+                    boolean ephemeral = data.readInt() != 0;
+                    createUserKey(userId, serialNumber, ephemeral);
                     reply.writeNoException();
                     return true;
                 }
@@ -2052,7 +2055,8 @@
                     String volumeUuid = data.readString();
                     int userId = data.readInt();
                     int serialNumber = data.readInt();
-                    prepareUserStorage(volumeUuid, userId, serialNumber);
+                    boolean ephemeral = data.readInt() != 0;
+                    prepareUserStorage(volumeUuid, userId, serialNumber, ephemeral);
                     reply.writeNoException();
                     return true;
                 }
@@ -2376,15 +2380,16 @@
     public void setPrimaryStorageUuid(String volumeUuid, IPackageMoveObserver callback)
             throws RemoteException;
 
-    public void createUserKey(int userId, int serialNumber) throws RemoteException;
+    public void createUserKey(int userId, int serialNumber, boolean ephemeral)
+            throws RemoteException;
     public void destroyUserKey(int userId) throws RemoteException;
 
     public void unlockUserKey(int userId, int serialNumber, byte[] token) throws RemoteException;
     public void lockUserKey(int userId) throws RemoteException;
     public boolean isUserKeyUnlocked(int userId) throws RemoteException;
 
-    public void prepareUserStorage(String volumeUuid, int userId, int serialNumber)
-            throws RemoteException;
+    public void prepareUserStorage(String volumeUuid, int userId, int serialNumber,
+            boolean ephemeral) throws RemoteException;
 
     public ParcelFileDescriptor mountAppFuse(String name) throws RemoteException;
 }
diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java
index 7f36a98..beda169 100644
--- a/core/java/android/os/storage/StorageManager.java
+++ b/core/java/android/os/storage/StorageManager.java
@@ -966,9 +966,9 @@
     }
 
     /** {@hide} */
-    public void createUserKey(int userId, int serialNumber) {
+    public void createUserKey(int userId, int serialNumber, boolean ephemeral) {
         try {
-            mMountService.createUserKey(userId, serialNumber);
+            mMountService.createUserKey(userId, serialNumber, ephemeral);
         } catch (RemoteException e) {
             throw e.rethrowAsRuntimeException();
         }
@@ -1002,9 +1002,10 @@
     }
 
     /** {@hide} */
-    public void prepareUserStorage(String volumeUuid, int userId, int serialNumber) {
+    public void prepareUserStorage(
+            String volumeUuid, int userId, int serialNumber, boolean ephemeral) {
         try {
-            mMountService.prepareUserStorage(volumeUuid, userId, serialNumber);
+            mMountService.prepareUserStorage(volumeUuid, userId, serialNumber, ephemeral);
         } catch (RemoteException e) {
             throw e.rethrowAsRuntimeException();
         }
diff --git a/core/java/android/provider/ContactsContract.java b/core/java/android/provider/ContactsContract.java
index c0d95a1..10432b5 100644
--- a/core/java/android/provider/ContactsContract.java
+++ b/core/java/android/provider/ContactsContract.java
@@ -6194,6 +6194,14 @@
                     "filter");
 
             /**
+             * It supports the similar semantics as {@link #CONTENT_FILTER_URI} and returns the same
+             * columns. This URI requires {@link ContactsContract#DIRECTORY_PARAM_KEY} in
+             * parameters, otherwise it will throw UnsupportedOperationException.
+             */
+            public static final Uri ENTERPRISE_CONTENT_FILTER_URI = Uri.withAppendedPath(
+                    CONTENT_URI, "filter_enterprise");
+
+            /**
              * The email address.
              * <P>Type: TEXT</P>
              */
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index dad3c67..521aa3c 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -7792,6 +7792,24 @@
         public static final String LTE_SERVICE_FORCED = "lte_service_forced";
 
         /**
+         * Ephemeral app cookie max size in bytes.
+         * <p>
+         * Type: int
+         * @hide
+         */
+        public static final String EPHEMERAL_COOKIE_MAX_SIZE_BYTES =
+                "ephemeral_cookie_max_size_bytes";
+
+        /**
+         * The duration for caching uninstalled ephemeral apps.
+         * <p>
+         * Type: long
+         * @hide
+         */
+        public static final String UNINSTALLED_EPHEMERAL_APP_CACHE_DURATION_MILLIS =
+                "uninstalled_ephemeral_app_cache_duration_millis";
+
+        /**
          * Settings to backup. This is here so that it's in the same place as the settings
          * keys and easy to update.
          *
diff --git a/core/java/android/speech/tts/FileSynthesisCallback.java b/core/java/android/speech/tts/FileSynthesisCallback.java
index c7a4ccc..dea766b 100644
--- a/core/java/android/speech/tts/FileSynthesisCallback.java
+++ b/core/java/android/speech/tts/FileSynthesisCallback.java
@@ -104,8 +104,8 @@
             Log.d(TAG, "FileSynthesisRequest.start(" + sampleRateInHz + "," + audioFormat
                     + "," + channelCount + ")");
         }
-        if (audioFormat != AudioFormat.ENCODING_PCM_8BIT ||
-            audioFormat != AudioFormat.ENCODING_PCM_16BIT ||
+        if (audioFormat != AudioFormat.ENCODING_PCM_8BIT &&
+            audioFormat != AudioFormat.ENCODING_PCM_16BIT &&
             audioFormat != AudioFormat.ENCODING_PCM_FLOAT) {
             Log.e(TAG, "Audio format encoding " + audioFormat + " not supported. Please use one " +
                        "of AudioFormat.ENCODING_PCM_8BIT, AudioFormat.ENCODING_PCM_16BIT or " +
diff --git a/core/java/android/speech/tts/PlaybackSynthesisCallback.java b/core/java/android/speech/tts/PlaybackSynthesisCallback.java
index a6fb543..dcc0095 100644
--- a/core/java/android/speech/tts/PlaybackSynthesisCallback.java
+++ b/core/java/android/speech/tts/PlaybackSynthesisCallback.java
@@ -123,8 +123,8 @@
     public int start(int sampleRateInHz, int audioFormat, int channelCount) {
         if (DBG) Log.d(TAG, "start(" + sampleRateInHz + "," + audioFormat + "," + channelCount
                 + ")");
-        if (audioFormat != AudioFormat.ENCODING_PCM_8BIT ||
-            audioFormat != AudioFormat.ENCODING_PCM_16BIT ||
+        if (audioFormat != AudioFormat.ENCODING_PCM_8BIT &&
+            audioFormat != AudioFormat.ENCODING_PCM_16BIT &&
             audioFormat != AudioFormat.ENCODING_PCM_FLOAT) {
             Log.w(TAG, "Audio format encoding " + audioFormat + " not supported. Please use one " +
                        "of AudioFormat.ENCODING_PCM_8BIT, AudioFormat.ENCODING_PCM_16BIT or " +
diff --git a/core/java/android/view/InputDevice.java b/core/java/android/view/InputDevice.java
index bae51d3..46b9a46 100644
--- a/core/java/android/view/InputDevice.java
+++ b/core/java/android/view/InputDevice.java
@@ -256,6 +256,15 @@
     public static final int SOURCE_TOUCH_NAVIGATION = 0x00200000 | SOURCE_CLASS_NONE;
 
     /**
+     * The input source is a rotating encoder device whose motions should be interpreted as akin to
+     * those of a scroll wheel.
+     *
+     * @see #SOURCE_CLASS_NONE
+     * {@hide}
+     */
+    public static final int SOURCE_ROTARY_ENCODER = 0x00400000 | SOURCE_CLASS_NONE;
+
+    /**
      * The input source is a joystick.
      * (It may also be a {@link #SOURCE_GAMEPAD}).
      *
diff --git a/core/java/android/view/MotionEvent.java b/core/java/android/view/MotionEvent.java
index 6026d04..527d7e5 100644
--- a/core/java/android/view/MotionEvent.java
+++ b/core/java/android/view/MotionEvent.java
@@ -962,6 +962,22 @@
     public static final int AXIS_TILT = 25;
 
     /**
+     * Axis constant: Generic scroll axis of a motion event.
+     * <p>
+     * <ul>
+     * <li>Reports the relative movement of the generic scrolling device.
+     * </ul>
+     * </p><p>
+     * This axis should be used for scroll events that are neither strictly vertical nor horizontal.
+     * A good example would be the rotation of a rotary encoder input device.
+     * </p>
+     *
+     * @see #getAxisValue(int, int)
+     * {@hide}
+     */
+    public static final int AXIS_SCROLL = 26;
+
+    /**
      * Axis constant: Generic 1 axis of a motion event.
      * The interpretation of a generic axis is device-specific.
      *
@@ -1171,6 +1187,7 @@
         names.append(AXIS_BRAKE, "AXIS_BRAKE");
         names.append(AXIS_DISTANCE, "AXIS_DISTANCE");
         names.append(AXIS_TILT, "AXIS_TILT");
+        names.append(AXIS_SCROLL, "AXIS_SCROLL");
         names.append(AXIS_GENERIC_1, "AXIS_GENERIC_1");
         names.append(AXIS_GENERIC_2, "AXIS_GENERIC_2");
         names.append(AXIS_GENERIC_3, "AXIS_GENERIC_3");
diff --git a/core/java/android/view/NotificationHeaderView.java b/core/java/android/view/NotificationHeaderView.java
index 82f6c7f..54fa764 100644
--- a/core/java/android/view/NotificationHeaderView.java
+++ b/core/java/android/view/NotificationHeaderView.java
@@ -17,10 +17,10 @@
 package android.view;
 
 import android.annotation.Nullable;
-import android.app.Notification;
 import android.content.Context;
 import android.graphics.Rect;
 import android.util.AttributeSet;
+import android.widget.ImageView;
 import android.widget.LinearLayout;
 import android.widget.RemoteViews;
 import android.widget.TextView;
@@ -34,14 +34,20 @@
  */
 @RemoteViews.RemoteView
 public class NotificationHeaderView extends LinearLayout {
+    public static final int NO_COLOR = -1;
     private final int mHeaderMinWidth;
+    private final int mExpandTopPadding;
     private View mAppName;
     private View mSubTextView;
     private OnClickListener mExpandClickListener;
     private HeaderTouchListener mTouchListener = new HeaderTouchListener();
-    private View mExpandButton;
+    private ImageView mExpandButton;
     private View mIcon;
     private TextView mChildCount;
+    private int mIconColor;
+    private int mOriginalNotificationColor;
+    private boolean mGroupHeader;
+    private boolean mExpanded;
 
     public NotificationHeaderView(Context context) {
         this(context, null);
@@ -59,6 +65,7 @@
         super(context, attrs, defStyleAttr, defStyleRes);
         mHeaderMinWidth = getResources().getDimensionPixelSize(
                 com.android.internal.R.dimen.notification_header_shrink_min_width);
+        mExpandTopPadding = (int) (1 * getResources().getDisplayMetrics().density);
     }
 
     @Override
@@ -66,7 +73,7 @@
         super.onFinishInflate();
         mAppName = findViewById(com.android.internal.R.id.app_name_text);
         mSubTextView = findViewById(com.android.internal.R.id.header_sub_text);
-        mExpandButton = findViewById(com.android.internal.R.id.expand_button);
+        mExpandButton = (ImageView) findViewById(com.android.internal.R.id.expand_button);
         mIcon = findViewById(com.android.internal.R.id.icon);
         mChildCount = (TextView) findViewById(com.android.internal.R.id.number_of_children);
     }
@@ -98,7 +105,7 @@
             int overFlow = totalWidth - givenWidth;
             // We are overflowing, lets shrink
             final int appWidth = mAppName.getMeasuredWidth();
-            if (appWidth > mHeaderMinWidth) {
+            if (mAppName.getVisibility() != GONE && appWidth > mHeaderMinWidth) {
                 int newSize = appWidth - Math.min(appWidth - mHeaderMinWidth, overFlow);
                 int childWidthSpec = MeasureSpec.makeMeasureSpec(newSize, MeasureSpec.AT_MOST);
                 mAppName.measure(childWidthSpec, wrapContentHeightSpec);
@@ -146,6 +153,57 @@
         }
     }
 
+    @RemotableViewMethod
+    public void setOriginalIconColor(int color) {
+        mIconColor = color;
+    }
+
+    public int getOriginalIconColor() {
+        return mIconColor;
+    }
+
+    @RemotableViewMethod
+    public void setOriginalNotificationColor(int color) {
+        mOriginalNotificationColor = color;
+    }
+
+    public int getOriginalNotificationColor() {
+        return mOriginalNotificationColor;
+    }
+
+    public void setIsGroupHeader(boolean isGroupHeader) {
+        mGroupHeader = isGroupHeader;
+        updateExpandButton();
+    }
+
+    @RemotableViewMethod
+    public void setExpanded(boolean expanded) {
+        mExpanded = expanded;
+        updateExpandButton();
+    }
+
+    private void updateExpandButton() {
+        int drawableId;
+        int paddingTop = 0;
+        if (mGroupHeader) {
+            if (mExpanded) {
+                drawableId = com.android.internal.R.drawable.ic_collapse_bundle;
+            } else {
+                drawableId =com.android.internal.R.drawable.ic_expand_bundle;
+            }
+        } else {
+            if (mExpanded) {
+                drawableId = com.android.internal.R.drawable.ic_collapse_notification;
+            } else {
+                drawableId = com.android.internal.R.drawable.ic_expand_notification;
+            }
+            paddingTop = mExpandTopPadding;
+        }
+        mExpandButton.setImageDrawable(getContext().getDrawable(drawableId));
+        mExpandButton.setColorFilter(mOriginalNotificationColor);
+        mExpandButton.setPadding(0, paddingTop, 0, 0);
+    }
+
     public class HeaderTouchListener implements View.OnTouchListener {
 
         private final ArrayList<Rect> mTouchRects = new ArrayList<>();
diff --git a/core/java/android/view/Window.java b/core/java/android/view/Window.java
index 3c4d45a..7a359e7 100644
--- a/core/java/android/view/Window.java
+++ b/core/java/android/view/Window.java
@@ -1158,27 +1158,6 @@
     public abstract void setContentView(View view);
 
     /**
-     * Install a view in the decoration (title) area, to be shown when
-     * the window is in multi-window mode. This view will be placed
-     * next to the window controls.
-     *
-     * The view may be restored to defaults by passing null.
-     *
-     * @param view The desired view to display in window decorations.
-     */
-    public abstract void setDecorView(View view);
-
-    /**
-     * Convenience for
-     * {@link #setDecorView(View)}
-     * to set the custom window decoration from a layout resource. The layout will be inflated
-     * adding all top level views to the decoration
-     *
-     * @param layoutResID Resource ID to be inflated.
-     */
-    public abstract void setDecorView(@LayoutRes int layoutResID);
-
-    /**
      * Set the screen content to an explicit view.  This view is placed
      * directly into the screen's view hierarchy.  It can itself be a complex
      * view hierarchy.
@@ -2060,4 +2039,6 @@
     public boolean getOverlayDecorCaption() {
         return mOverlayWithDecorCaption;
     }
+
+
 }
diff --git a/core/java/android/view/WindowManagerPolicy.java b/core/java/android/view/WindowManagerPolicy.java
index 7e45bbb..b1a8479 100644
--- a/core/java/android/view/WindowManagerPolicy.java
+++ b/core/java/android/view/WindowManagerPolicy.java
@@ -431,6 +431,11 @@
         public int getLidState();
 
         /**
+         * Lock the device now.
+         */
+        public void lockDeviceNow();
+
+        /**
          * Returns a code that descripbes whether the camera lens is covered or not.
          */
         public int getCameraLensCoverState();
diff --git a/core/java/com/android/internal/logging/MetricsConstants.java b/core/java/com/android/internal/logging/MetricsConstants.java
index bb4ca3d..37bf71c 100644
--- a/core/java/com/android/internal/logging/MetricsConstants.java
+++ b/core/java/com/android/internal/logging/MetricsConstants.java
@@ -281,6 +281,7 @@
     public static final int ACTION_DOUBLE_TAP_POWER_CAMERA_GESTURE = 255;
     public static final int ACTION_WIGGLE_CAMERA_GESTURE = 256;
     public static final int QS_WORKMODE = 257;
+    public static final int BACKGROUND_CHECK_SUMMARY = 258;
 
     // These constants must match those in the analytic pipeline, do not edit.
     // Add temporary values to the top of MetricsLogger instead.
diff --git a/core/java/com/android/internal/policy/PhoneWindow.java b/core/java/com/android/internal/policy/PhoneWindow.java
index 2178344..337bb69 100644
--- a/core/java/com/android/internal/policy/PhoneWindow.java
+++ b/core/java/com/android/internal/policy/PhoneWindow.java
@@ -423,30 +423,6 @@
     }
 
     @Override
-    public void setDecorView(int layoutResID) {
-        View v = mLayoutInflater.inflate(layoutResID, null);
-        setDecorView(v);
-    }
-
-    @Override
-    public void setDecorView(View view) {
-        if (mContentParent == null) {
-            installDecor();
-        }
-
-        LinearLayout clientDecorPlaceholder =
-                (LinearLayout) findViewById(R.id.client_decor_placeholder);
-
-        if (clientDecorPlaceholder != null) {
-            clientDecorPlaceholder.removeAllViews();
-
-            if (view != null) {
-                clientDecorPlaceholder.addView(view);
-            }
-        }
-    }
-
-    @Override
     public void addContentView(View view, ViewGroup.LayoutParams params) {
         if (mContentParent == null) {
             installDecor();
diff --git a/core/jni/android/graphics/MinikinUtils.cpp b/core/jni/android/graphics/MinikinUtils.cpp
index 9b774b3..0597d3f 100644
--- a/core/jni/android/graphics/MinikinUtils.cpp
+++ b/core/jni/android/graphics/MinikinUtils.cpp
@@ -33,11 +33,10 @@
     FontStyle resolved = resolvedFace->fStyle;
 
     /* Prepare minikin FontStyle */
-    const std::string& langs = paint->getTextLocales();
-    FontLanguages minikinLangs(langs.c_str(), langs.size());
     FontVariant minikinVariant = (paint->getFontVariant() == VARIANT_ELEGANT) ? VARIANT_ELEGANT
             : VARIANT_COMPACT;
-    FontStyle minikinStyle(minikinLangs, minikinVariant, resolved.getWeight(), resolved.getItalic());
+    const uint32_t langListId = paint->getMinikinLangListId();
+    FontStyle minikinStyle(langListId, minikinVariant, resolved.getWeight(), resolved.getItalic());
 
     /* Prepare minikin Paint */
     // Note: it would be nice to handle fractional size values (it would improve smooth zoom
diff --git a/core/jni/android/graphics/Paint.cpp b/core/jni/android/graphics/Paint.cpp
index 9c11dd1..654d148 100644
--- a/core/jni/android/graphics/Paint.cpp
+++ b/core/jni/android/graphics/Paint.cpp
@@ -71,13 +71,6 @@
     paint->setTextEncoding(Paint::kGlyphID_TextEncoding);
 }
 
-struct LocalesCacheEntry {
-    std::string javaLocales;
-    std::string languageTags;
-};
-
-static thread_local LocalesCacheEntry sSingleEntryLocalesCache;
-
 namespace PaintGlue {
     enum MoveOpt {
         AFTER, AT_OR_AFTER, BEFORE, AT_OR_BEFORE, AT
@@ -402,15 +395,20 @@
         }
     }
 
-    static void setTextLocales(JNIEnv* env, jobject clazz, jlong objHandle, jstring locales) {
+    static jint setTextLocales(JNIEnv* env, jobject clazz, jlong objHandle, jstring locales) {
         Paint* obj = reinterpret_cast<Paint*>(objHandle);
         ScopedUtfChars localesChars(env, locales);
-        if (sSingleEntryLocalesCache.javaLocales != localesChars.c_str()) {
-            sSingleEntryLocalesCache.javaLocales = localesChars.c_str();
-            toLanguageTags(&sSingleEntryLocalesCache.languageTags, localesChars.c_str());
-        }
+        std::string buf;
+        toLanguageTags(&buf, localesChars.c_str());
+        jint minikinLangListId = FontStyle::registerLanguageList(buf);
+        obj->setMinikinLangListId(minikinLangListId);
+        return minikinLangListId;
+    }
 
-        obj->setTextLocales(sSingleEntryLocalesCache.languageTags);
+    static void setTextLocalesByMinikinLangListId(JNIEnv* env, jobject clazz, jlong objHandle,
+            jint minikinLangListId) {
+        Paint* obj = reinterpret_cast<Paint*>(objHandle);
+        obj->setMinikinLangListId(minikinLangListId);
     }
 
     static jboolean isElegantTextHeight(JNIEnv* env, jobject, jlong paintHandle) {
@@ -991,7 +989,9 @@
     {"nSetRasterizer","!(JJ)J", (void*) PaintGlue::setRasterizer},
     {"nGetTextAlign","!(J)I", (void*) PaintGlue::getTextAlign},
     {"nSetTextAlign","!(JI)V", (void*) PaintGlue::setTextAlign},
-    {"nSetTextLocales","!(JLjava/lang/String;)V", (void*) PaintGlue::setTextLocales},
+    {"nSetTextLocales","!(JLjava/lang/String;)I", (void*) PaintGlue::setTextLocales},
+    {"nSetTextLocalesByMinikinLangListId","!(JI)V",
+            (void*) PaintGlue::setTextLocalesByMinikinLangListId},
     {"nIsElegantTextHeight","!(J)Z", (void*) PaintGlue::isElegantTextHeight},
     {"nSetElegantTextHeight","!(JZ)V", (void*) PaintGlue::setElegantTextHeight},
     {"nGetTextSize","!(J)F", (void*) PaintGlue::getTextSize},
diff --git a/core/jni/android/graphics/Paint.h b/core/jni/android/graphics/Paint.h
index 7a34bc2..cb6e622 100644
--- a/core/jni/android/graphics/Paint.h
+++ b/core/jni/android/graphics/Paint.h
@@ -53,12 +53,12 @@
         return mFontFeatureSettings;
     }
 
-    void setTextLocales(const std::string &textLocales) {
-        mTextLocales = textLocales;
+    void setMinikinLangListId(uint32_t minikinLangListId) {
+        mMinikinLangListId = minikinLangListId;
     }
 
-    const std::string& getTextLocales() const {
-        return mTextLocales;
+    uint32_t getMinikinLangListId() const {
+        return mMinikinLangListId;
     }
 
     void setFontVariant(FontVariant variant) {
@@ -80,7 +80,7 @@
 private:
     float mLetterSpacing = 0;
     std::string mFontFeatureSettings;
-    std::string mTextLocales;
+    uint32_t mMinikinLangListId;
     FontVariant mFontVariant;
     uint32_t mHyphenEdit = 0;
 };
diff --git a/core/jni/android/graphics/PaintImpl.cpp b/core/jni/android/graphics/PaintImpl.cpp
index d5a0972..bd513ae 100644
--- a/core/jni/android/graphics/PaintImpl.cpp
+++ b/core/jni/android/graphics/PaintImpl.cpp
@@ -22,13 +22,14 @@
 
 namespace android {
 
-Paint::Paint() : SkPaint(),
-        mLetterSpacing(0), mFontFeatureSettings(), mTextLocales(), mFontVariant(VARIANT_DEFAULT) {
+Paint::Paint() :
+        SkPaint(), mLetterSpacing(0), mFontFeatureSettings(), mMinikinLangListId(0),
+        mFontVariant(VARIANT_DEFAULT) {
 }
 
 Paint::Paint(const Paint& paint) : SkPaint(paint),
         mLetterSpacing(paint.mLetterSpacing), mFontFeatureSettings(paint.mFontFeatureSettings),
-        mTextLocales(paint.mTextLocales), mFontVariant(paint.mFontVariant),
+        mMinikinLangListId(paint.mMinikinLangListId), mFontVariant(paint.mFontVariant),
         mHyphenEdit(paint.mHyphenEdit) {
 }
 
@@ -39,7 +40,7 @@
     SkPaint::operator=(other);
     mLetterSpacing = other.mLetterSpacing;
     mFontFeatureSettings = other.mFontFeatureSettings;
-    mTextLocales = other.mTextLocales;
+    mMinikinLangListId = other.mMinikinLangListId;
     mFontVariant = other.mFontVariant;
     mHyphenEdit = other.mHyphenEdit;
     return *this;
@@ -49,7 +50,7 @@
     return static_cast<const SkPaint&>(a) == static_cast<const SkPaint&>(b)
             && a.mLetterSpacing == b.mLetterSpacing
             && a.mFontFeatureSettings == b.mFontFeatureSettings
-            && a.mTextLocales == b.mTextLocales
+            && a.mMinikinLangListId == b.mMinikinLangListId
             && a.mFontVariant == b.mFontVariant
             && a.mHyphenEdit == b.mHyphenEdit;
 }
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 4fda222..5251b20 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -2777,6 +2777,12 @@
          confirmation UI for full backup/restore -->
     <uses-permission android:name="android.permission.CONFIRM_FULL_BACKUP"/>
 
+
+    <!-- Allows the holder to access the ephemeral applications on the device.
+    @hide -->
+    <permission android:name="android.permission.ACCESS_EPHEMERAL_APPS"
+            android:protectionLevel="signature" />
+
     <application android:process="system"
                  android:persistent="true"
                  android:hasCode="false"
diff --git a/core/res/res/drawable/ic_arrow_drop_down.xml b/core/res/res/drawable/ic_collapse_bundle.xml
similarity index 68%
copy from core/res/res/drawable/ic_arrow_drop_down.xml
copy to core/res/res/drawable/ic_collapse_bundle.xml
index c8bb411..26c839d0 100644
--- a/core/res/res/drawable/ic_arrow_drop_down.xml
+++ b/core/res/res/drawable/ic_collapse_bundle.xml
@@ -15,11 +15,11 @@
     limitations under the License.
 -->
 <vector xmlns:android="http://schemas.android.com/apk/res/android"
-    android:width="14.0dp"
-    android:height="14.0dp"
-    android:viewportWidth="24.0"
-    android:viewportHeight="24.0">
+        android:width="14.0dp"
+        android:height="14.0dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
     <path
-        android:pathData="M16.600000,8.600000l-4.600000,4.599999 -4.600000,-4.599999 -1.400000,1.400000 6.000000,6.000000 6.000000,-6.000000z"
-        android:fillColor="#FF000000"/>
+        android:fillColor="#FF000000"
+        android:pathData="M12.0,10.0l5.3,-5.2l-1.4,-1.4L12.0,7.2L8.2,3.4L6.8,4.8L12.0,10.0zM6.8,19.2l1.4,1.4l3.8,-3.8l3.9,3.8l1.4,-1.4L12.0,14.0L6.8,19.2z"/>
 </vector>
diff --git a/core/res/res/drawable/ic_arrow_drop_down.xml b/core/res/res/drawable/ic_collapse_notification.xml
similarity index 67%
copy from core/res/res/drawable/ic_arrow_drop_down.xml
copy to core/res/res/drawable/ic_collapse_notification.xml
index c8bb411..603c159 100644
--- a/core/res/res/drawable/ic_arrow_drop_down.xml
+++ b/core/res/res/drawable/ic_collapse_notification.xml
@@ -15,11 +15,14 @@
     limitations under the License.
 -->
 <vector xmlns:android="http://schemas.android.com/apk/res/android"
-    android:width="14.0dp"
-    android:height="14.0dp"
-    android:viewportWidth="24.0"
-    android:viewportHeight="24.0">
+        android:width="14.0dp"
+        android:height="14.0dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
     <path
-        android:pathData="M16.600000,8.600000l-4.600000,4.599999 -4.600000,-4.599999 -1.400000,1.400000 6.000000,6.000000 6.000000,-6.000000z"
-        android:fillColor="#FF000000"/>
+        android:fillColor="#FF000000"
+        android:pathData="M12.0,8.0l-6.0,6.0l1.4,1.4l4.6,-4.6l4.6,4.6L18.0,14.0L12.0,8.0z"/>
+    <path
+        android:pathData="M0,0h24v24H0V0z"
+        android:fillColor="#00000000"/>
 </vector>
diff --git a/core/res/res/drawable/ic_arrow_drop_down.xml b/core/res/res/drawable/ic_expand_bundle.xml
similarity index 68%
rename from core/res/res/drawable/ic_arrow_drop_down.xml
rename to core/res/res/drawable/ic_expand_bundle.xml
index c8bb411..2cc5005 100644
--- a/core/res/res/drawable/ic_arrow_drop_down.xml
+++ b/core/res/res/drawable/ic_expand_bundle.xml
@@ -15,11 +15,11 @@
     limitations under the License.
 -->
 <vector xmlns:android="http://schemas.android.com/apk/res/android"
-    android:width="14.0dp"
-    android:height="14.0dp"
-    android:viewportWidth="24.0"
-    android:viewportHeight="24.0">
+        android:width="14.0dp"
+        android:height="14.0dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
     <path
-        android:pathData="M16.600000,8.600000l-4.600000,4.599999 -4.600000,-4.599999 -1.400000,1.400000 6.000000,6.000000 6.000000,-6.000000z"
-        android:fillColor="#FF000000"/>
+        android:fillColor="#FF000000"
+        android:pathData="M6.8,8.6L8.2,10.0L12.0,6.2l3.9,3.8l1.4,-1.4L12.0,3.4L6.8,8.6zM12.0,20.6l5.3,-5.2L15.8,14.0L12.0,17.8L8.2,14.0l-1.4,1.4L12.0,20.6z"/>
 </vector>
diff --git a/core/res/res/drawable/ic_arrow_drop_down.xml b/core/res/res/drawable/ic_expand_notification.xml
similarity index 67%
copy from core/res/res/drawable/ic_arrow_drop_down.xml
copy to core/res/res/drawable/ic_expand_notification.xml
index c8bb411..db7d3eb 100644
--- a/core/res/res/drawable/ic_arrow_drop_down.xml
+++ b/core/res/res/drawable/ic_expand_notification.xml
@@ -15,11 +15,14 @@
     limitations under the License.
 -->
 <vector xmlns:android="http://schemas.android.com/apk/res/android"
-    android:width="14.0dp"
-    android:height="14.0dp"
-    android:viewportWidth="24.0"
-    android:viewportHeight="24.0">
+        android:width="14.0dp"
+        android:height="14.0dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
     <path
-        android:pathData="M16.600000,8.600000l-4.600000,4.599999 -4.600000,-4.599999 -1.400000,1.400000 6.000000,6.000000 6.000000,-6.000000z"
-        android:fillColor="#FF000000"/>
+        android:fillColor="#FF000000"
+        android:pathData="M16.6,8.6L12.0,13.2L7.4,8.6L6.0,10.0l6.0,6.0l6.0,-6.0L16.6,8.6z"/>
+    <path
+        android:pathData="M0,0h24v24H0V0z"
+        android:fillColor="#00000000"/>
 </vector>
diff --git a/core/res/res/layout/decor_caption_dark.xml b/core/res/res/layout/decor_caption_dark.xml
index 273264d..95d2289 100644
--- a/core/res/res/layout/decor_caption_dark.xml
+++ b/core/res/res/layout/decor_caption_dark.xml
@@ -25,16 +25,11 @@
     <LinearLayout
         android:id="@+id/caption"
         android:layout_width="match_parent"
-        android:layout_gravity="end"
         android:layout_height="wrap_content"
+        android:gravity="end"
         android:background="@drawable/decor_caption_title"
         android:focusable="false"
         android:descendantFocusability="blocksDescendants" >
-        <LinearLayout
-            android:id="@+id/client_decor_placeholder"
-            android:layout_width="0dp"
-            android:layout_height="match_parent"
-            android:layout_weight="1"/>
         <Button
             android:id="@+id/maximize_window"
             android:layout_width="32dp"
diff --git a/core/res/res/layout/decor_caption_light.xml b/core/res/res/layout/decor_caption_light.xml
index fd9198e..f0f661e 100644
--- a/core/res/res/layout/decor_caption_light.xml
+++ b/core/res/res/layout/decor_caption_light.xml
@@ -25,16 +25,11 @@
     <LinearLayout
         android:id="@+id/caption"
         android:layout_width="match_parent"
-        android:layout_gravity="end"
         android:layout_height="wrap_content"
+        android:gravity="end"
         android:background="@drawable/decor_caption_title"
         android:focusable="false"
         android:descendantFocusability="blocksDescendants" >
-        <LinearLayout
-            android:id="@+id/client_decor_placeholder"
-            android:layout_width="0dp"
-            android:layout_height="match_parent"
-            android:layout_weight="1"/>
         <Button
             android:id="@+id/maximize_window"
             android:layout_width="32dp"
diff --git a/core/res/res/layout/notification_template_header.xml b/core/res/res/layout/notification_template_header.xml
index aceae9f..e45f52b 100644
--- a/core/res/res/layout/notification_template_header.xml
+++ b/core/res/res/layout/notification_template_header.xml
@@ -125,7 +125,6 @@
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         android:paddingTop="1dp"
-        android:src="@drawable/ic_arrow_drop_down"
         android:visibility="gone"
         />
 </NotificationHeaderView>
diff --git a/core/res/res/layout/notification_template_material_big_base.xml b/core/res/res/layout/notification_template_material_big_base.xml
index 8c78b8d..eb02e8b 100644
--- a/core/res/res/layout/notification_template_material_big_base.xml
+++ b/core/res/res/layout/notification_template_material_big_base.xml
@@ -55,12 +55,6 @@
         </FrameLayout>
         <include layout="@layout/notification_template_right_icon" />
     </FrameLayout>
-    <ImageView
-        android:layout_width="match_parent"
-        android:layout_height="1dp"
-        android:id="@+id/action_divider"
-        android:visibility="gone"
-        android:background="@drawable/notification_template_divider" />
     <include
         layout="@layout/notification_material_action_list"
         android:layout_width="match_parent"
diff --git a/core/res/res/layout/notification_template_material_big_picture.xml b/core/res/res/layout/notification_template_material_big_picture.xml
index 74e7775..58e3d1b 100644
--- a/core/res/res/layout/notification_template_material_big_picture.xml
+++ b/core/res/res/layout/notification_template_material_big_picture.xml
@@ -52,14 +52,6 @@
                 android:layout_marginBottom="16dp"
                 android:scaleType="centerCrop"
                 />
-        <ImageView
-                android:layout_width="match_parent"
-                android:layout_height="1dp"
-                android:layout_marginStart="-16dp"
-                android:layout_marginEnd="-16dp"
-                android:id="@+id/action_divider"
-                android:visibility="gone"
-                android:background="@drawable/notification_template_divider" />
         <include layout="@layout/notification_material_action_list" />
     </LinearLayout>
 </FrameLayout>
diff --git a/core/res/res/layout/notification_template_material_big_text.xml b/core/res/res/layout/notification_template_material_big_text.xml
index 354c0fb..9e010f2 100644
--- a/core/res/res/layout/notification_template_material_big_text.xml
+++ b/core/res/res/layout/notification_template_material_big_text.xml
@@ -62,14 +62,6 @@
                 android:contentDescription="@string/notification_work_profile_content_description"
                 />
         </LinearLayout>
-        <ImageView
-            android:layout_width="match_parent"
-            android:layout_height="1dp"
-            android:layout_marginStart="-16dp"
-            android:layout_marginEnd="-16dp"
-            android:id="@+id/action_divider"
-            android:visibility="gone"
-            android:background="@drawable/notification_template_divider" />
         <include layout="@layout/notification_material_action_list" />
     </LinearLayout>
     <include layout="@layout/notification_template_right_icon" />
diff --git a/core/res/res/layout/notification_template_material_inbox.xml b/core/res/res/layout/notification_template_material_inbox.xml
index 4f12d76..8c0abc9 100644
--- a/core/res/res/layout/notification_template_material_inbox.xml
+++ b/core/res/res/layout/notification_template_material_inbox.xml
@@ -130,14 +130,6 @@
             android:visibility="gone"
             android:layout_weight="0"
         />
-        <ImageView
-            android:layout_width="match_parent"
-            android:layout_height="1dip"
-            android:id="@+id/action_divider"
-            android:visibility="gone"
-            android:layout_marginStart="-16dp"
-            android:layout_marginEnd="-16dp"
-            android:background="@drawable/notification_template_divider" />
         <include layout="@layout/notification_material_action_list" />
     </LinearLayout>
     <include layout="@layout/notification_template_right_icon" />
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index decb1ef..97c0e07 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -673,6 +673,10 @@
          closed.  The default is 0. -->
     <integer name="config_lidNavigationAccessibility">0</integer>
 
+    <!-- Indicate whether closing the lid causes the lockscreen to appear.
+         The default is false. -->
+    <bool name="config_lidControlsScreenLock">false</bool>
+
     <!-- Indicate whether closing the lid causes the device to go to sleep and opening
          it causes the device to wake up.
          The default is false. -->
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index d92adc2..eadcae0 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -198,7 +198,6 @@
   <java-symbol type="id" name="inbox_text5" />
   <java-symbol type="id" name="inbox_text6" />
   <java-symbol type="id" name="status_bar_latest_event_content" />
-  <java-symbol type="id" name="action_divider" />
   <java-symbol type="id" name="notification_main_column" />
   <java-symbol type="id" name="sms_short_code_confirm_message" />
   <java-symbol type="id" name="sms_short_code_detail_layout" />
@@ -1480,6 +1479,7 @@
   <java-symbol type="bool" name="config_enableLockScreenRotation" />
   <java-symbol type="bool" name="config_enableLockScreenTranslucentDecor" />
   <java-symbol type="bool" name="config_enableTranslucentDecor" />
+  <java-symbol type="bool" name="config_lidControlsScreenLock" />
   <java-symbol type="bool" name="config_lidControlsSleep" />
   <java-symbol type="bool" name="config_lockDayNightMode" />
   <java-symbol type="bool" name="config_lockUiMode" />
@@ -1958,7 +1958,6 @@
   <java-symbol type="bool" name="config_built_in_sip_phone" />
   <java-symbol type="id" name="maximize_window" />
   <java-symbol type="id" name="close_window" />
-  <java-symbol type="id" name="client_decor_placeholder" />
   <java-symbol type="layout" name="decor_caption_light" />
   <java-symbol type="layout" name="decor_caption_dark" />
   <java-symbol type="drawable" name="decor_caption_title_focused" />
@@ -2370,7 +2369,10 @@
   <java-symbol type="id" name="sub_text_divider" />
   <java-symbol type="id" name="content_info_divider" />
   <java-symbol type="id" name="text_line_1" />
-  <java-symbol type="drawable" name="ic_arrow_up_14dp" />
+  <java-symbol type="drawable" name="ic_expand_notification" />
+  <java-symbol type="drawable" name="ic_collapse_notification" />
+  <java-symbol type="drawable" name="ic_expand_bundle" />
+  <java-symbol type="drawable" name="ic_collapse_bundle" />
   <java-symbol type="dimen" name="notification_header_height" />
   <java-symbol type="dimen" name="notification_big_picture_content_min_height_with_picture" />
   <java-symbol type="dimen" name="notification_header_shrink_min_width" />
diff --git a/core/tests/benchmarks/Android.mk b/core/tests/benchmarks/Android.mk
index 26a44a6..25181b5 100644
--- a/core/tests/benchmarks/Android.mk
+++ b/core/tests/benchmarks/Android.mk
@@ -25,7 +25,6 @@
 LOCAL_SRC_FILES := $(call all-java-files-under, src/)
 
 LOCAL_JAVA_LIBRARIES := \
-  caliper-api-target \
-  framework
+  caliper-api-target
 
 include $(BUILD_JAVA_LIBRARY)
diff --git a/data/fonts/fonts.xml b/data/fonts/fonts.xml
index 1b97b65..0e66374 100644
--- a/data/fonts/fonts.xml
+++ b/data/fonts/fonts.xml
@@ -344,7 +344,7 @@
     <family>
         <font weight="400" style="normal">NanumGothic.ttf</font>
     </family>
-    <family>
+    <family lang="und-Qaae">
         <font weight="400" style="normal">NotoColorEmoji.ttf</font>
     </family>
     <family>
diff --git a/graphics/java/android/graphics/Paint.java b/graphics/java/android/graphics/Paint.java
index 35182f9..90522f7 100644
--- a/graphics/java/android/graphics/Paint.java
+++ b/graphics/java/android/graphics/Paint.java
@@ -25,6 +25,9 @@
 import android.text.TextUtils;
 import android.util.LocaleList;
 
+import com.android.internal.annotations.GuardedBy;
+
+import java.util.HashMap;
 import java.util.Locale;
 
 /**
@@ -56,6 +59,16 @@
     private LocaleList  mLocales;
     private String      mFontFeatureSettings;
 
+    private static final Object sCacheLock = new Object();
+
+    /**
+     * Cache for the Minikin language list ID.
+     *
+     * A map from a string representation of the LocaleList to Minikin's language list ID.
+     */
+    @GuardedBy("sCacheLock")
+    private static final HashMap<String, Integer> sMinikinLangListIdCache = new HashMap<>();
+
     /**
      * @hide
      */
@@ -1335,7 +1348,7 @@
             return;
         }
         mLocales = new LocaleList(locale);
-        nSetTextLocales(mNativePaint, locale.toString());
+        syncTextLocalesWithMinikin();
     }
 
     /**
@@ -1372,7 +1385,21 @@
         }
         if (locales.equals(mLocales)) return;
         mLocales = locales;
-        nSetTextLocales(mNativePaint, locales.toLanguageTags());
+        syncTextLocalesWithMinikin();
+    }
+
+    private void syncTextLocalesWithMinikin() {
+        final String languageTags = mLocales.toLanguageTags();
+        final Integer minikinLangListId;
+        synchronized (sCacheLock) {
+            minikinLangListId = sMinikinLangListIdCache.get(languageTags);
+            if (minikinLangListId == null) {
+                final int newID = nSetTextLocales(mNativePaint, languageTags);
+                sMinikinLangListIdCache.put(languageTags, newID);
+                return;
+            }
+        }
+        nSetTextLocalesByMinikinLangListId(mNativePaint, minikinLangListId.intValue());
     }
 
     /**
@@ -2714,8 +2741,9 @@
     private static native void nSetTextAlign(long paintPtr,
                                                    int align);
 
-    private static native void nSetTextLocales(long paintPtr,
-                                                    String locales);
+    private static native int nSetTextLocales(long paintPtr, String locales);
+    private static native void nSetTextLocalesByMinikinLangListId(long paintPtr,
+            int mMinikinLangListId);
 
     private static native float nGetTextAdvances(long paintPtr, long typefacePtr,
             char[] text, int index, int count, int contextIndex, int contextCount,
diff --git a/graphics/java/android/graphics/drawable/AnimatedRotateDrawable.java b/graphics/java/android/graphics/drawable/AnimatedRotateDrawable.java
index 971a3a2..d714ca8 100644
--- a/graphics/java/android/graphics/drawable/AnimatedRotateDrawable.java
+++ b/graphics/java/android/graphics/drawable/AnimatedRotateDrawable.java
@@ -153,7 +153,7 @@
                 updateStateFromTypedArray(a);
                 verifyRequiredAttributes(a);
             } catch (XmlPullParserException e) {
-                throw new RuntimeException(e);
+                rethrowAsRuntimeException(e);
             } finally {
                 a.recycle();
             }
diff --git a/graphics/java/android/graphics/drawable/BitmapDrawable.java b/graphics/java/android/graphics/drawable/BitmapDrawable.java
index 4d2037b..daf2581 100644
--- a/graphics/java/android/graphics/drawable/BitmapDrawable.java
+++ b/graphics/java/android/graphics/drawable/BitmapDrawable.java
@@ -829,7 +829,7 @@
             try {
                 updateStateFromTypedArray(a);
             } catch (XmlPullParserException e) {
-                throw new RuntimeException(e);
+                rethrowAsRuntimeException(e);
             } finally {
                 a.recycle();
             }
diff --git a/graphics/java/android/graphics/drawable/ClipDrawable.java b/graphics/java/android/graphics/drawable/ClipDrawable.java
index cdd336d..d925b6b 100644
--- a/graphics/java/android/graphics/drawable/ClipDrawable.java
+++ b/graphics/java/android/graphics/drawable/ClipDrawable.java
@@ -111,7 +111,7 @@
                 updateStateFromTypedArray(a);
                 verifyRequiredAttributes(a);
             } catch (XmlPullParserException e) {
-                throw new RuntimeException(e);
+                rethrowAsRuntimeException(e);
             } finally {
                 a.recycle();
             }
diff --git a/graphics/java/android/graphics/drawable/Drawable.java b/graphics/java/android/graphics/drawable/Drawable.java
index 0ee877e..3d8437d 100644
--- a/graphics/java/android/graphics/drawable/Drawable.java
+++ b/graphics/java/android/graphics/drawable/Drawable.java
@@ -1430,6 +1430,20 @@
     }
 
     /**
+     * Re-throws an exception as a {@link RuntimeException} with an empty stack
+     * trace to avoid cluttering the log. The original exception's stack trace
+     * will still be included.
+     *
+     * @param cause the exception to re-throw
+     * @throws RuntimeException
+     */
+    static void rethrowAsRuntimeException(Exception cause) throws RuntimeException {
+        final RuntimeException e = new RuntimeException(cause);
+        e.setStackTrace(new StackTraceElement[0]);
+        throw e;
+    }
+
+    /**
      * Parses a {@link android.graphics.PorterDuff.Mode} from a tintMode
      * attribute's enum value.
      *
diff --git a/graphics/java/android/graphics/drawable/GradientDrawable.java b/graphics/java/android/graphics/drawable/GradientDrawable.java
index 719297a..f9208cd 100644
--- a/graphics/java/android/graphics/drawable/GradientDrawable.java
+++ b/graphics/java/android/graphics/drawable/GradientDrawable.java
@@ -23,7 +23,6 @@
 import android.content.res.Resources;
 import android.content.res.Resources.Theme;
 import android.content.res.TypedArray;
-import android.graphics.Bitmap;
 import android.graphics.Canvas;
 import android.graphics.Color;
 import android.graphics.ColorFilter;
@@ -1268,7 +1267,7 @@
             try {
                 updateGradientDrawableGradient(t.getResources(), a);
             } catch (XmlPullParserException e) {
-                throw new RuntimeException(e);
+                rethrowAsRuntimeException(e);
             } finally {
                 a.recycle();
             }
diff --git a/graphics/java/android/graphics/drawable/Icon.java b/graphics/java/android/graphics/drawable/Icon.java
index d800acb..0de4c2c 100644
--- a/graphics/java/android/graphics/drawable/Icon.java
+++ b/graphics/java/android/graphics/drawable/Icon.java
@@ -44,6 +44,7 @@
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
+import java.util.Objects;
 
 /**
  * An umbrella container for several serializable graphics representations, including Bitmaps,
@@ -459,6 +460,37 @@
     }
 
     /**
+     * Compares if this icon is constructed from the same resources as another icon.
+     * Note that this is an inexpensive operation and doesn't do deep Bitmap equality comparisons.
+     *
+     * @param otherIcon the other icon
+     * @return whether this icon is the same as the another one
+     * @hide
+     */
+    public boolean sameAs(Icon otherIcon) {
+        if (otherIcon == this) {
+            return true;
+        }
+        if (mType != otherIcon.getType()) {
+            return false;
+        }
+        switch (mType) {
+            case TYPE_BITMAP:
+                return getBitmap() == otherIcon.getBitmap();
+            case TYPE_DATA:
+                return getDataLength() == otherIcon.getDataLength()
+                        && getDataOffset() == otherIcon.getDataOffset()
+                        && getDataBytes() == otherIcon.getDataBytes();
+            case TYPE_RESOURCE:
+                return getResId() == otherIcon.getResId()
+                        && Objects.equals(getResPackage(), otherIcon.getResPackage());
+            case TYPE_URI:
+                return Objects.equals(getUriString(), otherIcon.getUriString());
+        }
+        return false;
+    }
+
+    /**
      * Create an Icon pointing to a drawable resource.
      * @param context The context for the application whose resources should be used to resolve the
      *                given resource ID.
diff --git a/graphics/java/android/graphics/drawable/InsetDrawable.java b/graphics/java/android/graphics/drawable/InsetDrawable.java
index 36d4272..d47cb56 100644
--- a/graphics/java/android/graphics/drawable/InsetDrawable.java
+++ b/graphics/java/android/graphics/drawable/InsetDrawable.java
@@ -124,7 +124,7 @@
                 updateStateFromTypedArray(a);
                 verifyRequiredAttributes(a);
             } catch (XmlPullParserException e) {
-                throw new RuntimeException(e);
+                rethrowAsRuntimeException(e);
             } finally {
                 a.recycle();
             }
diff --git a/graphics/java/android/graphics/drawable/NinePatchDrawable.java b/graphics/java/android/graphics/drawable/NinePatchDrawable.java
index 4d51d63..bfbdfa5 100644
--- a/graphics/java/android/graphics/drawable/NinePatchDrawable.java
+++ b/graphics/java/android/graphics/drawable/NinePatchDrawable.java
@@ -510,7 +510,7 @@
             try {
                 updateStateFromTypedArray(a);
             } catch (XmlPullParserException e) {
-                throw new RuntimeException(e);
+                rethrowAsRuntimeException(e);
             } finally {
                 a.recycle();
             }
diff --git a/graphics/java/android/graphics/drawable/RippleDrawable.java b/graphics/java/android/graphics/drawable/RippleDrawable.java
index aaab529..5213e10 100644
--- a/graphics/java/android/graphics/drawable/RippleDrawable.java
+++ b/graphics/java/android/graphics/drawable/RippleDrawable.java
@@ -40,7 +40,6 @@
 import android.graphics.Rect;
 import android.graphics.Shader;
 import android.util.AttributeSet;
-import android.util.DisplayMetrics;
 
 import java.io.IOException;
 import java.util.Arrays;
@@ -505,7 +504,7 @@
                 updateStateFromTypedArray(a);
                 verifyRequiredAttributes(a);
             } catch (XmlPullParserException e) {
-                throw new RuntimeException(e);
+                rethrowAsRuntimeException(e);
             } finally {
                 a.recycle();
             }
diff --git a/graphics/java/android/graphics/drawable/RotateDrawable.java b/graphics/java/android/graphics/drawable/RotateDrawable.java
index 1531ba2..78424e3 100644
--- a/graphics/java/android/graphics/drawable/RotateDrawable.java
+++ b/graphics/java/android/graphics/drawable/RotateDrawable.java
@@ -92,7 +92,7 @@
                 updateStateFromTypedArray(a);
                 verifyRequiredAttributes(a);
             } catch (XmlPullParserException e) {
-                throw new RuntimeException(e);
+                rethrowAsRuntimeException(e);
             } finally {
                 a.recycle();
             }
diff --git a/graphics/java/android/graphics/drawable/ScaleDrawable.java b/graphics/java/android/graphics/drawable/ScaleDrawable.java
index 330266f..51e143b 100644
--- a/graphics/java/android/graphics/drawable/ScaleDrawable.java
+++ b/graphics/java/android/graphics/drawable/ScaleDrawable.java
@@ -125,7 +125,7 @@
                 updateStateFromTypedArray(a);
                 verifyRequiredAttributes(a);
             } catch (XmlPullParserException e) {
-                throw new RuntimeException(e);
+                rethrowAsRuntimeException(e);
             } finally {
                 a.recycle();
             }
diff --git a/graphics/java/android/graphics/drawable/VectorDrawable.java b/graphics/java/android/graphics/drawable/VectorDrawable.java
index f630055e..3761a99 100644
--- a/graphics/java/android/graphics/drawable/VectorDrawable.java
+++ b/graphics/java/android/graphics/drawable/VectorDrawable.java
@@ -511,7 +511,7 @@
                 state.mCacheDirty = true;
                 updateStateFromTypedArray(a);
             } catch (XmlPullParserException e) {
-                throw new RuntimeException(e);
+                rethrowAsRuntimeException(e);
             } finally {
                 a.recycle();
             }
diff --git a/keystore/java/android/security/IKeyChainService.aidl b/keystore/java/android/security/IKeyChainService.aidl
index 20c94c5..cfcb4e0 100644
--- a/keystore/java/android/security/IKeyChainService.aidl
+++ b/keystore/java/android/security/IKeyChainService.aidl
@@ -33,6 +33,7 @@
 
     // APIs used by DevicePolicyManager
     boolean installKeyPair(in byte[] privateKey, in byte[] userCert, String alias);
+    boolean removeKeyPair(String alias);
 
     // APIs used by Settings
     boolean deleteCaCertificate(String alias);
diff --git a/media/java/android/media/ImageReader.java b/media/java/android/media/ImageReader.java
index 2164eec..66a174c 100644
--- a/media/java/android/media/ImageReader.java
+++ b/media/java/android/media/ImageReader.java
@@ -17,10 +17,10 @@
 package android.media;
 
 import android.graphics.ImageFormat;
-import android.graphics.PixelFormat;
 import android.os.Handler;
 import android.os.Looper;
 import android.os.Message;
+import android.util.Log;
 import android.view.Surface;
 
 import dalvik.system.VMRuntime;
@@ -29,6 +29,8 @@
 import java.nio.ByteBuffer;
 import java.nio.ByteOrder;
 import java.nio.NioUtils;
+import java.util.List;
+import java.util.concurrent.CopyOnWriteArrayList;
 import java.util.concurrent.atomic.AtomicBoolean;
 
 /**
@@ -145,7 +147,7 @@
                     "NV21 format is not supported");
         }
 
-        mNumPlanes = getNumPlanesFromFormat();
+        mNumPlanes = ImageUtils.getNumPlanesForFormat(mFormat);
 
         nativeInit(new WeakReference<ImageReader>(this), width, height, format, maxImages);
 
@@ -323,21 +325,27 @@
      * @see #ACQUIRE_SUCCESS
      */
     private int acquireNextSurfaceImage(SurfaceImage si) {
+        synchronized (mCloseLock) {
+            int status = nativeImageSetup(si);
 
-        int status = nativeImageSetup(si);
+            switch (status) {
+                case ACQUIRE_SUCCESS:
+                    si.createSurfacePlanes();
+                    si.mIsImageValid = true;
+                case ACQUIRE_NO_BUFS:
+                case ACQUIRE_MAX_IMAGES:
+                    break;
+                default:
+                    throw new AssertionError("Unknown nativeImageSetup return code " + status);
+            }
 
-        switch (status) {
-            case ACQUIRE_SUCCESS:
-                si.createSurfacePlanes();
-                si.mIsImageValid = true;
-            case ACQUIRE_NO_BUFS:
-            case ACQUIRE_MAX_IMAGES:
-                break;
-            default:
-                throw new AssertionError("Unknown nativeImageSetup return code " + status);
+            // Only keep track the successfully acquired image, as the native buffer is only mapped
+            // for such case.
+            if (status == ACQUIRE_SUCCESS) {
+                mAcquiredImages.add(si);
+            }
+            return status;
         }
-
-        return status;
     }
 
     /**
@@ -398,7 +406,11 @@
                 "This image was not produced by an ImageReader");
         }
         SurfaceImage si = (SurfaceImage) i;
-        if (si.getReader() != this) {
+        if (si.mIsImageValid == false) {
+            return;
+        }
+
+        if (si.getReader() != this || !mAcquiredImages.contains(i)) {
             throw new IllegalArgumentException(
                 "This image was not produced by this ImageReader");
         }
@@ -406,6 +418,7 @@
         si.clearSurfacePlanes();
         nativeReleaseImage(i);
         si.mIsImageValid = false;
+        mAcquiredImages.remove(i);
     }
 
     /**
@@ -475,7 +488,24 @@
     public void close() {
         setOnImageAvailableListener(null, null);
         if (mSurface != null) mSurface.release();
-        nativeClose();
+
+        /**
+         * Close all outstanding acquired images before closing the ImageReader. It is a good
+         * practice to close all the images as soon as it is not used to reduce system instantaneous
+         * memory pressure. CopyOnWrite list will use a copy of current list content. For the images
+         * being closed by other thread (e.g., GC thread), doubling the close call is harmless. For
+         * the image being acquired by other threads, mCloseLock is used to synchronize close and
+         * acquire operations.
+         */
+        synchronized (mCloseLock) {
+            for (Image image : mAcquiredImages) {
+                image.close();
+            }
+            mAcquiredImages.clear();
+
+            nativeClose();
+        }
+
         if (mEstimatedNativeAllocBytes > 0) {
             VMRuntime.getRuntime().registerNativeFree(mEstimatedNativeAllocBytes);
             mEstimatedNativeAllocBytes = 0;
@@ -542,45 +572,6 @@
         si.setDetached(true);
    }
 
-    /**
-     * Only a subset of the formats defined in
-     * {@link android.graphics.ImageFormat ImageFormat} and
-     * {@link android.graphics.PixelFormat PixelFormat} are supported by
-     * ImageReader. When reading RGB data from a surface, the formats defined in
-     * {@link android.graphics.PixelFormat PixelFormat} can be used, when
-     * reading YUV, JPEG or raw sensor data (for example, from camera or video
-     * decoder), formats from {@link android.graphics.ImageFormat ImageFormat}
-     * are used.
-     */
-    private int getNumPlanesFromFormat() {
-        switch (mFormat) {
-            case ImageFormat.YV12:
-            case ImageFormat.YUV_420_888:
-            case ImageFormat.NV21:
-                return 3;
-            case ImageFormat.NV16:
-                return 2;
-            case PixelFormat.RGB_565:
-            case PixelFormat.RGBA_8888:
-            case PixelFormat.RGBX_8888:
-            case PixelFormat.RGB_888:
-            case ImageFormat.JPEG:
-            case ImageFormat.YUY2:
-            case ImageFormat.Y8:
-            case ImageFormat.Y16:
-            case ImageFormat.RAW_SENSOR:
-            case ImageFormat.RAW10:
-            case ImageFormat.DEPTH16:
-            case ImageFormat.DEPTH_POINT_CLOUD:
-                return 1;
-            case ImageFormat.PRIVATE:
-                return 0;
-            default:
-                throw new UnsupportedOperationException(
-                        String.format("Invalid format specified %d", mFormat));
-        }
-    }
-
     private boolean isImageOwnedbyMe(Image image) {
         if (!(image instanceof SurfaceImage)) {
             return false;
@@ -612,7 +603,6 @@
         }
     }
 
-
     private final int mWidth;
     private final int mHeight;
     private final int mFormat;
@@ -622,8 +612,12 @@
     private int mEstimatedNativeAllocBytes;
 
     private final Object mListenerLock = new Object();
+    private final Object mCloseLock = new Object();
     private OnImageAvailableListener mListener;
     private ListenerHandler mListenerHandler;
+    // Keep track of the successfully acquired Images. This need to be thread safe as the images
+    // could be closed by different threads (e.g., application thread and GC thread).
+    private List<Image> mAcquiredImages = new CopyOnWriteArrayList<Image>();
 
     /**
      * This field is used by native code, do not access or modify.
@@ -657,9 +651,7 @@
 
         @Override
         public void close() {
-            if (mIsImageValid) {
-                ImageReader.this.releaseImage(this);
-            }
+            ImageReader.this.releaseImage(this);
         }
 
         public ImageReader getReader() {
diff --git a/media/java/android/media/ImageUtils.java b/media/java/android/media/ImageUtils.java
index ba3949a..eefd69c 100644
--- a/media/java/android/media/ImageUtils.java
+++ b/media/java/android/media/ImageUtils.java
@@ -58,6 +58,9 @@
             case ImageFormat.Y16:
             case ImageFormat.RAW_SENSOR:
             case ImageFormat.RAW10:
+            case ImageFormat.RAW12:
+            case ImageFormat.DEPTH16:
+            case ImageFormat.DEPTH_POINT_CLOUD:
                 return 1;
             case ImageFormat.PRIVATE:
                 return 0;
diff --git a/media/java/android/media/ImageWriter.java b/media/java/android/media/ImageWriter.java
index 9fb3286..851d436 100644
--- a/media/java/android/media/ImageWriter.java
+++ b/media/java/android/media/ImageWriter.java
@@ -18,17 +18,21 @@
 
 import android.graphics.ImageFormat;
 import android.graphics.Rect;
+import android.hardware.camera2.utils.SurfaceUtils;
 import android.os.Handler;
 import android.os.Looper;
 import android.os.Message;
+import android.util.Size;
 import android.view.Surface;
 
+import dalvik.system.VMRuntime;
+
 import java.lang.ref.WeakReference;
 import java.nio.ByteBuffer;
 import java.nio.ByteOrder;
 import java.nio.NioUtils;
-import java.util.ArrayList;
 import java.util.List;
+import java.util.concurrent.CopyOnWriteArrayList;
 
 /**
  * <p>
@@ -83,8 +87,10 @@
     private int mWriterFormat;
 
     private final int mMaxImages;
-    // Keep track of the currently dequeued Image.
-    private List<Image> mDequeuedImages = new ArrayList<Image>();
+    // Keep track of the currently dequeued Image. This need to be thread safe as the images
+    // could be closed by different threads (e.g., application thread and GC thread).
+    private List<Image> mDequeuedImages = new CopyOnWriteArrayList<Image>();
+    private int mEstimatedNativeAllocBytes;
 
     /**
      * <p>
@@ -128,6 +134,16 @@
         // Note that the underlying BufferQueue is working in synchronous mode
         // to avoid dropping any buffers.
         mNativeContext = nativeInit(new WeakReference<ImageWriter>(this), surface, maxImages);
+
+        // Estimate the native buffer allocation size and register it so it gets accounted for
+        // during GC. Note that this doesn't include the buffers required by the buffer queue
+        // itself and the buffers requested by the producer.
+        Size surfSize = SurfaceUtils.getSurfaceSize(surface);
+        int format = SurfaceUtils.getSurfaceFormat(surface);
+        mEstimatedNativeAllocBytes =
+                ImageUtils.getEstimatedNativeAllocBytes(surfSize.getWidth(),surfSize.getHeight(),
+                        format, maxImages);
+        VMRuntime.getRuntime().registerNativeAllocation(mEstimatedNativeAllocBytes);
     }
 
     /**
@@ -432,6 +448,11 @@
         mDequeuedImages.clear();
         nativeClose(mNativeContext);
         mNativeContext = 0;
+
+        if (mEstimatedNativeAllocBytes > 0) {
+            VMRuntime.getRuntime().registerNativeFree(mEstimatedNativeAllocBytes);
+            mEstimatedNativeAllocBytes = 0;
+        }
     }
 
     @Override
@@ -569,9 +590,8 @@
         }
 
         WriterSurfaceImage wi = (WriterSurfaceImage) image;
-
         if (!wi.mIsImageValid) {
-            throw new IllegalStateException("Image is invalid");
+            return;
         }
 
         /**
diff --git a/media/jni/android_media_ImageReader.cpp b/media/jni/android_media_ImageReader.cpp
index 3ffdb17..9a53186 100644
--- a/media/jni/android_media_ImageReader.cpp
+++ b/media/jni/android_media_ImageReader.cpp
@@ -933,7 +933,7 @@
         CpuConsumer* consumer = ctx->getCpuConsumer();
         CpuConsumer::LockedBuffer* buffer = Image_getLockedBuffer(env, image);
         if (!buffer) {
-            ALOGW("Image already released!!!");
+            // Release an already closed image is harmless.
             return;
         }
         consumer->unlockBuffer(*buffer);
@@ -1078,7 +1078,8 @@
     ALOGV("%s:", __FUNCTION__);
     JNIImageReaderContext* ctx = ImageReader_getContext(env, thiz);
     if (ctx == NULL) {
-        jniThrowRuntimeException(env, "ImageReaderContext is not initialized");
+        jniThrowException(env, "java/lang/IllegalStateException",
+                "ImageReader is not initialized or was already closed");
         return -1;
     }
 
diff --git a/media/jni/android_media_ImageWriter.cpp b/media/jni/android_media_ImageWriter.cpp
index f92a8ef..f50da85 100644
--- a/media/jni/android_media_ImageWriter.cpp
+++ b/media/jni/android_media_ImageWriter.cpp
@@ -403,8 +403,7 @@
     ALOGV("%s", __FUNCTION__);
     JNIImageWriterContext* const ctx = reinterpret_cast<JNIImageWriterContext *>(nativeCtx);
     if (ctx == NULL || thiz == NULL) {
-        jniThrowException(env, "java/lang/IllegalStateException",
-                "ImageWriterContext is not initialized");
+        ALOGW("ImageWriter#close called before Image#close, consider calling Image#close first");
         return;
     }
 
@@ -414,8 +413,7 @@
     int fenceFd = -1;
     Image_getNativeContext(env, image, &buffer, &fenceFd);
     if (buffer == NULL) {
-        jniThrowException(env, "java/lang/IllegalStateException",
-                "Image is not initialized");
+        // Cancel an already cancelled image is harmless.
         return;
     }
 
diff --git a/packages/DocumentsUI/src/com/android/documentsui/dirlist/DirectoryFragment.java b/packages/DocumentsUI/src/com/android/documentsui/dirlist/DirectoryFragment.java
index 2e7c908..2d77c6a 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/dirlist/DirectoryFragment.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/dirlist/DirectoryFragment.java
@@ -17,7 +17,6 @@
 package com.android.documentsui.dirlist;
 
 import static com.android.documentsui.Shared.DEBUG;
-import static com.android.documentsui.State.ACTION_CREATE;
 import static com.android.documentsui.State.ACTION_MANAGE;
 import static com.android.documentsui.State.MODE_GRID;
 import static com.android.documentsui.State.MODE_LIST;
@@ -28,6 +27,7 @@
 import static com.android.documentsui.model.DocumentInfo.getCursorString;
 import static com.android.internal.util.Preconditions.checkNotNull;
 import static com.android.internal.util.Preconditions.checkState;
+import static com.google.common.base.Preconditions.checkArgument;
 
 import android.app.Activity;
 import android.app.ActivityManager;
@@ -89,6 +89,7 @@
 import android.widget.TextView;
 
 import com.android.documentsui.BaseActivity;
+import com.android.documentsui.BaseActivity.DocumentContext;
 import com.android.documentsui.CopyService;
 import com.android.documentsui.DirectoryLoader;
 import com.android.documentsui.DirectoryResult;
@@ -101,20 +102,18 @@
 import com.android.documentsui.MessageBar;
 import com.android.documentsui.MimePredicate;
 import com.android.documentsui.ProviderExecutor;
+import com.android.documentsui.ProviderExecutor.Preemptable;
 import com.android.documentsui.R;
 import com.android.documentsui.RecentLoader;
 import com.android.documentsui.RecentsProvider;
+import com.android.documentsui.RecentsProvider.StateColumns;
 import com.android.documentsui.RootCursorWrapper;
 import com.android.documentsui.RootsCache;
 import com.android.documentsui.Shared;
+import com.android.documentsui.Shared;
 import com.android.documentsui.Snackbars;
 import com.android.documentsui.State;
 import com.android.documentsui.ThumbnailCache;
-import com.android.documentsui.BaseActivity.DocumentContext;
-import com.android.documentsui.ProviderExecutor.Preemptable;
-import com.android.documentsui.Shared;
-import com.android.documentsui.RecentsProvider.StateColumns;
-import com.android.documentsui.dirlist.MultiSelectManager.Callback;
 import com.android.documentsui.dirlist.MultiSelectManager.Selection;
 import com.android.documentsui.model.DocumentInfo;
 import com.android.documentsui.model.DocumentStack;
@@ -770,7 +769,9 @@
                     return true;
 
                 case R.id.menu_copy_to_clipboard:
-                    copySelectionToClipboard(selection);
+                    if (!selection.isEmpty()) {
+                        copySelectionToClipboard(selection);
+                    }
                     return true;
 
                 case R.id.menu_select_all:
@@ -1359,11 +1360,14 @@
     }
 
     public void copySelectedToClipboard() {
-        Selection sel = mSelectionManager.getSelection(new Selection());
-        copySelectionToClipboard(sel);
+        Selection selection = mSelectionManager.getSelection(new Selection());
+        if (!selection.isEmpty()) {
+            copySelectionToClipboard(selection);
+        }
     }
 
-    void copySelectionToClipboard(Selection items) {
+    void copySelectionToClipboard(Selection selection) {
+        checkArgument(!selection.isEmpty());
         new GetDocumentsTask() {
             @Override
             void onDocumentsReady(List<DocumentInfo> docs) {
@@ -1374,7 +1378,7 @@
                                 R.plurals.clipboard_files_clipped, docs.size(), docs.size()),
                                 Snackbar.LENGTH_SHORT).show();
             }
-        }.execute(items);
+        }.execute(selection);
     }
 
     public void pasteFromClipboard() {
diff --git a/packages/MtpDocumentsProvider/Android.mk b/packages/MtpDocumentsProvider/Android.mk
index ec18463..3c2fa36 100644
--- a/packages/MtpDocumentsProvider/Android.mk
+++ b/packages/MtpDocumentsProvider/Android.mk
@@ -5,6 +5,7 @@
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 LOCAL_PACKAGE_NAME := MtpDocumentsProvider
 LOCAL_CERTIFICATE := media
+LOCAL_PRIVILEGED_MODULE := true
 
 include $(BUILD_PACKAGE)
 include $(LOCAL_PATH)/tests/Android.mk
diff --git a/packages/MtpDocumentsProvider/AndroidManifest.xml b/packages/MtpDocumentsProvider/AndroidManifest.xml
index 0172a4f..2090d20 100644
--- a/packages/MtpDocumentsProvider/AndroidManifest.xml
+++ b/packages/MtpDocumentsProvider/AndroidManifest.xml
@@ -3,29 +3,26 @@
           package="com.android.mtp"
           android:sharedUserId="android.media">
     <uses-feature android:name="android.hardware.usb.host" />
+    <uses-permission android:name="android.permission.MANAGE_USB" />
     <application android:label="@string/app_label">
         <provider
             android:name=".MtpDocumentsProvider"
             android:authorities="com.android.mtp.documents"
             android:grantUriPermissions="true"
             android:exported="true"
-            android:permission="android.permission.MANAGE_DOCUMENTS"
-            android:enabled="false">
+            android:permission="android.permission.MANAGE_DOCUMENTS">
             <intent-filter>
                 <action android:name="android.content.action.DOCUMENTS_PROVIDER" />
             </intent-filter>
         </provider>
-        <activity android:name=".ReceiverActivity"
-                  android:theme="@android:style/Theme.NoDisplay"
-                  android:screenOrientation="locked"
-                  android:excludeFromRecents="true"
-                  android:enabled="false">
+        <service android:name=".MtpDocumentsService" />
+        <receiver android:name=".UsbIntentReceiver" android:exported="true">
             <intent-filter>
                 <action android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED" />
+                <action android:name="android.hardware.usb.action.USB_DEVICE_DETACHED" />
             </intent-filter>
             <meta-data android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED"
-                       android:resource="@xml/device_filter" />
-        </activity>
-        <service android:name=".MtpDocumentsService"></service>
+                    android:resource="@xml/device_filter" />
+        </receiver>
     </application>
 </manifest>
diff --git a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDocumentsProvider.java b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDocumentsProvider.java
index 9511e15..d5f00e6 100644
--- a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDocumentsProvider.java
+++ b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDocumentsProvider.java
@@ -253,9 +253,15 @@
         mRootScanner.notifyChange();
     }
 
-    boolean hasOpenedDevices() {
+    int[] getOpenedDeviceIds() {
         synchronized (mDeviceListLock) {
-            return mMtpManager.getOpenedDeviceIds().length != 0;
+            return mMtpManager.getOpenedDeviceIds();
+        }
+    }
+
+    String getDeviceName(int deviceId) throws IOException {
+        synchronized (mDeviceListLock) {
+            return mMtpManager.getDeviceName(deviceId);
         }
     }
 
@@ -308,7 +314,7 @@
         getDeviceToolkit(deviceId).mDocumentLoader.clearTasks();
         mDeviceToolkits.remove(deviceId);
         mMtpManager.closeDevice(deviceId);
-        if (!hasOpenedDevices()) {
+        if (getOpenedDeviceIds().length == 0) {
             mRootScanner.pause();
         }
     }
diff --git a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDocumentsService.java b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDocumentsService.java
index 9b3c20f..b0cff83 100644
--- a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDocumentsService.java
+++ b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDocumentsService.java
@@ -16,16 +16,16 @@
 
 package com.android.mtp;
 
+import android.app.Notification;
 import android.app.Service;
-import android.content.BroadcastReceiver;
-import android.content.Context;
+import android.app.NotificationManager;
 import android.content.Intent;
-import android.content.IntentFilter;
 import android.hardware.usb.UsbDevice;
-import android.hardware.usb.UsbManager;
 import android.os.IBinder;
 import android.util.Log;
 
+import com.android.internal.util.Preconditions;
+
 import java.io.IOException;
 
 /**
@@ -34,10 +34,12 @@
  * starts to run when the first MTP device is opened, and stops when the last MTP device is closed.
  */
 public class MtpDocumentsService extends Service {
-    static final String ACTION_OPEN_DEVICE = "com.android.mtp.action.ACTION_OPEN_DEVICE";
+    static final String ACTION_OPEN_DEVICE = "com.android.mtp.OPEN_DEVICE";
+    static final String ACTION_CLOSE_DEVICE = "com.android.mtp.CLOSE_DEVICE";
     static final String EXTRA_DEVICE = "device";
+    private static final int FOREGROUND_NOTIFICATION_ID = 1;
 
-    Receiver mReceiver;
+    NotificationManager mNotificationManager;
 
     @Override
     public IBinder onBind(Intent intent) {
@@ -48,60 +50,89 @@
     @Override
     public void onCreate() {
         super.onCreate();
-        final IntentFilter filter = new IntentFilter(UsbManager.ACTION_USB_DEVICE_DETACHED);
-        mReceiver = new Receiver();
-        registerReceiver(mReceiver, filter);
+        mNotificationManager = getSystemService(NotificationManager.class);
     }
 
     @Override
     public int onStartCommand(Intent intent, int flags, int startId) {
         // If intent is null, the service was restarted.
         if (intent != null) {
-            if (intent.getAction().equals(ACTION_OPEN_DEVICE)) {
-                final UsbDevice device = intent.<UsbDevice>getParcelableExtra(EXTRA_DEVICE);
-                try {
-                    final MtpDocumentsProvider provider = MtpDocumentsProvider.getInstance();
-                    provider.openDevice(device.getDeviceId());
-                    return START_STICKY;
-                } catch (IOException error) {
-                    Log.e(MtpDocumentsProvider.TAG, error.getMessage());
+            final MtpDocumentsProvider provider = MtpDocumentsProvider.getInstance();
+            final UsbDevice device = intent.<UsbDevice>getParcelableExtra(EXTRA_DEVICE);
+            try {
+                Preconditions.checkNotNull(device);
+                switch (intent.getAction()) {
+                    case ACTION_OPEN_DEVICE:
+                        provider.openDevice(device.getDeviceId());
+                        break;
+
+                    case ACTION_CLOSE_DEVICE:
+                        provider.closeDevice(device.getDeviceId());
+                        break;
+
+                    default:
+                        throw new IllegalArgumentException("Received unknown intent action.");
                 }
-            } else {
-                Log.e(MtpDocumentsProvider.TAG, "Received unknown intent action.");
+            } catch (IOException | InterruptedException | IllegalArgumentException error) {
+                logErrorMessage(error);
             }
+        } else {
+            // TODO: Fetch devices again.
         }
-        stopSelfIfNeeded();
-        return Service.START_NOT_STICKY;
+
+        return updateForegroundState() ? START_STICKY : START_NOT_STICKY;
     }
 
-    @Override
-    public void onDestroy() {
-        unregisterReceiver(mReceiver);
-        mReceiver = null;
-        super.onDestroy();
-    }
-
-    private void stopSelfIfNeeded() {
+    /**
+     * Updates the foreground state of the service.
+     * @return Whether the service is foreground or not.
+     */
+    private boolean updateForegroundState() {
         final MtpDocumentsProvider provider = MtpDocumentsProvider.getInstance();
-        if (!provider.hasOpenedDevices()) {
+        final int[] deviceIds = provider.getOpenedDeviceIds();
+        String message = null;
+        if (deviceIds.length != 0) {
+            // TODO: Localize the message.
+            // TODO: Add buttons "Open in Files" and "Open in Apps" if needed.
+            if (deviceIds.length > 1) {
+                message = deviceIds.length + " devices are being connected.";
+            } else {
+                try {
+                    message = provider.getDeviceName(deviceIds[0]) + " is being connected.";
+                } catch (IOException exp) {
+                    logErrorMessage(exp);
+                    // If we failed to obtain device name, it looks the device is unusable.
+                    // Because this is the last device we opened, we should hide the notification
+                    // for the case.
+                    try {
+                        provider.closeDevice(deviceIds[0]);
+                    } catch (IOException | InterruptedException closeError) {
+                        logErrorMessage(closeError);
+                    }
+                }
+            }
+        }
+        if (message != null) {
+            final Notification notification = new Notification.Builder(this)
+                    .setContentTitle(message)
+                    .setSmallIcon(android.R.drawable.ic_menu_camera)
+                    .setCategory(Notification.CATEGORY_SYSTEM)
+                    .setPriority(Notification.PRIORITY_LOW)
+                    .build();
+            startForeground(FOREGROUND_NOTIFICATION_ID, notification);
+            return true;
+        } else {
+            stopForeground(true /* removeNotification */);
             stopSelf();
+            return false;
         }
     }
 
-    private class Receiver extends BroadcastReceiver {
-        @Override
-        public void onReceive(Context context, Intent intent) {
-            if (UsbManager.ACTION_USB_DEVICE_DETACHED.equals(intent.getAction())) {
-                final UsbDevice device =
-                        (UsbDevice) intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
-                final MtpDocumentsProvider provider = MtpDocumentsProvider.getInstance();
-                try {
-                    provider.closeDevice(device.getDeviceId());
-                } catch (IOException | InterruptedException error) {
-                    Log.e(MtpDocumentsProvider.TAG, error.getMessage());
-                }
-                stopSelfIfNeeded();
-            }
+    private static void logErrorMessage(Exception exp) {
+        if (exp.getMessage() != null) {
+            Log.e(MtpDocumentsProvider.TAG, exp.getMessage());
+        } else {
+            Log.e(MtpDocumentsProvider.TAG, exp.toString());
         }
     }
 }
diff --git a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpManager.java b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpManager.java
index cd52f31..e7f94b7 100644
--- a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpManager.java
+++ b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpManager.java
@@ -32,7 +32,6 @@
 
 import java.io.FileNotFoundException;
 import java.io.IOException;
-import java.util.ArrayList;
 
 /**
  * The model wrapping android.mtp API.
@@ -62,8 +61,10 @@
         }
 
         if (!mManager.hasPermission(rawDevice)) {
-            // Permission should be obtained via app selection dialog for intent.
-            throw new IOException("No parmission to operate USB device.");
+            mManager.grantPermission(rawDevice);
+            if (!mManager.hasPermission(rawDevice)) {
+                throw new IOException("Failed to grant a device permission.");
+            }
         }
 
         final MtpDevice device = new MtpDevice(rawDevice);
@@ -99,6 +100,10 @@
         return result;
     }
 
+    String getDeviceName(int deviceId) throws IOException {
+        return getDevice(deviceId).getDeviceInfo().getModel();
+    }
+
     MtpRoot[] getRoots(int deviceId) throws IOException {
         final MtpDevice device = getDevice(deviceId);
         synchronized (device) {
diff --git a/packages/MtpDocumentsProvider/src/com/android/mtp/ReceiverActivity.java b/packages/MtpDocumentsProvider/src/com/android/mtp/ReceiverActivity.java
deleted file mode 100644
index 3ad2397..0000000
--- a/packages/MtpDocumentsProvider/src/com/android/mtp/ReceiverActivity.java
+++ /dev/null
@@ -1,47 +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.
- */
-
-package com.android.mtp;
-
-import android.app.Activity;
-import android.content.Intent;
-import android.hardware.usb.UsbManager;
-import android.os.Bundle;
-
-/**
- * Invisible activity to receive intents.
- * To show the application chooser for the UsbManager.ACTION_USB_DEVICE_ATTACHED intent, the intent
- * should be received by activity. The activity has NoDisplay theme and immediately terminate after
- * routing intent to MtpDocumentsService.
- */
-public class ReceiverActivity extends Activity {
-    @Override
-    protected void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-        if (UsbManager.ACTION_USB_DEVICE_ATTACHED.equals(getIntent().getAction())) {
-            final Intent serviceIntent = new Intent(
-                    MtpDocumentsService.ACTION_OPEN_DEVICE,
-                    null,
-                    this,
-                    MtpDocumentsService.class);
-            serviceIntent.putExtra(
-                    UsbManager.EXTRA_DEVICE,
-                    getIntent().getParcelableExtra(UsbManager.EXTRA_DEVICE));
-            startService(serviceIntent);
-        }
-        finish();
-    }
-}
diff --git a/packages/MtpDocumentsProvider/src/com/android/mtp/UsbIntentReceiver.java b/packages/MtpDocumentsProvider/src/com/android/mtp/UsbIntentReceiver.java
new file mode 100644
index 0000000..0ac130e
--- /dev/null
+++ b/packages/MtpDocumentsProvider/src/com/android/mtp/UsbIntentReceiver.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.mtp;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.hardware.usb.UsbDevice;
+import android.hardware.usb.UsbManager;
+import android.net.Uri;
+
+public class UsbIntentReceiver extends BroadcastReceiver {
+    @Override
+    public void onReceive(Context context, Intent intent) {
+        final UsbDevice device = intent.getExtras().getParcelable(UsbManager.EXTRA_DEVICE);
+        switch (intent.getAction()) {
+            case UsbManager.ACTION_USB_DEVICE_ATTACHED:
+                startService(context, MtpDocumentsService.ACTION_OPEN_DEVICE, device);
+                break;
+            case UsbManager.ACTION_USB_DEVICE_DETACHED:
+                startService(context, MtpDocumentsService.ACTION_CLOSE_DEVICE, device);
+                break;
+        }
+    }
+
+    private void startService(Context context, String action, UsbDevice device) {
+        final Intent intent = new Intent(action, Uri.EMPTY, context, MtpDocumentsService.class);
+        intent.putExtra(MtpDocumentsService.EXTRA_DEVICE, device);
+        context.startService(intent);
+    }
+}
diff --git a/packages/Shell/tests/src/com/android/shell/BugreportReceiverTest.java b/packages/Shell/tests/src/com/android/shell/BugreportReceiverTest.java
index a54d9ef..0e31cdf 100644
--- a/packages/Shell/tests/src/com/android/shell/BugreportReceiverTest.java
+++ b/packages/Shell/tests/src/com/android/shell/BugreportReceiverTest.java
@@ -49,6 +49,7 @@
 import android.os.SystemProperties;
 import android.service.notification.StatusBarNotification;
 import android.support.test.uiautomator.UiDevice;
+import android.support.test.uiautomator.UiObject;
 import android.test.InstrumentationTestCase;
 import android.util.Log;
 
@@ -68,8 +69,7 @@
  * <li>asserts the extras received by the custom activity
  * </ul>
  * <p>
- * TODO: currently, these tests only work if the bug report sharing warning is disabled and the
- * device screen is unlocked.
+ * <strong>NOTE</strong>: these tests only work if the device is unlocked.
  */
 public class BugreportReceiverTest extends InstrumentationTestCase {
 
@@ -99,6 +99,7 @@
         mUiBot = new UiBot(UiDevice.getInstance(instrumentation), TIMEOUT);
         mListener = ActionSendMultipleConsumerActivity.getListener(mContext);
         cancelExistingNotifications();
+        BugreportPrefs.setWarningState(mContext, BugreportPrefs.STATE_HIDE);
     }
 
     public void testFullWorkflow() throws Exception {
@@ -132,6 +133,36 @@
         // TODO: assert service is down
     }
 
+    public void testBugreportFinished_withWarning() throws Exception {
+        // Explicitly shows the warning.
+        BugreportPrefs.setWarningState(mContext, BugreportPrefs.STATE_SHOW);
+
+        // Send notification and click on share.
+        createTextFile(PLAIN_TEXT_PATH, BUGREPORT_CONTENT);
+        Intent intent = new Intent(INTENT_BUGREPORT_FINISHED);
+        intent.putExtra(EXTRA_BUGREPORT, PLAIN_TEXT_PATH);
+        mContext.sendBroadcast(intent);
+        mUiBot.clickOnNotification(mContext.getString(R.string.bugreport_finished_title));
+
+        // Handle the warning
+        mUiBot.getVisibleObject(mContext.getString(R.string.bugreport_confirm));
+        // TODO: get ok and showMessageAgain from the dialog reference above
+        UiObject showMessageAgain =
+                mUiBot.getVisibleObject(mContext.getString(R.string.bugreport_confirm_repeat));
+        mUiBot.click(showMessageAgain, "show-message-again");
+        UiObject ok = mUiBot.getVisibleObject(mContext.getString(com.android.internal.R.string.ok));
+        mUiBot.click(ok, "ok");
+
+        // Share the bugreport.
+        mUiBot.chooseActivity(UI_NAME);
+        Bundle extras = mListener.getExtras();
+        assertActionSendMultiple(extras, BUGREPORT_CONTENT, null);
+
+        // Make sure it's hidden now.
+        int newState = BugreportPrefs.getWarningState(mContext, BugreportPrefs.STATE_UNKNOWN);
+        assertEquals("Didn't change state", BugreportPrefs.STATE_HIDE, newState);
+    }
+
     public void testBugreportFinished_plainBugreportAndScreenshot() throws Exception {
         createTextFile(PLAIN_TEXT_PATH, BUGREPORT_CONTENT);
         createTextFile(SCREENSHOT_PATH, SCREENSHOT_CONTENT);
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index 2f79adf..2e65656 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -180,13 +180,6 @@
             </intent-filter>
         </receiver>
 
-        <receiver android:name=".qs.tiles.HotspotTile$APChangedReceiver"
-                androidprv:systemUserOnly="true">
-            <intent-filter>
-                <action android:name="android.net.wifi.WIFI_AP_STATE_CHANGED" />
-            </intent-filter>
-        </receiver>
-
         <activity android:name=".tuner.TunerActivity"
                   android:enabled="false"
                   android:icon="@drawable/tuner"
diff --git a/core/res/res/drawable/ic_arrow_up_14dp.xml b/packages/SystemUI/res/drawable/quick_header_bg.xml
similarity index 60%
rename from core/res/res/drawable/ic_arrow_up_14dp.xml
rename to packages/SystemUI/res/drawable/quick_header_bg.xml
index c4cc0d1..d45d673 100644
--- a/core/res/res/drawable/ic_arrow_up_14dp.xml
+++ b/packages/SystemUI/res/drawable/quick_header_bg.xml
@@ -1,3 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
 <!--
   ~ Copyright (C) 2015 The Android Open Source Project
   ~
@@ -13,12 +14,8 @@
   ~ See the License for the specific language governing permissions and
   ~ limitations under the License
   -->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
-        android:width="14.0dp"
-        android:height="14.0dp"
-        android:viewportWidth="24.0"
-        android:viewportHeight="24.0">
-    <path
-        android:pathData="M12.000000,8.000000l-6.000000,6.000000 1.400000,1.400000 4.600000,-4.599999 4.600000,4.599999 1.400000,-1.400000z"
-        android:fillColor="#FF000000"/>
-</vector>
+
+<ripple xmlns:android="http://schemas.android.com/apk/res/android"
+    android:color="?android:attr/colorControlHighlight" >
+    <item android:drawable="@color/system_primary_color"/>
+</ripple>
diff --git a/packages/SystemUI/res/layout/hybrid_notification.xml b/packages/SystemUI/res/layout/hybrid_notification.xml
index 9e64d2c..f667859 100644
--- a/packages/SystemUI/res/layout/hybrid_notification.xml
+++ b/packages/SystemUI/res/layout/hybrid_notification.xml
@@ -18,21 +18,23 @@
 <com.android.systemui.statusbar.notification.HybridNotificationView
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="match_parent"
-    android:layout_height="@dimen/notification_max_height">
+    android:layout_height="wrap_content"
+    android:paddingStart="@*android:dimen/notification_content_margin_start"
+    android:paddingEnd="12dp"
+    android:gravity="bottom">
     <TextView
         android:id="@+id/notification_title"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
-        android:layout_gravity="center_vertical"
+        android:paddingEnd="4dp"
         android:singleLine="true"
         android:textAppearance="@*android:style/TextAppearance.Material.Notification.Title"
-        android:paddingEnd="4dp"
     />
     <TextView
         android:id="@+id/notification_text"
-        android:layout_width="wrap_content"
+        android:layout_width="match_parent"
         android:layout_height="wrap_content"
-        android:layout_gravity="center_vertical"
+        android:paddingEnd="4dp"
         android:textAppearance="@*android:style/TextAppearance.Material.Notification"
         android:singleLine="true"
         />
diff --git a/packages/SystemUI/res/layout/notification_children_divider.xml b/packages/SystemUI/res/layout/notification_children_divider.xml
index f011afe..53273cf 100644
--- a/packages/SystemUI/res/layout/notification_children_divider.xml
+++ b/packages/SystemUI/res/layout/notification_children_divider.xml
@@ -20,4 +20,4 @@
     android:id="@+id/notification_more_divider"
     android:layout_width="match_parent"
     android:layout_height="@dimen/notification_children_divider_height"
-    android:background="@*android:drawable/notification_template_divider" />
+    android:background="#61000000" />
diff --git a/packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml b/packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml
index cc35c51c..8124eb7 100644
--- a/packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml
+++ b/packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml
@@ -25,9 +25,10 @@
     android:layout_gravity="@integer/notification_panel_layout_gravity"
     android:paddingStart="@dimen/notification_side_padding"
     android:paddingEnd="@dimen/notification_side_padding"
+    android:clipChildren="false"
+    android:clipToPadding="false"
     android:baselineAligned="false"
-    android:elevation="4dp"
-    android:background="@drawable/notification_header_bg"
+    android:background="@drawable/quick_header_bg"
     android:clickable="true"
     android:focusable="true"
     >
@@ -35,9 +36,11 @@
     <com.android.systemui.qs.QuickQSPanel
         android:id="@+id/quick_qs_panel"
         android:background="#0000"
-        android:layout_width="144dp"
+        android:layout_width="wrap_content"
         android:layout_height="match_parent"
         android:layout_alignParentEnd="true"
+        android:clipChildren="false"
+        android:clipToPadding="false"
         android:layout_marginEnd="12dp" />
 
     <LinearLayout
@@ -49,7 +52,7 @@
         android:clipToPadding="false"
         android:orientation="horizontal"
         android:layout_alignParentEnd="true"
-        android:layout_marginEnd="10dp">
+        android:layout_marginEnd="12dp">
 
         <com.android.systemui.statusbar.AlphaOptimizedFrameLayout
             android:id="@+id/settings_button_container"
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 086e9f4..65b0c45 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -36,18 +36,6 @@
     <!-- The font size for the clock -->
     <dimen name="status_bar_clock_size">14sp</dimen>
 
-    <!-- The margin on the start of the content view -->
-    <dimen name="notification_content_margin_start">16dp</dimen>
-
-    <!-- The maximum size of the title when in single line mode -->
-    <dimen name="notification_maximum_title_length">150sp</dimen>
-
-    <!-- The margin on the end of the content view -->
-    <dimen name="notification_content_margin_end">8dp</dimen>
-
-    <!-- Height of a single line notification in the status bar -->
-    <dimen name="notification_single_line_height">32sp</dimen>
-
     <!-- Height of a small notification in the status bar-->
     <dimen name="notification_min_height">84dp</dimen>
 
@@ -173,7 +161,7 @@
     <dimen name="borderless_button_radius">2dp</dimen>
 
     <!-- How far the expanded QS panel peeks from the header in collapsed state. -->
-    <dimen name="qs_peek_height">8dp</dimen>
+    <dimen name="qs_peek_height">0dp</dimen>
 
     <!-- Zen mode panel: condition item button padding -->
     <dimen name="zen_mode_condition_detail_button_padding">8dp</dimen>
@@ -351,8 +339,11 @@
     <!-- radius of the corners of the material rounded rect background but negative-->
     <dimen name="notification_material_rounded_rect_radius_negative">-2dp</dimen>
 
-    <!-- The padding between notification children -->
-    <dimen name="notification_children_padding">2dp</dimen>
+    <!-- The padding between notification children when collapsed -->
+    <dimen name="notification_children_padding">4dp</dimen>
+
+    <!-- The padding on top of the first notification to the children container -->
+    <dimen name="notification_children_container_top_padding">8dp</dimen>
 
     <!-- The height of the divider between the notfication children -->
     <dimen name="notification_children_divider_height">1dp</dimen>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 6c87cd6..666a024 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -1038,8 +1038,8 @@
     <!-- VolumeUI restoration notification: text -->
     <string name="volumeui_notification_text">Touch to restore the original.</string>
 
-    <!-- Describes the way 2 names are concatenated. An example would be ", " to produce "Peter Muller, Paul Curry". Please also include a space here if it's appropriate in the language and if it's a RTL language include it on the left. [CHAR LIMIT=3] -->
-    <string name="group_summary_concadenation">, </string>
+    <!-- Describes the way 2 names are concatenated. An example would be ", " to produce "Peter Muller, Paul Curry". Please also include a space here if it's appropriate in the language and if it's a RTL language include it on the left. The translation should start and end with " to keep the white space if desired [CHAR LIMIT=5] -->
+    <string name="group_summary_concadenation">", "</string>
 
     <!-- Toast shown when user unlocks screen and managed profile activity is in the foreground -->
     <string name="managed_profile_foreground_toast">You\'re using your work profile</string>
diff --git a/packages/SystemUI/src/com/android/systemui/QSQuickTileView.java b/packages/SystemUI/src/com/android/systemui/QSQuickTileView.java
deleted file mode 100644
index 3362650..0000000
--- a/packages/SystemUI/src/com/android/systemui/QSQuickTileView.java
+++ /dev/null
@@ -1,78 +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.
- */
-
-package com.android.systemui;
-
-import android.content.Context;
-import android.view.View;
-import android.widget.ImageView;
-import com.android.systemui.qs.QSTile;
-import com.android.systemui.qs.QSTileBaseView;
-
-public class QSQuickTileView extends QSTileBaseView {
-
-    private final int mPadding;
-    private final ImageView mIcon;
-
-    public QSQuickTileView(Context context) {
-        super(context);
-        mPadding = context.getResources().getDimensionPixelSize(R.dimen.qs_quick_tile_padding);
-        mIcon = createIcon();
-        addView(mIcon);
-    }
-
-    protected ImageView createIcon() {
-        final ImageView icon = new ImageView(mContext);
-        icon.setId(android.R.id.icon);
-        icon.setScaleType(ImageView.ScaleType.CENTER_INSIDE);
-        return icon;
-    }
-
-    @Override
-    public void init(OnClickListener click, OnClickListener clickSecondary,
-                     OnLongClickListener longClick) {
-        setClickable(true);
-        setOnClickListener(click);
-    }
-
-    @Override
-    protected void handleStateChanged(QSTile.State state) {
-        mIcon.setImageDrawable(state.icon.getDrawable(getContext()));
-        setContentDescription(state.contentDescription);
-    }
-
-    @Override
-    public boolean setType(int type) {
-        return false;
-    }
-
-    @Override
-    public View updateAccessibilityOrder(View previousView) {
-        return this;
-    }
-
-    @Override
-    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
-        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
-        mIcon.measure(exactly(getMeasuredWidth() - 2 * mPadding),
-                exactly(getMeasuredHeight() - 2 * mPadding));
-    }
-
-    @Override
-    protected void onLayout(boolean changed, int l, int t, int r, int b) {
-        layout(mIcon, mPadding, mPadding);
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java b/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
index 0e4a4e5..ca4a03a 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
@@ -73,11 +73,6 @@
     }
 
     @Override
-    public void setTileVisibility(TileRecord tile, int visibility) {
-        tile.tileView.setVisibility(visibility);
-    }
-
-    @Override
     public void addTile(TileRecord tile) {
         mTiles.add(tile);
         distributeTiles();
@@ -101,10 +96,6 @@
         final int NT = mTiles.size();
         for (int i = 0; i < NT; i++) {
             TileRecord tile = mTiles.get(i);
-            if (tile.tile.getTileType() == QSTileView.QS_TYPE_QUICK) {
-                // Don't show any quick tiles for now.
-                continue;
-            }
             if (mPages.get(index).isFull()) {
                 if (++index == mPages.size()) {
                     if (DEBUG) Log.d(TAG, "Adding page for " + tile.tile.getClass().getSimpleName());
@@ -178,7 +169,6 @@
 
         public TilePage(Context context, AttributeSet attrs) {
             super(context, attrs);
-            mAllowDual = false;
             updateResources();
         }
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSIconView.java b/packages/SystemUI/src/com/android/systemui/qs/QSIconView.java
new file mode 100644
index 0000000..b56ad76
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSIconView.java
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.graphics.drawable.Animatable;
+import android.graphics.drawable.Drawable;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ImageView;
+import com.android.systemui.R;
+
+import java.util.Objects;
+
+public class QSIconView extends ViewGroup {
+
+    private final View mIcon;
+    private final int mIconSizePx;
+    private final int mTilePaddingBelowIconPx;
+
+    public QSIconView(Context context) {
+        super(context);
+
+        final Resources res = context.getResources();
+        mIconSizePx = res.getDimensionPixelSize(R.dimen.qs_tile_icon_size);
+        mTilePaddingBelowIconPx =  res.getDimensionPixelSize(R.dimen.qs_tile_padding_below_icon);
+
+        mIcon = createIcon();
+        addView(mIcon);
+    }
+
+    @Override
+    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+        final int w = MeasureSpec.getSize(widthMeasureSpec);
+        final int iconSpec = exactly(mIconSizePx);
+        mIcon.measure(MeasureSpec.makeMeasureSpec(w, getIconMeasureMode()), iconSpec);
+        setMeasuredDimension(w, mIcon.getMeasuredHeight() + mTilePaddingBelowIconPx);
+    }
+
+    @Override
+    protected void onLayout(boolean changed, int l, int t, int r, int b) {
+        final int w = getMeasuredWidth();
+        final int h = getMeasuredHeight();
+        int top = 0;
+        final int iconLeft = (w - mIcon.getMeasuredWidth()) / 2;
+        layout(mIcon, iconLeft, top);
+    }
+
+    public void setIcon(QSTile.State state) {
+        setIcon((ImageView) mIcon, state);
+    }
+
+    protected void setIcon(ImageView iv, QSTile.State state) {
+        if (!Objects.equals(state.icon, iv.getTag(R.id.qs_icon_tag))) {
+            Drawable d = state.icon != null ? state.icon.getDrawable(mContext) : null;
+            if (d != null && state.autoMirrorDrawable) {
+                d.setAutoMirrored(true);
+            }
+            iv.setImageDrawable(d);
+            iv.setTag(R.id.qs_icon_tag, state.icon);
+            if (d instanceof Animatable) {
+                Animatable a = (Animatable) d;
+                if (state.icon instanceof QSTile.AnimationIcon && !iv.isShown()) {
+                    a.stop(); // skip directly to end state
+                }
+            }
+        }
+
+    }
+
+    protected int getIconMeasureMode() {
+        return MeasureSpec.EXACTLY;
+    }
+
+    protected View createIcon() {
+        final ImageView icon = new ImageView(mContext);
+        icon.setId(android.R.id.icon);
+        icon.setScaleType(ImageView.ScaleType.CENTER_INSIDE);
+        return icon;
+    }
+
+    protected static int exactly(int size) {
+        return MeasureSpec.makeMeasureSpec(size, MeasureSpec.EXACTLY);
+    }
+
+    protected static void layout(View child, int left, int top) {
+        child.layout(left, top, left + child.getMeasuredWidth(), top + child.getMeasuredHeight());
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
index bb2b8fc..5d3fbe2 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
@@ -298,19 +298,10 @@
         showDetail(show, r);
     }
 
-    private void showDetail(boolean show, Record r) {
+    protected void showDetail(boolean show, Record r) {
         mHandler.obtainMessage(H.SHOW_DETAIL, show ? 1 : 0, 0, r).sendToTarget();
     }
 
-    private void setTileVisibility(TileRecord record, int visibility) {
-        mHandler.obtainMessage(H.SET_TILE_VISIBILITY, visibility, 0, record).sendToTarget();
-    }
-
-    private void handleSetTileVisibility(TileRecord tile, int visibility) {
-        if (visibility == tile.tileView.getVisibility()) return;
-        mTileLayout.setTileVisibility(tile, visibility);
-    }
-
     public void setTiles(Collection<QSTile<?>> tiles) {
         for (TileRecord record : mRecords) {
             mTileLayout.removeTile(record);
@@ -325,16 +316,17 @@
     }
 
     private void drawTile(TileRecord r, QSTile.State state) {
-        final int visibility = state.visible ? VISIBLE : GONE;
-        setTileVisibility(r, visibility);
         r.tileView.onStateChanged(state);
     }
 
+    protected QSTileBaseView createTileView(QSTile<?> tile) {
+        return new QSTileView(mContext, tile.createTileView(mContext));
+    }
+
     protected void addTile(final QSTile<?> tile) {
         final TileRecord r = new TileRecord();
         r.tile = tile;
-        r.tileView = tile.createTileView(mContext);
-        r.tileView.setVisibility(View.GONE);
+        r.tileView = createTileView(tile);
         final QSTile.Callback callback = new QSTile.Callback() {
             @Override
             public void onStateChanged(QSTile.State state) {
@@ -369,13 +361,7 @@
         final View.OnClickListener click = new View.OnClickListener() {
             @Override
             public void onClick(View v) {
-                r.tile.click();
-            }
-        };
-        final View.OnClickListener clickSecondary = new View.OnClickListener() {
-            @Override
-            public void onClick(View v) {
-                r.tile.secondaryClick();
+                onTileClick(r.tile);
             }
         };
         final View.OnLongClickListener longClick = new View.OnLongClickListener() {
@@ -396,7 +382,7 @@
                 return true;
             }
         };
-        r.tileView.init(click, clickSecondary, longClick);
+        r.tileView.init(click, longClick);
         r.tile.setListening(mListening);
         callback.onStateChanged(r.tile.getState());
         r.tile.refreshState();
@@ -407,6 +393,10 @@
         }
     }
 
+    protected void onTileClick(QSTile<?> tile) {
+        tile.click();
+    }
+
     public boolean isShowingDetail() {
         return mDetailRecord != null
                 || (mCustomizePanel != null && mCustomizePanel.isCustomizing());
@@ -429,7 +419,7 @@
         return mQsContainer.getMeasuredHeight();
     }
 
-    private void handleShowDetail(Record r, boolean show) {
+    protected void handleShowDetail(Record r, boolean show) {
         if (r instanceof TileRecord) {
             handleShowDetailTile((TileRecord) r, show);
         } else {
@@ -515,9 +505,7 @@
     private void logTiles() {
         for (int i = 0; i < mRecords.size(); i++) {
             TileRecord tileRecord = mRecords.get(i);
-            if (tileRecord.tile.getState().visible) {
-                MetricsLogger.visible(mContext, tileRecord.tile.getMetricsCategory());
-            }
+            MetricsLogger.visible(mContext, tileRecord.tile.getMetricsCategory());
         }
     }
 
@@ -554,13 +542,11 @@
         public void handleMessage(Message msg) {
             if (msg.what == SHOW_DETAIL) {
                 handleShowDetail((Record)msg.obj, msg.arg1 != 0);
-            } else if (msg.what == SET_TILE_VISIBILITY) {
-                handleSetTileVisibility((TileRecord) msg.obj, msg.arg1);
             }
         }
     }
 
-    private static class Record {
+    protected static class Record {
         View detailView;
         DetailAdapter detailAdapter;
         int x;
@@ -619,7 +605,6 @@
     public interface QSTileLayout {
         void addTile(TileRecord tile);
         void removeTile(TileRecord tile);
-        void setTileVisibility(TileRecord tile, int visibility);
         int getOffsetTop(TileRecord tile);
         void updateResources();
     }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSTile.java b/packages/SystemUI/src/com/android/systemui/qs/QSTile.java
index 7f45545..39f0c55 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSTile.java
@@ -86,20 +86,12 @@
         mTileSpec = tileSpec;
     }
 
-    public int getTileType() {
-        return QSTileView.QS_TYPE_NORMAL;
-    }
-
-    public final boolean supportsDualTargets() {
-        return getTileType() == QSTileView.QS_TYPE_DUAL;
-    }
-
     public Host getHost() {
         return mHost;
     }
 
-    public QSTileBaseView createTileView(Context context) {
-        return new QSTileView(context);
+    public QSIconView createTileView(Context context) {
+        return new QSIconView(context);
     }
 
     public DetailAdapter getDetailAdapter() {
@@ -181,7 +173,8 @@
     }
 
     protected void handleSecondaryClick() {
-        // optional
+        // Default to normal click.
+        handleClick();
     }
 
     protected void handleLongClick() {
@@ -327,8 +320,10 @@
     public interface Host {
         void startActivityDismissingKeyguard(Intent intent);
         void startActivityDismissingKeyguard(PendingIntent intent);
+        void startRunnableDismissingKeyguard(Runnable runnable);
         void warn(String message, Throwable t);
         void collapsePanels();
+        void openPanels();
         Looper getLooper();
         Context getContext();
         Collection<QSTile<?>> getTiles();
@@ -345,6 +340,7 @@
         UserSwitcherController getUserSwitcherController();
         UserInfoController getUserInfoController();
         BatteryController getBatteryController();
+        void removeTile(String tileSpec);
 
         public interface Callback {
             void onTilesChanged();
@@ -451,7 +447,6 @@
     }
 
     public static class State {
-        public boolean visible;
         public Icon icon;
         public CharSequence label;
         public CharSequence contentDescription;
@@ -461,14 +456,12 @@
         public boolean copyTo(State other) {
             if (other == null) throw new IllegalArgumentException();
             if (!other.getClass().equals(getClass())) throw new IllegalArgumentException();
-            final boolean changed = other.visible != visible
-                    || !Objects.equals(other.icon, icon)
+            final boolean changed = !Objects.equals(other.icon, icon)
                     || !Objects.equals(other.label, label)
                     || !Objects.equals(other.contentDescription, contentDescription)
                     || !Objects.equals(other.autoMirrorDrawable, autoMirrorDrawable)
                     || !Objects.equals(other.dualLabelContentDescription,
                     dualLabelContentDescription);
-            other.visible = visible;
             other.icon = icon;
             other.label = label;
             other.contentDescription = contentDescription;
@@ -484,7 +477,6 @@
 
         protected StringBuilder toStringBuilder() {
             final StringBuilder sb = new StringBuilder(getClass().getSimpleName()).append('[');
-            sb.append("visible=").append(visible);
             sb.append(",icon=").append(icon);
             sb.append(",label=").append(label);
             sb.append(",contentDescription=").append(contentDescription);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSTileBaseView.java b/packages/SystemUI/src/com/android/systemui/qs/QSTileBaseView.java
index 72fc88d..68461f5 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSTileBaseView.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSTileBaseView.java
@@ -1,57 +1,112 @@
 /*
  * Copyright (C) 2015 The Android Open Source Project
  *
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at
+ * 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.
+ * 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.qs;
 
 import android.content.Context;
+import android.content.res.TypedArray;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.RippleDrawable;
 import android.os.Handler;
 import android.os.Looper;
 import android.os.Message;
-import android.util.AttributeSet;
 import android.view.View;
-import android.view.ViewGroup;
+import android.widget.LinearLayout;
+import com.android.systemui.R;
 
-public abstract class QSTileBaseView extends ViewGroup {
-
-    public static final int QS_TYPE_NORMAL = 0;
-    public static final int QS_TYPE_DUAL   = 1;
-    public static final int QS_TYPE_QUICK  = 2;
+public class QSTileBaseView extends LinearLayout {
 
     private final H mHandler = new H();
+    private QSIconView mIcon;
+    private RippleDrawable mRipple;
+    private Drawable mTileBackground;
 
-    public QSTileBaseView(Context context) {
+    public QSTileBaseView(Context context, QSIconView icon) {
         super(context);
+        mIcon = icon;
+        addView(mIcon);
+
+        mTileBackground = newTileBackground();
+        if (mTileBackground instanceof RippleDrawable) {
+            setRipple((RippleDrawable) mTileBackground);
+        }
+        setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_YES);
+        setBackground(mTileBackground);
+
+        // Default to Quick Tile padding, and QSTileView will specify its own padding.
+        int padding = context.getResources().getDimensionPixelSize(R.dimen.qs_quick_tile_padding);
+        setPadding(padding, padding, padding, padding);
     }
 
-    public QSTileBaseView(Context context, AttributeSet attrs) {
-        super(context, attrs);
+    private Drawable newTileBackground() {
+        final int[] attrs = new int[] { android.R.attr.selectableItemBackgroundBorderless };
+        final TypedArray ta = mContext.obtainStyledAttributes(attrs);
+        final Drawable d = ta.getDrawable(0);
+        ta.recycle();
+        return d;
     }
 
+    private void setRipple(RippleDrawable tileBackground) {
+        mRipple = tileBackground;
+        if (getWidth() != 0) {
+            updateRippleSize(getWidth(), getHeight());
+        }
+    }
+
+    private void updateRippleSize(int width, int height) {
+        // center the touch feedback on the center of the icon, and dial it down a bit
+        final int cx = width / 2;
+        final int cy = height / 2;
+        final int rad = (int)(mIcon.getHeight() * .85f);
+        mRipple.setHotspotBounds(cx - rad, cy - rad, cx + rad, cy + rad);
+    }
+
+    public void init(OnClickListener click, OnLongClickListener longClick) {
+        setClickable(true);
+        setOnClickListener(click);
+    }
+
+    @Override
+    protected void onLayout(boolean changed, int l, int t, int r, int b) {
+        super.onLayout(changed, l, t, r, b);
+        final int w = getMeasuredWidth();
+        final int h = getMeasuredHeight();
+
+        if (mRipple != null) {
+            updateRippleSize(w, h);
+        }
+    }
+
+    /**
+     * Update the accessibility order for this view.
+     *
+     * @param previousView the view which should be before this one
+     * @return the last view in this view which is accessible
+     */
+    public View updateAccessibilityOrder(View previousView) {
+        setAccessibilityTraversalAfter(previousView.getId());
+        return this;
+    }
 
     public void onStateChanged(QSTile.State state) {
         mHandler.obtainMessage(H.STATE_CHANGED, state).sendToTarget();
     }
 
-    public abstract void init(OnClickListener click, OnClickListener clickSecondary,
-                              OnLongClickListener longClick);
-    public abstract View updateAccessibilityOrder(View previousView);
-    public abstract boolean setType(int type);
-
-    protected abstract void handleStateChanged(QSTile.State state);
-
-    protected static int exactly(int size) {
-        return MeasureSpec.makeMeasureSpec(size, MeasureSpec.EXACTLY);
-    }
-
-    protected static void layout(View child, int left, int top) {
-        child.layout(left, top, left + child.getMeasuredWidth(), top + child.getMeasuredHeight());
+    protected void handleStateChanged(QSTile.State state) {
+        mIcon.setIcon(state);
+        setContentDescription(state.contentDescription);
     }
 
     private class H extends Handler {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSTileView.java b/packages/SystemUI/src/com/android/systemui/qs/QSTileView.java
index 4cc2b8d..41ac4d9 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSTileView.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSTileView.java
@@ -17,26 +17,16 @@
 package com.android.systemui.qs;
 
 import android.content.Context;
-import android.content.res.ColorStateList;
 import android.content.res.Configuration;
 import android.content.res.Resources;
-import android.content.res.TypedArray;
 import android.graphics.Typeface;
-import android.graphics.drawable.Animatable;
-import android.graphics.drawable.Drawable;
-import android.graphics.drawable.RippleDrawable;
 import android.util.MathUtils;
 import android.util.TypedValue;
 import android.view.Gravity;
 import android.view.View;
-import android.widget.ImageView;
-import android.widget.ImageView.ScaleType;
 import android.widget.TextView;
 import com.android.systemui.FontSizeUtils;
 import com.android.systemui.R;
-import com.android.systemui.qs.QSTile.AnimationIcon;
-
-import java.util.Objects;
 
 /** View that represents a standard quick settings tile. **/
 public class QSTileView extends QSTileBaseView {
@@ -44,59 +34,25 @@
             Typeface.NORMAL);
 
     protected final Context mContext;
-    private final View mIcon;
-    private final View mDivider;
-    private final int mIconSizePx;
     private final int mTileSpacingPx;
     private int mTilePaddingTopPx;
-    private final int mTilePaddingBelowIconPx;
-    private final int mDualTileVerticalPaddingPx;
-    private final View mTopBackgroundView;
 
     private TextView mLabel;
-    private QSDualTileLabel mDualLabel;
-    private int mType;
-    private OnClickListener mClickPrimary;
-    private OnClickListener mClickSecondary;
-    private OnLongClickListener mLongClick;
-    private Drawable mTileBackground;
-    private RippleDrawable mRipple;
 
-    private View mCircle;
-
-    public QSTileView(Context context) {
-        super(context);
+    public QSTileView(Context context, QSIconView icon) {
+        super(context, icon);
 
         mContext = context;
         final Resources res = context.getResources();
-        mIconSizePx = res.getDimensionPixelSize(R.dimen.qs_tile_icon_size);
         mTileSpacingPx = res.getDimensionPixelSize(R.dimen.qs_tile_spacing);
-        mTilePaddingBelowIconPx =  res.getDimensionPixelSize(R.dimen.qs_tile_padding_below_icon);
-        mDualTileVerticalPaddingPx =
-                res.getDimensionPixelSize(R.dimen.qs_dual_tile_padding_vertical);
-        mTileBackground = newTileBackground();
-        recreateLabel();
         setClipChildren(false);
 
-        mTopBackgroundView = new View(context);
-        mTopBackgroundView.setId(View.generateViewId());
-        addView(mTopBackgroundView);
-
-        mIcon = createIcon();
-        addView(mIcon);
-
-        mCircle = createCircleIcon();
-        addView(mCircle);
-
-        mDivider = new View(mContext);
-        mDivider.setBackgroundColor(context.getColor(R.color.qs_tile_divider));
-        final int dh = res.getDimensionPixelSize(R.dimen.qs_tile_divider_height);
-        mDivider.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, dh));
-        addView(mDivider);
-
         setClickable(true);
         updateTopPadding();
         setId(View.generateViewId());
+        createLabel();
+        setOrientation(VERTICAL);
+        setGravity(Gravity.CENTER);
     }
 
     private void updateTopPadding() {
@@ -106,6 +62,8 @@
         float largeFactor = (MathUtils.constrain(getResources().getConfiguration().fontScale,
                 1.0f, FontSizeUtils.LARGE_TEXT_SCALE) - 1f) / (FontSizeUtils.LARGE_TEXT_SCALE - 1f);
         mTilePaddingTopPx = Math.round((1 - largeFactor) * padding + largeFactor * largePadding);
+        setPadding(mTileSpacingPx, mTilePaddingTopPx + mTileSpacingPx, mTileSpacingPx,
+                mTileSpacingPx);
         requestLayout();
     }
 
@@ -114,260 +72,29 @@
         super.onConfigurationChanged(newConfig);
         updateTopPadding();
         FontSizeUtils.updateFontSize(mLabel, R.dimen.qs_tile_text_size);
-        if (mDualLabel != null) {
-            mDualLabel.setTextSize(TypedValue.COMPLEX_UNIT_PX,
-                    getResources().getDimensionPixelSize(R.dimen.qs_tile_text_size));
-        }
     }
 
-    private void recreateLabel() {
-        CharSequence labelText = null;
-        CharSequence labelDescription = null;
-        if (mLabel != null) {
-            labelText = mLabel.getText();
-            removeView(mLabel);
-            mLabel = null;
-        }
-        if (mDualLabel != null) {
-            labelText = mDualLabel.getText();
-            labelDescription = mLabel != null ? mLabel.getContentDescription() : null;
-            removeView(mDualLabel);
-            mDualLabel = null;
-        }
+    private void createLabel() {
         final Resources res = mContext.getResources();
-        if (mType == QS_TYPE_DUAL) {
-            mDualLabel = new QSDualTileLabel(mContext);
-            mDualLabel.setId(View.generateViewId());
-            mDualLabel.setBackgroundResource(R.drawable.btn_borderless_rect);
-            mDualLabel.setFirstLineCaret(mContext.getDrawable(R.drawable.qs_dual_tile_caret));
-            mDualLabel.setTextColor(mContext.getColor(R.color.qs_tile_text));
-            mDualLabel.setPadding(0, mDualTileVerticalPaddingPx, 0, mDualTileVerticalPaddingPx);
-            mDualLabel.setTypeface(CONDENSED);
-            mDualLabel.setTextSize(TypedValue.COMPLEX_UNIT_PX,
-                    res.getDimensionPixelSize(R.dimen.qs_tile_text_size));
-            mDualLabel.setClickable(true);
-            mDualLabel.setOnClickListener(mClickSecondary);
-            mDualLabel.setFocusable(true);
-            if (labelText != null) {
-                mDualLabel.setText(labelText);
-            }
-            if (labelDescription != null) {
-                mDualLabel.setContentDescription(labelDescription);
-            }
-            addView(mDualLabel);
-            mDualLabel.setAccessibilityTraversalAfter(mTopBackgroundView.getId());
-        } else if (mType == QS_TYPE_NORMAL) {
-            mLabel = new TextView(mContext);
-            mLabel.setTextColor(mContext.getColor(R.color.qs_tile_text));
-            mLabel.setGravity(Gravity.CENTER_HORIZONTAL);
-            mLabel.setMinLines(2);
-            mLabel.setPadding(0, 0, 0, 0);
-            mLabel.setTypeface(CONDENSED);
-            mLabel.setTextSize(TypedValue.COMPLEX_UNIT_PX,
-                    res.getDimensionPixelSize(R.dimen.qs_tile_text_size));
-            mLabel.setClickable(false);
-            if (labelText != null) {
-                mLabel.setText(labelText);
-            }
-            addView(mLabel);
-        }
+        mLabel = new TextView(mContext);
+        mLabel.setTextColor(mContext.getColor(R.color.qs_tile_text));
+        mLabel.setGravity(Gravity.CENTER_HORIZONTAL);
+        mLabel.setMinLines(2);
+        mLabel.setPadding(0, 0, 0, 0);
+        mLabel.setTypeface(CONDENSED);
+        mLabel.setTextSize(TypedValue.COMPLEX_UNIT_PX,
+                res.getDimensionPixelSize(R.dimen.qs_tile_text_size));
+        mLabel.setClickable(false);
+        addView(mLabel);
     }
 
-    public boolean setType(int type) {
-        final boolean changed = mType != type;
-        mType = type;
-        if (changed) {
-            recreateLabel();
-        }
-        if (mTileBackground instanceof RippleDrawable) {
-            setRipple((RippleDrawable) mTileBackground);
-        }
-        if (mType == QS_TYPE_DUAL) {
-            mTopBackgroundView.setOnClickListener(mClickPrimary);
-            setOnClickListener(null);
-            setClickable(false);
-            setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO);
-            mTopBackgroundView.setBackground(mTileBackground);
-        } else {
-            mTopBackgroundView.setOnClickListener(null);
-            mTopBackgroundView.setClickable(false);
-            setOnClickListener(mClickPrimary);
-            setOnLongClickListener(mLongClick);
-            setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_YES);
-            setBackground(mTileBackground);
-        }
-        mTopBackgroundView.setFocusable(mType == QS_TYPE_DUAL);
-        setFocusable(mType != QS_TYPE_DUAL);
-        mDivider.setVisibility(mType == QS_TYPE_DUAL ? VISIBLE : GONE);
-        mCircle.setVisibility(mType == QS_TYPE_QUICK ? VISIBLE : GONE);
-        postInvalidate();
-        return changed;
-    }
-
-    private void setRipple(RippleDrawable tileBackground) {
-        mRipple = tileBackground;
-        if (getWidth() != 0) {
-            updateRippleSize(getWidth(), getHeight());
-        }
-    }
-
-    public void init(OnClickListener clickPrimary, OnClickListener clickSecondary,
-            OnLongClickListener longClick) {
-        mClickPrimary = clickPrimary;
-        mClickSecondary = clickSecondary;
-        mLongClick = longClick;
-    }
-
-    protected View createIcon() {
-        final ImageView icon = new ImageView(mContext);
-        icon.setId(android.R.id.icon);
-        icon.setScaleType(ScaleType.CENTER_INSIDE);
-        return icon;
-    }
-
-    protected View createCircleIcon() {
-        final ImageView icon = new ImageView(mContext);
-        icon.setImageResource(R.drawable.ic_qs_circle);
-        // TODO: Not this.
-        icon.setPadding(20, 20, 20, 20);
-        return icon;
-    }
-
-    protected View createCircle() {
-        final ImageView icon = new ImageView(mContext);
-        icon.setId(android.R.id.icon);
-        icon.setScaleType(ScaleType.CENTER_INSIDE);
-        return icon;
-    }
-
-    private Drawable newTileBackground() {
-        final int[] attrs = new int[] { android.R.attr.selectableItemBackgroundBorderless };
-        final TypedArray ta = mContext.obtainStyledAttributes(attrs);
-        final Drawable d = ta.getDrawable(0);
-        ta.recycle();
-        return d;
-    }
-
-    private View labelView() {
-        return mType == QS_TYPE_DUAL ? mDualLabel : mLabel;
-    }
-
-    @Override
-    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
-        final int w = MeasureSpec.getSize(widthMeasureSpec);
-        final int h = MeasureSpec.getSize(heightMeasureSpec);
-        final int iconSpec = exactly(mIconSizePx);
-        mIcon.measure(MeasureSpec.makeMeasureSpec(w, getIconMeasureMode()), iconSpec);
-        switch (mType) {
-            case QS_TYPE_QUICK:
-                mCircle.measure(
-                        MeasureSpec.makeMeasureSpec(w, MeasureSpec.EXACTLY),
-                        MeasureSpec.makeMeasureSpec(h, MeasureSpec.EXACTLY));
-                break;
-            case QS_TYPE_DUAL:
-                mDivider.measure(widthMeasureSpec, exactly(mDivider.getLayoutParams().height));
-            default:
-                labelView().measure(widthMeasureSpec,
-                        MeasureSpec.makeMeasureSpec(h, MeasureSpec.AT_MOST));
-                break;
-        }
-        int heightSpec = exactly(
-                mIconSizePx + mTilePaddingBelowIconPx + mTilePaddingTopPx);
-        mTopBackgroundView.measure(widthMeasureSpec, heightSpec);
-        setMeasuredDimension(w, h);
-    }
-
-    protected int getIconMeasureMode() {
-        return MeasureSpec.EXACTLY;
-    }
-
-    @Override
-    protected void onLayout(boolean changed, int l, int t, int r, int b) {
-        final int w = getMeasuredWidth();
-        final int h = getMeasuredHeight();
-
-        layout(mTopBackgroundView, 0, mTileSpacingPx);
-
-        int top = 0;
-        top += mTileSpacingPx;
-        top += mTilePaddingTopPx;
-        final int iconLeft = (w - mIcon.getMeasuredWidth()) / 2;
-        if (mType == QS_TYPE_QUICK) {
-            top = (h - mIcon.getMeasuredHeight()) / 2;
-            layout(mCircle, 0, 0);
-        }
-        layout(mIcon, iconLeft, top);
-        if (mRipple != null) {
-            updateRippleSize(w, h);
-
-        }
-        top = mIcon.getBottom();
-        top += mTilePaddingBelowIconPx;
-        if (mType == QS_TYPE_DUAL) {
-            layout(mDivider, 0, top);
-            top = mDivider.getBottom();
-        }
-        if (mType != QS_TYPE_QUICK) {
-            layout(labelView(), 0, top);
-        }
-    }
-
-    private void updateRippleSize(int width, int height) {
-        // center the touch feedback on the center of the icon, and dial it down a bit
-        final int cx = width / 2;
-        final int cy = mType == QS_TYPE_DUAL ? mIcon.getTop() + mIcon.getHeight() / 2 : height / 2;
-        final int rad = (int)(mIcon.getHeight() * 1.25f);
-        mRipple.setHotspotBounds(cx - rad, cy - rad, cx + rad, cy + rad);
+    public void init(OnClickListener clickPrimary, OnLongClickListener longClick) {
+        setOnClickListener(clickPrimary);
+        setOnLongClickListener(longClick);
     }
 
     protected void handleStateChanged(QSTile.State state) {
-        if (mIcon instanceof ImageView) {
-            setIcon((ImageView) mIcon, state);
-        }
-        if (mType == QS_TYPE_DUAL) {
-            mDualLabel.setText(state.label);
-            mDualLabel.setContentDescription(state.dualLabelContentDescription);
-            mTopBackgroundView.setContentDescription(state.contentDescription);
-        } else if (mType == QS_TYPE_NORMAL) {
-            mLabel.setText(state.label);
-            setContentDescription(state.contentDescription);
-        }
-    }
-
-    protected void setIcon(ImageView iv, QSTile.State state) {
-        if (!Objects.equals(state.icon, iv.getTag(R.id.qs_icon_tag))) {
-            Drawable d = state.icon != null ? state.icon.getDrawable(mContext) : null;
-            if (d != null && state.autoMirrorDrawable) {
-                d.setAutoMirrored(true);
-            }
-            iv.setImageDrawable(d);
-            iv.setTag(R.id.qs_icon_tag, state.icon);
-            if (d instanceof Animatable) {
-                Animatable a = (Animatable) d;
-                if (state.icon instanceof AnimationIcon && !iv.isShown()) {
-                    a.stop(); // skip directly to end state
-                }
-            }
-        }
-    }
-
-    /**
-     * Update the accessibility order for this view.
-     *
-     * @param previousView the view which should be before this one
-     * @return the last view in this view which is accessible
-     */
-    public View updateAccessibilityOrder(View previousView) {
-        View firstView;
-        View lastView;
-        if (mType == QS_TYPE_DUAL) {
-            lastView = mDualLabel;
-            firstView = mTopBackgroundView;
-        } else {
-            firstView = this;
-            lastView = this;
-        }
-        firstView.setAccessibilityTraversalAfter(previousView.getId());
-        return lastView;
+        super.handleStateChanged(state);
+        mLabel.setText(state.label);
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java
index 6a053be..fe8ce9b 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java
@@ -19,7 +19,6 @@
 import android.content.Context;
 import android.content.res.ColorStateList;
 import android.util.AttributeSet;
-import android.util.Log;
 import android.view.Gravity;
 import android.view.View;
 import android.widget.ImageView;
@@ -30,10 +29,14 @@
 import java.util.Collection;
 
 /**
- * Version of QSPanel that only shows 4 Quick Tiles in the QS Header.
+ * Version of QSPanel that only shows N Quick Tiles in the QS Header.
  */
 public class QuickQSPanel extends QSPanel {
 
+    private int mMaxTiles;
+    private QSPanel mFullPanel;
+    private View mHeader;
+
     public QuickQSPanel(Context context, AttributeSet attrs) {
         super(context, attrs);
         if (mTileLayout != null) {
@@ -46,6 +49,36 @@
         mQsContainer.addView((View) mTileLayout, 1 /* Between brightness and footer */);
     }
 
+    public void setQSPanelAndHeader(QSPanel fullPanel, View header) {
+        mFullPanel = fullPanel;
+        mHeader = header;
+    }
+
+    @Override
+    protected void handleShowDetail(QSPanel.Record r, boolean show) {
+        if (show) {
+            mHeader.performClick();
+            mFullPanel.showDetail(show, r);
+        } else {
+            // Not sure how we would end up here...
+            super.handleShowDetail(r, show);
+        }
+    }
+
+    @Override
+    protected QSTileBaseView createTileView(QSTile<?> tile) {
+        return new QSTileBaseView(mContext, tile.createTileView(mContext));
+    }
+
+    public void setMaxTiles(int maxTiles) {
+        mMaxTiles = maxTiles;
+    }
+
+    @Override
+    protected void onTileClick(QSTile<?> tile) {
+        tile.secondaryClick();
+    }
+
     @Override
     public void onTuningChanged(String key, String newValue) {
         // No tunings for you.
@@ -59,11 +92,8 @@
     public void setTiles(Collection<QSTile<?>> tiles) {
         ArrayList<QSTile<?>> quickTiles = new ArrayList<>();
         for (QSTile<?> tile : tiles) {
-            if (tile.getTileType() == QSTileView.QS_TYPE_QUICK) {
-                Log.d("QSPanel", "Adding " + tile.getTileSpec());
-                quickTiles.add(tile);
-            }
-            if (quickTiles.size() == 2) {
+            quickTiles.add(tile);
+            if (quickTiles.size() == mMaxTiles) {
                 break;
             }
         }
@@ -74,6 +104,8 @@
 
         public HeaderTileLayout(Context context) {
             super(context);
+            setClipChildren(false);
+            setClipToPadding(false);
             setGravity(Gravity.CENTER_VERTICAL);
             setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
 
@@ -92,17 +124,13 @@
         @Override
         public void addTile(TileRecord tile) {
             tile.tileView.setLayoutParams(generateLayoutParams());
-            // These shouldn't be normal tiles, but they will be for now so that the circles don't
-            // show up.
-            tile.tileView.setType(QSTileView.QS_TYPE_NORMAL);
             addView(tile.tileView, getChildCount() - 1 /* Leave icon at end */);
         }
 
         private LayoutParams generateLayoutParams() {
             int size =
                     mContext.getResources().getDimensionPixelSize(R.dimen.qs_quick_tile_size);
-            LayoutParams lp = new LayoutParams(0, size);
-            lp.weight = 1;
+            LayoutParams lp = new LayoutParams(size, size);
             return lp;
         }
 
@@ -112,11 +140,6 @@
         }
 
         @Override
-        public void setTileVisibility(TileRecord tile, int visibility) {
-            tile.tileView.setVisibility(visibility);
-        }
-
-        @Override
         public int getOffsetTop(TileRecord tile) {
             return 0;
         }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/SignalTileView.java b/packages/SystemUI/src/com/android/systemui/qs/SignalTileView.java
index a2e1296..b2bfa06 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/SignalTileView.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/SignalTileView.java
@@ -27,7 +27,7 @@
 import com.android.systemui.qs.QSTile.SignalState;
 
 /** View that represents a custom quick settings tile for displaying signal info (wifi/cell). **/
-public final class SignalTileView extends QSTileView {
+public final class SignalTileView extends QSIconView {
     private static final long DEFAULT_DURATION = new ValueAnimator().getDuration();
     private static final long SHORT_DURATION = DEFAULT_DURATION / 3;
 
@@ -106,8 +106,7 @@
     }
 
     @Override
-    protected void handleStateChanged(QSTile.State state) {
-        super.handleStateChanged(state);
+    public void setIcon(QSTile.State state) {
         final SignalState s = (SignalState) state;
         setIcon(mSignal, s);
         if (s.overlayIconId > 0) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java b/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java
index 1336eec..ff11177 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java
@@ -18,14 +18,9 @@
 
     private static final String TAG = "TileLayout";
 
-    private int mDualTileUnderlap;
     protected int mColumns;
     private int mCellWidth;
     private int mCellHeight;
-    private int mLargeCellWidth;
-    private int mLargeCellHeight;
-
-    protected boolean mAllowDual = true;
 
     protected final ArrayList<TileRecord> mRecords = new ArrayList<>();
 
@@ -59,21 +54,11 @@
         super.removeAllViews();
     }
 
-    @Override
-    public void setTileVisibility(TileRecord tile, int visibility) {
-        tile.tileView.setVisibility(visibility);
-    }
-
     public void updateResources() {
         final Resources res = mContext.getResources();
         final int columns = Math.max(1, res.getInteger(R.integer.quick_settings_num_columns));
         mCellHeight = getCellHeight();
         mCellWidth = (int) (mCellHeight * TILE_ASPECT);
-        mLargeCellHeight = mAllowDual ? res.getDimensionPixelSize(R.dimen.qs_dual_tile_height)
-                : mCellHeight;
-        mLargeCellWidth = mAllowDual ? (int) (mLargeCellHeight * TILE_ASPECT) : mCellWidth;
-        mDualTileUnderlap = mAllowDual
-                ? res.getDimensionPixelSize(R.dimen.qs_dual_tile_padding_vertical) : 0;
         if (mColumns != columns) {
             mColumns = columns;
             postInvalidate();
@@ -90,16 +75,13 @@
         int r = -1;
         int c = -1;
         int rows = 0;
-        boolean rowIsDual = false;
         for (TileRecord record : mRecords) {
             if (record.tileView.getVisibility() == GONE) continue;
             // wrap to next column if we've reached the max # of columns
             // also don't allow dual + single tiles on the same row
-            if (r == -1 || c == (mColumns - 1)
-                    || rowIsDual != (mAllowDual && record.tile.supportsDualTargets())) {
+            if (r == -1 || c == (mColumns - 1)) {
                 r++;
                 c = 0;
-                rowIsDual = mAllowDual && record.tile.supportsDualTargets();
             } else {
                 c++;
             }
@@ -110,13 +92,9 @@
 
         View previousView = this;
         for (TileRecord record : mRecords) {
-            if (record.tileView.setType(mAllowDual ? record.tile.getTileType()
-                    : QSTileView.QS_TYPE_NORMAL)) {
-                record.tileView.handleStateChanged(record.tile.getState());
-            }
             if (record.tileView.getVisibility() == GONE) continue;
-            final int cw = record.row == 0 ? mLargeCellWidth : mCellWidth;
-            final int ch = record.row == 0 ? mLargeCellHeight : mCellHeight;
+            final int cw = mCellWidth;
+            final int ch = mCellHeight;
             record.tileView.measure(exactly(cw), exactly(ch));
             previousView = record.tileView.updateAccessibilityOrder(previousView);
         }
@@ -135,7 +113,7 @@
         for (TileRecord record : mRecords) {
             if (record.tileView.getVisibility() == GONE) continue;
             final int cols = getColumnCount(record.row);
-            final int cw = record.row == 0 ? mLargeCellWidth : mCellWidth;
+            final int cw = mCellWidth;
             final int extra = (w - cw * cols) / (cols + 1);
             int left = record.col * cw + (record.col + 1) * extra;
             final int top = getRowTop(record.row);
@@ -153,7 +131,7 @@
 
     private int getRowTop(int row) {
         if (row <= 0) return 0;
-        return mLargeCellHeight - mDualTileUnderlap + (row - 1) * mCellHeight;
+        return row * mCellHeight;
     }
 
     private int getColumnCount(int row) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/UsageTracker.java b/packages/SystemUI/src/com/android/systemui/qs/UsageTracker.java
deleted file mode 100644
index e64f6a0..0000000
--- a/packages/SystemUI/src/com/android/systemui/qs/UsageTracker.java
+++ /dev/null
@@ -1,99 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.qs;
-
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.DialogInterface;
-import android.content.Intent;
-import android.content.IntentFilter;
-
-import com.android.systemui.Prefs;
-import com.android.systemui.R;
-import com.android.systemui.statusbar.phone.SystemUIDialog;
-import com.android.systemui.statusbar.policy.Listenable;
-
-public class UsageTracker implements Listenable {
-    private static final long MILLIS_PER_DAY = 1000 * 60 * 60 * 24;
-
-    private final Context mContext;
-    private final long mTimeToShowTile;
-    @Prefs.Key private final String mPrefKey;
-    private final String mResetAction;
-
-    private boolean mRegistered;
-
-    public UsageTracker(Context context, @Prefs.Key String prefKey, Class<?> tile,
-            int timeoutResource) {
-        mContext = context;
-        mPrefKey = prefKey;
-        mTimeToShowTile = MILLIS_PER_DAY * mContext.getResources().getInteger(timeoutResource);
-        mResetAction = "com.android.systemui.qs." + tile.getSimpleName() + ".usage_reset";
-    }
-
-    @Override
-    public void setListening(boolean listen) {
-        if (listen && !mRegistered) {
-             mContext.registerReceiver(mReceiver, new IntentFilter(mResetAction));
-             mRegistered = true;
-        } else if (!listen && mRegistered) {
-            mContext.unregisterReceiver(mReceiver);
-            mRegistered = false;
-        }
-    }
-
-    public boolean isRecentlyUsed() {
-        long lastUsed = Prefs.getLong(mContext, mPrefKey, 0L /* defaultValue */);
-        return (System.currentTimeMillis() - lastUsed) < mTimeToShowTile;
-    }
-
-    public void trackUsage() {
-        Prefs.putLong(mContext, mPrefKey, System.currentTimeMillis());
-    }
-
-    public void reset() {
-        Prefs.remove(mContext, mPrefKey);
-    }
-
-    public void showResetConfirmation(String title, final Runnable onConfirmed) {
-        final SystemUIDialog d = new SystemUIDialog(mContext);
-        d.setTitle(title);
-        d.setMessage(mContext.getString(R.string.quick_settings_reset_confirmation_message));
-        d.setNegativeButton(android.R.string.cancel, null);
-        d.setPositiveButton(R.string.quick_settings_reset_confirmation_button,
-                new DialogInterface.OnClickListener() {
-            @Override
-            public void onClick(DialogInterface dialog, int which) {
-                reset();
-                if (onConfirmed != null) {
-                    onConfirmed.run();
-                }
-            }
-        });
-        d.setCanceledOnTouchOutside(true);
-        d.show();
-    }
-
-    private BroadcastReceiver mReceiver = new BroadcastReceiver() {
-        @Override
-        public void onReceive(Context context, Intent intent) {
-            if (mResetAction.equals(intent.getAction())) {
-                reset();
-            }
-        }
-    };
-}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/BlankCustomTile.java b/packages/SystemUI/src/com/android/systemui/qs/customize/BlankCustomTile.java
index a4ff685..95ff611 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/customize/BlankCustomTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/BlankCustomTile.java
@@ -72,12 +72,10 @@
         try {
             PackageManager pm = mContext.getPackageManager();
             ServiceInfo info = pm.getServiceInfo(mComponent, 0);
-            state.visible = true;
             state.icon = new DrawableIcon(info.loadIcon(pm));
             state.label = info.loadLabel(pm).toString();
             state.contentDescription = state.label;
         } catch (Exception e) {
-            state.visible = false;
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/CustomQSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/customize/CustomQSPanel.java
index 422ae4d..8dda9ba 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/customize/CustomQSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/CustomQSPanel.java
@@ -176,13 +176,16 @@
             if (mTiles.get(i).startsWith(CustomTile.PREFIX)) {
                 mCurrentTiles.add(BlankCustomTile.create(mHost, mTiles.get(i)));
             } else {
-                mCurrentTiles.add(mHost.createTile(mTiles.get(i)));
+                QSTile<?> tile = mHost.createTile(mTiles.get(i));
+                if (tile != null) {
+                    mCurrentTiles.add(tile);
+                }
             }
             mCurrentTiles.get(mCurrentTiles.size() - 1).setTileSpec(mTiles.get(i));
         }
         super.setTiles(mCurrentTiles);
     }
-    
+
     public void addTile(String spec) {
         mTiles.add(spec);
         setTilesInternal();
diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/NonPagedTileLayout.java b/packages/SystemUI/src/com/android/systemui/qs/customize/NonPagedTileLayout.java
index d0d5b54..3acbed8 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/customize/NonPagedTileLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/NonPagedTileLayout.java
@@ -25,14 +25,12 @@
 import android.view.View;
 import android.view.View.OnTouchListener;
 import android.widget.LinearLayout;
-
 import com.android.systemui.R;
 import com.android.systemui.qs.PagedTileLayout;
 import com.android.systemui.qs.PagedTileLayout.TilePage;
 import com.android.systemui.qs.QSPanel.QSTileLayout;
 import com.android.systemui.qs.QSPanel.TileRecord;
 import com.android.systemui.qs.QSTile;
-import com.android.systemui.qs.QSTileView;
 import com.android.systemui.qs.QuickTileLayout;
 
 import java.util.ArrayList;
@@ -75,13 +73,12 @@
     public void addTile(TileRecord record) {
         mTiles.add(record);
         distributeTiles();
-        if (record.tile.getTileType() == QSTileView.QS_TYPE_QUICK
-                || record.tileView.getTag() == record.tile) {
+        if (record.tileView.getTag() == record.tile) {
             return;
         }
         record.tileView.setTag(record.tile);
         record.tileView.setVisibility(View.VISIBLE);
-        record.tileView.init(null, null, null);
+        record.tileView.init(null, null);
         record.tileView.setOnTouchListener(this);
         if (mCurrentClip != null && mCurrentClip.getItemAt(0)
                 .getText().toString().equals(record.tile.getTileSpec())) {
@@ -107,10 +104,6 @@
         final int NT = mTiles.size();
         for (int i = 0; i < NT; i++) {
             TileRecord tile = mTiles.get(i);
-            if (tile.tile.getTileType() == QSTileView.QS_TYPE_QUICK) {
-                // Ignore quick tiles for now.
-                continue;
-            }
             mPages.get(index).addTile(tile);
             // Keep everything in one layout for now.
             if (false && mPages.get(index).isFull()) {
@@ -125,12 +118,6 @@
     }
 
     @Override
-    public void setTileVisibility(TileRecord tile, int visibility) {
-        // All tiles visible here, so that they can be re-arranged.
-        tile.tileView.setVisibility(View.VISIBLE);
-    }
-
-    @Override
     public int getOffsetTop(TileRecord tile) {
         // No touch feedback, so this isn't required.
         return 0;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java b/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java
index 144b202..6706c7a 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java
@@ -88,6 +88,9 @@
                 }
                 Log.d(TAG, "Trying " + spec);
                 final QSTile<?> tile = host.createTile(spec);
+                if (tile == null) {
+                    continue;
+                }
                 // Bad, bad, very bad.
                 tile.setListening(true);
                 tile.clearState();
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/AirplaneModeTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/AirplaneModeTile.java
index 49f8d1c..fc802dd 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/AirplaneModeTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/AirplaneModeTile.java
@@ -73,7 +73,6 @@
         final int value = arg instanceof Integer ? (Integer)arg : mSetting.getValue();
         final boolean airplaneMode = value != 0;
         state.value = airplaneMode;
-        state.visible = true;
         state.label = mContext.getString(R.string.airplane_mode);
         if (airplaneMode) {
             state.icon = mEnable;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/BatteryTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/BatteryTile.java
index 8f9655d..84eac65 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/BatteryTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/BatteryTile.java
@@ -73,7 +73,6 @@
         int level = (arg != null) ? (Integer) arg : mLevel;
         String percentage = NumberFormat.getPercentInstance().format((double) level / 100.0);
 
-        state.visible = true;
         state.icon = new Icon() {
             @Override
             public Drawable getDrawable(Context context) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java
index 7f07ddc..cfc09a0 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java
@@ -24,18 +24,15 @@
 import android.text.TextUtils;
 import android.view.View;
 import android.view.ViewGroup;
-
 import com.android.internal.logging.MetricsLogger;
 import com.android.settingslib.bluetooth.CachedBluetoothDevice;
 import com.android.systemui.R;
 import com.android.systemui.qs.QSDetailItems;
 import com.android.systemui.qs.QSDetailItems.Item;
 import com.android.systemui.qs.QSTile;
-import com.android.systemui.qs.QSTileView;
 import com.android.systemui.statusbar.policy.BluetoothController;
 
 import java.util.Collection;
-import java.util.Set;
 
 /** Quick settings tile: Bluetooth **/
 public class BluetoothTile extends QSTile<QSTile.BooleanState>  {
@@ -44,21 +41,13 @@
     private final BluetoothController mController;
     private final BluetoothDetailAdapter mDetailAdapter;
 
-    private final boolean mAlwaysDetail;
-
-    public BluetoothTile(Host host, boolean alwaysDetail) {
+    public BluetoothTile(Host host) {
         super(host);
-        mAlwaysDetail = alwaysDetail;
         mController = host.getBluetoothController();
         mDetailAdapter = new BluetoothDetailAdapter();
     }
 
     @Override
-    public int getTileType() {
-        return QSTileView.QS_TYPE_DUAL;
-    }
-
-    @Override
     public DetailAdapter getDetailAdapter() {
         return mDetailAdapter;
     }
@@ -78,18 +67,15 @@
     }
 
     @Override
-    protected void handleClick() {
-        if (mAlwaysDetail) {
-            handleSecondaryClick();
-            return;
-        }
+    protected void handleSecondaryClick() {
+        // Secondary clicks are header clicks, just toggle.
         final boolean isEnabled = (Boolean)mState.value;
         MetricsLogger.action(mContext, getMetricsCategory(), !isEnabled);
         mController.setBluetoothEnabled(!isEnabled);
     }
 
     @Override
-    protected void handleSecondaryClick() {
+    protected void handleClick() {
         if (!mState.value) {
             mState.value = true;
             mController.setBluetoothEnabled(true);
@@ -99,11 +85,9 @@
 
     @Override
     protected void handleUpdateState(BooleanState state, Object arg) {
-        final boolean supported = mController.isBluetoothSupported();
         final boolean enabled = mController.isBluetoothEnabled();
         final boolean connected = mController.isBluetoothConnected();
         final boolean connecting = mController.isBluetoothConnecting();
-        state.visible = supported;
         state.value = enabled;
         state.autoMirrorDrawable = false;
         if (enabled) {
@@ -155,6 +139,10 @@
         }
     }
 
+    public static boolean isSupported(Host host) {
+        return host.getBluetoothController().isBluetoothSupported();
+    }
+
     private final BluetoothController.Callback mCallback = new BluetoothController.Callback() {
         @Override
         public void onBluetoothStateChange(boolean enabled) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java
index 48b74a4..a8e139c 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java
@@ -23,12 +23,10 @@
 import android.view.View;
 import android.view.View.OnAttachStateChangeListener;
 import android.view.ViewGroup;
-
 import com.android.internal.logging.MetricsLogger;
 import com.android.systemui.R;
 import com.android.systemui.qs.QSDetailItems;
 import com.android.systemui.qs.QSDetailItems.Item;
-import com.android.systemui.qs.QSPanel;
 import com.android.systemui.qs.QSTile;
 import com.android.systemui.statusbar.policy.CastController;
 import com.android.systemui.statusbar.policy.CastController.CastDevice;
@@ -87,14 +85,23 @@
 
     @Override
     protected void handleClick() {
+        if (mKeyguard.isSecure() && !mKeyguard.canSkipBouncer()) {
+            mHost.startRunnableDismissingKeyguard(new Runnable() {
+                @Override
+                public void run() {
+                    MetricsLogger.action(mContext, getMetricsCategory());
+                    showDetail(true);
+                    mHost.openPanels();
+                }
+            });
+            return;
+        }
         MetricsLogger.action(mContext, getMetricsCategory());
         showDetail(true);
     }
 
     @Override
     protected void handleUpdateState(BooleanState state, Object arg) {
-        state.visible = !mKeyguard.isSecure() || !mKeyguard.isShowing()
-                || mKeyguard.canSkipBouncer() || QSPanel.isTheNewQS(mContext);
         state.label = mContext.getString(R.string.quick_settings_cast_title);
         state.value = false;
         state.autoMirrorDrawable = false;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java
index a0bbbe3..5fb76c8 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java
@@ -26,8 +26,8 @@
 import com.android.internal.logging.MetricsLogger;
 import com.android.settingslib.net.MobileDataController;
 import com.android.systemui.R;
+import com.android.systemui.qs.QSIconView;
 import com.android.systemui.qs.QSTile;
-import com.android.systemui.qs.QSTileBaseView;
 import com.android.systemui.qs.SignalTileView;
 import com.android.systemui.statusbar.policy.NetworkController;
 import com.android.systemui.statusbar.policy.NetworkController.IconState;
@@ -71,7 +71,7 @@
     }
 
     @Override
-    public QSTileBaseView createTileView(Context context) {
+    public QSIconView createTileView(Context context) {
         return new SignalTileView(context);
     }
 
@@ -87,8 +87,6 @@
 
     @Override
     protected void handleUpdateState(SignalState state, Object arg) {
-        state.visible = mController.hasMobileDataFeature();
-        if (!state.visible) return;
         CallbackInfo cb = (CallbackInfo) arg;
         if (cb == null) {
             cb = mSignalCallback.mInfo;
@@ -138,6 +136,10 @@
         return string;
     }
 
+    public static boolean isSupported(Host host) {
+        return host.getNetworkController().hasMobileDataFeature();
+    }
+
     private static final class CallbackInfo {
         boolean enabled;
         boolean wifiEnabled;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/ColorInversionTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/ColorInversionTile.java
index 2f9a496..f73ee35 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/ColorInversionTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/ColorInversionTile.java
@@ -17,14 +17,10 @@
 package com.android.systemui.qs.tiles;
 
 import android.provider.Settings.Secure;
-
 import com.android.internal.logging.MetricsLogger;
-import com.android.systemui.Prefs;
 import com.android.systemui.R;
-import com.android.systemui.qs.QSPanel;
 import com.android.systemui.qs.QSTile;
 import com.android.systemui.qs.SecureSetting;
-import com.android.systemui.qs.UsageTracker;
 
 /** Quick settings tile: Invert colors **/
 public class ColorInversionTile extends QSTile<QSTile.BooleanState> {
@@ -34,7 +30,6 @@
     private final AnimationIcon mDisable
             = new AnimationIcon(R.drawable.ic_invert_colors_disable_animation);
     private final SecureSetting mSetting;
-    private final UsageTracker mUsageTracker;
 
     private boolean mListening;
 
@@ -45,28 +40,14 @@
                 Secure.ACCESSIBILITY_DISPLAY_INVERSION_ENABLED) {
             @Override
             protected void handleValueChanged(int value, boolean observedChange) {
-                if (value != 0 || observedChange) {
-                    mUsageTracker.trackUsage();
-                }
-                if (mListening) {
-                    handleRefreshState(value);
-                }
+                handleRefreshState(value);
             }
         };
-        mUsageTracker = new UsageTracker(host.getContext(),
-                Prefs.Key.COLOR_INVERSION_TILE_LAST_USED, ColorInversionTile.class,
-                R.integer.days_to_show_color_inversion_tile);
-        if (mSetting.getValue() != 0 && !mUsageTracker.isRecentlyUsed()) {
-            mUsageTracker.trackUsage();
-        }
-        mUsageTracker.setListening(true);
-        mSetting.setListening(true);
     }
 
     @Override
     protected void handleDestroy() {
         super.handleDestroy();
-        mUsageTracker.setListening(false);
         mSetting.setListening(false);
     }
 
@@ -77,7 +58,7 @@
 
     @Override
     public void setListening(boolean listening) {
-        mListening = listening;
+        mSetting.setListening(listening);
     }
 
     @Override
@@ -95,23 +76,9 @@
     }
 
     @Override
-    protected void handleLongClick() {
-        if (mState.value) return;  // don't allow usage reset if inversion is active
-        final String title = mContext.getString(R.string.quick_settings_reset_confirmation_title,
-                mState.label);
-        mUsageTracker.showResetConfirmation(title, new Runnable() {
-            @Override
-            public void run() {
-                refreshState();
-            }
-        });
-    }
-
-    @Override
     protected void handleUpdateState(BooleanState state, Object arg) {
         final int value = arg instanceof Integer ? (Integer) arg : mSetting.getValue();
         final boolean enabled = value != 0;
-        state.visible = enabled || mUsageTracker.isRecentlyUsed() || QSPanel.isTheNewQS(mContext);
         state.value = enabled;
         state.label = mContext.getString(R.string.quick_settings_inversion_label);
         state.icon = enabled ? mEnable : mDisable;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/CustomTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/CustomTile.java
index 04006eb..bb74f34 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/CustomTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/CustomTile.java
@@ -104,9 +104,15 @@
                         mServiceConnection, Service.BIND_AUTO_CREATE,
                         new UserHandle(ActivityManager.getCurrentUser()));
                 mBound = true;
+            } else {
+                if (mService != null) {
+                    mService.onStartListening();
+                } else {
+                    Log.d(TAG, "Can't start service listening");
+                }
             }
         } else {
-            if (mService!= null) {
+            if (mService != null) {
                 mService.onStopListening();
             }
             if (mIsTokenGranted && !mIsShowingDialog) {
@@ -168,7 +174,6 @@
 
     @Override
     protected void handleUpdateState(State state, Object arg) {
-        state.visible = true;
         Drawable drawable = mTile.getIcon().loadDrawable(mContext);
         drawable.setTint(mContext.getColor(android.R.color.white));
         state.icon = new DrawableIcon(drawable);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java
index 781ab1c..d96f735 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java
@@ -29,7 +29,6 @@
 import android.view.View.OnAttachStateChangeListener;
 import android.view.ViewGroup;
 import android.widget.Toast;
-
 import com.android.internal.logging.MetricsLogger;
 import com.android.systemui.Prefs;
 import com.android.systemui.R;
@@ -126,7 +125,6 @@
         final boolean newValue = zen != Global.ZEN_MODE_OFF;
         final boolean valueChanged = state.value != newValue;
         state.value = newValue;
-        state.visible = isVisible(mContext);
         switch (zen) {
             case Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS:
                 state.icon = ResourceIcon.get(R.drawable.ic_qs_dnd_on);
@@ -215,6 +213,10 @@
         }
     };
 
+    public static boolean isSupported(Host host) {
+        return isVisible(host.getContext());
+    }
+
     private final class DndDetailAdapter implements DetailAdapter, OnAttachStateChangeListener {
 
         @Override
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java
index 21cbef2..12c1298 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java
@@ -17,7 +17,6 @@
 package com.android.systemui.qs.tiles;
 
 import android.app.ActivityManager;
-
 import com.android.internal.logging.MetricsLogger;
 import com.android.systemui.R;
 import com.android.systemui.qs.QSTile;
@@ -71,7 +70,8 @@
 
     @Override
     protected void handleUpdateState(BooleanState state, Object arg) {
-        state.visible = mFlashlightController.isAvailable();
+        // TODO: Flashlight available handling...
+//        state.visible = mFlashlightController.isAvailable();
         state.label = mHost.getContext().getString(R.string.quick_settings_flashlight_label);
         if (arg instanceof UserBoolean) {
             boolean value = ((UserBoolean) arg).value;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java
index 79084ae..250d567 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java
@@ -16,16 +16,9 @@
 
 package com.android.systemui.qs.tiles;
 
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-
 import com.android.internal.logging.MetricsLogger;
-import com.android.systemui.Prefs;
 import com.android.systemui.R;
-import com.android.systemui.qs.QSPanel;
 import com.android.systemui.qs.QSTile;
-import com.android.systemui.qs.UsageTracker;
 import com.android.systemui.statusbar.policy.HotspotController;
 
 /** Quick settings tile: Hotspot **/
@@ -36,19 +29,15 @@
             new AnimationIcon(R.drawable.ic_hotspot_disable_animation);
     private final HotspotController mController;
     private final Callback mCallback = new Callback();
-    private final UsageTracker mUsageTracker;
 
     public HotspotTile(Host host) {
         super(host);
         mController = host.getHotspotController();
-        mUsageTracker = newUsageTracker(host.getContext());
-        mUsageTracker.setListening(true);
     }
 
     @Override
     protected void handleDestroy() {
         super.handleDestroy();
-        mUsageTracker.setListening(false);
     }
 
     @Override
@@ -75,22 +64,7 @@
     }
 
     @Override
-    protected void handleLongClick() {
-        if (mState.value) return;  // don't allow usage reset if hotspot is active
-        final String title = mContext.getString(R.string.quick_settings_reset_confirmation_title,
-                mState.label);
-        mUsageTracker.showResetConfirmation(title, new Runnable() {
-            @Override
-            public void run() {
-                refreshState();
-            }
-        });
-    }
-
-    @Override
     protected void handleUpdateState(BooleanState state, Object arg) {
-        state.visible = (mController.isHotspotSupported() && mUsageTracker.isRecentlyUsed())
-                || QSPanel.isTheNewQS(mContext);
         state.label = mContext.getString(R.string.quick_settings_hotspot_label);
 
         if (arg instanceof Boolean) {
@@ -98,7 +72,7 @@
         } else {
             state.value = mController.isHotspotEnabled();
         }
-        state.icon = state.visible && state.value ? mEnable : mDisable;
+        state.icon = state.value ? mEnable : mDisable;
     }
 
     @Override
@@ -115,31 +89,10 @@
         }
     }
 
-    private static UsageTracker newUsageTracker(Context context) {
-        return new UsageTracker(context, Prefs.Key.HOTSPOT_TILE_LAST_USED, HotspotTile.class,
-                R.integer.days_to_show_hotspot_tile);
-    }
-
     private final class Callback implements HotspotController.Callback {
         @Override
         public void onHotspotChanged(boolean enabled) {
             refreshState(enabled);
         }
     };
-
-    /**
-     * This will catch broadcasts for changes in hotspot state so we can show
-     * the hotspot tile for a number of days after use.
-     */
-    public static class APChangedReceiver extends BroadcastReceiver {
-        private UsageTracker mUsageTracker;
-
-        @Override
-        public void onReceive(Context context, Intent intent) {
-            if (mUsageTracker == null) {
-                mUsageTracker = newUsageTracker(context);
-            }
-            mUsageTracker.trackUsage();
-        }
-    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/IntentTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/IntentTile.java
index c7f2284..0883445 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/IntentTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/IntentTile.java
@@ -124,7 +124,6 @@
         }
         // Save the last one in case we need it later.
         mLastIntent = intent;
-        state.visible = intent.getBooleanExtra("visible", true);
         state.contentDescription = intent.getStringExtra("contentDescription");
         state.label = intent.getStringExtra("label");
         state.icon = null;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/LocationTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/LocationTile.java
index 0e2672c..08540f6 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/LocationTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/LocationTile.java
@@ -18,7 +18,6 @@
 
 import com.android.internal.logging.MetricsLogger;
 import com.android.systemui.R;
-import com.android.systemui.qs.QSPanel;
 import com.android.systemui.qs.QSTile;
 import com.android.systemui.statusbar.policy.KeyguardMonitor;
 import com.android.systemui.statusbar.policy.LocationController;
@@ -60,6 +59,20 @@
 
     @Override
     protected void handleClick() {
+        if (mKeyguard.isSecure() && mKeyguard.isShowing()) {
+            mHost.startRunnableDismissingKeyguard(new Runnable() {
+                @Override
+                public void run() {
+                    final boolean wasEnabled = (Boolean) mState.value;
+                    mHost.openPanels();
+                    MetricsLogger.action(mContext, getMetricsCategory(), !wasEnabled);
+                    mController.setLocationEnabled(!wasEnabled);
+                    mEnable.setAllowAnimation(true);
+                    mDisable.setAllowAnimation(true);
+                }
+            });
+            return;
+        }
         final boolean wasEnabled = (Boolean) mState.value;
         MetricsLogger.action(mContext, getMetricsCategory(), !wasEnabled);
         mController.setLocationEnabled(!wasEnabled);
@@ -74,7 +87,6 @@
         // Work around for bug 15916487: don't show location tile on top of lock screen. After the
         // bug is fixed, this should be reverted to only hiding it on secure lock screens:
         // state.visible = !(mKeyguard.isSecure() && mKeyguard.isShowing());
-        state.visible = !mKeyguard.isShowing() || QSPanel.isTheNewQS(mContext);
         state.value = locationEnabled;
         if (locationEnabled) {
             state.icon = mEnable;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/QAirplaneTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/QAirplaneTile.java
deleted file mode 100644
index b77191e..0000000
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/QAirplaneTile.java
+++ /dev/null
@@ -1,40 +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.
- */
-
-package com.android.systemui.qs.tiles;
-
-import android.content.Context;
-import com.android.systemui.QSQuickTileView;
-import com.android.systemui.qs.QSTileBaseView;
-import com.android.systemui.qs.QSTileView;
-
-/** Quick settings tile: Airplane mode **/
-public class QAirplaneTile extends AirplaneModeTile {
-
-    public QAirplaneTile(Host host) {
-        super(host);
-    }
-
-    @Override
-    public QSTileBaseView createTileView(Context context) {
-        return new QSQuickTileView(context);
-    }
-
-    @Override
-    public int getTileType() {
-        return QSTileView.QS_TYPE_QUICK;
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/QBluetoothTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/QBluetoothTile.java
deleted file mode 100644
index 4fe7e45..0000000
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/QBluetoothTile.java
+++ /dev/null
@@ -1,40 +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.
- */
-
-package com.android.systemui.qs.tiles;
-
-import android.content.Context;
-import com.android.systemui.QSQuickTileView;
-import com.android.systemui.qs.QSTileBaseView;
-import com.android.systemui.qs.QSTileView;
-
-/** Quick settings tile: Bluetooth **/
-public class QBluetoothTile extends BluetoothTile  {
-
-    public QBluetoothTile(Host host) {
-        super(host, false);
-    }
-
-    @Override
-    public QSTileBaseView createTileView(Context context) {
-        return new QSQuickTileView(context);
-    }
-
-    @Override
-    public int getTileType() {
-        return QSTileView.QS_TYPE_QUICK;
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/QFlashlightTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/QFlashlightTile.java
deleted file mode 100644
index e115755e..0000000
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/QFlashlightTile.java
+++ /dev/null
@@ -1,40 +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.
- */
-
-package com.android.systemui.qs.tiles;
-
-import android.content.Context;
-import com.android.systemui.QSQuickTileView;
-import com.android.systemui.qs.QSTileBaseView;
-import com.android.systemui.qs.QSTileView;
-
-/** Quick settings tile: Flashlight **/
-public class QFlashlightTile extends FlashlightTile {
-
-    public QFlashlightTile(Host host) {
-        super(host);
-    }
-
-    @Override
-    public QSTileBaseView createTileView(Context context) {
-        return new QSQuickTileView(context);
-    }
-
-    @Override
-    public int getTileType() {
-        return QSTileView.QS_TYPE_QUICK;
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/QLockTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/QLockTile.java
deleted file mode 100644
index 8b3013a..0000000
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/QLockTile.java
+++ /dev/null
@@ -1,89 +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.
- */
-package com.android.systemui.qs.tiles;
-
-import android.content.Context;
-import com.android.internal.logging.MetricsLogger;
-import com.android.systemui.QSQuickTileView;
-import com.android.systemui.R;
-import com.android.systemui.qs.QSTile;
-import com.android.systemui.qs.QSTileBaseView;
-import com.android.systemui.qs.QSTileView;
-import com.android.systemui.statusbar.policy.KeyguardMonitor;
-
-public class QLockTile extends QSTile<QSTile.State> implements KeyguardMonitor.Callback {
-
-    private final KeyguardMonitor mKeyguard;
-
-    public QLockTile(Host host) {
-        super(host);
-        mKeyguard = host.getKeyguardMonitor();
-    }
-
-    @Override
-    public QSTileBaseView createTileView(Context context) {
-        return new QSQuickTileView(context);
-    }
-
-    @Override
-    public int getTileType() {
-        return QSTileView.QS_TYPE_QUICK;
-    }
-
-    @Override
-    protected State newTileState() {
-        return new State();
-    }
-
-    @Override
-    public void setListening(boolean listening) {
-        if (listening) {
-            mKeyguard.addCallback(this);
-        } else {
-            mKeyguard.removeCallback(this);
-        }
-    }
-
-    @Override
-    public int getMetricsCategory() {
-        return MetricsLogger.QS_LOCK_TILE;
-    }
-
-    @Override
-    public void onKeyguardChanged() {
-        refreshState();
-    }
-
-    @Override
-    protected void handleClick() {
-        if (mKeyguard.isShowing()) {
-            mKeyguard.unlock();
-        } else {
-            mKeyguard.lock();
-        }
-    }
-
-    @Override
-    protected void handleUpdateState(State state, Object arg) {
-        // TOD: Content description.
-        state.visible = true;
-        if (mKeyguard.isShowing()) {
-            state.icon = ResourceIcon.get(R.drawable.ic_qs_lock);
-        } else {
-            state.icon = ResourceIcon.get(R.drawable.ic_qs_lock_open);
-        }
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/QRotationLockTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/QRotationLockTile.java
deleted file mode 100644
index 5f5cab5..0000000
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/QRotationLockTile.java
+++ /dev/null
@@ -1,40 +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.
- */
-
-package com.android.systemui.qs.tiles;
-
-import android.content.Context;
-import com.android.systemui.QSQuickTileView;
-import com.android.systemui.qs.QSTileBaseView;
-import com.android.systemui.qs.QSTileView;
-
-/** Quick settings tile: Rotation **/
-public class QRotationLockTile extends RotationLockTile {
-
-    public QRotationLockTile(Host host) {
-        super(host);
-    }
-
-    @Override
-    public QSTileBaseView createTileView(Context context) {
-        return new QSQuickTileView(context);
-    }
-
-    @Override
-    public int getTileType() {
-        return QSTileView.QS_TYPE_QUICK;
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/QWifiTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/QWifiTile.java
deleted file mode 100644
index f0fe87d..0000000
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/QWifiTile.java
+++ /dev/null
@@ -1,60 +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.
- */
-
-package com.android.systemui.qs.tiles;
-
-import android.content.Context;
-import com.android.systemui.QSQuickTileView;
-import com.android.systemui.qs.QSTileBaseView;
-import com.android.systemui.qs.QSTileView;
-import com.android.systemui.statusbar.policy.WifiIcons;
-
-/** Quick settings tile: Wifi **/
-public class QWifiTile extends WifiTile {
-
-    public QWifiTile(Host host) {
-        super(host, false);
-    }
-
-    @Override
-    public QSTileBaseView createTileView(Context context) {
-        return new QSQuickTileView(context);
-    }
-
-    @Override
-    public int getTileType() {
-        return QSTileView.QS_TYPE_QUICK;
-    }
-
-    @Override
-    protected void handleUpdateState(SignalState state, Object arg) {
-        super.handleUpdateState(state, arg);
-
-        CallbackInfo cb = (CallbackInfo) arg;
-        if (cb == null) {
-            cb = mSignalCallback.mInfo;
-        }
-        boolean wifiConnected = cb.enabled && (cb.wifiSignalIconId > 0) && (cb.enabledDesc != null);
-
-        if (state.enabled && wifiConnected) {
-            // Only show full signal here.
-            state.icon = ResourceIcon.get(WifiIcons.QS_WIFI_SIGNAL_STRENGTH[1][4]);
-        }
-        // No activity in the quick toggle.
-        state.activityIn = false;
-        state.activityOut = false;
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/RotationLockTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/RotationLockTile.java
index 1a26a4d..d85cf60 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/RotationLockTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/RotationLockTile.java
@@ -72,7 +72,8 @@
         final boolean rotationLocked = arg != null ? ((UserBoolean) arg).value
                 : mController.isRotationLocked();
         final boolean userInitiated = arg != null ? ((UserBoolean) arg).userInitiated : false;
-        state.visible = mController.isRotationLockAffordanceVisible();
+        // TODO: Handle accessibility rotation lock and whatnot.
+//        state.visible = mController.isRotationLockAffordanceVisible();
         if (state.value == rotationLocked && state.contentDescription != null) {
             // No change and initialized, no need to update all the values.
             return;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/UserTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/UserTile.java
index 3c5ab8d..d29cae4 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/UserTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/UserTile.java
@@ -67,17 +67,19 @@
     @Override
     protected void handleUpdateState(State state, Object arg) {
         final Pair<String, Drawable> p = arg != null ? (Pair<String, Drawable>) arg : mLastUpdate;
-        state.visible = p != null;
-        if (!state.visible) return;
-        state.label = p.first;
-        // TODO: Better content description.
-        state.contentDescription = p.first;
-        state.icon = new Icon() {
-            @Override
-            public Drawable getDrawable(Context context) {
-                return p.second;
-            }
-        };
+        if (p != null) {
+            state.label = p.first;
+            // TODO: Better content description.
+            state.contentDescription = p.first;
+            state.icon = new Icon() {
+                @Override
+                public Drawable getDrawable(Context context) {
+                    return p.second;
+                }
+            };
+        } else {
+            // TODO: Default state.
+        }
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java
index 7f4442a..48b4096 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java
@@ -18,20 +18,19 @@
 
 import android.content.Context;
 import android.content.Intent;
+import android.content.pm.PackageManager;
 import android.content.res.Resources;
 import android.provider.Settings;
 import android.util.Log;
 import android.view.View;
 import android.view.ViewGroup;
-
 import com.android.internal.logging.MetricsLogger;
 import com.android.settingslib.wifi.AccessPoint;
 import com.android.systemui.R;
 import com.android.systemui.qs.QSDetailItems;
 import com.android.systemui.qs.QSDetailItems.Item;
+import com.android.systemui.qs.QSIconView;
 import com.android.systemui.qs.QSTile;
-import com.android.systemui.qs.QSTileBaseView;
-import com.android.systemui.qs.QSTileView;
 import com.android.systemui.qs.SignalTileView;
 import com.android.systemui.statusbar.policy.NetworkController;
 import com.android.systemui.statusbar.policy.NetworkController.AccessPointController;
@@ -51,22 +50,14 @@
 
     protected final WifiSignalCallback mSignalCallback = new WifiSignalCallback();
 
-    private final boolean mAlwaysDetail;
-
-    public WifiTile(Host host, boolean alwaysDetail) {
+    public WifiTile(Host host) {
         super(host);
-        mAlwaysDetail = alwaysDetail;
         mController = host.getNetworkController();
         mWifiController = mController.getAccessPointController();
         mDetailAdapter = new WifiDetailAdapter();
     }
 
     @Override
-    public int getTileType() {
-        return QSTileView.QS_TYPE_DUAL;
-    }
-
-    @Override
     protected SignalState newTileState() {
         return new SignalState();
     }
@@ -95,23 +86,20 @@
     }
 
     @Override
-    public QSTileBaseView createTileView(Context context) {
+    public QSIconView createTileView(Context context) {
         return new SignalTileView(context);
     }
 
     @Override
-    protected void handleClick() {
-        if (mAlwaysDetail) {
-            handleSecondaryClick();
-            return;
-        }
+    protected void handleSecondaryClick() {
+        // Secondary clicks are header clicks, just toggle.
         mState.copyTo(mStateBeforeClick);
         MetricsLogger.action(mContext, getMetricsCategory(), !mState.enabled);
         mController.setWifiEnabled(!mState.enabled);
     }
 
     @Override
-    protected void handleSecondaryClick() {
+    protected void handleClick() {
         if (!mWifiController.canConfigWifi()) {
             mHost.startActivityDismissingKeyguard(new Intent(Settings.ACTION_WIFI_SETTINGS));
             return;
@@ -125,7 +113,6 @@
 
     @Override
     protected void handleUpdateState(SignalState state, Object arg) {
-        state.visible = true;
         if (DEBUG) Log.d(TAG, "handleUpdateState arg=" + arg);
         CallbackInfo cb = (CallbackInfo) arg;
         if (cb == null) {
@@ -201,6 +188,10 @@
         return string;
     }
 
+    public static boolean isSupported(Host host) {
+        return host.getContext().getPackageManager().hasSystemFeature(PackageManager.FEATURE_WIFI);
+    }
+
     protected static final class CallbackInfo {
         boolean enabled;
         boolean connected;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/WorkModeTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/WorkModeTile.java
index 7605f3b8..07915f8 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/WorkModeTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/WorkModeTile.java
@@ -22,14 +22,8 @@
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.content.pm.UserInfo;
-import android.os.IUserManager;
-import android.os.RemoteException;
-import android.os.ServiceManager;
 import android.os.UserHandle;
 import android.os.UserManager;
-import android.util.ArraySet;
-import android.util.Log;
-
 import com.android.internal.logging.MetricsLogger;
 import com.android.systemui.R;
 import com.android.systemui.qs.QSTile;
@@ -110,8 +104,7 @@
     @Override
     protected void handleUpdateState(BooleanState state, Object arg) {
         if (!hasActiveProfile()) {
-            state.visible = false;
-            state.value = false;
+            mHost.removeTile(getTileSpec());
             return;
         }
 
@@ -124,7 +117,6 @@
             userInitialized = false;
         }
 
-        state.visible = true;
         final AnimationIcon icon;
         state.label = mContext.getString(R.string.quick_settings_work_mode_label);
         if (state.value) {
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 5d17e2c..8979843 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
@@ -52,6 +52,7 @@
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.provider.Settings;
+import android.util.ArraySet;
 import android.util.Log;
 import android.util.MutableBoolean;
 import android.util.Pair;
@@ -154,7 +155,7 @@
 
     /** Returns a list of the recents tasks */
     public List<ActivityManager.RecentTaskInfo> getRecentTasks(int numLatestTasks, int userId,
-            boolean isTopTaskHome) {
+            boolean isTopTaskHome, ArraySet<Integer> quietProfileIds) {
         if (mAm == null) return null;
 
         // If we are mocking, then create some recent tasks
@@ -215,6 +216,8 @@
             // tasks if it is not the first active task.
             boolean isExcluded = (t.baseIntent.getFlags() & Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS)
                     == Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS;
+            // Filter out recent tasks from managed profiles which are in quiet mode.
+            isExcluded |= quietProfileIds.contains(t.userId);
             if (isExcluded && (isTopTaskHome || !isFirstValidTask)) {
                 iter.remove();
                 continue;
diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoadPlan.java b/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoadPlan.java
index 63d09ee..1845bf9 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoadPlan.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoadPlan.java
@@ -18,10 +18,13 @@
 
 import android.app.ActivityManager;
 import android.content.Context;
+import android.content.pm.UserInfo;
 import android.content.res.Resources;
 import android.graphics.Bitmap;
 import android.graphics.drawable.Drawable;
 import android.os.UserHandle;
+import android.os.UserManager;
+import android.util.ArraySet;
 import android.util.Log;
 import com.android.systemui.Prefs;
 import com.android.systemui.recents.Recents;
@@ -66,20 +69,42 @@
 
     List<ActivityManager.RecentTaskInfo> mRawTasks;
     TaskStack mStack;
+    ArraySet<Integer> mCurrentQuietProfiles = new ArraySet<Integer>();
 
     /** Package level ctor */
     RecentsTaskLoadPlan(Context context) {
         mContext = context;
     }
 
+    private void updateCurrentQuietProfilesCache(int currentUserId) {
+        mCurrentQuietProfiles.clear();
+
+        if (currentUserId == UserHandle.USER_CURRENT) {
+            currentUserId = ActivityManager.getCurrentUser();
+        }
+        UserManager userManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
+        List<UserInfo> profiles = userManager.getProfiles(currentUserId);
+        if (profiles != null) {
+            for (int i = 0; i < profiles.size(); i++) {
+                UserInfo user  = profiles.get(i);
+                if (user.isManagedProfile() && user.isQuietModeEnabled()) {
+                    mCurrentQuietProfiles.add(user.id);
+                }
+            }
+        }
+    }
+
     /**
      * An optimization to preload the raw list of tasks.  The raw tasks are saved in least-recent
      * to most-recent order.
      */
     public synchronized void preloadRawTasks(boolean isTopTaskHome) {
+        int currentUserId = UserHandle.USER_CURRENT;
+        updateCurrentQuietProfilesCache(currentUserId);
         SystemServicesProxy ssp = Recents.getSystemServices();
         mRawTasks = ssp.getRecentTasks(ActivityManager.getMaxRecentTasksStatic(),
-                UserHandle.CURRENT.getIdentifier(), isTopTaskHome);
+                currentUserId, isTopTaskHome, mCurrentQuietProfiles);
+
         // Since the raw tasks are given in most-recent to least-recent order, we need to reverse it
         Collections.reverse(mRawTasks);
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
index 3c7ff7f..879624e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
@@ -1544,7 +1544,6 @@
             }
         }
         entry.row = row;
-        updateNotificationHeightRange(entry);
         entry.row.setOnActivatedListener(this);
         entry.row.setExpandable(bigContentViewLocal != null);
 
@@ -1557,19 +1556,11 @@
             row.setUserExpanded(userExpanded);
         }
         row.setUserLocked(userLocked);
-        row.updateStatusBarNotification(entry.notification);
+        row.onNotificationUpdated(entry);
         applyRemoteInput(entry);
         return true;
     }
 
-    private void updateNotificationHeightRange(Entry entry) {
-        boolean customView = entry.getContentView().getId()
-                != com.android.internal.R.id.status_bar_latest_event_content;
-        boolean beforeN = entry.targetSdk < Build.VERSION_CODES.N;
-        int minHeight = customView && beforeN ? mRowMinHeightLegacy : mRowMinHeight;
-        entry.row.setHeightRange(minHeight, mRowMaxHeight);
-    }
-
     /**
      * Adds RemoteInput actions from the WearableExtender; to be removed once more apps support this
      * via first-class API.
@@ -2191,7 +2182,7 @@
         // update the contentIntent
         mNotificationClicker.register(entry.row, sbn);
 
-        entry.row.updateStatusBarNotification(entry.notification);
+        entry.row.onNotificationUpdated(entry);
         entry.row.resetHeight();
 
         applyRemoteInput(entry);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
index 7a94a58..5c79c7d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
@@ -22,6 +22,7 @@
 import android.graphics.drawable.AnimationDrawable;
 import android.graphics.drawable.ColorDrawable;
 import android.graphics.drawable.Drawable;
+import android.os.Build;
 import android.service.notification.StatusBarNotification;
 import android.util.AttributeSet;
 import android.view.MotionEvent;
@@ -49,6 +50,9 @@
     private static final int DEFAULT_DIVIDER_ALPHA = 0x29;
     private static final int COLORED_DIVIDER_ALPHA = 0x7B;
     private final LinearInterpolator mLinearInterpolator = new LinearInterpolator();
+    private final int mNotificationMinHeightLegacy;
+    private final int mNotificationMinHeight;
+    private final int mNotificationMaxHeight;
     private int mRowMinHeight;
 
     /** Does this row contain layouts that can adapt to row expansion */
@@ -86,6 +90,7 @@
     private String mLoggingKey;
     private boolean mWasReset;
     private NotificationGuts mGuts;
+    private NotificationData.Entry mEntry;
     private StatusBarNotification mStatusBarNotification;
     private boolean mIsHeadsUp;
     private boolean mLastChronometerRunning = true;
@@ -99,6 +104,7 @@
     private boolean mIsSystemChildExpanded;
     private boolean mIsPinned;
     private FalsingManager mFalsingManager;
+    private NotificationHeaderUtil mHeaderUtil = new NotificationHeaderUtil(this);
 
     private boolean mJustClicked;
     private boolean mIconAnimationRunning;
@@ -187,10 +193,11 @@
         }
     }
 
-    public void updateStatusBarNotification(StatusBarNotification statusBarNotification) {
-        mStatusBarNotification = statusBarNotification;
-        mPrivateLayout.onNotificationUpdated(statusBarNotification);
-        mPublicLayout.onNotificationUpdated(statusBarNotification);
+    public void onNotificationUpdated(NotificationData.Entry entry) {
+        mEntry = entry;
+        mStatusBarNotification = entry.notification;
+        mPrivateLayout.onNotificationUpdated(entry.notification);
+        mPublicLayout.onNotificationUpdated(entry.notification);
         updateVetoButton();
         if (mIsSummaryWithChildren) {
             recreateNotificationHeader();
@@ -198,7 +205,23 @@
         if (mIconAnimationRunning) {
             setIconAnimationRunning(true);
         }
+        if (mNotificationParent != null) {
+            mNotificationParent.updateChildrenHeaderAppearance();
+        }
         onChildrenCountChanged();
+        updateLimits();
+    }
+
+    private void updateLimits() {
+        boolean customView = getPrivateLayout().getContractedChild().getId()
+                != com.android.internal.R.id.status_bar_latest_event_content;
+        boolean beforeN = mEntry.targetSdk < Build.VERSION_CODES.N;
+        int minHeight = customView && beforeN && !mIsSummaryWithChildren ?
+                mNotificationMinHeightLegacy : mNotificationMinHeight;
+        mRowMinHeight = minHeight;
+        mMaxViewHeight = mNotificationMaxHeight;
+        mPrivateLayout.setSmallHeight(mRowMinHeight);
+        mPublicLayout.setSmallHeight(mRowMinHeight);
     }
 
     public StatusBarNotification getStatusBarNotification() {
@@ -246,6 +269,7 @@
         if (mChildrenContainer != null) {
             mChildrenContainer.removeNotification(row);
         }
+        mHeaderUtil.restoreNotificationHeader(row);
         onChildrenCountChanged();
         row.setIsChildInGroup(false, null);
     }
@@ -414,17 +438,11 @@
         }
     }
 
-    public CharSequence getSubText() {
-        Notification notification = mStatusBarNotification.getNotification();
-        CharSequence subText = notification.extras.getCharSequence(Notification.EXTRA_SUMMARY_TEXT);
-        if (subText == null) {
-            subText = notification.extras.getCharSequence(Notification.EXTRA_SUB_TEXT);
+    public NotificationHeaderView getNotificationHeader() {
+        if (mNotificationHeader != null) {
+            return mNotificationHeader;
         }
-        return subText;
-    }
-
-    public void setContentSubTextVisible(boolean visible) {
-        mPrivateLayout.setSubTextVisible(visible);
+        return mPrivateLayout.getNotificationHeader();
     }
 
     public void setOnExpandClickListener(OnExpandClickListener onExpandClickListener) {
@@ -438,6 +456,12 @@
     public ExpandableNotificationRow(Context context, AttributeSet attrs) {
         super(context, attrs);
         mFalsingManager = FalsingManager.getInstance(context);
+        mNotificationMinHeightLegacy =  getResources().getDimensionPixelSize(
+                R.dimen.notification_min_height_legacy);
+        mNotificationMinHeight =  getResources().getDimensionPixelSize(
+                R.dimen.notification_min_height);
+        mNotificationMaxHeight =  getResources().getDimensionPixelSize(
+                R.dimen.notification_max_height);
     }
 
     /**
@@ -514,13 +538,15 @@
         }
     }
 
-    private void updateChildrenVisibility(boolean animated) {
+    private void updateChildrenVisibility() {
         if (mChildrenContainer == null) {
             return;
         }
         mChildrenContainer.setVisibility(mIsSummaryWithChildren ? VISIBLE : INVISIBLE);
         mNotificationHeader.setVisibility(mIsSummaryWithChildren ? VISIBLE : INVISIBLE);
         mPrivateLayout.setVisibility(!mIsSummaryWithChildren ? VISIBLE : INVISIBLE);
+        // The limits might have changed if the view suddenly became a group or vice versa
+        updateLimits();
     }
 
     @Override
@@ -546,13 +572,6 @@
         }
     }
 
-    public void setHeightRange(int rowMinHeight, int rowMaxHeight) {
-        mRowMinHeight = rowMinHeight;
-        mMaxViewHeight = rowMaxHeight;
-        mPrivateLayout.setSmallHeight(mRowMinHeight);
-        mPublicLayout.setSmallHeight(mRowMinHeight);
-    }
-
     public boolean isExpandable() {
         if (mIsSummaryWithChildren && !mShowingPublic) {
             return !mChildrenExpanded;
@@ -720,8 +739,9 @@
             }
         }
         mPrivateLayout.updateExpandButtons(isExpandable());
+        updateChildrenHeaderAppearance();
         updateHeaderChildCount();
-        updateChildrenVisibility(true);
+        updateChildrenVisibility();
     }
 
     /**
@@ -841,6 +861,9 @@
 
     public void setChildrenExpanded(boolean expanded, boolean animate) {
         mChildrenExpanded = expanded;
+        if (mNotificationHeader != null) {
+            mNotificationHeader.setExpanded(expanded);
+        }
         if (mChildrenContainer != null) {
             mChildrenContainer.setChildrenExpanded(expanded);
         }
@@ -948,9 +971,23 @@
         } else {
             header.reapply(getContext(), mNotificationHeader);
         }
+        updateHeaderExpandButton();
+        updateChildrenHeaderAppearance();
         updateHeaderChildCount();
     }
 
+    private void updateHeaderExpandButton() {
+        if (mIsSummaryWithChildren) {
+            mNotificationHeader.setIsGroupHeader(true /* isGroupHeader*/);
+        }
+    }
+
+    public void updateChildrenHeaderAppearance() {
+        if (mIsSummaryWithChildren) {
+            mHeaderUtil.updateChildrenHeaderAppearance();
+        }
+    }
+
     public boolean isMaxExpandHeightInitialized() {
         return mMaxExpandHeight != 0;
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java
index fb8086c..da01d54 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java
@@ -52,7 +52,6 @@
     private static final int VISIBLE_TYPE_SINGLELINE = 3;
 
     private final Rect mClipBounds = new Rect();
-    private final int mSingleLineHeight;
     private final int mHeadsUpHeight;
     private final int mRoundRectRadius;
     private final Interpolator mLinearInterpolator = new LinearInterpolator();
@@ -104,8 +103,6 @@
         super(context, attrs);
         mHybridViewManager = new HybridNotificationViewManager(getContext(), this);
         mFadePaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.ADD));
-        mSingleLineHeight = getResources().getDimensionPixelSize(
-                R.dimen.notification_single_line_height);
         mHeadsUpHeight = getResources().getDimensionPixelSize(R.dimen.notification_mid_height);
         mRoundRectRadius = getResources().getDimensionPixelSize(
                 R.dimen.notification_material_rounded_rect_radius);
@@ -160,9 +157,8 @@
             maxChildHeight = Math.max(maxChildHeight, mHeadsUpChild.getMeasuredHeight());
         }
         if (mSingleLineView != null) {
-            int size = Math.min(maxSize, mSingleLineHeight);
             mSingleLineView.measure(widthMeasureSpec,
-                    MeasureSpec.makeMeasureSpec(size, MeasureSpec.EXACTLY));
+                    MeasureSpec.makeMeasureSpec(maxSize, MeasureSpec.AT_MOST));
             maxChildHeight = Math.max(maxChildHeight, mSingleLineView.getMeasuredHeight());
         }
         int ownHeight = Math.min(maxChildHeight, maxSize);
@@ -297,7 +293,7 @@
 
     public int getMinHeight() {
         if (mIsChildInGroup && !isGroupExpanded()) {
-            return mSingleLineHeight;
+            return mSingleLineView.getHeight();
         } else {
             return mSmallHeight;
         }
@@ -509,18 +505,6 @@
         }
     }
 
-    public void setSubTextVisible(boolean visible) {
-        if (mExpandedChild != null) {
-            mExpandedWrapper.setSubTextVisible(visible);
-        }
-        if (mContractedChild != null) {
-            mContractedWrapper.setSubTextVisible(visible);
-        }
-        if (mHeadsUpChild != null) {
-            mHeadsUpWrapper.setSubTextVisible(visible);
-        }
-    }
-
     public void setGroupManager(NotificationGroupManager groupManager) {
         mGroupManager = groupManager;
     }
@@ -540,4 +524,18 @@
             mHeadsUpWrapper.updateExpandability(expandable,  mExpandClickListener);
         }
     }
+
+    public NotificationHeaderView getNotificationHeader() {
+        NotificationHeaderView header = null;
+        if (mContractedChild != null) {
+            header = mContractedWrapper.getNotificationHeader();
+        }
+        if (header == null && mExpandedChild != null) {
+            header = mExpandedWrapper.getNotificationHeader();
+        }
+        if (header == null && mHeadsUpChild != null) {
+            header = mHeadsUpWrapper.getNotificationHeader();
+        }
+        return header;
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationHeaderUtil.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationHeaderUtil.java
new file mode 100644
index 0000000..859a330
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationHeaderUtil.java
@@ -0,0 +1,346 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.statusbar;
+
+import android.app.Notification;
+import android.graphics.PorterDuff;
+import android.graphics.drawable.Icon;
+import android.text.TextUtils;
+import android.view.NotificationHeaderView;
+import android.view.View;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+
+/**
+ * A Util to manage {@link android.view.NotificationHeaderView} objects and their redundancies.
+ */
+public class NotificationHeaderUtil {
+
+    private static final TextViewComparator sTextViewComparator = new TextViewComparator();
+    private static final VisibilityApplicator sVisibilityApplicator = new VisibilityApplicator();
+    private static  final DataExtractor sIconExtractor = new DataExtractor() {
+        @Override
+        public Object extractData(ExpandableNotificationRow row) {
+            return row.getStatusBarNotification().getNotification();
+        }
+    };
+    private static final IconComparator sIconVisibilityComparator = new IconComparator() {
+        public boolean compare(View parent, View child, Object parentData,
+                Object childData) {
+            return hasSameIcon(parentData, childData)
+                    && hasSameColor(parentData, childData);
+        }
+    };
+    private static final IconComparator sGreyComparator = new IconComparator() {
+        public boolean compare(View parent, View child, Object parentData,
+                Object childData) {
+            return !hasSameIcon(parentData, childData)
+                    || hasSameColor(parentData, childData);
+        }
+    };
+    private final static ResultApplicator mGreyApplicator = new ResultApplicator() {
+        @Override
+        public void apply(View view, boolean apply) {
+            NotificationHeaderView header = (NotificationHeaderView) view;
+            ImageView icon = (ImageView) view.findViewById(
+                    com.android.internal.R.id.icon);
+            ImageView expand = (ImageView) view.findViewById(
+                    com.android.internal.R.id.expand_button);
+            applyToChild(icon, apply, header.getOriginalIconColor());
+            int color = header.getOriginalNotificationColor();
+            if (color == Notification.COLOR_DEFAULT) {
+                color = view.getContext().getColor(
+                        com.android.internal.R.color.notification_icon_default_color);
+            }
+            applyToChild(expand, apply, color);
+        }
+
+        private void applyToChild(View view, boolean shouldApply, int originalColor) {
+            if (view.getVisibility() == View.VISIBLE
+                    && originalColor != NotificationHeaderView.NO_COLOR) {
+                ImageView imageView = (ImageView) view;
+                imageView.getDrawable().mutate();
+                if (shouldApply) {
+                    // lets gray it out
+                    int grey = view.getContext().getColor(
+                            com.android.internal.R.color.notification_icon_default_color);
+                    imageView.getDrawable().setColorFilter(grey, PorterDuff.Mode.SRC_ATOP);
+                } else {
+                    // lets reset it
+                    imageView.getDrawable().setColorFilter(originalColor,
+                            PorterDuff.Mode.SRC_ATOP);
+                }
+            }
+        }
+    };
+
+    private final ExpandableNotificationRow mRow;
+    private final ArrayList<HeaderProcessor> mComparators = new ArrayList<>();
+    private final HashSet<Integer> mDividers = new HashSet<>();
+
+    NotificationHeaderUtil(ExpandableNotificationRow row) {
+        mRow = row;
+        // To hide the icons if they are the same and the color is the same
+        mComparators.add(new HeaderProcessor(mRow,
+                com.android.internal.R.id.icon,
+                sIconExtractor,
+                sIconVisibilityComparator,
+                sVisibilityApplicator));
+        // To grey them out the icons and expand button when the icons are not the same
+        mComparators.add(new HeaderProcessor(mRow,
+                com.android.internal.R.id.notification_header,
+                sIconExtractor,
+                sGreyComparator,
+                mGreyApplicator));
+        mComparators.add(HeaderProcessor.forTextView(mRow,
+                com.android.internal.R.id.app_name_text));
+        mComparators.add(HeaderProcessor.forTextView(mRow,
+                com.android.internal.R.id.header_sub_text));
+        mComparators.add(HeaderProcessor.forTextView(mRow,
+                com.android.internal.R.id.header_content_info));
+        mDividers.add(com.android.internal.R.id.sub_text_divider);
+        mDividers.add(com.android.internal.R.id.content_info_divider);
+        mDividers.add(com.android.internal.R.id.time_divider);
+    }
+
+    public void updateChildrenHeaderAppearance() {
+        List<ExpandableNotificationRow> notificationChildren = mRow.getNotificationChildren();
+        if (notificationChildren == null) {
+            return;
+        }
+        // Initialize the comparators
+        for (int compI = 0; compI < mComparators.size(); compI++) {
+            mComparators.get(compI).init();
+        }
+
+        // Compare all notification headers
+        for (int i = 0; i < notificationChildren.size(); i++) {
+            ExpandableNotificationRow row = notificationChildren.get(i);
+            for (int compI = 0; compI < mComparators.size(); compI++) {
+                mComparators.get(compI).compareToHeader(row);
+            }
+        }
+
+        // Apply the comparison to the row
+        for (int i = 0; i < notificationChildren.size(); i++) {
+            ExpandableNotificationRow row = notificationChildren.get(i);
+            for (int compI = 0; compI < mComparators.size(); compI++) {
+                mComparators.get(compI).apply(row);
+            }
+            // We need to sanitize the dividers since they might be off-balance now
+            sanitizeDividers(row);
+        }
+    }
+
+    private void sanitizeDividers(ExpandableNotificationRow row) {
+        if (row.isSummaryWithChildren()) {
+            sanitizeHeader(row.getNotificationHeader());
+            return;
+        }
+        final NotificationContentView layout = row.getPrivateLayout();
+        sanitizeChild(layout.getContractedChild());
+        sanitizeChild(layout.getHeadsUpChild());
+        sanitizeChild(layout.getExpandedChild());
+    }
+
+    private void sanitizeChild(View child) {
+        if (child != null) {
+            NotificationHeaderView header = (NotificationHeaderView) child.findViewById(
+                    com.android.internal.R.id.notification_header);
+            sanitizeHeader(header);
+        }
+    }
+
+    private void sanitizeHeader(NotificationHeaderView rowHeader) {
+        if (rowHeader == null) {
+            return;
+        }
+        View left = null;
+        View right;
+        final int childCount = rowHeader.getChildCount();
+        for (int i = 1; i < childCount - 1 ; i++) {
+            View child = rowHeader.getChildAt(i);
+            if (mDividers.contains(Integer.valueOf(child.getId()))) {
+                boolean visible = false;
+                // Lets find the item to the right
+                for (i++; i < childCount - 1; i++) {
+                    right = rowHeader.getChildAt(i);
+                    if (mDividers.contains(Integer.valueOf(right.getId()))) {
+                        // A divider was found, this needs to be hidden
+                        i--;
+                        break;
+                    } else if (right.getVisibility() == View.VISIBLE) {
+                        visible = left != null;
+                        left = right;
+                        break;
+                    }
+                }
+                child.setVisibility(visible ? View.VISIBLE : View.GONE);
+            } else if (child.getVisibility() == View.VISIBLE) {
+                left = child;
+            }
+        }
+    }
+
+    public void restoreNotificationHeader(ExpandableNotificationRow row) {
+        for (int compI = 0; compI < mComparators.size(); compI++) {
+            mComparators.get(compI).apply(row, true /* reset */);
+        }
+        sanitizeDividers(row);
+    }
+
+    private static class HeaderProcessor {
+        private final int mId;
+        private final DataExtractor mExtractor;
+        private final ResultApplicator mApplicator;
+        private final ExpandableNotificationRow mParentRow;
+        private boolean mApply;
+        private View mParentView;
+        private ViewComparator mComparator;
+        private Object mParentData;
+
+        public static HeaderProcessor forTextView(ExpandableNotificationRow row, int id) {
+            return new HeaderProcessor(row, id, null, sTextViewComparator, sVisibilityApplicator);
+        }
+
+        HeaderProcessor(ExpandableNotificationRow row, int id, DataExtractor extractor,
+                ViewComparator comparator,
+                ResultApplicator applicator) {
+            mId = id;
+            mExtractor = extractor;
+            mApplicator = applicator;
+            mComparator = comparator;
+            mParentRow = row;
+        }
+
+        public void init() {
+            mParentView = mParentRow.getNotificationHeader().findViewById(mId);
+            mParentData = mExtractor == null ? null : mExtractor.extractData(mParentRow);
+            mApply = !mComparator.isEmpty(mParentView);
+        }
+        public void compareToHeader(ExpandableNotificationRow row) {
+            if (!mApply) {
+                return;
+            }
+            NotificationHeaderView header = row.getNotificationHeader();
+            if (header == null) {
+                mApply = false;
+                return;
+            }
+            Object childData = mExtractor == null ? null : mExtractor.extractData(row);
+            mApply = mComparator.compare(mParentView, header.findViewById(mId),
+                    mParentData, childData);
+        }
+
+        public void apply(ExpandableNotificationRow row) {
+            apply(row, false /* reset */);
+        }
+
+        public void apply(ExpandableNotificationRow row, boolean reset) {
+            boolean apply = mApply && !reset;
+            if (row.isSummaryWithChildren()) {
+                applyToView(apply, row.getNotificationHeader());
+                return;
+            }
+            applyToView(apply, row.getPrivateLayout().getContractedChild());
+            applyToView(apply, row.getPrivateLayout().getHeadsUpChild());
+            applyToView(apply, row.getPrivateLayout().getExpandedChild());
+        }
+
+        private void applyToView(boolean apply, View parent) {
+            if (parent != null) {
+                View view = parent.findViewById(mId);
+                if (view != null && !mComparator.isEmpty(view)) {
+                    mApplicator.apply(view, apply);
+                }
+            }
+        }
+    }
+
+    private interface ViewComparator {
+        /**
+         * @param parent the parent view
+         * @param child the child view
+         * @param parentData optional data for the parent
+         * @param childData optional data for the child
+         * @return whether to views are the same
+         */
+        boolean compare(View parent, View child, Object parentData, Object childData);
+        boolean isEmpty(View view);
+    }
+
+    private interface DataExtractor {
+        Object extractData(ExpandableNotificationRow row);
+    }
+
+    private static class TextViewComparator implements ViewComparator {
+        @Override
+        public boolean compare(View parent, View child, Object parentData, Object childData) {
+            TextView parentView = (TextView) parent;
+            TextView childView = (TextView) child;
+            return parentView.getText().equals(childView.getText());
+        }
+
+        @Override
+        public boolean isEmpty(View view) {
+            return TextUtils.isEmpty(((TextView) view).getText());
+        }
+    }
+
+    private static abstract class IconComparator implements ViewComparator {
+        @Override
+        public boolean compare(View parent, View child, Object parentData, Object childData) {
+            return false;
+        }
+
+        protected boolean hasSameIcon(Object parentData, Object childData) {
+            Icon parentIcon = ((Notification) parentData).getSmallIcon();
+            Icon childIcon = ((Notification) childData).getSmallIcon();
+            return parentIcon.sameAs(childIcon);
+        }
+
+        /**
+         * @return whether two ImageViews have the same colorFilterSet or none at all
+         */
+        protected boolean hasSameColor(Object parentData, Object childData) {
+            int parentColor = ((Notification) parentData).color;
+            int childColor = ((Notification) childData).color;
+            return parentColor == childColor;
+        }
+
+        @Override
+        public boolean isEmpty(View view) {
+            return false;
+        }
+    }
+
+    private interface ResultApplicator {
+        void apply(View view, boolean apply);
+    }
+
+    private static class VisibilityApplicator implements ResultApplicator {
+
+        @Override
+        public void apply(View view, boolean apply) {
+            view.setVisibility(apply ? View.GONE : View.VISIBLE);
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationTemplateViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationTemplateViewWrapper.java
index f20ccd5..fb0a419 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationTemplateViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationTemplateViewWrapper.java
@@ -31,6 +31,7 @@
 import android.graphics.drawable.Drawable;
 import android.text.TextUtils;
 import android.view.MotionEvent;
+import android.view.NotificationHeaderView;
 import android.view.View;
 import android.view.ViewConfiguration;
 import android.view.ViewGroup;
@@ -64,10 +65,8 @@
     private ImageView mIcon;
     protected ImageView mPicture;
 
-    private TextView mSubText;
-    private View mSubTextDivider;
     private ImageView mExpandButton;
-    private ViewGroup mNotificationHeader;
+    private NotificationHeaderView mNotificationHeader;
     private ProgressBar mProgressBar;
 
     protected NotificationTemplateViewWrapper(Context ctx, View view) {
@@ -83,8 +82,6 @@
         View mainColumn = mView.findViewById(com.android.internal.R.id.notification_main_column);
         mIcon = (ImageView) mView.findViewById(com.android.internal.R.id.icon);
         mPicture = (ImageView) mView.findViewById(com.android.internal.R.id.right_icon);
-        mSubText = (TextView) mView.findViewById(com.android.internal.R.id.header_sub_text);
-        mSubTextDivider = mView.findViewById(com.android.internal.R.id.sub_text_divider);
         mExpandButton = (ImageView) mView.findViewById(com.android.internal.R.id.expand_button);
         mColor = resolveColor(mExpandButton);
         final View progress = mView.findViewById(com.android.internal.R.id.progress);
@@ -94,7 +91,7 @@
             // It's still a viewstub
             mProgressBar = null;
         }
-        mNotificationHeader = (ViewGroup) mView.findViewById(
+        mNotificationHeader = (NotificationHeaderView) mView.findViewById(
                 com.android.internal.R.id.notification_header);
         ArrayList<View> viewsToInvert = new ArrayList<>();
         if (mainColumn != null) {
@@ -138,12 +135,22 @@
             }
         }
         if (mIcon != null) {
+            boolean hadColorFilter = mNotificationHeader.getOriginalIconColor()
+                    != NotificationHeaderView.NO_COLOR;
             if (fade) {
-                fadeIconColorFilter(mIcon, dark, delay);
-                fadeIconAlpha(mIcon, dark, delay);
+                if (hadColorFilter) {
+                    fadeIconColorFilter(mIcon, dark, delay);
+                    fadeIconAlpha(mIcon, dark, delay);
+                } else {
+                    fadeGrayscale(mIcon, dark, delay);
+                }
             } else {
-                updateIconColorFilter(mIcon, dark);
-                updateIconAlpha(mIcon, dark);
+                if (hadColorFilter) {
+                    updateIconColorFilter(mIcon, dark);
+                    updateIconAlpha(mIcon, dark);
+                } else {
+                    updateGrayscale(mIcon, dark);
+                }
             }
         }
         setPictureGrayscale(dark, fade, delay);
@@ -271,21 +278,6 @@
     }
 
     @Override
-    public void setSubTextVisible(boolean visible) {
-        if (mSubText == null) {
-            return;
-        }
-        boolean subTextAvailable = !TextUtils.isEmpty(mSubText.getText());
-        if (visible && subTextAvailable) {
-            mSubText.setVisibility(View.VISIBLE);
-            mSubTextDivider.setVisibility(View.VISIBLE);
-        } else {
-            mSubText.setVisibility(View.GONE);
-            mSubTextDivider.setVisibility(View.GONE);
-        }
-    }
-
-    @Override
     public void updateExpandability(boolean expandable, View.OnClickListener onClickListener) {
         mExpandButton.setVisibility(expandable ? View.VISIBLE : View.GONE);
         mNotificationHeader.setOnClickListener(expandable ? onClickListener : null);
@@ -310,4 +302,9 @@
                 (int) (gSource * (1f - t) + gTarget * t),
                 (int) (bSource * (1f - t) + bTarget * t));
     }
+
+    @Override
+    public NotificationHeaderView getNotificationHeader() {
+        return mNotificationHeader;
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewWrapper.java
index e83ecb7..119d57b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewWrapper.java
@@ -17,6 +17,7 @@
 package com.android.systemui.statusbar;
 
 import android.content.Context;
+import android.view.NotificationHeaderView;
 import android.view.View;
 
 /**
@@ -83,4 +84,11 @@
      * @param onClickListener the listener to invoke when the expand affordance is clicked on
      */
     public void updateExpandability(boolean expandable, View.OnClickListener onClickListener) {}
+
+    /**
+     * @return the notification header if it exists
+     */
+    public NotificationHeaderView getNotificationHeader() {
+        return null;
+    }
 }
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 8f46e89..fafea98 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/HybridNotificationView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/HybridNotificationView.java
@@ -18,21 +18,17 @@
 
 import android.annotation.Nullable;
 import android.content.Context;
-import android.text.TextUtils;
 import android.util.AttributeSet;
 import android.widget.TextView;
 
+import com.android.keyguard.AlphaOptimizedLinearLayout;
 import com.android.systemui.R;
-import com.android.systemui.statusbar.AlphaOptimizedFrameLayout;
 
 /**
  * A hybrid view which may contain information about one ore more notifications.
  */
-public class HybridNotificationView extends AlphaOptimizedFrameLayout {
+public class HybridNotificationView extends AlphaOptimizedLinearLayout {
 
-    protected final int mSingleLineHeight;
-    protected final int mStartMargin;
-    protected final int mEndMargin;
     protected TextView mTitleView;
     protected TextView mTextView;
 
@@ -51,62 +47,6 @@
     public HybridNotificationView(Context context, @Nullable AttributeSet attrs, int defStyleAttr,
             int defStyleRes) {
         super(context, attrs, defStyleAttr, defStyleRes);
-        mSingleLineHeight = context.getResources().getDimensionPixelSize(
-                R.dimen.notification_single_line_height);
-        mStartMargin = context.getResources().getDimensionPixelSize(
-                R.dimen.notification_content_margin_start);
-        mEndMargin = context.getResources().getDimensionPixelSize(
-                R.dimen.notification_content_margin_end);
-    }
-
-    @Override
-    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
-        int totalWidth = MeasureSpec.getSize(widthMeasureSpec);
-        int remainingWidth = totalWidth - mStartMargin - mEndMargin;
-        int newHeightSpec = MeasureSpec.makeMeasureSpec(
-                MeasureSpec.getSize(heightMeasureSpec), MeasureSpec.AT_MOST);
-        int newWidthSpec = MeasureSpec.makeMeasureSpec(remainingWidth, MeasureSpec.AT_MOST);
-        mTitleView.measure(newWidthSpec, newHeightSpec);
-        int maxTitleLength = getResources().getDimensionPixelSize(
-                R.dimen.notification_maximum_title_length);
-        int titleWidth = mTitleView.getMeasuredWidth();
-        int heightSpec = MeasureSpec.makeMeasureSpec(mSingleLineHeight, MeasureSpec.AT_MOST);
-        boolean hasText = !TextUtils.isEmpty(mTextView.getText());
-        if (titleWidth > maxTitleLength && hasText) {
-            titleWidth = maxTitleLength;
-            int widthSpec = MeasureSpec.makeMeasureSpec(titleWidth, MeasureSpec.EXACTLY);
-            mTitleView.measure(widthSpec, heightSpec);
-        }
-        if (hasText) {
-            remainingWidth -= titleWidth;
-            int widthSpec = MeasureSpec.makeMeasureSpec(remainingWidth, MeasureSpec.AT_MOST);
-            mTextView.measure(widthSpec, newHeightSpec);
-        }
-        setMeasuredDimension(totalWidth, mSingleLineHeight);
-    }
-
-    @Override
-    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
-        int childLeft = mStartMargin;
-        int childRight = childLeft + mTitleView.getMeasuredWidth();
-        int childBottom = (mSingleLineHeight + mTitleView.getMeasuredHeight()) / 2;
-        int childTop = childBottom - mTitleView.getMeasuredHeight();
-        int rtlLeft = transformForRtl(childLeft);
-        int rtlRight = transformForRtl(childRight);
-        mTitleView.layout(Math.min(rtlLeft, rtlRight), childTop, Math.max(rtlLeft, rtlRight),
-                childBottom);
-        childLeft = childRight;
-        childRight = childLeft + mTextView.getMeasuredWidth();
-        childTop = mTitleView.getTop() + mTitleView.getBaseline() - mTextView.getBaseline();
-        childBottom = childTop + mTextView.getMeasuredHeight();
-        rtlLeft = transformForRtl(childLeft);
-        rtlRight = transformForRtl(childRight);
-        mTextView.layout(Math.min(rtlLeft, rtlRight), childTop, Math.max(rtlLeft, rtlRight),
-                childBottom);
-    }
-
-    private int transformForRtl(int left) {
-        return isLayoutRtl() ? getWidth() - left : left;
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/HybridNotificationViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/HybridNotificationViewManager.java
index 987f7b8..b8adf5b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/HybridNotificationViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/HybridNotificationViewManager.java
@@ -68,9 +68,9 @@
     }
 
     private CharSequence resolveText(Notification notification) {
-        CharSequence contentText = notification.extras.getCharSequence(Notification.EXTRA_BIG_TEXT);
+        CharSequence contentText = notification.extras.getCharSequence(Notification.EXTRA_TEXT);
         if (contentText == null) {
-            contentText = notification.extras.getCharSequence(Notification.EXTRA_TEXT);
+            contentText = notification.extras.getCharSequence(Notification.EXTRA_BIG_TEXT);
         }
         return contentText;
     }
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 08da0d3..f6fc259 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java
@@ -21,6 +21,7 @@
 
 import com.android.systemui.statusbar.ExpandableNotificationRow;
 import com.android.systemui.statusbar.NotificationData;
+import com.android.systemui.statusbar.StatusBarState;
 
 import java.util.HashMap;
 import java.util.HashSet;
@@ -146,6 +147,20 @@
         return !group.children.isEmpty();
     }
 
+    public void setStatusBarState(int newState) {
+        if (mBarState == newState) {
+            return;
+        }
+        mBarState = newState;
+        if (mBarState == StatusBarState.KEYGUARD) {
+            for (NotificationGroup group : mGroupMap.values()) {
+                if (group.expanded) {
+                    setGroupExpanded(group, false);
+                }
+            }
+        }
+    }
+
     /**
      * @return whether a given notification is a child in a group which has a summary
      */
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 e51cf7ac..181e6aa 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -2233,6 +2233,10 @@
         mHandler.post(mAnimateCollapsePanels);
     }
 
+    public void postAnimateOpenPanels() {
+        mHandler.sendEmptyMessage(MSG_OPEN_SETTINGS_PANEL);
+    }
+
     public void animateCollapsePanels(int flags) {
         animateCollapsePanels(flags, false /* force */, false /* delayed */,
                 1.0f /* speedUpFactor */);
@@ -3132,10 +3136,6 @@
         mNaturalBarHeight = res.getDimensionPixelSize(
                 com.android.internal.R.dimen.status_bar_height);
 
-        mRowMinHeightLegacy =  res.getDimensionPixelSize(R.dimen.notification_min_height_legacy);
-        mRowMinHeight =  res.getDimensionPixelSize(R.dimen.notification_min_height);
-        mRowMaxHeight =  res.getDimensionPixelSize(R.dimen.notification_max_height);
-
         mMaxAllowedKeyguardNotifications = res.getInteger(R.integer.keyguard_max_notification_count);
 
         if (DEBUG) Log.v(TAG, "updateResources");
@@ -3282,6 +3282,16 @@
         return !isDeviceProvisioned() || (mDisabled1 & StatusBarManager.DISABLE_SEARCH) != 0;
     }
 
+    public void postQSRunnableDismissingKeyguard(final Runnable runnable) {
+        mHandler.post(new Runnable() {
+            @Override
+            public void run() {
+                mLeaveOpenOnKeyguardHide = true;
+                executeRunnableDismissingKeyguard(runnable, null, false, true);
+            }
+        });
+    }
+
     public void postStartActivityDismissingKeyguard(final PendingIntent intent) {
         mHandler.post(new Runnable() {
             @Override
@@ -3862,6 +3872,7 @@
             clearNotificationEffects();
         }
         mState = state;
+        mGroupManager.setStatusBarState(state);
         mFalsingManager.setStatusBarState(state);
         mStatusBarWindowManager.setStatusBarState(state);
         updateDozing();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QSTileHost.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QSTileHost.java
index 327b81e..c740b08 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QSTileHost.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QSTileHost.java
@@ -16,6 +16,7 @@
 
 package com.android.systemui.statusbar.phone;
 
+import android.app.ActivityManager;
 import android.app.PendingIntent;
 import android.content.ComponentName;
 import android.content.Context;
@@ -27,10 +28,11 @@
 import android.os.Looper;
 import android.os.Process;
 import android.os.RemoteException;
+import android.provider.Settings;
 import android.service.quicksettings.IQSService;
 import android.service.quicksettings.Tile;
+import android.text.TextUtils;
 import android.util.Log;
-
 import com.android.systemui.R;
 import com.android.systemui.qs.QSTile;
 import com.android.systemui.qs.tiles.AirplaneModeTile;
@@ -45,12 +47,6 @@
 import com.android.systemui.qs.tiles.HotspotTile;
 import com.android.systemui.qs.tiles.IntentTile;
 import com.android.systemui.qs.tiles.LocationTile;
-import com.android.systemui.qs.tiles.QAirplaneTile;
-import com.android.systemui.qs.tiles.QBluetoothTile;
-import com.android.systemui.qs.tiles.QFlashlightTile;
-import com.android.systemui.qs.tiles.QLockTile;
-import com.android.systemui.qs.tiles.QRotationLockTile;
-import com.android.systemui.qs.tiles.QWifiTile;
 import com.android.systemui.qs.tiles.RotationLockTile;
 import com.android.systemui.qs.tiles.UserTile;
 import com.android.systemui.qs.tiles.WifiTile;
@@ -166,6 +162,11 @@
     }
 
     @Override
+    public void startRunnableDismissingKeyguard(Runnable runnable) {
+        mStatusBar.postQSRunnableDismissingKeyguard(runnable);
+    }
+
+    @Override
     public void warn(String message, Throwable t) {
         // already logged
     }
@@ -176,6 +177,11 @@
     }
 
     @Override
+    public void openPanels() {
+        mStatusBar.postAnimateOpenPanels();
+    }
+
+    @Override
     public Looper getLooper() {
         return mLooper;
     }
@@ -273,8 +279,10 @@
                 if (DEBUG) Log.d(TAG, "Creating tile: " + tileSpec);
                 try {
                     QSTile<?> tile = createTile(tileSpec);
-                    tile.setTileSpec(tileSpec);
-                    newTiles.put(tileSpec, tile);
+                    if (tile != null) {
+                        tile.setTileSpec(tileSpec);
+                        newTiles.put(tileSpec, tile);
+                    }
                 } catch (Throwable t) {
                     Log.w(TAG, "Error creating tile for spec: " + tileSpec, t);
                 }
@@ -290,6 +298,14 @@
     }
 
     @Override
+    public void removeTile(String tileSpec) {
+        ArrayList<String> specs = new ArrayList<>(mTileSpecs);
+        specs.remove(tileSpec);
+        Settings.Secure.putStringForUser(mContext.getContentResolver(), TILES_SETTING,
+                TextUtils.join(",", specs), ActivityManager.getCurrentUser());
+    }
+
+    @Override
     public void updateQsTile(Tile tile) throws RemoteException {
         verifyCaller(tile.getComponentName().getPackageName());
         CustomTile customTile = getTileForComponent(tile.getComponentName());
@@ -334,13 +350,17 @@
     }
 
     public QSTile<?> createTile(String tileSpec) {
-        if (tileSpec.equals("wifi")) return new WifiTile(this, false);
-        else if (tileSpec.equals("bt")) return new BluetoothTile(this, false);
+        if (tileSpec.equals("wifi")) return WifiTile.isSupported(this)
+                ? new WifiTile(this) : null;
+        else if (tileSpec.equals("bt")) return BluetoothTile.isSupported(this)
+                ? new BluetoothTile(this) : null;
+        else if (tileSpec.equals("cell")) return CellularTile.isSupported(this)
+                ? new CellularTile(this) : null;
+        else if (tileSpec.equals("dnd")) return DndTile.isSupported(this)
+                ? new DndTile(this) : null;
         else if (tileSpec.equals("inversion")) return new ColorInversionTile(this);
-        else if (tileSpec.equals("cell")) return new CellularTile(this);
         else if (tileSpec.equals("airplane")) return new AirplaneModeTile(this);
         else if (tileSpec.equals("work")) return new WorkModeTile(this);
-        else if (tileSpec.equals("dnd")) return new DndTile(this);
         else if (tileSpec.equals("rotation")) return new RotationLockTile(this);
         else if (tileSpec.equals("flashlight")) return new FlashlightTile(this);
         else if (tileSpec.equals("location")) return new LocationTile(this);
@@ -348,16 +368,6 @@
         else if (tileSpec.equals("hotspot")) return new HotspotTile(this);
         else if (tileSpec.equals("user")) return new UserTile(this);
         else if (tileSpec.equals("battery")) return new BatteryTile(this);
-        // Detail only versions of wifi and bluetooth.
-        else if (tileSpec.equals("dwifi")) return new WifiTile(this, true);
-        else if (tileSpec.equals("dbt")) return new BluetoothTile(this, true);
-        // Quick tiles, no text.
-        else if (tileSpec.equals("qwifi")) return new QWifiTile(this);
-        else if (tileSpec.equals("qbt")) return new QBluetoothTile(this);
-        else if (tileSpec.equals("qairplane")) return new QAirplaneTile(this);
-        else if (tileSpec.equals("qrotation")) return new QRotationLockTile(this);
-        else if (tileSpec.equals("qflashlight")) return new QFlashlightTile(this);
-        else if (tileSpec.equals("qlock")) return new QLockTile(this);
         // Intent tiles.
         else if (tileSpec.startsWith(IntentTile.PREFIX)) return IntentTile.create(this,tileSpec);
         else if (tileSpec.startsWith(CustomTile.PREFIX)) return CustomTile.create(this,tileSpec);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStatusBarHeader.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStatusBarHeader.java
index cc9f5c7..26ff97a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStatusBarHeader.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStatusBarHeader.java
@@ -30,10 +30,10 @@
 import android.widget.Switch;
 import android.widget.TextView;
 import android.widget.Toast;
-
 import com.android.systemui.R;
 import com.android.systemui.qs.QSPanel;
 import com.android.systemui.qs.QSTile;
+import com.android.systemui.qs.QuickQSPanel;
 import com.android.systemui.statusbar.policy.BatteryController;
 import com.android.systemui.statusbar.policy.NextAlarmController;
 import com.android.systemui.statusbar.policy.UserInfoController;
@@ -63,7 +63,7 @@
     private boolean mListening;
     private AlarmManager.AlarmClockInfo mNextAlarm;
 
-    private QSPanel mHeaderQsPanel;
+    private QuickQSPanel mHeaderQsPanel;
 
     public QuickStatusBarHeader(Context context, AttributeSet attrs) {
         super(context, attrs);
@@ -75,7 +75,7 @@
 
         mExpandedGroup = (ViewGroup) findViewById(R.id.expanded_group);
 
-        mHeaderQsPanel = (QSPanel) findViewById(R.id.quick_qs_panel);
+        mHeaderQsPanel = (QuickQSPanel) findViewById(R.id.quick_qs_panel);
 
         mSettingsButton = (SettingsButton) findViewById(R.id.settings_button);
         mSettingsContainer = findViewById(R.id.settings_button_container);
@@ -133,9 +133,10 @@
 
     @Override
     public void setExpansion(float headerExpansionFraction) {
-        float offset = getHeight() * headerExpansionFraction;
-        mExpandedGroup.setTranslationY(offset - getHeight());
-        mHeaderQsPanel.setTranslationY(offset);
+        mExpandedGroup.setAlpha(headerExpansionFraction);
+        mExpandedGroup.setVisibility(headerExpansionFraction > 0 ? View.VISIBLE : View.INVISIBLE);
+        mHeaderQsPanel.setAlpha(1 - headerExpansionFraction);
+        mHeaderQsPanel.setVisibility(headerExpansionFraction < 1 ? View.VISIBLE : View.INVISIBLE);
     }
 
     public void setListening(boolean listening) {
@@ -190,7 +191,9 @@
                 host.getUserSwitcherController(), host.getUserInfoController(),
                 host.getKeyguardMonitor(), host.getSecurityController(),
                 host.getBatteryController());
+        mHeaderQsPanel.setQSPanelAndHeader(mQsPanel, this);
         mHeaderQsPanel.setHost(myHost);
+        mHeaderQsPanel.setMaxTiles(3);
         mHeaderQsPanel.setTiles(myHost.getTiles());
         myHost.addCallback(new QSTile.Host.Callback() {
             @Override
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 77a9871..9015a0e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationChildrenContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationChildrenContainer.java
@@ -46,7 +46,7 @@
     private final List<ExpandableNotificationRow> mChildren = new ArrayList<>();
     private final int mNotificationHeaderHeight;
     private final int mNotificationAppearDistance;
-    private final float mHeaderTopPaddingSubstraction;
+    private final int mNotificatonTopPadding;
     private final HybridNotificationViewManager mHybridViewManager;
     private final float mCollapsedBottompadding;
     private boolean mChildrenExpanded;
@@ -78,28 +78,23 @@
         mNotificationAppearDistance = getResources().getDimensionPixelSize(
                 R.dimen.notification_appear_distance);
         mNotificationHeaderHeight = getResources().getDimensionPixelSize(
-                com.android.internal.R.dimen.notification_header_height);
-        mHeaderTopPaddingSubstraction = 2 * getResources().getDisplayMetrics().density;
-        mCollapsedBottompadding = 10 * getResources().getDisplayMetrics().density;
+                com.android.internal.R.dimen.notification_content_margin_top);
+        mNotificatonTopPadding = getResources().getDimensionPixelSize(
+                R.dimen.notification_children_container_top_padding);
+        mCollapsedBottompadding = 11.5f * getResources().getDisplayMetrics().density;
         mHybridViewManager = new HybridNotificationViewManager(getContext(), this);
     }
 
     @Override
     protected void onLayout(boolean changed, int l, int t, int r, int b) {
         int childCount = mChildren.size();
-        boolean firstChild = true;
         for (int i = 0; i < childCount; i++) {
             View child = mChildren.get(i);
-            boolean viewGone = child.getVisibility() == View.GONE;
-            if (viewGone) {
+            if (child.getVisibility() == View.GONE) {
                 continue;
             }
             child.layout(0, 0, getWidth(), child.getMeasuredHeight());
-            if (!firstChild) {
-                mDividers.get(i - 1).layout(0, 0, getWidth(), mDividerHeight);
-            } else {
-                firstChild = false;
-            }
+            mDividers.get(i).layout(0, 0, getWidth(), mDividerHeight);
         }
         if (mGroupOverflowContainer != null) {
             mGroupOverflowContainer.layout(0, 0, getWidth(),
@@ -119,21 +114,17 @@
         }
         int newHeightSpec = MeasureSpec.makeMeasureSpec(ownMaxHeight, MeasureSpec.AT_MOST);
         int dividerHeightSpec = MeasureSpec.makeMeasureSpec(mDividerHeight, MeasureSpec.EXACTLY);
-        int height = mNotificationHeaderHeight;
+        int height = mNotificationHeaderHeight + mNotificatonTopPadding;
         int childCount = mChildren.size();
-        boolean firstChild = true;
         for (int i = 0; i < childCount; i++) {
             View child = mChildren.get(i);
             child.measure(widthMeasureSpec, newHeightSpec);
             height += child.getMeasuredHeight();
-            if (!firstChild) {
-                // layout the divider
-                View divider = mDividers.get(i - 1);
-                divider.measure(widthMeasureSpec, dividerHeightSpec);
-                height += mChildPadding;
-            } else {
-                firstChild = false;
-            }
+
+            // layout the divider
+            View divider = mDividers.get(i);
+            divider.measure(widthMeasureSpec, dividerHeightSpec);
+            height += mDividerHeight;
         }
         int width = MeasureSpec.getSize(widthMeasureSpec);
         if (mGroupOverflowContainer != null) {
@@ -152,11 +143,11 @@
         int newIndex = childIndex < 0 ? mChildren.size() : childIndex;
         mChildren.add(newIndex, row);
         addView(row);
-        if (mChildren.size() != 1) {
-            View divider = inflateDivider();
-            addView(divider);
-            mDividers.add(Math.max(newIndex - 1, 0), divider);
-        }
+
+        View divider = inflateDivider();
+        addView(divider);
+        mDividers.add(newIndex, divider);
+
         updateGroupOverflow();
     }
 
@@ -164,10 +155,10 @@
         int childIndex = mChildren.indexOf(row);
         mChildren.remove(row);
         removeView(row);
-        if (!mDividers.isEmpty()) {
-            View divider = mDividers.remove(Math.max(childIndex - 1, 0));
-            removeView(divider);
-        }
+
+        View divider = mDividers.remove(childIndex);
+        removeView(divider);
+
         row.setSystemChildExpanded(false);
         updateGroupOverflow();
     }
@@ -246,7 +237,10 @@
      *         in @param maxAllowedVisibleChildren
      */
     private int getIntrinsicHeight(float maxAllowedVisibleChildren) {
-        int intrinsicHeight = mNotificationHeaderHeight;;
+        int intrinsicHeight = mNotificationHeaderHeight;
+        if (mChildrenExpanded) {
+            intrinsicHeight += mNotificatonTopPadding;
+        }
         int visibleChildren = 0;
         int childCount = mChildren.size();
         for (int i = 0; i < childCount; i++) {
@@ -254,14 +248,15 @@
                 break;
             }
             ExpandableNotificationRow child = mChildren.get(i);
-            if (i == 0 && child.hasSameBgColor(mNotificationParent)) {
-                intrinsicHeight -= mHeaderTopPaddingSubstraction;
-            }
             intrinsicHeight += child.getIntrinsicHeight();
             visibleChildren++;
         }
         if (visibleChildren > 0) {
-            intrinsicHeight += (visibleChildren - 1) * mDividerHeight;
+            if (mChildrenExpanded) {
+                intrinsicHeight += visibleChildren * mDividerHeight;
+            } else {
+                intrinsicHeight += (visibleChildren - 1) * mChildPadding;
+            }
         }
         if (!mChildrenExpanded) {
             intrinsicHeight += mCollapsedBottompadding;
@@ -288,12 +283,9 @@
         for (int i = 0; i < childCount; i++) {
             ExpandableNotificationRow child = mChildren.get(i);
             if (!firstChild) {
-                // There's a divider
-                yPosition += mChildPadding;
+                yPosition += mChildrenExpanded ? mDividerHeight : mChildPadding;
             } else {
-                if (child.hasSameBgColor(mNotificationParent)) {
-                    yPosition -= mHeaderTopPaddingSubstraction;
-                }
+                yPosition += mChildrenExpanded ? mNotificatonTopPadding + mDividerHeight : 0;
                 firstChild = false;
             }
             StackViewState childState = resultState.getViewStateForView(child);
@@ -340,23 +332,18 @@
 
     public void applyState(StackScrollState state) {
         int childCount = mChildren.size();
-        boolean firstChild = true;
         ViewState tmpState = new ViewState();
         for (int i = 0; i < childCount; i++) {
             ExpandableNotificationRow child = mChildren.get(i);
             StackViewState viewState = state.getViewStateForView(child);
-            if (!firstChild) {
-                // layout the divider
-                View divider = mDividers.get(i - 1);
-                tmpState.initFrom(divider);
-                tmpState.yTranslation = (int) (viewState.yTranslation
-                        - (mChildPadding + mDividerHeight) / 2.0f);
-                tmpState.alpha = mChildrenExpanded && viewState.alpha != 0 ? 0.5f : 0;
-                state.applyViewState(divider, tmpState);
-            } else {
-                firstChild = false;
-            }
             state.applyState(child, viewState);
+
+            // layout the divider
+            View divider = mDividers.get(i);
+            tmpState.initFrom(divider);
+            tmpState.yTranslation = viewState.yTranslation - mDividerHeight;
+            tmpState.alpha = mChildrenExpanded && viewState.alpha != 0 ? 0.5f : 0;
+            state.applyViewState(divider, tmpState);
         }
         if (mGroupOverflowContainer != null) {
             state.applyViewState(mGroupOverflowContainer, mGroupOverFlowState);
@@ -375,34 +362,30 @@
             return;
         }
         int childCount = mChildren.size();
-        boolean firstChild = true;
         StackViewState sourceState = new StackViewState();
         ViewState dividerState = new ViewState();
         for (int i = 0; i < childCount; i++) {
             ExpandableNotificationRow child = mChildren.get(i);
             StackViewState viewState = state.getViewStateForView(child);
-            if (!firstChild) {
-                // layout the divider
-                View divider = mDividers.get(i - 1);
-                dividerState.initFrom(divider);
-                dividerState.yTranslation = viewState.yTranslation
-                        - (mChildPadding + mDividerHeight) / 2.0f + mNotificationAppearDistance;
-                dividerState.alpha = 0;
-                state.applyViewState(divider, dividerState);
-            } else {
-                firstChild = false;
-            }
             sourceState.copyFrom(viewState);
             sourceState.alpha = 0;
             sourceState.yTranslation += mNotificationAppearDistance;
             state.applyState(child, sourceState);
+
+            // layout the divider
+            View divider = mDividers.get(i);
+            dividerState.initFrom(divider);
+            dividerState.yTranslation = viewState.yTranslation - mDividerHeight
+                    + mNotificationAppearDistance;
+            dividerState.alpha = 0;
+            state.applyViewState(divider, dividerState);
+
         }
     }
 
     public void startAnimationToState(StackScrollState state, StackStateAnimator stateAnimator,
             boolean withDelays, long baseDelay, long duration) {
         int childCount = mChildren.size();
-        boolean firstChild = true;
         ViewState tmpState = new ViewState();
         int notGoneIndex = 0;
         for (int i = 0; i < childCount; i++) {
@@ -414,18 +397,15 @@
                     ? difference * StackStateAnimator.ANIMATION_DELAY_PER_ELEMENT_EXPAND_CHILDREN
                     : 0;
             delay += baseDelay;
-            if (!firstChild) {
-                // layout the divider
-                View divider = mDividers.get(i - 1);
-                tmpState.initFrom(divider);
-                tmpState.yTranslation = viewState.yTranslation
-                        - (mChildPadding + mDividerHeight) / 2.0f;
-                tmpState.alpha = mChildrenExpanded && viewState.alpha != 0 ? 0.5f : 0;;
-                stateAnimator.startViewAnimations(divider, tmpState, delay, duration);
-            } else {
-                firstChild = false;
-            }
             stateAnimator.startStackAnimations(child, viewState, state, -1, delay);
+
+            // layout the divider
+            View divider = mDividers.get(i);
+            tmpState.initFrom(divider);
+            tmpState.yTranslation = viewState.yTranslation - mDividerHeight;
+            tmpState.alpha = mChildrenExpanded && viewState.alpha != 0 ? 0.5f : 0;
+            stateAnimator.startViewAnimations(divider, tmpState, delay, duration);
+
             notGoneIndex++;
         }
         if (mGroupOverflowContainer != null) {
diff --git a/packages/SystemUI/src/com/android/systemui/tuner/QSPagingSwitch.java b/packages/SystemUI/src/com/android/systemui/tuner/QSPagingSwitch.java
index 04a51f0..d19a825 100644
--- a/packages/SystemUI/src/com/android/systemui/tuner/QSPagingSwitch.java
+++ b/packages/SystemUI/src/com/android/systemui/tuner/QSPagingSwitch.java
@@ -9,8 +9,8 @@
 public class QSPagingSwitch extends TunerSwitch {
 
     public static final String QS_PAGE_TILES =
-            "dwifi,dbt,dnd,cell,battery,user,rotation,flashlight,location,"
-             + "hotspot,qwifi,qbt,qlock,qflashlight,qairplane,inversion,cast";
+            "dnd,cell,battery,user,rotation,flashlight,location,"
+             + "hotspot,inversion,cast";
 
     public QSPagingSwitch(Context context, AttributeSet attrs) {
         super(context, attrs);
diff --git a/services/core/java/com/android/server/AlarmManagerService.java b/services/core/java/com/android/server/AlarmManagerService.java
index f6af942..960fb4b 100644
--- a/services/core/java/com/android/server/AlarmManagerService.java
+++ b/services/core/java/com/android/server/AlarmManagerService.java
@@ -1363,7 +1363,7 @@
 
             pw.print("  nowRTC="); pw.print(nowRTC);
             pw.print("="); pw.print(sdf.format(new Date(nowRTC)));
-            pw.print(" nowELAPSED="); TimeUtils.formatDuration(nowELAPSED, pw);
+            pw.print(" nowELAPSED="); pw.print(nowELAPSED);
             pw.println();
             pw.print("  mLastTimeChangeClockTime="); pw.print(mLastTimeChangeClockTime);
             pw.print("="); pw.println(sdf.format(new Date(mLastTimeChangeClockTime)));
@@ -1464,6 +1464,15 @@
             pw.print("  Broadcast ref count: "); pw.println(mBroadcastRefCount);
             pw.println();
 
+            if (mInFlight.size() > 0) {
+                pw.println("Outstanding deliveries:");
+                for (int i = 0; i < mInFlight.size(); i++) {
+                    pw.print("   #"); pw.print(i); pw.print(": ");
+                    pw.println(mInFlight.get(i));
+                }
+                pw.println();
+            }
+
             pw.print("  mAllowWhileIdleMinTime=");
             TimeUtils.formatDuration(mAllowWhileIdleMinTime, pw);
             pw.println();
@@ -2956,6 +2965,11 @@
                         // is a repeating alarm, so toss it
                         removeImpl(alarm.operation);
                     }
+                    // No actual delivery was possible, so the delivery tracker's
+                    // 'finished' callback won't be invoked.  We also don't need
+                    // to do any wakelock or stats tracking, so we have nothing
+                    // left to do here but go on to the next thing.
+                    return;
                 }
             } else {
                 // Direct listener callback alarm
@@ -2974,6 +2988,11 @@
                         Slog.i(TAG, "Alarm undeliverable to listener "
                                 + alarm.listener.asBinder(), e);
                     }
+                    // As in the PendingIntent.CanceledException case, delivery of the
+                    // alarm was not possible, so we have no wakelock or timeout or
+                    // stats management to do.  It threw before we posted the delayed
+                    // timeout message, so we're done here.
+                    return;
                 }
             }
 
diff --git a/services/core/java/com/android/server/MountService.java b/services/core/java/com/android/server/MountService.java
index 473eff6..184f890 100644
--- a/services/core/java/com/android/server/MountService.java
+++ b/services/core/java/com/android/server/MountService.java
@@ -2716,12 +2716,13 @@
     }
 
     @Override
-    public void createUserKey(int userId, int serialNumber) {
+    public void createUserKey(int userId, int serialNumber, boolean ephemeral) {
         enforcePermission(android.Manifest.permission.STORAGE_INTERNAL);
         waitForReady();
 
         try {
-            mCryptConnector.execute("cryptfs", "create_user_key", userId, serialNumber);
+            mCryptConnector.execute("cryptfs", "create_user_key", userId, serialNumber,
+                ephemeral ? 1 : 0);
         } catch (NativeDaemonConnectorException e) {
             throw e.rethrowAsParcelableException();
         }
@@ -2797,13 +2798,14 @@
     }
 
     @Override
-    public void prepareUserStorage(String volumeUuid, int userId, int serialNumber) {
+    public void prepareUserStorage(
+            String volumeUuid, int userId, int serialNumber, boolean ephemeral) {
         enforcePermission(android.Manifest.permission.STORAGE_INTERNAL);
         waitForReady();
 
         try {
             mCryptConnector.execute("cryptfs", "prepare_user_storage", escapeNull(volumeUuid),
-                    userId, serialNumber);
+                    userId, serialNumber, ephemeral ? 1 : 0);
         } catch (NativeDaemonConnectorException e) {
             throw e.rethrowAsParcelableException();
         }
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index 2742c65..4348913 100755
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -1240,8 +1240,9 @@
                     try {
                         // Before going further -- if this app is not allowed to run in the
                         // background, then at this point we aren't going to let it period.
-                        if (!mAm.checkAllowBackgroundLocked(sInfo.applicationInfo.uid,
-                                sInfo.packageName, callingPid)) {
+                        final int allowed = mAm.checkAllowBackgroundLocked(
+                                sInfo.applicationInfo.uid, sInfo.packageName, callingPid);
+                        if (allowed != ActivityManager.APP_START_MODE_NORMAL) {
                             Slog.w(TAG, "Background execution not allowed: service "
                                     + r.intent + " to " + name.flattenToShortString()
                                     + " from pid=" + callingPid + " uid=" + callingUid
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index f22062c..507233f 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -7463,13 +7463,11 @@
 
     public int getAppStartMode(int uid, String packageName) {
         synchronized (this) {
-            boolean bg = checkAllowBackgroundLocked(uid, packageName, -1);
-            return bg ? ActivityManager.APP_START_MODE_NORMAL
-                    : ActivityManager.APP_START_MODE_DISABLED;
+            return checkAllowBackgroundLocked(uid, packageName, -1);
         }
     }
 
-    boolean checkAllowBackgroundLocked(int uid, String packageName, int callingPid) {
+    int checkAllowBackgroundLocked(int uid, String packageName, int callingPid) {
         UidRecord uidRec = mActiveUids.get(uid);
         if (uidRec == null || uidRec.idle) {
             if (callingPid >= 0) {
@@ -7480,15 +7478,15 @@
                 if (proc != null && proc.curProcState < ActivityManager.PROCESS_STATE_RECEIVER) {
                     // Whoever is instigating this is in the foreground, so we will allow it
                     // to go through.
-                    return true;
+                    return ActivityManager.APP_START_MODE_NORMAL;
                 }
             }
             if (mAppOpsService.noteOperation(AppOpsManager.OP_RUN_IN_BACKGROUND, uid, packageName)
                     != AppOpsManager.MODE_ALLOWED) {
-                return false;
+                return ActivityManager.APP_START_MODE_DELAYED;
             }
         }
-        return true;
+        return ActivityManager.APP_START_MODE_NORMAL;
     }
 
     private ProviderInfo getProviderInfoLocked(String authority, int userHandle) {
diff --git a/services/core/java/com/android/server/am/ActivityRecord.java b/services/core/java/com/android/server/am/ActivityRecord.java
index 66371d1..d215fc6 100755
--- a/services/core/java/com/android/server/am/ActivityRecord.java
+++ b/services/core/java/com/android/server/am/ActivityRecord.java
@@ -180,6 +180,7 @@
     private boolean inHistory;  // are we in the history stack?
     final ActivityStackSupervisor mStackSupervisor;
     boolean mStartingWindowShown = false;
+    boolean mUpdateTaskThumbnailWhenHidden;
     ActivityContainer mInitialActivityContainer;
 
     TaskDescription taskDescription; // the recents information for this activity
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index 60f26e2..b052d17 100644
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -925,7 +925,7 @@
         final ActivityRecord next = mStackSupervisor.topRunningActivityLocked();
         if (mService.mHasRecents
                 && (next == null || next.noDisplay || next.task != prev.task || uiSleeping)) {
-            prev.updateThumbnailLocked(screenshotActivitiesLocked(prev), null);
+            prev.mUpdateTaskThumbnailWhenHidden = true;
         }
         stopFullyDrawnTraceIfNeeded();
 
@@ -1221,6 +1221,10 @@
 
     private void setVisible(ActivityRecord r, boolean visible) {
         r.visible = visible;
+        if (!visible && r.mUpdateTaskThumbnailWhenHidden) {
+            r.updateThumbnailLocked(r.task.stack.screenshotActivitiesLocked(r), null);
+            r.mUpdateTaskThumbnailWhenHidden = false;
+        }
         mWindowManager.setAppVisibility(r.appToken, visible);
         final ArrayList<ActivityContainer> containers = r.mChildContainers;
         for (int containerNdx = containers.size() - 1; containerNdx >= 0; --containerNdx) {
diff --git a/services/core/java/com/android/server/am/BroadcastQueue.java b/services/core/java/com/android/server/am/BroadcastQueue.java
index b160981..2fb71c3 100644
--- a/services/core/java/com/android/server/am/BroadcastQueue.java
+++ b/services/core/java/com/android/server/am/BroadcastQueue.java
@@ -562,8 +562,9 @@
             skip = true;
         }
         if (!skip) {
-            if (!mService.checkAllowBackgroundLocked(filter.receiverList.uid, filter.packageName,
-                    -1)) {
+            final int allowed = mService.checkAllowBackgroundLocked(filter.receiverList.uid,
+                    filter.packageName, -1);
+            if (allowed == ActivityManager.APP_START_MODE_DISABLED) {
                 Slog.w(TAG, "Background execution not allowed: receiving "
                         + r.intent
                         + " to " + filter.receiverList.app
@@ -1013,15 +1014,6 @@
                 skip = true;
             }
             if (!skip) {
-                if (!mService.checkAllowBackgroundLocked(info.activityInfo.applicationInfo.uid,
-                        info.activityInfo.packageName, -1)) {
-                    Slog.w(TAG, "Background execution not allowed: receiving "
-                            + r.intent + " to "
-                            + component.flattenToShortString());
-                    skip = true;
-                }
-            }
-            if (!skip) {
                 skip = !mService.mIntentFirewall.checkBroadcast(r.intent, r.callingUid,
                         r.callingPid, r.resolvedType, info.activityInfo.applicationInfo.uid);
             }
@@ -1083,6 +1075,28 @@
                 }
             }
 
+            String targetProcess = info.activityInfo.processName;
+            ProcessRecord app = mService.getProcessRecordLocked(targetProcess,
+                    info.activityInfo.applicationInfo.uid, false);
+            if (!skip) {
+                final int allowed = mService.checkAllowBackgroundLocked(
+                        info.activityInfo.applicationInfo.uid, info.activityInfo.packageName, -1);
+                if (allowed != ActivityManager.APP_START_MODE_NORMAL) {
+                    // We won't allow this receiver to be launched if the app has been
+                    // completely disabled from launches, or it is delayed and the broadcast
+                    // was not explicitly sent to it and this would result in a new process
+                    // for it being created.
+                    if (allowed == ActivityManager.APP_START_MODE_DISABLED
+                            || (r.intent.getComponent() == null
+                            && r.intent.getPackage() == null && app == null)) {
+                        Slog.w(TAG, "Background execution not allowed: receiving "
+                                + r.intent + " to "
+                                + component.flattenToShortString());
+                        skip = true;
+                    }
+                }
+            }
+
             if (skip) {
                 if (DEBUG_BROADCAST)  Slog.v(TAG_BROADCAST,
                         "Skipping delivery of ordered [" + mQueueName + "] "
@@ -1095,7 +1109,6 @@
             }
 
             r.state = BroadcastRecord.APP_RECEIVE;
-            String targetProcess = info.activityInfo.processName;
             r.curComponent = component;
             final int receiverUid = info.activityInfo.applicationInfo.uid;
             // If it's a singleton, it needs to be the same app or a special app
@@ -1126,8 +1139,6 @@
             }
 
             // Is this receiver's application already running?
-            ProcessRecord app = mService.getProcessRecordLocked(targetProcess,
-                    info.activityInfo.applicationInfo.uid, false);
             if (app != null && app.thread != null) {
                 try {
                     app.addPackage(info.activityInfo.packageName,
diff --git a/services/core/java/com/android/server/pm/EphemeralApplicationRegistry.java b/services/core/java/com/android/server/pm/EphemeralApplicationRegistry.java
new file mode 100644
index 0000000..8786350
--- /dev/null
+++ b/services/core/java/com/android/server/pm/EphemeralApplicationRegistry.java
@@ -0,0 +1,698 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm;
+
+import android.content.Context;
+import android.content.pm.EphemeralApplicationInfo;
+import android.content.pm.PackageParser;
+import android.content.pm.PackageUserState;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.graphics.Canvas;
+import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.Drawable;
+import android.os.Binder;
+import android.os.Environment;
+import android.provider.Settings;
+import android.util.AtomicFile;
+import android.util.Slog;
+import android.util.SparseArray;
+import android.util.Xml;
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.util.ArrayUtils;
+import com.android.internal.util.XmlUtils;
+import libcore.io.IoUtils;
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlSerializer;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * This class is a part of the package manager service that is responsible
+ * for managing data associated with ephemeral apps such as cached uninstalled
+ * ephemeral apps and ephemeral apps' cookies.
+ */
+class EphemeralApplicationRegistry {
+    private static final boolean DEBUG = false;
+
+    private static final String LOG_TAG = "EphemeralAppRegistry";
+
+    private static final long DEFAULT_UNINSTALLED_EPHEMERAL_APP_CACHE_DURATION_MILLIS =
+            DEBUG ? 60 * 1000L /* one min */ : 30 * 24 * 60 * 60 * 1000L; /* one month */
+
+    private final static char[] HEX_ARRAY = "0123456789ABCDEF".toCharArray();
+
+    private static final String EPHEMERAL_APPS_FOLDER = "ephemeral";
+    private static final String EPHEMERAL_APP_ICON_FILE = "icon.png";
+    private static final String EPHEMERAL_APP_COOKIE_FILE_PREFIX = "cookie_";
+    private static final String EPHEMERAL_APP_COOKIE_FILE_SIFFIX = ".dat";
+    private static final String EPHEMERAL_APP_METADATA_FILE = "metadata.xml";
+
+    private static final String TAG_PACKAGE = "package";
+    private static final String TAG_PERMS = "perms";
+    private static final String TAG_PERM = "perm";
+
+    private static final String ATTR_LABEL = "label";
+    private static final String ATTR_NAME = "name";
+    private static final String ATTR_GRANTED = "granted";
+
+    private final PackageManagerService mService;
+
+    @GuardedBy("mService.mPackages")
+    private SparseArray<List<UninstalledEphemeralAppState>> mUninstalledEphemeralApps;
+
+    public EphemeralApplicationRegistry(PackageManagerService service) {
+        mService = service;
+    }
+
+    public byte[] getEphemeralApplicationCookieLPw(String packageName, int userId) {
+        pruneUninstalledEphemeralAppsLPw(userId);
+
+        File cookieFile = peekEphemeralCookieFile(packageName, userId);
+        if (cookieFile != null && cookieFile.exists()) {
+            try {
+                return IoUtils.readFileAsByteArray(cookieFile.toString());
+            } catch (IOException e) {
+                Slog.w(LOG_TAG, "Error reading cookie file: " + cookieFile);
+            }
+        }
+        return null;
+    }
+
+    public boolean setEphemeralApplicationCookieLPw(String packageName,
+            byte[] cookie, int userId) {
+        pruneUninstalledEphemeralAppsLPw(userId);
+
+        PackageParser.Package pkg = mService.mPackages.get(packageName);
+        if (pkg == null) {
+            return false;
+        }
+
+        if (!isValidCookie(mService.mContext, cookie)) {
+            return false;
+        }
+
+        File appDir = getEphemeralApplicationDir(pkg.packageName, userId);
+        if (!appDir.exists() && !appDir.mkdirs()) {
+            return false;
+        }
+
+        File cookieFile = computeEphemeralCookieFile(pkg, userId);
+        if (cookieFile.exists() && !cookieFile.delete()) {
+            return false;
+        }
+
+        try (FileOutputStream fos = new FileOutputStream(cookieFile)) {
+            fos.write(cookie, 0, cookie.length);
+        } catch (IOException e) {
+            Slog.w(LOG_TAG, "Error writing cookie file: " + cookieFile);
+            return false;
+        }
+        return true;
+    }
+
+    public Bitmap getEphemeralApplicationIconLPw(String packageName, int userId) {
+        pruneUninstalledEphemeralAppsLPw(userId);
+
+        File iconFile = new File(getEphemeralApplicationDir(packageName, userId),
+                EPHEMERAL_APP_ICON_FILE);
+        if (iconFile.exists()) {
+            return BitmapFactory.decodeFile(iconFile.toString());
+        }
+        return null;
+    }
+
+    public List<EphemeralApplicationInfo> getEphemeralApplicationsLPw(int userId) {
+        pruneUninstalledEphemeralAppsLPw(userId);
+
+        List<EphemeralApplicationInfo> result = getInstalledEphemeralApplicationsLPr(userId);
+        result.addAll(getUninstalledEphemeralApplicationsLPr(userId));
+        return result;
+    }
+
+    public void onPackageInstalledLPw(PackageParser.Package pkg) {
+        PackageSetting ps = (PackageSetting) pkg.mExtras;
+        if (ps == null) {
+            return;
+        }
+        for (int userId : UserManagerService.getInstance().getUserIds()) {
+            pruneUninstalledEphemeralAppsLPw(userId);
+
+            // Ignore not installed apps
+            if (mService.mPackages.get(pkg.packageName) == null || !ps.getInstalled(userId)) {
+                continue;
+            }
+
+            // Propagate permissions before removing any state
+            propagateEphemeralAppPermissionsIfNeeded(pkg, userId);
+
+            // Remove the in-memory state
+            if (mUninstalledEphemeralApps != null) {
+                List<UninstalledEphemeralAppState> uninstalledAppStates =
+                        mUninstalledEphemeralApps.get(userId);
+                if (uninstalledAppStates != null) {
+                    final int appCount = uninstalledAppStates.size();
+                    for (int i = 0; i < appCount; i++) {
+                        UninstalledEphemeralAppState uninstalledAppState =
+                                uninstalledAppStates.get(i);
+                        if (uninstalledAppState.mEphemeralApplicationInfo
+                                .getPackageName().equals(pkg.packageName)) {
+                            uninstalledAppStates.remove(i);
+                            break;
+                        }
+                    }
+                }
+            }
+
+            // Remove the on-disk state except the cookie
+            File ephemeralAppDir = getEphemeralApplicationDir(pkg.packageName, userId);
+            new File(ephemeralAppDir, EPHEMERAL_APP_METADATA_FILE).delete();
+            new File(ephemeralAppDir, EPHEMERAL_APP_ICON_FILE).delete();
+
+            // If app signature changed - wipe the cookie
+            File currentCookieFile = peekEphemeralCookieFile(pkg.packageName, userId);
+            if (currentCookieFile == null) {
+                continue;
+            }
+            File expectedCookeFile = computeEphemeralCookieFile(pkg, userId);
+            if (!currentCookieFile.equals(expectedCookeFile)) {
+                Slog.i(LOG_TAG, "Signature for package " + pkg.packageName
+                        + " changed - dropping cookie");
+                currentCookieFile.delete();
+            }
+        }
+    }
+
+    public void onPackageUninstalledLPw(PackageParser.Package pkg) {
+        PackageSetting ps = (PackageSetting) pkg.mExtras;
+        if (ps == null) {
+            return;
+        }
+        for (int userId : UserManagerService.getInstance().getUserIds()) {
+            pruneUninstalledEphemeralAppsLPw(userId);
+
+            if (mService.mPackages.get(pkg.packageName) != null && ps.getInstalled(userId)) {
+                continue;
+            }
+
+            if (pkg.applicationInfo.isEphemeralApp()) {
+                // Add a record for an uninstalled ephemeral app
+                addUninstalledEphemeralAppLPw(pkg, userId);
+            } else {
+                // Deleting an app prunes all ephemeral state such as cookie
+                deleteDir(getEphemeralApplicationDir(pkg.packageName, userId));
+            }
+        }
+    }
+
+    public void onUserRemovedLPw(int userId) {
+        if (mUninstalledEphemeralApps != null) {
+            mUninstalledEphemeralApps.remove(userId);
+        }
+        deleteDir(getEphemeralApplicationsDir(userId));
+    }
+
+    private void addUninstalledEphemeralAppLPw(PackageParser.Package pkg, int userId) {
+        EphemeralApplicationInfo uninstalledApp = createEphemeralAppInfoForPackage(pkg, userId);
+        if (uninstalledApp == null) {
+            return;
+        }
+        if (mUninstalledEphemeralApps == null) {
+            mUninstalledEphemeralApps = new SparseArray<>();
+        }
+        List<UninstalledEphemeralAppState> uninstalledAppStates =
+                mUninstalledEphemeralApps.get(userId);
+        if (uninstalledAppStates == null) {
+            uninstalledAppStates = new ArrayList<>();
+            mUninstalledEphemeralApps.put(userId, uninstalledAppStates);
+        }
+        UninstalledEphemeralAppState uninstalledAppState = new UninstalledEphemeralAppState(
+                uninstalledApp, System.currentTimeMillis());
+        uninstalledAppStates.add(uninstalledAppState);
+
+        writeUninstalledEphemeralAppMetadata(uninstalledApp, userId);
+        writeEphemeralApplicationIconLPw(pkg, userId);
+    }
+
+    private void writeEphemeralApplicationIconLPw(PackageParser.Package pkg, int userId) {
+        File appDir = getEphemeralApplicationDir(pkg.packageName, userId);
+        if (!appDir.exists()) {
+            return;
+        }
+
+        Drawable icon = pkg.applicationInfo.loadIcon(mService.mContext.getPackageManager());
+
+        final Bitmap bitmap;
+        if (icon instanceof BitmapDrawable) {
+            bitmap = ((BitmapDrawable) icon).getBitmap();
+        } else  {
+            bitmap = Bitmap.createBitmap(icon.getIntrinsicWidth(),
+                    icon.getIntrinsicHeight(), Bitmap.Config.ARGB_8888);
+            Canvas canvas = new Canvas(bitmap);
+            icon.draw(canvas);
+        }
+
+        File iconFile = new File(getEphemeralApplicationDir(pkg.packageName, userId),
+                EPHEMERAL_APP_ICON_FILE);
+
+        try (FileOutputStream out = new FileOutputStream(iconFile)) {
+            bitmap.compress(Bitmap.CompressFormat.PNG, 100, out);
+        } catch (Exception e) {
+            Slog.e(LOG_TAG, "Error writing ephemeral app icon", e);
+        }
+    }
+
+    private void pruneUninstalledEphemeralAppsLPw(int userId) {
+        final long maxCacheDurationMillis = Settings.Global.getLong(
+                mService.mContext.getContentResolver(),
+                Settings.Global.UNINSTALLED_EPHEMERAL_APP_CACHE_DURATION_MILLIS,
+                DEFAULT_UNINSTALLED_EPHEMERAL_APP_CACHE_DURATION_MILLIS);
+
+        // Prune in-memory state
+        if (mUninstalledEphemeralApps != null) {
+            List<UninstalledEphemeralAppState> uninstalledAppStates =
+                    mUninstalledEphemeralApps.get(userId);
+            if (uninstalledAppStates != null) {
+                final int appCount = uninstalledAppStates.size();
+                for (int j = appCount - 1; j >= 0; j--) {
+                    UninstalledEphemeralAppState uninstalledAppState = uninstalledAppStates.get(j);
+                    final long elapsedCachingMillis = System.currentTimeMillis()
+                            - uninstalledAppState.mTimestamp;
+                    if (elapsedCachingMillis > maxCacheDurationMillis) {
+                        uninstalledAppStates.remove(j);
+                    }
+                }
+                if (uninstalledAppStates.isEmpty()) {
+                    mUninstalledEphemeralApps.remove(userId);
+                }
+            }
+        }
+
+        // Prune on-disk state
+        File ephemeralAppsDir = getEphemeralApplicationsDir(userId);
+        if (!ephemeralAppsDir.exists()) {
+            return;
+        }
+        File[] files = ephemeralAppsDir.listFiles();
+        if (files == null) {
+            return;
+        }
+        for (File ephemeralDir : files) {
+            if (!ephemeralDir.isDirectory()) {
+                continue;
+            }
+
+            File metadataFile = new File(ephemeralDir, EPHEMERAL_APP_METADATA_FILE);
+            if (!metadataFile.exists()) {
+                continue;
+            }
+
+            final long elapsedCachingMillis = System.currentTimeMillis()
+                    - metadataFile.lastModified();
+            if (elapsedCachingMillis > maxCacheDurationMillis) {
+                deleteDir(ephemeralDir);
+            }
+        }
+    }
+
+    private List<EphemeralApplicationInfo> getInstalledEphemeralApplicationsLPr(int userId) {
+        List<EphemeralApplicationInfo> result = null;
+
+        final int packageCount = mService.mPackages.size();
+        for (int i = 0; i < packageCount; i++) {
+            PackageParser.Package pkg = mService.mPackages.valueAt(i);
+            if (!pkg.applicationInfo.isEphemeralApp()) {
+                continue;
+            }
+            EphemeralApplicationInfo info = createEphemeralAppInfoForPackage(pkg, userId);
+            if (info == null) {
+                continue;
+            }
+            if (result == null) {
+                result = new ArrayList<>();
+            }
+            result.add(info);
+        }
+
+        return result;
+    }
+
+    private EphemeralApplicationInfo createEphemeralAppInfoForPackage(
+            PackageParser.Package pkg, int userId) {
+        PackageSetting ps = (PackageSetting) pkg.mExtras;
+        if (ps == null) {
+            return null;
+        }
+        PackageUserState userState = ps.readUserState(userId);
+        if (userState == null || !userState.installed || userState.hidden) {
+            return null;
+        }
+
+        String[] requestedPermissions = new String[pkg.requestedPermissions.size()];
+        pkg.requestedPermissions.toArray(requestedPermissions);
+
+        Set<String> permissions = ps.getPermissionsState().getPermissions(userId);
+        String[] grantedPermissions = new String[permissions.size()];
+        permissions.toArray(grantedPermissions);
+
+        return new EphemeralApplicationInfo(pkg.applicationInfo,
+                requestedPermissions, grantedPermissions);
+    }
+
+    private List<EphemeralApplicationInfo> getUninstalledEphemeralApplicationsLPr(int userId) {
+        List<UninstalledEphemeralAppState> uninstalledAppStates =
+                getUninstalledEphemeralAppStatesLPr(userId);
+        if (uninstalledAppStates == null || uninstalledAppStates.isEmpty()) {
+            return Collections.emptyList();
+        }
+
+        List<EphemeralApplicationInfo> uninstalledApps = new ArrayList<>();
+        final int stateCount = uninstalledAppStates.size();
+        for (int i = 0; i < stateCount; i++) {
+            UninstalledEphemeralAppState uninstalledAppState = uninstalledAppStates.get(i);
+            uninstalledApps.add(uninstalledAppState.mEphemeralApplicationInfo);
+        }
+        return uninstalledApps;
+    }
+
+    private void propagateEphemeralAppPermissionsIfNeeded(PackageParser.Package pkg, int userId) {
+        EphemeralApplicationInfo appInfo = getOrParseUninstalledEphemeralAppInfo(pkg.packageName, userId);
+        if (appInfo == null) {
+            return;
+        }
+        if (ArrayUtils.isEmpty(appInfo.getGrantedPermissions())) {
+            return;
+        }
+        final long identity = Binder.clearCallingIdentity();
+        try {
+            for (String grantedPermission : appInfo.getGrantedPermissions()) {
+                mService.grantRuntimePermission(pkg.packageName, grantedPermission, userId);
+            }
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
+    }
+
+    private EphemeralApplicationInfo getOrParseUninstalledEphemeralAppInfo(String packageName,
+            int userId) {
+        if (mUninstalledEphemeralApps != null) {
+            List<UninstalledEphemeralAppState> uninstalledAppStates =
+                    mUninstalledEphemeralApps.get(userId);
+            if (uninstalledAppStates != null) {
+                final int appCount = uninstalledAppStates.size();
+                for (int i = 0; i < appCount; i++) {
+                    UninstalledEphemeralAppState uninstalledAppState = uninstalledAppStates.get(i);
+                    if (uninstalledAppState.mEphemeralApplicationInfo
+                            .getPackageName().equals(packageName)) {
+                        return uninstalledAppState.mEphemeralApplicationInfo;
+                    }
+                }
+            }
+        }
+
+        File metadataFile = new File(getEphemeralApplicationDir(packageName, userId),
+                EPHEMERAL_APP_METADATA_FILE);
+        UninstalledEphemeralAppState uninstalledAppState = parseMetadataFile(metadataFile);
+        if (uninstalledAppState == null) {
+            return null;
+        }
+
+        return uninstalledAppState.mEphemeralApplicationInfo;
+    }
+
+    private List<UninstalledEphemeralAppState> getUninstalledEphemeralAppStatesLPr(int userId) {
+        List<UninstalledEphemeralAppState> uninstalledAppStates = null;
+        if (mUninstalledEphemeralApps != null) {
+            uninstalledAppStates = mUninstalledEphemeralApps.get(userId);
+            if (uninstalledAppStates != null) {
+                return uninstalledAppStates;
+            }
+        }
+
+        File ephemeralAppsDir = getEphemeralApplicationsDir(userId);
+        if (ephemeralAppsDir.exists()) {
+            File[] files = ephemeralAppsDir.listFiles();
+            if (files != null) {
+                for (File ephemeralDir : files) {
+                    if (!ephemeralDir.isDirectory()) {
+                        continue;
+                    }
+                    File metadataFile = new File(ephemeralDir,
+                            EPHEMERAL_APP_METADATA_FILE);
+                    UninstalledEphemeralAppState uninstalledAppState =
+                            parseMetadataFile(metadataFile);
+                    if (uninstalledAppState == null) {
+                        continue;
+                    }
+                    if (uninstalledAppStates == null) {
+                        uninstalledAppStates = new ArrayList<>();
+                    }
+                    uninstalledAppStates.add(uninstalledAppState);
+                }
+            }
+        }
+
+        if (uninstalledAppStates != null) {
+            if (mUninstalledEphemeralApps == null) {
+                mUninstalledEphemeralApps = new SparseArray<>();
+            }
+            mUninstalledEphemeralApps.put(userId, uninstalledAppStates);
+        }
+
+        return uninstalledAppStates;
+    }
+
+    private static boolean isValidCookie(Context context, byte[] cookie) {
+        if (ArrayUtils.isEmpty(cookie)) {
+            return true;
+        }
+        return cookie.length <= context.getPackageManager().getEphemeralCookieMaxSizeBytes();
+    }
+
+    private static UninstalledEphemeralAppState parseMetadataFile(File metadataFile) {
+        if (!metadataFile.exists()) {
+            return null;
+        }
+        FileInputStream in;
+        try {
+            in = new AtomicFile(metadataFile).openRead();
+        } catch (FileNotFoundException fnfe) {
+            Slog.i(LOG_TAG, "No ephemeral metadata file");
+            return null;
+        }
+
+        final File ephemeralDir = metadataFile.getParentFile();
+        final long timestamp = metadataFile.lastModified();
+        final String packageName = ephemeralDir.getName();
+
+        try {
+            XmlPullParser parser = Xml.newPullParser();
+            parser.setInput(in, StandardCharsets.UTF_8.name());
+            return new UninstalledEphemeralAppState(
+                    parseMetadata(parser, packageName), timestamp);
+        } catch (XmlPullParserException | IOException e) {
+            throw new IllegalStateException("Failed parsing ephemeral"
+                    + " metadata file: " + metadataFile, e);
+        } finally {
+            IoUtils.closeQuietly(in);
+        }
+    }
+
+    private static File computeEphemeralCookieFile(PackageParser.Package pkg, int userId) {
+        File appDir = getEphemeralApplicationDir(pkg.packageName, userId);
+        String cookieFile = EPHEMERAL_APP_COOKIE_FILE_PREFIX + computePackageCertDigest(pkg)
+                + EPHEMERAL_APP_COOKIE_FILE_SIFFIX;
+        return new File(appDir, cookieFile);
+    }
+
+    private static File peekEphemeralCookieFile(String packageName, int userId) {
+        File appDir = getEphemeralApplicationDir(packageName, userId);
+        if (!appDir.exists()) {
+            return null;
+        }
+        for (File file : appDir.listFiles()) {
+            if (!file.isDirectory()
+                    && file.getName().startsWith(EPHEMERAL_APP_COOKIE_FILE_PREFIX)
+                    && file.getName().endsWith(EPHEMERAL_APP_COOKIE_FILE_SIFFIX)) {
+                return file;
+            }
+        }
+        return null;
+    }
+
+    private static EphemeralApplicationInfo parseMetadata(XmlPullParser parser, String packageName)
+            throws IOException, XmlPullParserException {
+        final int outerDepth = parser.getDepth();
+        while (XmlUtils.nextElementWithin(parser, outerDepth)) {
+            if (TAG_PACKAGE.equals(parser.getName())) {
+                return parsePackage(parser, packageName);
+            }
+        }
+        return null;
+    }
+
+    private static EphemeralApplicationInfo parsePackage(XmlPullParser parser, String packageName)
+            throws IOException, XmlPullParserException {
+        String label = parser.getAttributeValue(null, ATTR_LABEL);
+
+        List<String> outRequestedPermissions = new ArrayList<>();
+        List<String> outGrantedPermissions = new ArrayList<>();
+
+        final int outerDepth = parser.getDepth();
+        while (XmlUtils.nextElementWithin(parser, outerDepth)) {
+            if (TAG_PERMS.equals(parser.getName())) {
+                parsePermissions(parser, outRequestedPermissions, outGrantedPermissions);
+            }
+        }
+
+        String[] requestedPermissions = new String[outRequestedPermissions.size()];
+        outRequestedPermissions.toArray(requestedPermissions);
+
+        String[] grantedPermissions = new String[outGrantedPermissions.size()];
+        outGrantedPermissions.toArray(grantedPermissions);
+
+        return new EphemeralApplicationInfo(packageName, label,
+                requestedPermissions, grantedPermissions);
+    }
+
+    private static void parsePermissions(XmlPullParser parser, List<String> outRequestedPermissions,
+            List<String> outGrantedPermissions) throws IOException, XmlPullParserException {
+        final int outerDepth = parser.getDepth();
+        while (XmlUtils.nextElementWithin(parser,outerDepth)) {
+            if (TAG_PERM.equals(parser.getName())) {
+                String permission = XmlUtils.readStringAttribute(parser, ATTR_NAME);
+                outRequestedPermissions.add(permission);
+                if (XmlUtils.readBooleanAttribute(parser, ATTR_GRANTED)) {
+                    outGrantedPermissions.add(permission);
+                }
+            }
+        }
+    }
+
+    private void writeUninstalledEphemeralAppMetadata(
+            EphemeralApplicationInfo ephemeralApp, int userId) {
+        File appDir = getEphemeralApplicationDir(ephemeralApp.getPackageName(), userId);
+        if (!appDir.exists() && !appDir.mkdirs()) {
+            return;
+        }
+
+        File metadataFile = new File(appDir, EPHEMERAL_APP_METADATA_FILE);
+
+        AtomicFile destination = new AtomicFile(metadataFile);
+        FileOutputStream out = null;
+        try {
+            out = destination.startWrite();
+
+            XmlSerializer serializer = Xml.newSerializer();
+            serializer.setOutput(out, StandardCharsets.UTF_8.name());
+            serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);
+
+            serializer.startDocument(null, true);
+
+            serializer.startTag(null, TAG_PACKAGE);
+            serializer.attribute(null, ATTR_LABEL, ephemeralApp.loadLabel(
+                    mService.mContext.getPackageManager()).toString());
+
+            serializer.startTag(null, TAG_PERMS);
+            for (String permission : ephemeralApp.getRequestedPermissions()) {
+                serializer.startTag(null, TAG_PERM);
+                serializer.attribute(null, ATTR_NAME, permission);
+                if (ArrayUtils.contains(ephemeralApp.getGrantedPermissions(), permission)) {
+                    serializer.attribute(null, ATTR_GRANTED, String.valueOf(true));
+                }
+                serializer.endTag(null, TAG_PERM);
+            }
+            serializer.endTag(null, TAG_PERMS);
+
+            serializer.endTag(null, TAG_PACKAGE);
+
+            serializer.endDocument();
+            destination.finishWrite(out);
+        } catch (Throwable t) {
+            Slog.wtf(LOG_TAG, "Failed to write ephemeral state, restoring backup", t);
+            destination.failWrite(out);
+        } finally {
+            IoUtils.closeQuietly(out);
+        }
+    }
+
+    private static String computePackageCertDigest(PackageParser.Package pkg) {
+        MessageDigest messageDigest;
+        try {
+            messageDigest = MessageDigest.getInstance("SHA256");
+        } catch (NoSuchAlgorithmException e) {
+            /* can't happen */
+            return null;
+        }
+
+        messageDigest.update(pkg.mSignatures[0].toByteArray());
+
+        final byte[] digest = messageDigest.digest();
+        final int digestLength = digest.length;
+        final int charCount = 2 * digestLength;
+
+        final char[] chars = new char[charCount];
+        for (int i = 0; i < digestLength; i++) {
+            final int byteHex = digest[i] & 0xFF;
+            chars[i * 2] = HEX_ARRAY[byteHex >>> 4];
+            chars[i * 2 + 1] = HEX_ARRAY[byteHex & 0x0F];
+        }
+        return new String(chars);
+    }
+
+    private static File getEphemeralApplicationsDir(int userId) {
+        return new File(Environment.getUserSystemDirectory(userId),
+                EPHEMERAL_APPS_FOLDER);
+    }
+
+    private static File getEphemeralApplicationDir(String packageName, int userId) {
+        return new File (getEphemeralApplicationsDir(userId), packageName);
+    }
+
+    private static void deleteDir(File dir) {
+        File[] files = dir.listFiles();
+        if (files != null) {
+            for (File file : dir.listFiles()) {
+                deleteDir(file);
+            }
+        }
+        dir.delete();
+    }
+
+    private static final class UninstalledEphemeralAppState {
+        final EphemeralApplicationInfo mEphemeralApplicationInfo;
+        final long mTimestamp;
+
+        public UninstalledEphemeralAppState(EphemeralApplicationInfo ephemeralApp,
+                long timestamp) {
+            mEphemeralApplicationInfo = ephemeralApp;
+            mTimestamp = timestamp;
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index d94e5f4..2f8157e 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -105,6 +105,7 @@
 import android.content.pm.ActivityInfo;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.AppsQueryHelper;
+import android.content.pm.EphemeralApplicationInfo;
 import android.content.pm.FeatureInfo;
 import android.content.pm.IOnPermissionsChangeListener;
 import android.content.pm.IPackageDataObserver;
@@ -144,6 +145,7 @@
 import android.content.pm.VerifierDeviceIdentity;
 import android.content.pm.VerifierInfo;
 import android.content.res.Resources;
+import android.graphics.Bitmap;
 import android.hardware.display.DisplayManager;
 import android.net.Uri;
 import android.os.Debug;
@@ -200,6 +202,7 @@
 import android.util.Xml;
 import android.view.Display;
 
+import com.android.internal.annotations.GuardedBy;
 import dalvik.system.DexFile;
 import dalvik.system.VMRuntime;
 
@@ -207,7 +210,6 @@
 import libcore.util.EmptyArray;
 
 import com.android.internal.R;
-import com.android.internal.annotations.GuardedBy;
 import com.android.internal.app.EphemeralResolveInfo;
 import com.android.internal.app.IMediaContainerService;
 import com.android.internal.app.ResolverActivity;
@@ -512,6 +514,8 @@
     // If a recursive restorecon of /data/data/<pkg> is needed.
     private boolean mShouldRestoreconData = SELinuxMMAC.shouldRestorecon();
 
+    private final EphemeralApplicationRegistry mEphemeralApplicationRegistry;
+
     public static final class SharedLibraryEntry {
         public final String path;
         public final String apk;
@@ -1396,6 +1400,10 @@
                                         args.installGrantPermissions);
                             }
 
+                            synchronized (mPackages) {
+                                mEphemeralApplicationRegistry.onPackageInstalledLPw(res.pkg);
+                            }
+
                             // Determine the set of users who are adding this
                             // package for the first time vs. those who are seeing
                             // an update.
@@ -2405,6 +2413,8 @@
                 mEphemeralInstallerComponent = null;
                 mEphemeralResolverConnection = null;
             }
+
+            mEphemeralApplicationRegistry = new EphemeralApplicationRegistry(this);
         } // synchronized (mPackages)
         } // synchronized (mInstallLock)
 
@@ -5784,6 +5794,82 @@
         }
     }
 
+    @Override
+    public ParceledListSlice<EphemeralApplicationInfo> getEphemeralApplications(int userId) {
+        mContext.enforceCallingOrSelfPermission(Manifest.permission.ACCESS_EPHEMERAL_APPS,
+                "getEphemeralApplications");
+        enforceCrossUserPermission(Binder.getCallingUid(), userId, true, false,
+                "getEphemeralApplications");
+        synchronized (mPackages) {
+            List<EphemeralApplicationInfo> ephemeralApps = mEphemeralApplicationRegistry
+                    .getEphemeralApplicationsLPw(userId);
+            if (ephemeralApps != null) {
+                return new ParceledListSlice<>(ephemeralApps);
+            }
+        }
+        return null;
+    }
+
+    @Override
+    public boolean isEphemeralApplication(String packageName, int userId) {
+        enforceCrossUserPermission(Binder.getCallingUid(), userId, true, false,
+                "isEphemeral");
+        if (!isCallerSameApp(packageName)) {
+            return false;
+        }
+        synchronized (mPackages) {
+            PackageParser.Package pkg = mPackages.get(packageName);
+            if (pkg != null) {
+                return pkg.applicationInfo.isEphemeralApp();
+            }
+        }
+        return false;
+    }
+
+    @Override
+    public byte[] getEphemeralApplicationCookie(String packageName, int userId) {
+        enforceCrossUserPermission(Binder.getCallingUid(), userId, true, false,
+                "getCookie");
+        if (!isCallerSameApp(packageName)) {
+            return null;
+        }
+        synchronized (mPackages) {
+            return mEphemeralApplicationRegistry.getEphemeralApplicationCookieLPw(
+                    packageName, userId);
+        }
+    }
+
+    @Override
+    public boolean setEphemeralApplicationCookie(String packageName, byte[] cookie, int userId) {
+        enforceCrossUserPermission(Binder.getCallingUid(), userId, true, false,
+                "setCookie");
+        if (!isCallerSameApp(packageName)) {
+            return false;
+        }
+        synchronized (mPackages) {
+            return mEphemeralApplicationRegistry.setEphemeralApplicationCookieLPw(
+                    packageName, cookie, userId);
+        }
+    }
+
+    @Override
+    public Bitmap getEphemeralApplicationIcon(String packageName, int userId) {
+        mContext.enforceCallingOrSelfPermission(Manifest.permission.ACCESS_EPHEMERAL_APPS,
+                "getEphemeralApplicationIcon");
+        enforceCrossUserPermission(Binder.getCallingUid(), userId, true, false,
+                "getEphemeralApplicationIcon");
+        synchronized (mPackages) {
+            return mEphemeralApplicationRegistry.getEphemeralApplicationIconLPw(
+                    packageName, userId);
+        }
+    }
+
+    private boolean isCallerSameApp(String packageName) {
+        PackageParser.Package pkg = mPackages.get(packageName);
+        return pkg != null
+                && UserHandle.getAppId(Binder.getCallingUid()) == pkg.applicationInfo.uid;
+    }
+
     public List<ApplicationInfo> getPersistentApplications(int flags) {
         final ArrayList<ApplicationInfo> finalList = new ArrayList<ApplicationInfo>();
 
@@ -12270,8 +12356,7 @@
         // First find the old package info and check signatures
         synchronized(mPackages) {
             oldPackage = mPackages.get(pkgName);
-            final boolean oldIsEphemeral
-                    = ((oldPackage.applicationInfo.flags & ApplicationInfo.FLAG_EPHEMERAL) != 0);
+            final boolean oldIsEphemeral = oldPackage.applicationInfo.isEphemeralApp();
             if (isEphemeral && !oldIsEphemeral) {
                 // can't downgrade from full to ephemeral
                 Slog.w(TAG, "Can't replace app with ephemeral: " + pkgName);
@@ -13090,11 +13175,11 @@
     }
 
     private static boolean isEphemeral(PackageParser.Package pkg) {
-        return (pkg.applicationInfo.flags & ApplicationInfo.FLAG_EPHEMERAL) != 0;
+        return pkg.applicationInfo.isEphemeralApp();
     }
 
     private static boolean isEphemeral(PackageSetting ps) {
-        return (ps.pkgFlags & ApplicationInfo.FLAG_EPHEMERAL) != 0;
+        return ps.pkg != null && isEphemeral(ps.pkg);
     }
 
     private static boolean isSystemApp(PackageParser.Package pkg) {
@@ -13295,11 +13380,14 @@
         boolean removedForAllUsers = false;
         boolean systemUpdate = false;
 
+        PackageParser.Package uninstalledPkg;
+
         // for the uninstall-updates case and restricted profiles, remember the per-
         // userhandle installed state
         int[] allUsers;
         boolean[] perUserInstalled;
         synchronized (mPackages) {
+            uninstalledPkg = mPackages.get(packageName);
             PackageSetting ps = mSettings.mPackages.get(packageName);
             allUsers = sUserManager.getUserIds();
             perUserInstalled = new boolean[allUsers.length];
@@ -13314,8 +13402,13 @@
                     true, allUsers, perUserInstalled,
                     flags | REMOVE_CHATTY, info, true);
             systemUpdate = info.isRemovedPackageSystemUpdate;
-            if (res && !systemUpdate && mPackages.get(packageName) == null) {
-                removedForAllUsers = true;
+            synchronized (mPackages) {
+                if (res) {
+                    if (!systemUpdate && mPackages.get(packageName) == null) {
+                        removedForAllUsers = true;
+                    }
+                    mEphemeralApplicationRegistry.onPackageUninstalledLPw(uninstalledPkg);
+                }
             }
             if (DEBUG_REMOVE) Slog.d(TAG, "delete res: systemUpdate=" + systemUpdate
                     + " removedForAllUsers=" + removedForAllUsers);
@@ -16450,7 +16543,7 @@
             if (userDir.exists()) continue;
 
             try {
-                sm.prepareUserStorage(volumeUuid, user.id, user.serialNumber);
+                sm.prepareUserStorage(volumeUuid, user.id, user.serialNumber, user.isEphemeral());
                 UserManagerService.enforceSerialNumber(userDir, user.serialNumber);
             } catch (IOException e) {
                 Log.wtf(TAG, "Failed to create user directory on " + volumeUuid, e);
@@ -16837,6 +16930,7 @@
             mUserNeedsBadging.delete(userHandle);
             mSettings.removeUserLPw(userHandle);
             mPendingBroadcasts.remove(userHandle);
+            mEphemeralApplicationRegistry.onUserRemovedLPw(userHandle);
         }
         synchronized (mInstallLock) {
             final StorageManager storage = mContext.getSystemService(StorageManager.class);
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index 5910c10..0b59c16 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -16,6 +16,7 @@
 
 package com.android.server.pm;
 
+import android.Manifest;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.Activity;
@@ -58,6 +59,7 @@
 import android.system.OsConstants;
 import android.util.AtomicFile;
 import android.util.Log;
+import android.util.Pair;
 import android.util.Slog;
 import android.util.SparseArray;
 import android.util.SparseBooleanArray;
@@ -90,6 +92,7 @@
 import java.util.List;
 
 import libcore.io.IoUtils;
+import libcore.util.Objects;
 
 /**
  * Service for {@link UserManager}.
@@ -107,6 +110,7 @@
     private static final boolean DBG_WITH_STACKTRACE = false; // DO NOT SUBMIT WITH TRUE
 
     private static final String TAG_NAME = "name";
+    private static final String TAG_ACCOUNT = "account";
     private static final String ATTR_FLAGS = "flags";
     private static final String ATTR_ICON_PATH = "icon";
     private static final String ATTR_ID = "id";
@@ -178,6 +182,14 @@
     private final SparseArray<UserInfo> mUsers = new SparseArray<>();
 
     /**
+     * This collection contains each user's account name if the user chose to set one up
+     * during the initial user creation process.  Keeping this information separate from mUsers
+     * to avoid accidentally leak it.
+     */
+    @GuardedBy("mUsersLock")
+    private final SparseArray<String> mUserAccounts = new SparseArray<>();
+
+    /**
      * User restrictions set via UserManager.  This doesn't include restrictions set by
      * device owner / profile owners.
      *
@@ -306,13 +318,13 @@
     }
 
     void systemReady() {
-        // Prune out any partially created/partially removed users.
+        // Prune out any partially created, partially removed and ephemeral users.
         ArrayList<UserInfo> partials = new ArrayList<>();
         synchronized (mUsersLock) {
             final int userSize = mUsers.size();
             for (int i = 0; i < userSize; i++) {
                 UserInfo ui = mUsers.valueAt(i);
-                if ((ui.partial || ui.guestToRemove) && i != 0) {
+                if ((ui.partial || ui.guestToRemove || ui.isEphemeral()) && i != 0) {
                     partials.add(ui);
                 }
             }
@@ -336,6 +348,33 @@
     }
 
     @Override
+    public String getUserAccount(int userId) {
+        checkManageUserAndAcrossUsersFullPermission("get user account");
+        synchronized (mUsersLock) {
+            return mUserAccounts.get(userId);
+        }
+    }
+
+    @Override
+    public void setUserAccount(int userId, String accountName) {
+        checkManageUserAndAcrossUsersFullPermission("set user account");
+        UserInfo userToUpdate = null;
+        synchronized (mPackagesLock) {
+            synchronized (mUsersLock) {
+                String currentAccount = mUserAccounts.get(userId);
+                if (!Objects.equal(currentAccount, accountName)) {
+                    mUserAccounts.put(userId, accountName);
+                    userToUpdate = mUsers.get(userId);
+                }
+            }
+
+            if (userToUpdate != null) {
+                writeUserLP(userToUpdate);
+            }
+        }
+    }
+
+    @Override
     public UserInfo getPrimaryUser() {
         checkManageUsersPermission("query users");
         synchronized (mUsersLock) {
@@ -845,6 +884,15 @@
     }
 
     @Override
+    public boolean hasBaseUserRestriction(String restrictionKey, int userId) {
+        checkManageUsersPermission("hasBaseUserRestriction");
+        synchronized (mRestrictionsLock) {
+            Bundle bundle = mBaseUserRestrictions.get(userId);
+            return (bundle != null && bundle.getBoolean(restrictionKey, false));
+        }
+    }
+
+    @Override
     public void setUserRestriction(String key, boolean value, int userId) {
         checkManageUsersPermission("setUserRestriction");
         synchronized (mRestrictionsLock) {
@@ -1043,6 +1091,30 @@
 
     /**
      * Enforces that only the system UID or root's UID or apps that have the
+     * {@link android.Manifest.permission#MANAGE_USERS MANAGE_USERS} and
+     * {@link android.Manifest.permission#INTERACT_ACROSS_USERS_FULL INTERACT_ACROSS_USERS_FULL}
+     * permissions can make certain calls to the UserManager.
+     *
+     * @param message used as message if SecurityException is thrown
+     * @throws SecurityException if the caller does not have enough privilege.
+     */
+    private static final void checkManageUserAndAcrossUsersFullPermission(String message) {
+        final int uid = Binder.getCallingUid();
+        if (uid != Process.SYSTEM_UID && uid != 0
+                && ActivityManager.checkComponentPermission(
+                Manifest.permission.MANAGE_USERS,
+                uid, -1, true) != PackageManager.PERMISSION_GRANTED
+                && ActivityManager.checkComponentPermission(
+                Manifest.permission.INTERACT_ACROSS_USERS_FULL,
+                uid, -1, true) != PackageManager.PERMISSION_GRANTED) {
+            throw new SecurityException(
+                    "You need MANAGE_USERS and INTERACT_ACROSS_USERS_FULL permission to: "
+                            + message);
+        }
+    }
+
+    /**
+     * Enforces that only the system UID or root's UID or apps that have the
      * {@link android.Manifest.permission#MANAGE_USERS MANAGE_USERS}
      * permission can make certain calls to the UserManager.
      *
@@ -1059,13 +1131,6 @@
         }
     }
 
-    private static void checkSystemOrRoot(String message) {
-        final int uid = Binder.getCallingUid();
-        if (uid != Process.SYSTEM_UID && uid != 0) {
-            throw new SecurityException("Only system may call: " + message);
-        }
-    }
-
     private void writeBitmapLP(UserInfo info, Bitmap bitmap) {
         try {
             File dir = new File(mUsersDir, Integer.toString(info.id));
@@ -1147,11 +1212,14 @@
                     final String name = parser.getName();
                     if (name.equals(TAG_USER)) {
                         String id = parser.getAttributeValue(null, ATTR_ID);
-                        UserInfo user = readUserLP(Integer.parseInt(id));
+                        Pair<UserInfo, String> userPair = readUserLP(Integer.parseInt(id));
 
-                        if (user != null) {
+                        if (userPair != null) {
+                            UserInfo user = userPair.first;
+                            String account = userPair.second;
                             synchronized (mUsersLock) {
                                 mUsers.put(user.id, user);
+                                mUserAccounts.put(user.id, account);
                                 if (mNextSerialNumber < 0 || mNextSerialNumber <= user.id) {
                                     mNextSerialNumber = user.id + 1;
                                 }
@@ -1351,6 +1419,17 @@
                         mDevicePolicyLocalUserRestrictions.get(userInfo.id),
                         TAG_DEVICE_POLICY_RESTRICTIONS);
             }
+            // Update the account field if it is set.
+            String account;
+            synchronized (mUsersLock) {
+                account = mUserAccounts.get(userInfo.id);
+            }
+            if (account != null) {
+                serializer.startTag(null, TAG_ACCOUNT);
+                serializer.text(account);
+                serializer.endTag(null, TAG_ACCOUNT);
+            }
+
             serializer.endTag(null, TAG_USER);
 
             serializer.endDocument();
@@ -1423,10 +1502,11 @@
         }
     }
 
-    private UserInfo readUserLP(int id) {
+    private Pair<UserInfo, String> readUserLP(int id) {
         int flags = 0;
         int serialNumber = id;
         String name = null;
+        String account = null;
         String iconPath = null;
         long creationTime = 0L;
         long lastLoggedInTime = 0L;
@@ -1495,6 +1575,11 @@
                         UserRestrictionsUtils.readRestrictions(parser, baseRestrictions);
                     } else if (TAG_DEVICE_POLICY_RESTRICTIONS.equals(tag)) {
                         UserRestrictionsUtils.readRestrictions(parser, localRestrictions);
+                    } else if (TAG_ACCOUNT.equals(tag)) {
+                        type = parser.next();
+                        if (type == XmlPullParser.TEXT) {
+                            account = parser.getText();
+                        }
                     }
                 }
             }
@@ -1511,7 +1596,7 @@
                 mBaseUserRestrictions.put(id, baseRestrictions);
                 mDevicePolicyLocalUserRestrictions.put(id, localRestrictions);
             }
-            return userInfo;
+            return new Pair<>(userInfo, account);
 
         } catch (IOException ioe) {
         } catch (XmlPullParserException pe) {
@@ -1666,6 +1751,10 @@
                         }
                     }
                 }
+
+                if (parent != null && parent.isEphemeral()) {
+                    flags |= UserInfo.FLAG_EPHEMERAL;
+                }
                 userId = getNextAvailableId();
                 userInfo = new UserInfo(userId, name, null, flags);
                 userInfo.serialNumber = mNextSerialNumber++;
@@ -1694,12 +1783,13 @@
                 }
             }
             final StorageManager storage = mContext.getSystemService(StorageManager.class);
-            storage.createUserKey(userId, userInfo.serialNumber);
+            storage.createUserKey(userId, userInfo.serialNumber, userInfo.isEphemeral());
             for (VolumeInfo vol : storage.getWritablePrivateVolumes()) {
                 final String volumeUuid = vol.getFsUuid();
                 try {
                     final File userDir = Environment.getDataUserDirectory(volumeUuid, userId);
-                    storage.prepareUserStorage(volumeUuid, userId, userInfo.serialNumber);
+                    storage.prepareUserStorage(
+                            volumeUuid, userId, userInfo.serialNumber, userInfo.isEphemeral());
                     enforceSerialNumber(userDir, userInfo.serialNumber);
                 } catch (IOException e) {
                     Log.wtf(LOG_TAG, "Failed to create user directory on " + volumeUuid, e);
@@ -1933,6 +2023,7 @@
         // Remove this user from the list
         synchronized (mUsersLock) {
             mUsers.remove(userHandle);
+            mUserAccounts.delete(userHandle);
             mIsUserManaged.delete(userHandle);
         }
         synchronized (mRestrictionsLock) {
@@ -2493,6 +2584,11 @@
                                 pw, "      ", mCachedEffectiveUserRestrictions.get(user.id));
                     }
                     pw.println();
+                    String accountName = mUserAccounts.get(userId);
+                    if (accountName != null) {
+                        pw.print("    Account name: " + accountName);
+                        pw.println();
+                    }
                 }
             }
             pw.println("  Device policy global restrictions:");
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index fe427d3..b9a9d6e 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -396,6 +396,7 @@
     boolean mDeskDockEnablesAccelerometer;
     int mLidKeyboardAccessibility;
     int mLidNavigationAccessibility;
+    boolean mLidControlsScreenLock;
     boolean mLidControlsSleep;
     int mShortPressOnPowerBehavior;
     int mLongPressOnPowerBehavior;
@@ -1440,6 +1441,8 @@
                 com.android.internal.R.integer.config_lidKeyboardAccessibility);
         mLidNavigationAccessibility = mContext.getResources().getInteger(
                 com.android.internal.R.integer.config_lidNavigationAccessibility);
+        mLidControlsScreenLock = mContext.getResources().getBoolean(
+                com.android.internal.R.bool.config_lidControlsScreenLock);
         mLidControlsSleep = mContext.getResources().getBoolean(
                 com.android.internal.R.bool.config_lidControlsSleep);
         mTranslucentDecorEnabled = mContext.getResources().getBoolean(
@@ -6378,6 +6381,8 @@
             mPowerManager.goToSleep(SystemClock.uptimeMillis(),
                     PowerManager.GO_TO_SLEEP_REASON_LID_SWITCH,
                     PowerManager.GO_TO_SLEEP_FLAG_NO_DOZE);
+        } else if (mLidState == LID_CLOSED && mLidControlsScreenLock) {
+            mWindowManagerFuncs.lockDeviceNow();
         }
 
         synchronized (mLock) {
@@ -6970,6 +6975,7 @@
         pw.print(prefix); pw.print("mLidKeyboardAccessibility=");
                 pw.print(mLidKeyboardAccessibility);
                 pw.print(" mLidNavigationAccessibility="); pw.print(mLidNavigationAccessibility);
+                pw.print(" mLidControlsScreenLock="); pw.println(mLidControlsScreenLock);
                 pw.print(" mLidControlsSleep="); pw.println(mLidControlsSleep);
         pw.print(prefix);
                 pw.print("mShortPressOnPowerBehavior="); pw.print(mShortPressOnPowerBehavior);
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 334a4dcc..dbbbb58 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -5252,6 +5252,12 @@
 
     // Called by window manager policy. Not exposed externally.
     @Override
+    public void lockDeviceNow() {
+        lockNow(null);
+    }
+
+    // Called by window manager policy. Not exposed externally.
+    @Override
     public int getCameraLensCoverState() {
         int sw = mInputManager.getSwitchState(-1, InputDevice.SOURCE_ANY,
                 InputManagerService.SW_CAMERA_LENS_COVER);
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index eed5419..5cf8ac0e 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -3541,6 +3541,18 @@
         }
     }
 
+    private void enforceCanManageInstalledKeys(ComponentName who) {
+        if (who == null) {
+            if (!isCallerDelegatedCertInstaller()) {
+                throw new SecurityException("who == null, but caller is not cert installer");
+            }
+        } else {
+            synchronized (this) {
+                getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
+            }
+        }
+    }
+
     private boolean isCallerDelegatedCertInstaller() {
         final int callingUid = mInjector.binderGetCallingUid();
         final int userHandle = UserHandle.getUserId(callingUid);
@@ -3630,27 +3642,20 @@
 
     @Override
     public boolean installKeyPair(ComponentName who, byte[] privKey, byte[] cert, String alias) {
-        if (who == null) {
-            if (!isCallerDelegatedCertInstaller()) {
-                throw new SecurityException("who == null, but caller is not cert installer");
-            }
-        } else {
-            synchronized (this) {
-                getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
-            }
-        }
+        enforceCanManageInstalledKeys(who);
+
         final UserHandle userHandle = new UserHandle(UserHandle.getCallingUserId());
         final long id = mInjector.binderClearCallingIdentity();
         try {
-          final KeyChainConnection keyChainConnection = KeyChain.bindAsUser(mContext, userHandle);
-          try {
-              IKeyChainService keyChain = keyChainConnection.getService();
-              return keyChain.installKeyPair(privKey, cert, alias);
-          } catch (RemoteException e) {
-              Log.e(LOG_TAG, "Installing certificate", e);
-          } finally {
-              keyChainConnection.close();
-          }
+            final KeyChainConnection keyChainConnection = KeyChain.bindAsUser(mContext, userHandle);
+            try {
+                IKeyChainService keyChain = keyChainConnection.getService();
+                return keyChain.installKeyPair(privKey, cert, alias);
+            } catch (RemoteException e) {
+                Log.e(LOG_TAG, "Installing certificate", e);
+            } finally {
+                keyChainConnection.close();
+            }
         } catch (InterruptedException e) {
             Log.w(LOG_TAG, "Interrupted while installing certificate", e);
             Thread.currentThread().interrupt();
@@ -3661,6 +3666,31 @@
     }
 
     @Override
+    public boolean removeKeyPair(ComponentName who, String alias) {
+        enforceCanManageInstalledKeys(who);
+
+        final UserHandle userHandle = new UserHandle(UserHandle.getCallingUserId());
+        final long id = Binder.clearCallingIdentity();
+        try {
+            final KeyChainConnection keyChainConnection = KeyChain.bindAsUser(mContext, userHandle);
+            try {
+                IKeyChainService keyChain = keyChainConnection.getService();
+                return keyChain.removeKeyPair(alias);
+            } catch (RemoteException e) {
+                Log.e(LOG_TAG, "Removing keypair", e);
+            } finally {
+                keyChainConnection.close();
+            }
+        } catch (InterruptedException e) {
+            Log.w(LOG_TAG, "Interrupted while removing keypair", e);
+            Thread.currentThread().interrupt();
+        } finally {
+            Binder.restoreCallingIdentity(id);
+        }
+        return false;
+    }
+
+    @Override
     public void choosePrivateKeyAlias(final int uid, final Uri uri, final String alias,
             final IBinder response) {
         // Caller UID needs to be trusted, so we restrict this method to SYSTEM_UID callers.
diff --git a/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceTest.java
index 9e70138..48a11c5 100644
--- a/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceTest.java
@@ -16,9 +16,12 @@
 
 package com.android.server.pm;
 
+import android.content.pm.UserInfo;
 import android.os.Bundle;
 import android.os.FileUtils;
 import android.os.Parcelable;
+import android.os.UserHandle;
+import android.os.UserManager;
 import android.test.AndroidTestCase;
 import android.util.AtomicFile;
 
@@ -29,6 +32,7 @@
 public class UserManagerServiceTest extends AndroidTestCase {
     private static String[] STRING_ARRAY = new String[] {"<tag", "<![CDATA["};
     private File restrictionsFile;
+    private int tempUserId = UserHandle.USER_NULL;
 
     @Override
     protected void setUp() throws Exception {
@@ -40,6 +44,9 @@
     @Override
     protected void tearDown() throws Exception {
         restrictionsFile.delete();
+        if (tempUserId != UserHandle.USER_NULL) {
+            UserManager.get(mContext).removeUser(tempUserId);
+        }
         super.tearDown();
     }
 
@@ -55,6 +62,16 @@
         assertBundle(bundle);
     }
 
+    public void testAddUserWithAccount() {
+        UserManager um = UserManager.get(mContext);
+        UserInfo user = um.createUser("Test User", 0);
+        assertNotNull(user);
+        tempUserId = user.id;
+        String accountName = "Test Account";
+        um.setUserAccount(tempUserId, accountName);
+        assertEquals(accountName, um.getUserAccount(tempUserId));
+    }
+
     private Bundle createBundle() {
         Bundle result = new Bundle();
         // Tests for 6 allowed types: Integer, Boolean, String, String[], Bundle and Parcelable[]
diff --git a/services/usb/java/com/android/server/usb/UsbSettingsManager.java b/services/usb/java/com/android/server/usb/UsbSettingsManager.java
index 4786d11..7976cb8 100644
--- a/services/usb/java/com/android/server/usb/UsbSettingsManager.java
+++ b/services/usb/java/com/android/server/usb/UsbSettingsManager.java
@@ -52,12 +52,10 @@
 import org.xmlpull.v1.XmlSerializer;
 
 import java.io.File;
-import java.io.FileDescriptor;
 import java.io.FileInputStream;
 import java.io.FileNotFoundException;
 import java.io.FileOutputStream;
 import java.io.IOException;
-import java.io.PrintWriter;
 import java.nio.charset.StandardCharsets;
 import java.util.ArrayList;
 import java.util.HashMap;
@@ -984,14 +982,7 @@
     public boolean hasPermission(UsbDevice device) {
         synchronized (mLock) {
             int uid = Binder.getCallingUid();
-            int androidMediaUid;
-            try {
-                androidMediaUid = mPackageManager.getApplicationInfo("com.android.mtp", 0).uid;
-            } catch (NameNotFoundException e) {
-                androidMediaUid = -1;
-            }
-            if (uid == Process.SYSTEM_UID || UserHandle.getAppId(uid) == androidMediaUid ||
-                    mDisablePermissionDialogs) {
+            if (uid == Process.SYSTEM_UID || mDisablePermissionDialogs) {
                 return true;
             }
             SparseBooleanArray uidList = mDevicePermissionMap.get(device.getDeviceName());
diff --git a/telecomm/java/android/telecom/TelecomManager.java b/telecomm/java/android/telecom/TelecomManager.java
index b5b4e5f..9eb1665 100644
--- a/telecomm/java/android/telecom/TelecomManager.java
+++ b/telecomm/java/android/telecom/TelecomManager.java
@@ -247,6 +247,14 @@
             "android.telecom.extra.CONNECTION_SERVICE";
 
     /**
+     * Optional extra for communicating the call technology used by a
+     * {@link com.android.internal.telephony.Connection} to Telecom
+     * @hide
+     */
+    public static final String EXTRA_CALL_TECHNOLOGY_TYPE =
+            "android.telecom.extra.CALL_TECHNOLOGY_TYPE";
+
+    /**
      * An optional {@link android.content.Intent#ACTION_CALL} intent extra denoting the
      * package name of the app specifying an alternative gateway for the call.
      * The value is a string.
diff --git a/test-runner/src/android/test/mock/MockContext.java b/test-runner/src/android/test/mock/MockContext.java
index 225edf8..4c3b598 100644
--- a/test-runner/src/android/test/mock/MockContext.java
+++ b/test-runner/src/android/test/mock/MockContext.java
@@ -181,18 +181,6 @@
     }
 
     @Override
-    public File getDeviceEncryptedFilesDir() {
-        throw new UnsupportedOperationException();
-    }
-
-    /** {@hide} */
-    @SystemApi
-    @Override
-    public File getCredentialEncryptedFilesDir() {
-        throw new UnsupportedOperationException();
-    }
-
-    @Override
     public File getNoBackupFilesDir() {
         throw new UnsupportedOperationException();
     }
@@ -705,4 +693,24 @@
     public File[] getExternalMediaDirs() {
         throw new UnsupportedOperationException();
     }
+
+    @Override
+    public Context createDeviceEncryptedContext(Context context) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public Context createCredentialEncryptedContext(Context context) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public boolean isDeviceEncrypted() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public boolean isCredentialEncrypted() {
+        throw new UnsupportedOperationException();
+    }
 }
diff --git a/test-runner/src/android/test/mock/MockPackageManager.java b/test-runner/src/android/test/mock/MockPackageManager.java
index 17c24af..f7c63d1 100644
--- a/test-runner/src/android/test/mock/MockPackageManager.java
+++ b/test-runner/src/android/test/mock/MockPackageManager.java
@@ -16,6 +16,7 @@
 
 package android.test.mock;
 
+import android.annotation.NonNull;
 import android.app.PackageInstallObserver;
 import android.content.ComponentName;
 import android.content.Intent;
@@ -24,6 +25,7 @@
 import android.content.pm.ActivityInfo;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.ContainerEncryptionParams;
+import android.content.pm.EphemeralApplicationInfo;
 import android.content.pm.FeatureInfo;
 import android.content.pm.IPackageDataObserver;
 import android.content.pm.IPackageDeleteObserver;
@@ -286,6 +288,38 @@
         throw new UnsupportedOperationException();
     }
 
+    /** @hide */
+    @Override
+    public List<EphemeralApplicationInfo> getEphemeralApplications() {
+        throw new UnsupportedOperationException();
+    }
+
+    /** @hide */
+    @Override
+    public Drawable getEphemeralApplicationIcon(String packageName) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public byte[] getEphemeralCookie() {
+        return new byte[0];
+    }
+
+    @Override
+    public boolean isEphemeralApplication() {
+        return false;
+    }
+
+    @Override
+    public int getEphemeralCookieMaxSizeBytes() {
+        return 0;
+    }
+
+    @Override
+    public boolean setEphemeralCookie(@NonNull byte[] cookie) {
+        return false;
+    }
+
     @Override
     public ResolveInfo resolveActivity(Intent intent, int flags) {
         throw new UnsupportedOperationException();
diff --git a/tools/aapt2/flatten/TableFlattener.cpp b/tools/aapt2/flatten/TableFlattener.cpp
index f42e6b7..46ee914 100644
--- a/tools/aapt2/flatten/TableFlattener.cpp
+++ b/tools/aapt2/flatten/TableFlattener.cpp
@@ -23,7 +23,7 @@
 #include "flatten/TableFlattener.h"
 #include "util/BigBuffer.h"
 
-#include <base/macros.h>
+#include <android-base/macros.h>
 #include <type_traits>
 #include <numeric>
 
diff --git a/tools/aapt2/unflatten/BinaryResourceParser.cpp b/tools/aapt2/unflatten/BinaryResourceParser.cpp
index 5cc7aa7..46b5205 100644
--- a/tools/aapt2/unflatten/BinaryResourceParser.cpp
+++ b/tools/aapt2/unflatten/BinaryResourceParser.cpp
@@ -27,7 +27,7 @@
 
 #include <androidfw/ResourceTypes.h>
 #include <androidfw/TypeWrappers.h>
-#include <base/macros.h>
+#include <android-base/macros.h>
 
 #include <map>
 #include <string>
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java
index 9214759..bd5335e 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java
@@ -1377,18 +1377,6 @@
     }
 
     @Override
-    public File getDeviceEncryptedFilesDir() {
-        // pass
-        return null;
-    }
-
-    @Override
-    public File getCredentialEncryptedFilesDir() {
-        // pass
-        return null;
-    }
-
-    @Override
     public File getNoBackupFilesDir() {
         // pass
         return null;
@@ -1814,4 +1802,26 @@
         Integer pos = mScrollYPos.get(view);
         return pos != null ? pos : 0;
     }
+
+    @Override
+    public Context createDeviceEncryptedContext(Context context) {
+        // pass
+        return null;
+    }
+
+    @Override
+    public Context createCredentialEncryptedContext(Context context) {
+        // pass
+        return null;
+    }
+
+    @Override
+    public boolean isDeviceEncrypted() {
+        return false;
+    }
+
+    @Override
+    public boolean isCredentialEncrypted() {
+        return false;
+    }
 }
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgePackageManager.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgePackageManager.java
index e3a19e7..bab25c0 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgePackageManager.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgePackageManager.java
@@ -16,6 +16,7 @@
 
 package com.android.layoutlib.bridge.android;
 
+import android.annotation.NonNull;
 import android.app.PackageInstallObserver;
 import android.content.ComponentName;
 import android.content.Intent;
@@ -24,6 +25,7 @@
 import android.content.pm.ActivityInfo;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.ContainerEncryptionParams;
+import android.content.pm.EphemeralApplicationInfo;
 import android.content.pm.FeatureInfo;
 import android.content.pm.IPackageDataObserver;
 import android.content.pm.IPackageDeleteObserver;
@@ -251,6 +253,36 @@
     }
 
     @Override
+    public List<EphemeralApplicationInfo> getEphemeralApplications() {
+        return null;
+    }
+
+    @Override
+    public Drawable getEphemeralApplicationIcon(String packageName) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public byte[] getEphemeralCookie() {
+        return new byte[0];
+    }
+
+    @Override
+    public boolean isEphemeralApplication() {
+        return false;
+    }
+
+    @Override
+    public int getEphemeralCookieMaxSizeBytes() {
+        return 0;
+    }
+
+    @Override
+    public boolean setEphemeralCookie(@NonNull byte[] cookie) {
+        return false;
+    }
+
+    @Override
     public String[] getSystemSharedLibraryNames() {
         return new String[0];
     }