Merge "Add a framework service tracking VR mode state."
diff --git a/Android.mk b/Android.mk
index 137ef85..51dfa57 100644
--- a/Android.mk
+++ b/Android.mk
@@ -226,6 +226,7 @@
 	core/java/android/service/carrier/ICarrierMessagingService.aidl \
 	core/java/android/service/gatekeeper/IGateKeeperService.aidl \
 	core/java/android/service/notification/INotificationListener.aidl \
+	core/java/android/service/notification/INotificationAssistant.aidl \
 	core/java/android/service/notification/IStatusBarNotificationHolder.aidl \
 	core/java/android/service/notification/IConditionListener.aidl \
 	core/java/android/service/notification/IConditionProvider.aidl \
@@ -308,7 +309,7 @@
 	core/java/com/android/internal/textservice/ISpellCheckerSessionListener.aidl \
 	core/java/com/android/internal/textservice/ITextServicesManager.aidl \
 	core/java/com/android/internal/textservice/ITextServicesSessionListener.aidl \
-	core/java/com/android/internal/view/IDropPermissionHolder.aidl \
+	core/java/com/android/internal/view/IDropPermissions.aidl \
 	core/java/com/android/internal/view/IInputContext.aidl \
 	core/java/com/android/internal/view/IInputContextCallback.aidl \
 	core/java/com/android/internal/view/IInputMethod.aidl \
diff --git a/api/current.txt b/api/current.txt
index 557afcd..36430bb 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -5741,6 +5741,7 @@
     method public java.lang.String[] getAccountTypesWithManagementDisabled();
     method public java.util.List<android.content.ComponentName> getActiveAdmins();
     method public android.os.Bundle getApplicationRestrictions(android.content.ComponentName, java.lang.String);
+    method public java.lang.String getApplicationRestrictionsManagingPackage(android.content.ComponentName);
     method public boolean getAutoTimeRequired();
     method public boolean getBluetoothContactSharingDisabled(android.content.ComponentName);
     method public boolean getCameraDisabled(android.content.ComponentName);
@@ -5784,6 +5785,7 @@
     method public boolean isActivePasswordSufficient();
     method public boolean isAdminActive(android.content.ComponentName);
     method public boolean isApplicationHidden(android.content.ComponentName, java.lang.String);
+    method public boolean isCallerApplicationRestrictionsManagingPackage();
     method public boolean isDeviceOwnerApp(java.lang.String);
     method public boolean isLockTaskPermitted(java.lang.String);
     method public boolean isMasterVolumeMuted(android.content.ComponentName);
@@ -5791,6 +5793,7 @@
     method public boolean isProvisioningAllowed(java.lang.String);
     method public boolean isUninstallBlocked(android.content.ComponentName, java.lang.String);
     method public void lockNow();
+    method public void reboot(android.content.ComponentName);
     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);
@@ -5799,6 +5802,7 @@
     method public void setAccountManagementDisabled(android.content.ComponentName, java.lang.String, boolean);
     method public boolean setApplicationHidden(android.content.ComponentName, java.lang.String, boolean);
     method public void setApplicationRestrictions(android.content.ComponentName, java.lang.String, android.os.Bundle);
+    method public void setApplicationRestrictionsManagingPackage(android.content.ComponentName, java.lang.String);
     method public void setAutoTimeRequired(android.content.ComponentName, boolean);
     method public void setBluetoothContactSharingDisabled(android.content.ComponentName, boolean);
     method public void setCameraDisabled(android.content.ComponentName, boolean);
@@ -29310,6 +29314,7 @@
     field public static final java.lang.String GEOCODED_LOCATION = "geocoded_location";
     field public static final int INCOMING_TYPE = 1; // 0x1
     field public static final java.lang.String IS_READ = "is_read";
+    field public static final java.lang.String LAST_MODIFIED = "last_modified";
     field public static final java.lang.String LIMIT_PARAM_KEY = "limit";
     field public static final int MISSED_TYPE = 3; // 0x3
     field public static final java.lang.String NEW = "new";
@@ -30918,6 +30923,7 @@
     field public static final java.lang.String ACTION_LOCATION_SOURCE_SETTINGS = "android.settings.LOCATION_SOURCE_SETTINGS";
     field public static final java.lang.String ACTION_MANAGE_ALL_APPLICATIONS_SETTINGS = "android.settings.MANAGE_ALL_APPLICATIONS_SETTINGS";
     field public static final java.lang.String ACTION_MANAGE_APPLICATIONS_SETTINGS = "android.settings.MANAGE_APPLICATIONS_SETTINGS";
+    field public static final java.lang.String ACTION_MANAGE_DEFAULT_APPS_SETTINGS = "android.settings.MANAGE_DEFAULT_APPS_SETTINGS";
     field public static final java.lang.String ACTION_MANAGE_OVERLAY_PERMISSION = "android.settings.action.MANAGE_OVERLAY_PERMISSION";
     field public static final java.lang.String ACTION_MANAGE_WRITE_SETTINGS = "android.settings.action.MANAGE_WRITE_SETTINGS";
     field public static final java.lang.String ACTION_MEMORY_CARD_SETTINGS = "android.settings.MEMORY_CARD_SETTINGS";
@@ -31579,6 +31585,7 @@
 
   public static final class VoicemailContract.Status implements android.provider.BaseColumns {
     method public static android.net.Uri buildSourceUri(java.lang.String);
+    method public static void setQuota(android.content.Context, android.telecom.PhoneAccountHandle, int, int);
     field public static final java.lang.String CONFIGURATION_STATE = "configuration_state";
     field public static final int CONFIGURATION_STATE_CAN_BE_CONFIGURED = 2; // 0x2
     field public static final int CONFIGURATION_STATE_NOT_CONFIGURED = 1; // 0x1
@@ -31595,6 +31602,9 @@
     field public static final int NOTIFICATION_CHANNEL_STATE_OK = 0; // 0x0
     field public static final java.lang.String PHONE_ACCOUNT_COMPONENT_NAME = "phone_account_component_name";
     field public static final java.lang.String PHONE_ACCOUNT_ID = "phone_account_id";
+    field public static final java.lang.String QUOTA_OCCUPIED = "quota_occupied";
+    field public static final java.lang.String QUOTA_TOTAL = "quota_total";
+    field public static final int QUOTA_UNAVAILABLE = -1; // 0xffffffff
     field public static final java.lang.String SETTINGS_URI = "settings_uri";
     field public static final java.lang.String SOURCE_PACKAGE = "source_package";
     field public static final java.lang.String VOICEMAIL_ACCESS_URI = "voicemail_access_uri";
@@ -31611,6 +31621,7 @@
     field public static final java.lang.String HAS_CONTENT = "has_content";
     field public static final java.lang.String IS_READ = "is_read";
     field public static final java.lang.String ITEM_TYPE = "vnd.android.cursor.item/voicemail";
+    field public static final java.lang.String LAST_MODIFIED = "last_modified";
     field public static final java.lang.String MIME_TYPE = "mime_type";
     field public static final java.lang.String NUMBER = "number";
     field public static final java.lang.String PHONE_ACCOUNT_COMPONENT_NAME = "subscription_component_name";
@@ -33194,13 +33205,20 @@
     field public static final java.lang.String SERVICE_INTERFACE = "android.service.notification.ConditionProviderService";
   }
 
+  public class NotificationAdjustment implements android.os.Parcelable {
+    ctor public NotificationAdjustment(int, java.lang.CharSequence, android.net.Uri);
+    method public int describeContents();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.service.notification.NotificationAdjustment> CREATOR;
+  }
+
   public abstract class NotificationAssistantService extends android.service.notification.NotificationListenerService {
     ctor public NotificationAssistantService();
-    method public final void adjustImportance(java.lang.String, android.service.notification.NotificationAssistantService.Adjustment);
+    method public final void adjustImportance(java.lang.String, android.service.notification.NotificationAdjustment);
     method public final void clearAnnotation(java.lang.String);
     method public void onNotificationActionClick(java.lang.String, long, int);
     method public void onNotificationClick(java.lang.String, long);
-    method public abstract android.service.notification.NotificationAssistantService.Adjustment onNotificationEnqueued(android.service.notification.StatusBarNotification, int, boolean);
+    method public abstract android.service.notification.NotificationAdjustment onNotificationEnqueued(android.service.notification.StatusBarNotification, int, boolean);
     method public void onNotificationRemoved(java.lang.String, long, int);
     method public void onNotificationVisibilityChanged(java.lang.String, long, boolean);
     method public final void setAnnotation(java.lang.String, android.app.Notification);
@@ -33217,10 +33235,7 @@
     field public static final int REASON_PACKAGE_BANNED = 7; // 0x7
     field public static final int REASON_PACKAGE_CHANGED = 5; // 0x5
     field public static final int REASON_USER_STOPPED = 6; // 0x6
-  }
-
-  public class NotificationAssistantService.Adjustment {
-    ctor public NotificationAssistantService.Adjustment(int, java.lang.CharSequence, android.net.Uri);
+    field public static final java.lang.String SERVICE_INTERFACE = "android.service.notification.NotificationAssistantService";
   }
 
   public abstract class NotificationListenerService extends android.app.Service {
@@ -39302,11 +39317,11 @@
     method public int getAction();
     method public android.content.ClipData getClipData();
     method public android.content.ClipDescription getClipDescription();
-    method public android.view.DropPermissionHolder getDropPermissionHolder();
     method public java.lang.Object getLocalState();
     method public boolean getResult();
     method public float getX();
     method public float getY();
+    method public android.view.DropPermissions requestDropPermissions();
     method public void writeToParcel(android.os.Parcel, int);
     field public static final int ACTION_DRAG_ENDED = 4; // 0x4
     field public static final int ACTION_DRAG_ENTERED = 5; // 0x5
@@ -39317,12 +39332,8 @@
     field public static final android.os.Parcelable.Creator<android.view.DragEvent> CREATOR;
   }
 
-  public class DropPermissionHolder implements android.os.Parcelable {
-    method public int describeContents();
-    method public void grant();
-    method public void revoke();
-    method public void writeToParcel(android.os.Parcel, int);
-    field public static final android.os.Parcelable.Creator<android.view.DropPermissionHolder> CREATOR;
+  public final class DropPermissions {
+    method public void release();
   }
 
   public class FocusFinder {
diff --git a/api/system-current.txt b/api/system-current.txt
index 57ae432..1a15555 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -5866,6 +5866,7 @@
     method public java.lang.String[] getAccountTypesWithManagementDisabled();
     method public java.util.List<android.content.ComponentName> getActiveAdmins();
     method public android.os.Bundle getApplicationRestrictions(android.content.ComponentName, java.lang.String);
+    method public java.lang.String getApplicationRestrictionsManagingPackage(android.content.ComponentName);
     method public boolean getAutoTimeRequired();
     method public boolean getBluetoothContactSharingDisabled(android.content.ComponentName);
     method public boolean getCameraDisabled(android.content.ComponentName);
@@ -5916,6 +5917,7 @@
     method public boolean isActivePasswordSufficient();
     method public boolean isAdminActive(android.content.ComponentName);
     method public boolean isApplicationHidden(android.content.ComponentName, java.lang.String);
+    method public boolean isCallerApplicationRestrictionsManagingPackage();
     method public boolean isDeviceOwnerApp(java.lang.String);
     method public boolean isLockTaskPermitted(java.lang.String);
     method public boolean isMasterVolumeMuted(android.content.ComponentName);
@@ -5924,6 +5926,7 @@
     method public boolean isUninstallBlocked(android.content.ComponentName, java.lang.String);
     method public void lockNow();
     method public void notifyPendingSystemUpdate(long);
+    method public void reboot(android.content.ComponentName);
     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);
@@ -5933,6 +5936,7 @@
     method public deprecated boolean setActiveProfileOwner(android.content.ComponentName, java.lang.String) throws java.lang.IllegalArgumentException;
     method public boolean setApplicationHidden(android.content.ComponentName, java.lang.String, boolean);
     method public void setApplicationRestrictions(android.content.ComponentName, java.lang.String, android.os.Bundle);
+    method public void setApplicationRestrictionsManagingPackage(android.content.ComponentName, java.lang.String);
     method public void setAutoTimeRequired(android.content.ComponentName, boolean);
     method public void setBluetoothContactSharingDisabled(android.content.ComponentName, boolean);
     method public void setCameraDisabled(android.content.ComponentName, boolean);
@@ -9654,7 +9658,6 @@
     method public void setAppLabel(java.lang.CharSequence);
     method public void setAppPackageName(java.lang.String);
     method public void setGrantedRuntimePermissions(java.lang.String[]);
-    method public void setInstallFlagsQuick();
     method public void setInstallLocation(int);
     method public void setOriginatingUid(int);
     method public void setOriginatingUri(android.net.Uri);
@@ -23794,6 +23797,7 @@
 
   public class TvStreamConfig implements android.os.Parcelable {
     method public int describeContents();
+    method public int getFlags();
     method public int getGeneration();
     method public int getMaxHeight();
     method public int getMaxWidth();
@@ -23801,6 +23805,7 @@
     method public int getType();
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.media.tv.TvStreamConfig> CREATOR;
+    field public static final int FLAG_MASK_SIGNAL_DETECTION = 1; // 0x1
     field public static final int STREAM_TYPE_BUFFER_PRODUCER = 2; // 0x2
     field public static final int STREAM_TYPE_INDEPENDENT_VIDEO_SOURCE = 1; // 0x1
   }
@@ -23808,6 +23813,7 @@
   public static final class TvStreamConfig.Builder {
     ctor public TvStreamConfig.Builder();
     method public android.media.tv.TvStreamConfig build();
+    method public android.media.tv.TvStreamConfig.Builder flags(int);
     method public android.media.tv.TvStreamConfig.Builder generation(int);
     method public android.media.tv.TvStreamConfig.Builder maxHeight(int);
     method public android.media.tv.TvStreamConfig.Builder maxWidth(int);
@@ -31318,6 +31324,7 @@
     field public static final java.lang.String GEOCODED_LOCATION = "geocoded_location";
     field public static final int INCOMING_TYPE = 1; // 0x1
     field public static final java.lang.String IS_READ = "is_read";
+    field public static final java.lang.String LAST_MODIFIED = "last_modified";
     field public static final java.lang.String LIMIT_PARAM_KEY = "limit";
     field public static final int MISSED_TYPE = 3; // 0x3
     field public static final java.lang.String NEW = "new";
@@ -33058,6 +33065,7 @@
     field public static final java.lang.String ACTION_LOCATION_SOURCE_SETTINGS = "android.settings.LOCATION_SOURCE_SETTINGS";
     field public static final java.lang.String ACTION_MANAGE_ALL_APPLICATIONS_SETTINGS = "android.settings.MANAGE_ALL_APPLICATIONS_SETTINGS";
     field public static final java.lang.String ACTION_MANAGE_APPLICATIONS_SETTINGS = "android.settings.MANAGE_APPLICATIONS_SETTINGS";
+    field public static final java.lang.String ACTION_MANAGE_DEFAULT_APPS_SETTINGS = "android.settings.MANAGE_DEFAULT_APPS_SETTINGS";
     field public static final java.lang.String ACTION_MANAGE_OVERLAY_PERMISSION = "android.settings.action.MANAGE_OVERLAY_PERMISSION";
     field public static final java.lang.String ACTION_MANAGE_WRITE_SETTINGS = "android.settings.action.MANAGE_WRITE_SETTINGS";
     field public static final java.lang.String ACTION_MEMORY_CARD_SETTINGS = "android.settings.MEMORY_CARD_SETTINGS";
@@ -33720,6 +33728,7 @@
 
   public static final class VoicemailContract.Status implements android.provider.BaseColumns {
     method public static android.net.Uri buildSourceUri(java.lang.String);
+    method public static void setQuota(android.content.Context, android.telecom.PhoneAccountHandle, int, int);
     field public static final java.lang.String CONFIGURATION_STATE = "configuration_state";
     field public static final int CONFIGURATION_STATE_CAN_BE_CONFIGURED = 2; // 0x2
     field public static final int CONFIGURATION_STATE_NOT_CONFIGURED = 1; // 0x1
@@ -33736,6 +33745,9 @@
     field public static final int NOTIFICATION_CHANNEL_STATE_OK = 0; // 0x0
     field public static final java.lang.String PHONE_ACCOUNT_COMPONENT_NAME = "phone_account_component_name";
     field public static final java.lang.String PHONE_ACCOUNT_ID = "phone_account_id";
+    field public static final java.lang.String QUOTA_OCCUPIED = "quota_occupied";
+    field public static final java.lang.String QUOTA_TOTAL = "quota_total";
+    field public static final int QUOTA_UNAVAILABLE = -1; // 0xffffffff
     field public static final java.lang.String SETTINGS_URI = "settings_uri";
     field public static final java.lang.String SOURCE_PACKAGE = "source_package";
     field public static final java.lang.String VOICEMAIL_ACCESS_URI = "voicemail_access_uri";
@@ -33752,6 +33764,7 @@
     field public static final java.lang.String HAS_CONTENT = "has_content";
     field public static final java.lang.String IS_READ = "is_read";
     field public static final java.lang.String ITEM_TYPE = "vnd.android.cursor.item/voicemail";
+    field public static final java.lang.String LAST_MODIFIED = "last_modified";
     field public static final java.lang.String MIME_TYPE = "mime_type";
     field public static final java.lang.String NUMBER = "number";
     field public static final java.lang.String PHONE_ACCOUNT_COMPONENT_NAME = "subscription_component_name";
@@ -35335,13 +35348,20 @@
     field public static final java.lang.String SERVICE_INTERFACE = "android.service.notification.ConditionProviderService";
   }
 
+  public class NotificationAdjustment implements android.os.Parcelable {
+    ctor public NotificationAdjustment(int, java.lang.CharSequence, android.net.Uri);
+    method public int describeContents();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.service.notification.NotificationAdjustment> CREATOR;
+  }
+
   public abstract class NotificationAssistantService extends android.service.notification.NotificationListenerService {
     ctor public NotificationAssistantService();
-    method public final void adjustImportance(java.lang.String, android.service.notification.NotificationAssistantService.Adjustment);
+    method public final void adjustImportance(java.lang.String, android.service.notification.NotificationAdjustment);
     method public final void clearAnnotation(java.lang.String);
     method public void onNotificationActionClick(java.lang.String, long, int);
     method public void onNotificationClick(java.lang.String, long);
-    method public abstract android.service.notification.NotificationAssistantService.Adjustment onNotificationEnqueued(android.service.notification.StatusBarNotification, int, boolean);
+    method public abstract android.service.notification.NotificationAdjustment onNotificationEnqueued(android.service.notification.StatusBarNotification, int, boolean);
     method public void onNotificationRemoved(java.lang.String, long, int);
     method public void onNotificationVisibilityChanged(java.lang.String, long, boolean);
     method public final void setAnnotation(java.lang.String, android.app.Notification);
@@ -35358,10 +35378,7 @@
     field public static final int REASON_PACKAGE_BANNED = 7; // 0x7
     field public static final int REASON_PACKAGE_CHANGED = 5; // 0x5
     field public static final int REASON_USER_STOPPED = 6; // 0x6
-  }
-
-  public class NotificationAssistantService.Adjustment {
-    ctor public NotificationAssistantService.Adjustment(int, java.lang.CharSequence, android.net.Uri);
+    field public static final java.lang.String SERVICE_INTERFACE = "android.service.notification.NotificationAssistantService";
   }
 
   public abstract class NotificationListenerService extends android.app.Service {
@@ -41654,11 +41671,11 @@
     method public int getAction();
     method public android.content.ClipData getClipData();
     method public android.content.ClipDescription getClipDescription();
-    method public android.view.DropPermissionHolder getDropPermissionHolder();
     method public java.lang.Object getLocalState();
     method public boolean getResult();
     method public float getX();
     method public float getY();
+    method public android.view.DropPermissions requestDropPermissions();
     method public void writeToParcel(android.os.Parcel, int);
     field public static final int ACTION_DRAG_ENDED = 4; // 0x4
     field public static final int ACTION_DRAG_ENTERED = 5; // 0x5
@@ -41669,12 +41686,8 @@
     field public static final android.os.Parcelable.Creator<android.view.DragEvent> CREATOR;
   }
 
-  public class DropPermissionHolder implements android.os.Parcelable {
-    method public int describeContents();
-    method public void grant();
-    method public void revoke();
-    method public void writeToParcel(android.os.Parcel, int);
-    field public static final android.os.Parcelable.Creator<android.view.DropPermissionHolder> CREATOR;
+  public final class DropPermissions {
+    method public void release();
   }
 
   public class FocusFinder {
diff --git a/api/test-current.txt b/api/test-current.txt
index e506cfc..3531e34 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -5741,6 +5741,7 @@
     method public java.lang.String[] getAccountTypesWithManagementDisabled();
     method public java.util.List<android.content.ComponentName> getActiveAdmins();
     method public android.os.Bundle getApplicationRestrictions(android.content.ComponentName, java.lang.String);
+    method public java.lang.String getApplicationRestrictionsManagingPackage(android.content.ComponentName);
     method public boolean getAutoTimeRequired();
     method public boolean getBluetoothContactSharingDisabled(android.content.ComponentName);
     method public boolean getCameraDisabled(android.content.ComponentName);
@@ -5784,6 +5785,7 @@
     method public boolean isActivePasswordSufficient();
     method public boolean isAdminActive(android.content.ComponentName);
     method public boolean isApplicationHidden(android.content.ComponentName, java.lang.String);
+    method public boolean isCallerApplicationRestrictionsManagingPackage();
     method public boolean isDeviceOwnerApp(java.lang.String);
     method public boolean isLockTaskPermitted(java.lang.String);
     method public boolean isMasterVolumeMuted(android.content.ComponentName);
@@ -5791,6 +5793,7 @@
     method public boolean isProvisioningAllowed(java.lang.String);
     method public boolean isUninstallBlocked(android.content.ComponentName, java.lang.String);
     method public void lockNow();
+    method public void reboot(android.content.ComponentName);
     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);
@@ -5799,6 +5802,7 @@
     method public void setAccountManagementDisabled(android.content.ComponentName, java.lang.String, boolean);
     method public boolean setApplicationHidden(android.content.ComponentName, java.lang.String, boolean);
     method public void setApplicationRestrictions(android.content.ComponentName, java.lang.String, android.os.Bundle);
+    method public void setApplicationRestrictionsManagingPackage(android.content.ComponentName, java.lang.String);
     method public void setAutoTimeRequired(android.content.ComponentName, boolean);
     method public void setBluetoothContactSharingDisabled(android.content.ComponentName, boolean);
     method public void setCameraDisabled(android.content.ComponentName, boolean);
@@ -29312,6 +29316,7 @@
     field public static final java.lang.String GEOCODED_LOCATION = "geocoded_location";
     field public static final int INCOMING_TYPE = 1; // 0x1
     field public static final java.lang.String IS_READ = "is_read";
+    field public static final java.lang.String LAST_MODIFIED = "last_modified";
     field public static final java.lang.String LIMIT_PARAM_KEY = "limit";
     field public static final int MISSED_TYPE = 3; // 0x3
     field public static final java.lang.String NEW = "new";
@@ -30920,6 +30925,7 @@
     field public static final java.lang.String ACTION_LOCATION_SOURCE_SETTINGS = "android.settings.LOCATION_SOURCE_SETTINGS";
     field public static final java.lang.String ACTION_MANAGE_ALL_APPLICATIONS_SETTINGS = "android.settings.MANAGE_ALL_APPLICATIONS_SETTINGS";
     field public static final java.lang.String ACTION_MANAGE_APPLICATIONS_SETTINGS = "android.settings.MANAGE_APPLICATIONS_SETTINGS";
+    field public static final java.lang.String ACTION_MANAGE_DEFAULT_APPS_SETTINGS = "android.settings.MANAGE_DEFAULT_APPS_SETTINGS";
     field public static final java.lang.String ACTION_MANAGE_OVERLAY_PERMISSION = "android.settings.action.MANAGE_OVERLAY_PERMISSION";
     field public static final java.lang.String ACTION_MANAGE_WRITE_SETTINGS = "android.settings.action.MANAGE_WRITE_SETTINGS";
     field public static final java.lang.String ACTION_MEMORY_CARD_SETTINGS = "android.settings.MEMORY_CARD_SETTINGS";
@@ -31581,6 +31587,7 @@
 
   public static final class VoicemailContract.Status implements android.provider.BaseColumns {
     method public static android.net.Uri buildSourceUri(java.lang.String);
+    method public static void setQuota(android.content.Context, android.telecom.PhoneAccountHandle, int, int);
     field public static final java.lang.String CONFIGURATION_STATE = "configuration_state";
     field public static final int CONFIGURATION_STATE_CAN_BE_CONFIGURED = 2; // 0x2
     field public static final int CONFIGURATION_STATE_NOT_CONFIGURED = 1; // 0x1
@@ -31597,6 +31604,9 @@
     field public static final int NOTIFICATION_CHANNEL_STATE_OK = 0; // 0x0
     field public static final java.lang.String PHONE_ACCOUNT_COMPONENT_NAME = "phone_account_component_name";
     field public static final java.lang.String PHONE_ACCOUNT_ID = "phone_account_id";
+    field public static final java.lang.String QUOTA_OCCUPIED = "quota_occupied";
+    field public static final java.lang.String QUOTA_TOTAL = "quota_total";
+    field public static final int QUOTA_UNAVAILABLE = -1; // 0xffffffff
     field public static final java.lang.String SETTINGS_URI = "settings_uri";
     field public static final java.lang.String SOURCE_PACKAGE = "source_package";
     field public static final java.lang.String VOICEMAIL_ACCESS_URI = "voicemail_access_uri";
@@ -31613,6 +31623,7 @@
     field public static final java.lang.String HAS_CONTENT = "has_content";
     field public static final java.lang.String IS_READ = "is_read";
     field public static final java.lang.String ITEM_TYPE = "vnd.android.cursor.item/voicemail";
+    field public static final java.lang.String LAST_MODIFIED = "last_modified";
     field public static final java.lang.String MIME_TYPE = "mime_type";
     field public static final java.lang.String NUMBER = "number";
     field public static final java.lang.String PHONE_ACCOUNT_COMPONENT_NAME = "subscription_component_name";
@@ -33196,13 +33207,20 @@
     field public static final java.lang.String SERVICE_INTERFACE = "android.service.notification.ConditionProviderService";
   }
 
+  public class NotificationAdjustment implements android.os.Parcelable {
+    ctor public NotificationAdjustment(int, java.lang.CharSequence, android.net.Uri);
+    method public int describeContents();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.service.notification.NotificationAdjustment> CREATOR;
+  }
+
   public abstract class NotificationAssistantService extends android.service.notification.NotificationListenerService {
     ctor public NotificationAssistantService();
-    method public final void adjustImportance(java.lang.String, android.service.notification.NotificationAssistantService.Adjustment);
+    method public final void adjustImportance(java.lang.String, android.service.notification.NotificationAdjustment);
     method public final void clearAnnotation(java.lang.String);
     method public void onNotificationActionClick(java.lang.String, long, int);
     method public void onNotificationClick(java.lang.String, long);
-    method public abstract android.service.notification.NotificationAssistantService.Adjustment onNotificationEnqueued(android.service.notification.StatusBarNotification, int, boolean);
+    method public abstract android.service.notification.NotificationAdjustment onNotificationEnqueued(android.service.notification.StatusBarNotification, int, boolean);
     method public void onNotificationRemoved(java.lang.String, long, int);
     method public void onNotificationVisibilityChanged(java.lang.String, long, boolean);
     method public final void setAnnotation(java.lang.String, android.app.Notification);
@@ -33219,10 +33237,7 @@
     field public static final int REASON_PACKAGE_BANNED = 7; // 0x7
     field public static final int REASON_PACKAGE_CHANGED = 5; // 0x5
     field public static final int REASON_USER_STOPPED = 6; // 0x6
-  }
-
-  public class NotificationAssistantService.Adjustment {
-    ctor public NotificationAssistantService.Adjustment(int, java.lang.CharSequence, android.net.Uri);
+    field public static final java.lang.String SERVICE_INTERFACE = "android.service.notification.NotificationAssistantService";
   }
 
   public abstract class NotificationListenerService extends android.app.Service {
@@ -39304,11 +39319,11 @@
     method public int getAction();
     method public android.content.ClipData getClipData();
     method public android.content.ClipDescription getClipDescription();
-    method public android.view.DropPermissionHolder getDropPermissionHolder();
     method public java.lang.Object getLocalState();
     method public boolean getResult();
     method public float getX();
     method public float getY();
+    method public android.view.DropPermissions requestDropPermissions();
     method public void writeToParcel(android.os.Parcel, int);
     field public static final int ACTION_DRAG_ENDED = 4; // 0x4
     field public static final int ACTION_DRAG_ENTERED = 5; // 0x5
@@ -39319,12 +39334,8 @@
     field public static final android.os.Parcelable.Creator<android.view.DragEvent> CREATOR;
   }
 
-  public class DropPermissionHolder implements android.os.Parcelable {
-    method public int describeContents();
-    method public void grant();
-    method public void revoke();
-    method public void writeToParcel(android.os.Parcel, int);
-    field public static final android.os.Parcelable.Creator<android.view.DropPermissionHolder> CREATOR;
+  public final class DropPermissions {
+    method public void release();
   }
 
   public class FocusFinder {
diff --git a/core/java/android/app/ActionBar.java b/core/java/android/app/ActionBar.java
index 72f8c77..3e6b595 100644
--- a/core/java/android/app/ActionBar.java
+++ b/core/java/android/app/ActionBar.java
@@ -31,9 +31,11 @@
 import android.view.Gravity;
 import android.view.KeyEvent;
 import android.view.View;
+import android.view.View.OnFocusChangeListener;
 import android.view.ViewDebug;
 import android.view.ViewGroup;
 import android.view.ViewHierarchyEncoder;
+import android.view.ViewParent;
 import android.view.Window;
 import android.widget.SpinnerAdapter;
 import java.lang.annotation.Retention;
@@ -1071,6 +1073,62 @@
     }
 
     /**
+     * Attempts to move focus to the ActionBar if it does not already contain the focus.
+     *
+     * @return {@code true} if focus changes or {@code false} if focus doesn't change.
+     * @hide
+     */
+    public boolean requestFocus() {
+        return false;
+    }
+
+    /**
+     * Common implementation for requestFocus that takes in the Toolbar and moves focus
+     * to the contents. This makes the ViewGroups containing the toolbar allow focus while it stays
+     * in the ActionBar and then prevents it again once it leaves.
+     *
+     * @param viewGroup The toolbar ViewGroup
+     * @return {@code true} if focus changes or {@code false} if focus doesn't change.
+     * @hide
+     */
+    protected boolean requestFocus(ViewGroup viewGroup) {
+        if (viewGroup != null && !viewGroup.hasFocus()) {
+            final ViewGroup toolbar = viewGroup.getTouchscreenBlocksFocus() ? viewGroup : null;
+            ViewParent parent = viewGroup.getParent();
+            ViewGroup container = null;
+            while (parent != null && parent instanceof ViewGroup) {
+                final ViewGroup vgParent = (ViewGroup) parent;
+                if (vgParent.getTouchscreenBlocksFocus()) {
+                    container = vgParent;
+                    break;
+                }
+                parent = vgParent.getParent();
+            }
+            if (container != null) {
+                container.setTouchscreenBlocksFocus(false);
+            }
+            if (toolbar != null) {
+                toolbar.setTouchscreenBlocksFocus(false);
+            }
+            viewGroup.requestFocus();
+            final View focused = viewGroup.findFocus();
+            if (focused != null) {
+                focused.setOnFocusChangeListener(new FollowOutOfActionBar(viewGroup,
+                        container, toolbar));
+            } else {
+                if (container != null) {
+                    container.setTouchscreenBlocksFocus(true);
+                }
+                if (toolbar != null) {
+                    toolbar.setTouchscreenBlocksFocus(true);
+                }
+            }
+            return true;
+        }
+        return false;
+    }
+
+    /**
      * Listener interface for ActionBar navigation events.
      *
      * @deprecated Action bar navigation modes are deprecated and not supported by inline
@@ -1388,4 +1446,43 @@
             encoder.addProperty("gravity", gravity);
         }
     }
+
+    /**
+     * Tracks the focused View until it leaves the ActionBar, then it resets the
+     * touchscreenBlocksFocus value.
+     */
+    private static class FollowOutOfActionBar implements OnFocusChangeListener, Runnable {
+        private final ViewGroup mFocusRoot;
+        private final ViewGroup mContainer;
+        private final ViewGroup mToolbar;
+
+        public FollowOutOfActionBar(ViewGroup focusRoot, ViewGroup container, ViewGroup toolbar) {
+            mContainer = container;
+            mToolbar = toolbar;
+            mFocusRoot = focusRoot;
+        }
+
+        @Override
+        public void onFocusChange(View v, boolean hasFocus) {
+            if (!hasFocus) {
+                v.setOnFocusChangeListener(null);
+                final View focused = mFocusRoot.findFocus();
+                if (focused != null) {
+                    focused.setOnFocusChangeListener(this);
+                } else {
+                    mFocusRoot.post(this);
+                }
+            }
+        }
+
+        @Override
+        public void run() {
+            if (mContainer != null) {
+                mContainer.setTouchscreenBlocksFocus(true);
+            }
+            if (mToolbar != null) {
+                mToolbar.setTouchscreenBlocksFocus(true);
+            }
+        }
+    }
 }
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 68cf72a..6d72059 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -816,6 +816,7 @@
     SharedElementCallback mExitTransitionListener = SharedElementCallback.NULL_CALLBACK;
 
     private boolean mHasCurrentPermissionsRequest;
+    private boolean mEatKeyUpEvent;
 
     /** Return the intent that started this activity. */
     public Intent getIntent() {
@@ -2827,9 +2828,25 @@
 
         // Let action bars open menus in response to the menu key prioritized over
         // the window handling it
-        if (event.getKeyCode() == KeyEvent.KEYCODE_MENU &&
+        final int keyCode = event.getKeyCode();
+        if (keyCode == KeyEvent.KEYCODE_MENU &&
                 mActionBar != null && mActionBar.onMenuKeyEvent(event)) {
             return true;
+        } else if (keyCode == KeyEvent.KEYCODE_DPAD_UP) {
+            // Capture the Alt-up and send focus to the ActionBar
+            final int action = event.getAction();
+            if (action == KeyEvent.ACTION_DOWN) {
+                if (event.hasModifiers(KeyEvent.META_ALT_ON)) {
+                    final ActionBar actionBar = getActionBar();
+                    if (actionBar != null && actionBar.isShowing() && actionBar.requestFocus()) {
+                        mEatKeyUpEvent = true;
+                        return true;
+                    }
+                }
+            } else if (action == KeyEvent.ACTION_UP && mEatKeyUpEvent) {
+                mEatKeyUpEvent = false;
+                return true;
+            }
         }
 
         Window win = getWindow();
@@ -5300,8 +5317,7 @@
      */
     public boolean isTaskRoot() {
         try {
-            return ActivityManagerNative.getDefault()
-                .getTaskForActivity(mToken, true) >= 0;
+            return ActivityManagerNative.getDefault().getTaskForActivity(mToken, true) >= 0;
         } catch (RemoteException e) {
             return false;
         }
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index c39ee75..8637dde 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -556,14 +556,6 @@
         public static boolean isAlwaysOnTop(int stackId) {
             return stackId == PINNED_STACK_ID;
         }
-
-        /**
-         * Returns true if the application windows in this stack should be displayed above all
-         * other application windows, including during the animation.
-         */
-        public static boolean shouldIncreaseApplicationWindowLayer(int stackId) {
-            return stackId == PINNED_STACK_ID || stackId == DOCKED_STACK_ID;
-        }
     }
 
     /**
@@ -1245,6 +1237,18 @@
     public static final int RECENT_IGNORE_HOME_STACK_TASKS = 0x0008;
 
     /**
+     * Ignores all tasks that are on the docked stack.
+     * @hide
+     */
+    public static final int RECENT_INGORE_DOCKED_STACK_TASKS = 0x0010;
+
+    /**
+     * Ignores all tasks that are on the pinned stack.
+     * @hide
+     */
+    public static final int RECENT_INGORE_PINNED_STACK_TASKS = 0x0020;
+
+    /**
      * <p></p>Return a list of the tasks that the user has recently launched, with
      * the most recent being first and older ones after in order.
      *
diff --git a/core/java/android/app/EnterTransitionCoordinator.java b/core/java/android/app/EnterTransitionCoordinator.java
index 20f3495..c6cc452 100644
--- a/core/java/android/app/EnterTransitionCoordinator.java
+++ b/core/java/android/app/EnterTransitionCoordinator.java
@@ -31,6 +31,7 @@
 import android.view.ViewGroupOverlay;
 import android.view.ViewTreeObserver;
 import android.view.Window;
+import android.view.accessibility.AccessibilityEvent;
 
 import java.util.ArrayList;
 
@@ -500,6 +501,10 @@
     @Override
     protected void onTransitionsComplete() {
         moveSharedElementsFromOverlay();
+        final ViewGroup decorView = getDecor();
+        if (decorView != null) {
+            decorView.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED);
+        }
     }
 
     private void sharedElementTransitionStarted() {
diff --git a/core/java/android/app/INotificationManager.aidl b/core/java/android/app/INotificationManager.aidl
index 136b810..bce5bf3 100644
--- a/core/java/android/app/INotificationManager.aidl
+++ b/core/java/android/app/INotificationManager.aidl
@@ -54,6 +54,7 @@
     int getTopicPriority(String pkg, int uid, in Notification.Topic topic);
     void setTopicImportance(String pkg, int uid, in Notification.Topic topic, int importance);
     int getTopicImportance(String pkg, int uid, in Notification.Topic topic);
+    void setAppImportance(String pkg, int uid, int importance);
 
     // TODO: Remove this when callers have been migrated to the equivalent
     // INotificationListener method.
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 660ce3b..5df6ba8 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -40,8 +40,8 @@
 import android.os.ServiceManager;
 import android.os.UserHandle;
 import android.os.UserManager;
-import android.provider.Settings;
 import android.provider.ContactsContract.Directory;
+import android.provider.Settings;
 import android.security.Credentials;
 import android.service.restrictions.RestrictionsReceiver;
 import android.util.Log;
@@ -56,14 +56,14 @@
 import java.net.InetSocketAddress;
 import java.net.Proxy;
 import java.security.KeyFactory;
+import java.security.NoSuchAlgorithmException;
 import java.security.PrivateKey;
 import java.security.cert.Certificate;
 import java.security.cert.CertificateException;
 import java.security.cert.CertificateFactory;
 import java.security.cert.X509Certificate;
-import java.security.spec.PKCS8EncodedKeySpec;
 import java.security.spec.InvalidKeySpecException;
-import java.security.NoSuchAlgorithmException;
+import java.security.spec.PKCS8EncodedKeySpec;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
@@ -3312,8 +3312,69 @@
     }
 
     /**
-     * Called by a profile or device owner to set the application restrictions for a given target
-     * application running in the profile.
+     * Called by a profile owner or device owner to grant permission to a package to manage
+     * application restrictions for the calling user via {@link #setApplicationRestrictions} and
+     * {@link #getApplicationRestrictions}.
+     * <p>
+     * This permission is persistent until it is later cleared by calling this method with a
+     * {@code null} value or uninstalling the managing package.
+     *
+     * @param admin Which {@link DeviceAdminReceiver} this request is associated with.
+     * @param packageName The package name which will be given access to application restrictions
+     * APIs. If {@code null} is given the current package will be cleared.
+     */
+    public void setApplicationRestrictionsManagingPackage(@NonNull ComponentName admin,
+            @Nullable String packageName) {
+        if (mService != null) {
+            try {
+                mService.setApplicationRestrictionsManagingPackage(admin, packageName);
+            } catch (RemoteException e) {
+                Log.w(TAG, REMOTE_EXCEPTION_MESSAGE, e);
+            }
+        }
+    }
+
+    /**
+     * Called by a profile owner or device owner to retrieve the application restrictions managing
+     * package for the current user, or {@code null} if none is set.
+     *
+     * @param admin Which {@link DeviceAdminReceiver} this request is associated with.
+     * @return The package name allowed to manage application restrictions on the current user, or
+     * {@code null} if none is set.
+     */
+    public String getApplicationRestrictionsManagingPackage(@NonNull ComponentName admin) {
+        if (mService != null) {
+            try {
+                return mService.getApplicationRestrictionsManagingPackage(admin);
+            } catch (RemoteException e) {
+                Log.w(TAG, REMOTE_EXCEPTION_MESSAGE, e);
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Returns {@code true} if the calling package has been granted permission via
+     * {@link #setApplicationRestrictionsManagingPackage} to manage application
+     * restrictions for the calling user.
+     */
+    public boolean isCallerApplicationRestrictionsManagingPackage() {
+        if (mService != null) {
+            try {
+                return mService.isCallerApplicationRestrictionsManagingPackage();
+            } catch (RemoteException e) {
+                Log.w(TAG, REMOTE_EXCEPTION_MESSAGE, e);
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Sets the application restrictions for a given target application running in the calling user.
+     *
+     * <p>The caller must be a profile or device owner on that user, or the package allowed to
+     * manage application restrictions via {@link #setApplicationRestrictionsManagingPackage};
+     * otherwise a security exception will be thrown.
      *
      * <p>The provided {@link Bundle} consists of key-value pairs, where the types of values may be:
      * <ul>
@@ -3323,24 +3384,25 @@
      * <li>From {@link android.os.Build.VERSION_CODES#M}, {@code Bundle} or {@code Bundle[]}
      * </ul>
      *
-     * <p>The application restrictions are only made visible to the target application and the
-     * profile or device owner.
-     *
      * <p>If the restrictions are not available yet, but may be applied in the near future,
-     * the admin can notify the target application of that by adding
+     * the caller can notify the target application of that by adding
      * {@link UserManager#KEY_RESTRICTIONS_PENDING} to the settings parameter.
      *
-     * <p>The calling device admin must be a profile or device owner; if it is not, a security
-     * exception will be thrown.
+     * <p>The application restrictions are only made visible to the target application via
+     * {@link UserManager#getApplicationRestrictions(String)}, in addition to the profile or
+     * device owner, and the application restrictions managing package via
+     * {@link #getApplicationRestrictions}.
      *
-     * @param admin Which {@link DeviceAdminReceiver} this request is associated with.
+     * @param admin Which {@link DeviceAdminReceiver} this request is associated with, or
+     * {@code null} if called by the application restrictions managing package.
      * @param packageName The name of the package to update restricted settings for.
      * @param settings A {@link Bundle} to be parsed by the receiving application, conveying a new
      * set of active restrictions.
      *
+     * @see #setApplicationRestrictionsManagingPackage
      * @see UserManager#KEY_RESTRICTIONS_PENDING
      */
-    public void setApplicationRestrictions(@NonNull ComponentName admin, String packageName,
+    public void setApplicationRestrictions(@Nullable ComponentName admin, String packageName,
             Bundle settings) {
         if (mService != null) {
             try {
@@ -3896,19 +3958,23 @@
     }
 
     /**
-     * Called by a profile or device owner to get the application restrictions for a given target
-     * application running in the profile.
+     * Retrieves the application restrictions for a given target application running in the calling
+     * user.
      *
-     * <p>The calling device admin must be a profile or device owner; if it is not, a security
-     * exception will be thrown.
+     * <p>The caller must be a profile or device owner on that user, or the package allowed to
+     * manage application restrictions via {@link #setApplicationRestrictionsManagingPackage};
+     * otherwise a security exception will be thrown.
      *
-     * @param admin Which {@link DeviceAdminReceiver} this request is associated with.
+     * @param admin Which {@link DeviceAdminReceiver} this request is associated with, or
+     * {@code null} if called by the application restrictions managing package.
      * @param packageName The name of the package to fetch restricted settings of.
      * @return {@link Bundle} of settings corresponding to what was set last time
      * {@link DevicePolicyManager#setApplicationRestrictions} was called, or an empty {@link Bundle}
      * if no restrictions have been set.
+     *
+     * @see {@link #setApplicationRestrictionsManagingPackage}
      */
-    public Bundle getApplicationRestrictions(@NonNull ComponentName admin, String packageName) {
+    public Bundle getApplicationRestrictions(@Nullable ComponentName admin, String packageName) {
         if (mService != null) {
             try {
                 return mService.getApplicationRestrictions(admin, packageName);
@@ -4721,4 +4787,15 @@
             return null;
         }
     }
+
+    /**
+     * Called by device owner to reboot the device.
+     */
+    public void reboot(@NonNull ComponentName admin) {
+        try {
+            mService.reboot(admin);
+        } catch (RemoteException re) {
+            Log.w(TAG, REMOTE_EXCEPTION_MESSAGE, re);
+        }
+    }
 }
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index d8cc2ea..30ce682 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -149,6 +149,9 @@
 
     void setApplicationRestrictions(in ComponentName who, in String packageName, in Bundle settings);
     Bundle getApplicationRestrictions(in ComponentName who, in String packageName);
+    void setApplicationRestrictionsManagingPackage(in ComponentName admin, in String packageName);
+    String getApplicationRestrictionsManagingPackage(in ComponentName admin);
+    boolean isCallerApplicationRestrictionsManagingPackage();
 
     void setRestrictionsProvider(in ComponentName who, in ComponentName provider);
     ComponentName getRestrictionsProvider(int userHandle);
@@ -241,4 +244,5 @@
     boolean isManagedProfile(in ComponentName admin);
     boolean isSystemOnlyUser(in ComponentName admin);
     String getWifiMacAddress();
+    void reboot(in ComponentName admin);
 }
diff --git a/core/java/android/bluetooth/BluetoothProfile.java b/core/java/android/bluetooth/BluetoothProfile.java
index cbce22c..6bf3fab 100644
--- a/core/java/android/bluetooth/BluetoothProfile.java
+++ b/core/java/android/bluetooth/BluetoothProfile.java
@@ -131,6 +131,18 @@
     public static final int HEADSET_CLIENT = 16;
 
     /**
+     * HID Profile
+     * @hide
+     */
+    public static final int HID = 17;
+
+    /**
+     * HDP Profile
+     * @hide
+     */
+    public static final int HDP = 18;
+
+    /**
      * Default priority for devices that we try to auto-connect to and
      * and allow incoming connections for the profile
      * @hide
diff --git a/core/java/android/content/ClipData.java b/core/java/android/content/ClipData.java
index c934e8d..0b80f8a 100644
--- a/core/java/android/content/ClipData.java
+++ b/core/java/android/content/ClipData.java
@@ -36,6 +36,7 @@
 import java.io.IOException;
 import java.io.InputStreamReader;
 import java.util.ArrayList;
+import java.util.List;
 
 /**
  * Representation of a clipped data on the clipboard.
@@ -914,6 +915,27 @@
         }
     }
 
+    /** @hide */
+    public void collectUris(List<Uri> out) {
+        for (int i = 0; i < mItems.size(); ++i) {
+            ClipData.Item item = getItemAt(i);
+
+            if (item.getUri() != null) {
+                out.add(item.getUri());
+            }
+
+            Intent intent = item.getIntent();
+            if (intent != null) {
+                if (intent.getData() != null) {
+                    out.add(intent.getData());
+                }
+                if (intent.getClipData() != null) {
+                    intent.getClipData().collectUris(out);
+                }
+            }
+        }
+    }
+
     @Override
     public int describeContents() {
         return 0;
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 38a4475..67bdad5 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -326,10 +326,30 @@
      */
     public static final int BIND_NOT_VISIBLE = 0x40000000;
 
-    /** Return an AssetManager instance for your application's package. */
+    /**
+     * Returns an AssetManager instance for the application's package.
+     * <p>
+     * <strong>Note:</strong> Implementations of this method should return
+     * an AssetManager instance that is consistent with the Resources instance
+     * returned by {@link #getResources()}. For example, they should share the
+     * same {@link Configuration} object.
+     *
+     * @return an AssetManager instance for the application's package
+     * @see #getResources()
+     */
     public abstract AssetManager getAssets();
 
-    /** Return a Resources instance for your application's package. */
+    /**
+     * Returns a Resources instance for the application's package.
+     * <p>
+     * <strong>Note:</strong> Implementations of this method should return
+     * a Resources instance that is consistent with the AssetManager instance
+     * returned by {@link #getAssets()}. For example, they should share the
+     * same {@link Configuration} object.
+     *
+     * @return a Resources instance for the application's package
+     * @see #getAssets()
+     */
     public abstract Resources getResources();
 
     /** Return PackageManager instance to find global package information. */
diff --git a/core/java/android/content/ContextWrapper.java b/core/java/android/content/ContextWrapper.java
index 1a3d262..c99ddc8 100644
--- a/core/java/android/content/ContextWrapper.java
+++ b/core/java/android/content/ContextWrapper.java
@@ -82,8 +82,7 @@
     }
 
     @Override
-    public Resources getResources()
-    {
+    public Resources getResources() {
         return mBase.getResources();
     }
 
diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java
index d6d395b..3283005 100644
--- a/core/java/android/content/pm/PackageInstaller.java
+++ b/core/java/android/content/pm/PackageInstaller.java
@@ -1042,12 +1042,6 @@
         }
 
         /** {@hide} */
-        @SystemApi
-        public void setInstallFlagsQuick() {
-            installFlags |= PackageManager.INSTALL_QUICK;
-        }
-
-        /** {@hide} */
         public void dump(IndentingPrintWriter pw) {
             pw.printPair("mode", mode);
             pw.printHexPair("installFlags", installFlags);
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 38242fb..3235bcf 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -457,19 +457,11 @@
 
     /**
      * Flag parameter for {@link #installPackage} to indicate that this package is
-     * to be installed quickly.
-     *
-     * @hide
-     */
-    public static final int INSTALL_QUICK = 0x00000800;
-
-    /**
-     * Flag parameter for {@link #installPackage} to indicate that this package is
      * to be installed as a lightweight "ephemeral" app.
      *
      * @hide
      */
-    public static final int INSTALL_EPHEMERAL = 0x00001000;
+    public static final int INSTALL_EPHEMERAL = 0x00000800;
 
     /**
      * Flag parameter for
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index 019ed2b..f445cf8 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -625,9 +625,7 @@
     public final static int PARSE_COLLECT_CERTIFICATES = 1<<8;
     public final static int PARSE_TRUSTED_OVERLAY = 1<<9;
     public final static int PARSE_ENFORCE_CODE = 1<<10;
-    // TODO: fix b/25118622; remove this entirely once signature processing is quick
-    public final static int PARSE_SKIP_VERIFICATION = 1<<11;
-    public final static int PARSE_IS_EPHEMERAL = 1<<12;
+    public final static int PARSE_IS_EPHEMERAL = 1<<11;
 
     private static final Comparator<String> sSplitNameComparator = new SplitNameComparator();
 
@@ -1060,8 +1058,7 @@
 
     /**
      * Collect certificates from all the APKs described in the given package,
-     * populating {@link Package#mSignatures}.
-     * <p>Depending upon the parser flags, this may also asserts that all APK
+     * populating {@link Package#mSignatures}. Also asserts that all APK
      * contents are signed correctly and consistently.
      */
     public void collectCertificates(Package pkg, int parseFlags) throws PackageParserException {
@@ -1084,10 +1081,8 @@
         final boolean hasCode = (apkFlags & ApplicationInfo.FLAG_HAS_CODE) != 0;
         final boolean requireCode = ((parseFlags & PARSE_ENFORCE_CODE) != 0) && hasCode;
         final String apkPath = apkFile.getAbsolutePath();
-        final boolean skipVerification = Build.IS_DEBUGGABLE
-                && ((parseFlags & PARSE_SKIP_VERIFICATION) != 0);
 
-        boolean codeFound = skipVerification;
+        boolean codeFound = false;
         StrictJarFile jarFile = null;
         try {
             Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "strictJarFileCtor");
@@ -1106,7 +1101,7 @@
             toVerify.add(manifestEntry);
 
             // If we're parsing an untrusted package, verify all contents
-            if (!skipVerification && (parseFlags & PARSE_IS_SYSTEM) == 0) {
+            if ((parseFlags & PARSE_IS_SYSTEM) == 0) {
                 final Iterator<ZipEntry> i = jarFile.iterator();
                 while (i.hasNext()) {
                     final ZipEntry entry = i.next();
@@ -1150,9 +1145,6 @@
                     for (int i=0; i < entryCerts.length; i++) {
                         pkg.mSigningKeys.add(entryCerts[i][0].getPublicKey());
                     }
-                    if (skipVerification) {
-                        break;
-                    }
                 } else {
                     if (!Signature.areExactMatch(pkg.mSignatures, entrySignatures)) {
                         throw new PackageParserException(
@@ -1218,9 +1210,7 @@
             if ((flags & PARSE_COLLECT_CERTIFICATES) != 0) {
                 // TODO: factor signature related items out of Package object
                 final Package tempPkg = new Package(null);
-                // TODO: fix b/25118622; pass in '0' for parse flags
-                collectCertificates(tempPkg, apkFile, 0 /*apkFlags*/,
-                        flags & PARSE_SKIP_VERIFICATION);
+                collectCertificates(tempPkg, apkFile, 0 /*apkFlags*/, 0 /*flags*/);
                 signatures = tempPkg.mSignatures;
             } else {
                 signatures = null;
diff --git a/core/java/android/os/BaseBundle.java b/core/java/android/os/BaseBundle.java
index cd483b1..ee7bd9a 100644
--- a/core/java/android/os/BaseBundle.java
+++ b/core/java/android/os/BaseBundle.java
@@ -32,7 +32,9 @@
     private static final String TAG = "Bundle";
     static final boolean DEBUG = false;
 
+    // Keep in sync with frameworks/native/libs/binder/PersistableBundle.cpp.
     static final int BUNDLE_MAGIC = 0x4C444E42; // 'B' 'N' 'D' 'L'
+
     static final Parcel EMPTY_PARCEL;
 
     static {
@@ -1308,6 +1310,8 @@
      * @param parcel The parcel to copy this bundle to.
      */
     void writeToParcelInner(Parcel parcel, int flags) {
+        // Keep implementation in sync with writeToParcel() in
+        // frameworks/native/libs/binder/PersistableBundle.cpp.
         if (mParcelledData != null) {
             if (mParcelledData == EMPTY_PARCEL) {
                 parcel.writeInt(0);
@@ -1345,6 +1349,8 @@
      * @param parcel The parcel to overwrite this bundle from.
      */
     void readFromParcelInner(Parcel parcel) {
+        // Keep implementation in sync with readFromParcel() in
+        // frameworks/native/libs/binder/PersistableBundle.cpp.
         int length = parcel.readInt();
         readFromParcelInner(parcel, length);
     }
diff --git a/core/java/android/os/Environment.java b/core/java/android/os/Environment.java
index a01d34a..2ca9ab8a 100644
--- a/core/java/android/os/Environment.java
+++ b/core/java/android/os/Environment.java
@@ -37,6 +37,7 @@
     private static final String ENV_ANDROID_STORAGE = "ANDROID_STORAGE";
     private static final String ENV_DOWNLOAD_CACHE = "DOWNLOAD_CACHE";
     private static final String ENV_OEM_ROOT = "OEM_ROOT";
+    private static final String ENV_ODM_ROOT = "ODM_ROOT";
     private static final String ENV_VENDOR_ROOT = "VENDOR_ROOT";
 
     /** {@hide} */
@@ -56,6 +57,7 @@
     private static final File DIR_ANDROID_STORAGE = getDirectory(ENV_ANDROID_STORAGE, "/storage");
     private static final File DIR_DOWNLOAD_CACHE = getDirectory(ENV_DOWNLOAD_CACHE, "/cache");
     private static final File DIR_OEM_ROOT = getDirectory(ENV_OEM_ROOT, "/oem");
+    private static final File DIR_ODM_ROOT = getDirectory(ENV_ODM_ROOT, "/odm");
     private static final File DIR_VENDOR_ROOT = getDirectory(ENV_VENDOR_ROOT, "/vendor");
 
     private static UserEnvironment sCurrentUser;
@@ -156,6 +158,16 @@
     }
 
     /**
+     * Return root directory of the "odm" partition holding ODM customizations,
+     * if any. If present, the partition is mounted read-only.
+     *
+     * @hide
+     */
+    public static File getOdmDirectory() {
+        return DIR_ODM_ROOT;
+    }
+
+    /**
      * Return root directory of the "vendor" partition that holds vendor-provided
      * software that should persist across simple reflashing of the "system" partition.
      * @hide
diff --git a/core/java/android/os/Parcel.java b/core/java/android/os/Parcel.java
index 9b68f90..2631247 100644
--- a/core/java/android/os/Parcel.java
+++ b/core/java/android/os/Parcel.java
@@ -204,6 +204,7 @@
     private static final Parcel[] sOwnedPool = new Parcel[POOL_SIZE];
     private static final Parcel[] sHolderPool = new Parcel[POOL_SIZE];
 
+    // Keep in sync with frameworks/native/libs/binder/PersistableBundle.cpp.
     private static final int VAL_NULL = -1;
     private static final int VAL_STRING = 0;
     private static final int VAL_INTEGER = 1;
@@ -704,6 +705,8 @@
             writeInt(-1);
             return;
         }
+        // Keep the format of this Parcel in sync with writeToParcelInner() in
+        // frameworks/native/libs/binder/PersistableBundle.cpp.
         final int N = val.size();
         writeInt(N);
         if (DEBUG_ARRAY_MAP) {
@@ -1370,7 +1373,13 @@
             // Must be before Parcelable
             writeInt(VAL_BUNDLE);
             writeBundle((Bundle) v);
+        } else if (v instanceof PersistableBundle) {
+            writeInt(VAL_PERSISTABLEBUNDLE);
+            writePersistableBundle((PersistableBundle) v);
         } else if (v instanceof Parcelable) {
+            // IMPOTANT: cases for classes that implement Parcelable must
+            // come before the Parcelable case, so that their specific VAL_*
+            // types will be written.
             writeInt(VAL_PARCELABLE);
             writeParcelable((Parcelable) v, 0);
         } else if (v instanceof Short) {
@@ -1426,9 +1435,6 @@
         } else if (v instanceof Byte) {
             writeInt(VAL_BYTE);
             writeInt((Byte) v);
-        } else if (v instanceof PersistableBundle) {
-            writeInt(VAL_PERSISTABLEBUNDLE);
-            writePersistableBundle((PersistableBundle) v);
         } else if (v instanceof Size) {
             writeInt(VAL_SIZE);
             writeSize((Size) v);
diff --git a/core/java/android/os/PersistableBundle.aidl b/core/java/android/os/PersistableBundle.aidl
index 5b05873..94e8607 100644
--- a/core/java/android/os/PersistableBundle.aidl
+++ b/core/java/android/os/PersistableBundle.aidl
@@ -2,19 +2,19 @@
 **
 ** Copyright 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 
+** 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.
 */
 
 package android.os;
 
-parcelable PersistableBundle;
+parcelable PersistableBundle cpp_header "binder/PersistableBundle.h";
diff --git a/core/java/android/os/PowerManager.java b/core/java/android/os/PowerManager.java
index a0a0060..126824f 100644
--- a/core/java/android/os/PowerManager.java
+++ b/core/java/android/os/PowerManager.java
@@ -389,6 +389,13 @@
     public static final String REBOOT_RECOVERY = "recovery";
 
     /**
+     * The value to pass as the 'reason' argument to reboot() when device owner requests a reboot on
+     * the device.
+     * @hide
+     */
+    public static final String REBOOT_REQUESTED_BY_DEVICE_OWNER = "deviceowner";
+
+    /**
      * The value to pass as the 'reason' argument to android_reboot().
      * @hide
      */
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index c5adafe..ddd16e2 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -852,9 +852,14 @@
      * @param user to retrieve the unlocked state for.
      */
     public boolean isUserUnlocked(UserHandle user) {
+        return isUserUnlocked(user.getIdentifier());
+    }
+
+    /** {@hide} */
+    public boolean isUserUnlocked(int userId) {
         try {
-            return ActivityManagerNative.getDefault().isUserRunning(
-                    user.getIdentifier(), ActivityManager.FLAG_AND_UNLOCKED);
+            return ActivityManagerNative.getDefault().isUserRunning(userId,
+                    ActivityManager.FLAG_AND_UNLOCKED);
         } catch (RemoteException e) {
             return false;
         }
diff --git a/core/java/android/provider/CallLog.java b/core/java/android/provider/CallLog.java
index 1d4d572..6a5d857c 100644
--- a/core/java/android/provider/CallLog.java
+++ b/core/java/android/provider/CallLog.java
@@ -421,6 +421,13 @@
         public static final String ADD_FOR_ALL_USERS = "add_for_all_users";
 
         /**
+         * The date the row is last inserted, updated, or marked as deleted, in milliseconds
+         * since the epoch. Read only.
+         * <P>Type: INTEGER (long)</P>
+         */
+        public static final String LAST_MODIFIED = "last_modified";
+
+        /**
          * If a successful call is made that is longer than this duration, update the phone number
          * in the ContactsProvider with the normalized version of the number, based on the user's
          * current country code.
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index a1e5510..b883f9c 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -1092,6 +1092,22 @@
     public static final String ACTION_HOME_SETTINGS
             = "android.settings.HOME_SETTINGS";
 
+
+
+    /**
+     * Activity Action: Show Default apps settings.
+     * <p>
+     * In some cases, a matching Activity may not exist, so ensure you
+     * safeguard against this.
+     * <p>
+     * Input: Nothing.
+     * <p>
+     * Output: Nothing.
+     */
+    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+    public static final String ACTION_MANAGE_DEFAULT_APPS_SETTINGS
+            = "android.settings.MANAGE_DEFAULT_APPS_SETTINGS";
+
     /**
      * Activity Action: Show notification settings.
      *
@@ -5617,6 +5633,15 @@
         public static final String ASSIST_SCREENSHOT_ENABLED = "assist_screenshot_enabled";
 
         /**
+         * Names of the service component that the current user has explicitly allowed to
+         * see and change the importance of all of the user's notifications.
+         *
+         * @hide
+         */
+        public static final String ENABLED_NOTIFICATION_ASSISTANT
+                = "enabled_notification_assistant";
+
+        /**
          * Names of the service components that the current user has explicitly allowed to
          * see all of the user's notifications, separated by ':'.
          *
@@ -6108,6 +6133,13 @@
         public static final String AIRPLANE_MODE_TOGGLEABLE_RADIOS = "airplane_mode_toggleable_radios";
 
         /**
+         * A Long representing a bitmap of profiles that should be disabled when bluetooth starts.
+         * See {@link android.bluetooth.BluetoothProfile}.
+         * {@hide}
+         */
+        public static final String BLUETOOTH_DISABLED_PROFILES = "bluetooth_disabled_profiles";
+
+        /**
          * The policy for deciding when Wi-Fi should go to sleep (which will in
          * turn switch to using the mobile data as an Internet connection).
          * <p>
diff --git a/core/java/android/provider/VoicemailContract.java b/core/java/android/provider/VoicemailContract.java
index 76eaea9..24683cb 100644
--- a/core/java/android/provider/VoicemailContract.java
+++ b/core/java/android/provider/VoicemailContract.java
@@ -245,6 +245,13 @@
         public static final String DELETED = "deleted";
 
         /**
+         * The date the row is last inserted, updated, or marked as deleted, in milliseconds
+         * since the epoch. Read only.
+         * <P>Type: INTEGER (long)</P>
+         */
+        public static final String LAST_MODIFIED = "last_modified";
+
+        /**
          * A convenience method to build voicemail URI specific to a source package by appending
          * {@link VoicemailContract#PARAM_KEY_SOURCE_PACKAGE} param to the base URI.
          */
@@ -449,6 +456,26 @@
         public static final int NOTIFICATION_CHANNEL_STATE_MESSAGE_WAITING = 2;
 
         /**
+         * Amount of resource that is used by existing voicemail in the visual voicemail inbox,
+         * or {@link #QUOTA_UNAVAILABLE}. Unit is not specified.
+         * <P>Type: INTEGER</P>
+         */
+        public static final String QUOTA_OCCUPIED = "quota_occupied";
+
+        /**
+         * Total resource in the visual voicemail inbox that can be used, or
+         * {@link #QUOTA_UNAVAILABLE}. Unit is not specified.
+         * <P>Type: INTEGER</P>
+         */
+        public static final String QUOTA_TOTAL = "quota_total";
+
+        /**
+         * Value for {@link #QUOTA_OCCUPIED} and {@link #QUOTA_TOTAL} to indicate that no
+         * information is available.
+         */
+        public static final int QUOTA_UNAVAILABLE = -1;
+
+        /**
          * A convenience method to build status URI specific to a source package by appending
          * {@link VoicemailContract#PARAM_KEY_SOURCE_PACKAGE} param to the base URI.
          */
@@ -488,6 +515,39 @@
         }
 
         /**
+         * A helper method to set the quota of a voicemail source. Unit is unspecified.
+         *
+         * @param context The context from the package calling the method. This will be the source.
+         * @param accountHandle The handle for the account the source is associated with.
+         * @param occupied See {@link Status#QUOTA_OCCUPIED}
+         * @param total See {@link Status#QUOTA_TOTAL}
+         */
+        public static void setQuota(Context context, PhoneAccountHandle accountHandle, int occupied,
+                int total) {
+            if (occupied == QUOTA_UNAVAILABLE && total == QUOTA_UNAVAILABLE) {
+                return;
+            }
+            ContentValues values = new ContentValues();
+            values.put(Status.PHONE_ACCOUNT_COMPONENT_NAME,
+                    accountHandle.getComponentName().flattenToString());
+            values.put(Status.PHONE_ACCOUNT_ID, accountHandle.getId());
+            if (occupied != QUOTA_UNAVAILABLE) {
+                values.put(Status.QUOTA_OCCUPIED,occupied);
+            }
+            if (total != QUOTA_UNAVAILABLE) {
+                values.put(Status.QUOTA_TOTAL,total);
+            }
+
+            ContentResolver contentResolver = context.getContentResolver();
+            Uri statusUri = buildSourceUri(context.getPackageName());
+            if (isStatusPresent(contentResolver, statusUri)) {
+                contentResolver.update(statusUri, values, null, null);
+            } else {
+                contentResolver.insert(statusUri, values);
+            }
+        }
+
+        /**
          * Determines if a voicemail source exists in the status table.
          *
          * @param contentResolver A content resolver constructed from the appropriate context.
diff --git a/core/java/android/service/notification/INotificationAssistant.aidl b/core/java/android/service/notification/INotificationAssistant.aidl
new file mode 100644
index 0000000..5c5f358
--- /dev/null
+++ b/core/java/android/service/notification/INotificationAssistant.aidl
@@ -0,0 +1,37 @@
+/**
+ * Copyright (c) 2015, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.notification;
+
+import android.service.notification.NotificationAdjustment;
+import android.service.notification.IStatusBarNotificationHolder;
+import android.service.notification.NotificationRankingUpdate;
+
+/** @hide */
+interface INotificationAssistant
+{
+    void onListenerConnected(in NotificationRankingUpdate update);
+    void onNotificationPosted(in IStatusBarNotificationHolder notificationHolder,
+            in NotificationRankingUpdate update);
+    void onNotificationRankingUpdate(in NotificationRankingUpdate update);
+    void onListenerHintsChanged(int hints);
+    void onInterruptionFilterChanged(int interruptionFilter);
+    NotificationAdjustment onNotificationEnqueued(in IStatusBarNotificationHolder notificationHolder, int importance, boolean user);
+    void onNotificationVisibilityChanged(String key, long time, boolean visible);
+    void onNotificationClick(String key, long time);
+    void onNotificationActionClick(String key, long time, int actionIndex);
+    void onNotificationRemoved(String key, long time, int reason);
+}
\ No newline at end of file
diff --git a/core/java/android/service/notification/NotificationAdjustment.aidl b/core/java/android/service/notification/NotificationAdjustment.aidl
new file mode 100644
index 0000000..805fe2c
--- /dev/null
+++ b/core/java/android/service/notification/NotificationAdjustment.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.service.notification;
+
+parcelable NotificationAdjustment;
\ No newline at end of file
diff --git a/core/java/android/service/notification/NotificationAdjustment.java b/core/java/android/service/notification/NotificationAdjustment.java
new file mode 100644
index 0000000..c5f0db9
--- /dev/null
+++ b/core/java/android/service/notification/NotificationAdjustment.java
@@ -0,0 +1,57 @@
+package android.service.notification;
+
+import android.net.Uri;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+public class NotificationAdjustment implements Parcelable {
+    int mImportance;
+    CharSequence mExplanation;
+    Uri mReference;
+
+    /**
+     * Create a notification importance adjustment.
+     *
+     * @param importance The final importance of the notification.
+     * @param explanation A human-readable justification for the adjustment.
+     * @param reference A reference to an external object that augments the
+     *                  explanation, such as a
+     *                  {@link android.provider.ContactsContract.Contacts#CONTENT_LOOKUP_URI},
+     *                  or null.
+     */
+    public NotificationAdjustment(int importance, CharSequence explanation, Uri reference) {
+        mImportance = importance;
+        mExplanation = explanation;
+        mReference = reference;
+    }
+
+    private NotificationAdjustment(Parcel source) {
+        this(source.readInt(), source.readCharSequence(),
+                (Uri) source.readParcelable(NotificationAdjustment.class.getClassLoader()));
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeInt(mImportance);
+        dest.writeCharSequence(mExplanation);
+        dest.writeParcelable(mReference, 0);
+    }
+
+    public static final Parcelable.Creator<NotificationAdjustment> CREATOR
+            = new Parcelable.Creator<NotificationAdjustment>() {
+        @Override
+        public NotificationAdjustment createFromParcel(Parcel source) {
+            return new NotificationAdjustment(source);
+        }
+
+        @Override
+        public NotificationAdjustment[] newArray(int size) {
+            return new NotificationAdjustment[size];
+        }
+    };
+}
diff --git a/core/java/android/service/notification/NotificationAssistantService.java b/core/java/android/service/notification/NotificationAssistantService.java
index 5d1317c..7ce5e1f 100644
--- a/core/java/android/service/notification/NotificationAssistantService.java
+++ b/core/java/android/service/notification/NotificationAssistantService.java
@@ -16,8 +16,12 @@
 
 package android.service.notification;
 
+import android.annotation.SdkConstant;
 import android.app.Notification;
+import android.content.Intent;
 import android.net.Uri;
+import android.os.Parcel;
+import android.os.Parcelable;
 
 /**
  * A service that helps the user manage notifications by modifying the
@@ -35,6 +39,13 @@
  * &lt;/service></pre>
  */
 public abstract class NotificationAssistantService extends NotificationListenerService {
+    /**
+     * The {@link Intent} that must be declared as handled by the service.
+     */
+    @SdkConstant(SdkConstant.SdkConstantType.SERVICE_ACTION)
+    public static final String SERVICE_INTERFACE
+            = "android.service.notification.NotificationAssistantService";
+
     /** Notification was canceled by the status bar reporting a click. */
     public static final int REASON_DELEGATE_CLICK = 1;
 
@@ -74,28 +85,6 @@
     /** Notification was canceled because it was an invisible member of a group. */
     public static final int REASON_GROUP_OPTIMIZATION = 13;
 
-    public class Adjustment {
-        int mImportance;
-        CharSequence mExplanation;
-        Uri mReference;
-
-        /**
-         * Create a notification importance adjustment.
-         *
-         * @param importance The final importance of the notification.
-         * @param explanation A human-readable justification for the adjustment.
-         * @param reference A reference to an external object that augments the
-         *                  explanation, such as a
-         *                  {@link android.provider.ContactsContract.Contacts#CONTENT_LOOKUP_URI},
-         *                  or null.
-         */
-        public Adjustment(int importance, CharSequence explanation, Uri reference) {
-            mImportance = importance;
-            mExplanation = explanation;
-            mReference = reference;
-        }
-    }
-
     /**
      * A notification was posted by an app. Called before alert.
      *
@@ -104,7 +93,7 @@
      * @param user true if the initial importance reflects an explicit user preference.
      * @return an adjustment or null to take no action, within 100ms.
      */
-    abstract public Adjustment onNotificationEnqueued(StatusBarNotification sbn,
+    abstract public NotificationAdjustment onNotificationEnqueued(StatusBarNotification sbn,
           int importance, boolean user);
 
     /**
@@ -161,7 +150,7 @@
      * @param key the notification key
      * @param adjustment the new importance with an explanation
      */
-    public final void adjustImportance(String key, Adjustment adjustment)
+    public final void adjustImportance(String key, NotificationAdjustment adjustment)
     {
         // TODO: pack up the adjustment and send it to the NotificationManager.
     }
diff --git a/core/java/android/service/quicksettings/TileService.java b/core/java/android/service/quicksettings/TileService.java
index fd2d5b0..d8787b4 100644
--- a/core/java/android/service/quicksettings/TileService.java
+++ b/core/java/android/service/quicksettings/TileService.java
@@ -77,6 +77,15 @@
     private Tile mTile;
     private IBinder mToken;
 
+    @Override
+    public void onDestroy() {
+        if (mListening) {
+            onStopListening();
+            mListening = false;
+        }
+        super.onDestroy();
+    }
+
     /**
      * Called when the user adds this tile to Quick Settings.
      * <p/>
@@ -197,10 +206,10 @@
                     mTile = (Tile) msg.obj;
                     break;
                 case MSG_TILE_ADDED:
-                    TileService.this.onTileRemoved();
+                    TileService.this.onTileAdded();
                     break;
                 case MSG_TILE_REMOVED:
-                    TileService.this.onTileAdded();
+                    TileService.this.onTileRemoved();
                     break;
                 case MSG_STOP_LISTENING:
                     if (mListening) {
diff --git a/core/java/android/util/StateSet.java b/core/java/android/util/StateSet.java
index c2a6a7a..051de8a 100644
--- a/core/java/android/util/StateSet.java
+++ b/core/java/android/util/StateSet.java
@@ -260,6 +260,12 @@
             case R.attr.state_enabled:
                 sb.append("E ");
                 break;
+            case R.attr.state_checked:
+                sb.append("C ");
+                break;
+            case R.attr.state_activated:
+                sb.append("A ");
+                break;
             }
         }
 
diff --git a/core/java/android/view/ContextThemeWrapper.java b/core/java/android/view/ContextThemeWrapper.java
index ea0873d..4888877 100644
--- a/core/java/android/view/ContextThemeWrapper.java
+++ b/core/java/android/view/ContextThemeWrapper.java
@@ -104,11 +104,15 @@
     @Override
     public AssetManager getAssets() {
         // Ensure we're returning assets with the correct configuration.
-        return getResources().getAssets();
+        return getResourcesInternal().getAssets();
     }
 
     @Override
     public Resources getResources() {
+        return getResourcesInternal();
+    }
+
+    private Resources getResourcesInternal() {
         if (mResources == null) {
             if (mOverrideConfiguration == null) {
                 mResources = super.getResources();
@@ -117,7 +121,6 @@
                 mResources = resContext.getResources();
             }
         }
-
         return mResources;
     }
 
diff --git a/core/java/android/view/DragEvent.java b/core/java/android/view/DragEvent.java
index 34835f4..5903d4a 100644
--- a/core/java/android/view/DragEvent.java
+++ b/core/java/android/view/DragEvent.java
@@ -21,6 +21,8 @@
 import android.os.Parcel;
 import android.os.Parcelable;
 
+import com.android.internal.view.IDropPermissions;
+
 //TODO: Improve Javadoc
 /**
  * Represents an event that is sent out by the system at various times during a drag and drop
@@ -128,7 +130,7 @@
     float mX, mY;
     ClipDescription mClipDescription;
     ClipData mClipData;
-    DropPermissionHolder mDropPermissionHolder;
+    IDropPermissions mDropPermissions;
 
     Object mLocalState;
     boolean mDragResult;
@@ -146,7 +148,7 @@
      * Action constant returned by {@link #getAction()}: Signals the start of a
      * drag and drop operation. The View should return {@code true} from its
      * {@link View#onDragEvent(DragEvent) onDragEvent()} handler method or
-     * {@link View.View.OnDragListener#onDrag(View,DragEvent) OnDragListener.onDrag()} listener
+     * {@link View.OnDragListener#onDrag(View,DragEvent) OnDragListener.onDrag()} listener
      * if it can accept a drop. The onDragEvent() or onDrag() methods usually inspect the metadata
      * from {@link #getClipDescription()} to determine if they can accept the data contained in
      * this drag. For an operation that doesn't represent data transfer, these methods may
@@ -190,7 +192,7 @@
      * within the View object's bounding box.
      * <p>
      * The View should return {@code true} from its {@link View#onDragEvent(DragEvent)}
-     * handler or {@link View.View.OnDragListener#onDrag(View,DragEvent) OnDragListener.onDrag()}
+     * handler or {@link View.OnDragListener#onDrag(View,DragEvent) OnDragListener.onDrag()}
      * listener if it accepted the drop, and {@code false} if it ignored the drop.
      * </p>
      * <p>
@@ -255,13 +257,13 @@
     }
 
     private void init(int action, float x, float y, ClipDescription description, ClipData data,
-            DropPermissionHolder dropPermissionHolder, Object localState, boolean result) {
+            IDropPermissions dropPermissions, Object localState, boolean result) {
         mAction = action;
         mX = x;
         mY = y;
         mClipDescription = description;
         mClipData = data;
-        mDropPermissionHolder = dropPermissionHolder;
+        mDropPermissions = dropPermissions;
         mLocalState = localState;
         mDragResult = result;
     }
@@ -272,13 +274,13 @@
 
     /** @hide */
     public static DragEvent obtain(int action, float x, float y, Object localState,
-            ClipDescription description, ClipData data, DropPermissionHolder dropPermissionHolder,
+            ClipDescription description, ClipData data, IDropPermissions dropPermissions,
             boolean result) {
         final DragEvent ev;
         synchronized (gRecyclerLock) {
             if (gRecyclerTop == null) {
                 ev = new DragEvent();
-                ev.init(action, x, y, description, data, dropPermissionHolder, localState, result);
+                ev.init(action, x, y, description, data, dropPermissions, localState, result);
                 return ev;
             }
             ev = gRecyclerTop;
@@ -289,7 +291,7 @@
         ev.mRecycled = false;
         ev.mNext = null;
 
-        ev.init(action, x, y, description, data, dropPermissionHolder, localState, result);
+        ev.init(action, x, y, description, data, dropPermissions, localState, result);
 
         return ev;
     }
@@ -297,7 +299,7 @@
     /** @hide */
     public static DragEvent obtain(DragEvent source) {
         return obtain(source.mAction, source.mX, source.mY, source.mLocalState,
-                source.mClipDescription, source.mClipData, source.mDropPermissionHolder,
+                source.mClipDescription, source.mClipData, source.mDropPermissions,
                 source.mDragResult);
     }
 
@@ -363,14 +365,19 @@
     }
 
     /**
-     * Returns the {@link android.view.DropPermissionHolder} object that can be used by the drag
-     * listener to request and release the permissions for the content URIs contained in the
-     * {@link android.content.ClipData} object associated with this event.
+     * Requests the permissions for the content URIs contained in {@link android.content.ClipData}
+     * object associated with this event. Which permissions will be granted is defined by the set of
+     * flags passed to {@link View#startDragAndDrop(ClipData, View.DragShadowBuilder, Object, int)}.
+     * Returns the {@link DropPermissions} object that can be used by the receiving app to release
+     * the permissions for the content URIs when they are no longer needed.
      * This method only returns valid data if the event action is {@link #ACTION_DROP}.
-     * @return The DropPermissionHolder object used to handle content URI permissions.
+     * @return The DropPermissions object used to control access to the content URIs.
      */
-    public DropPermissionHolder getDropPermissionHolder() {
-        return mDropPermissionHolder;
+    public DropPermissions requestDropPermissions() {
+        if (mDropPermissions == null) {
+            return null;
+        }
+        return new DropPermissions(mDropPermissions);
     }
 
     /**
@@ -493,11 +500,11 @@
             dest.writeInt(1);
             mClipDescription.writeToParcel(dest, flags);
         }
-        if (mDropPermissionHolder == null) {
+        if (mDropPermissions == null) {
             dest.writeInt(0);
         } else {
             dest.writeInt(1);
-            mDropPermissionHolder.writeToParcel(dest, flags);
+            dest.writeStrongBinder(mDropPermissions.asBinder());
         }
     }
 
@@ -519,7 +526,7 @@
                 event.mClipDescription = ClipDescription.CREATOR.createFromParcel(in);
             }
             if (in.readInt() != 0) {
-                event.mDropPermissionHolder = DropPermissionHolder.CREATOR.createFromParcel(in);
+                event.mDropPermissions = IDropPermissions.Stub.asInterface(in.readStrongBinder());;
             }
             return event;
         }
diff --git a/core/java/android/view/DropPermissionHolder.java b/core/java/android/view/DropPermissionHolder.java
deleted file mode 100644
index 993e67a..0000000
--- a/core/java/android/view/DropPermissionHolder.java
+++ /dev/null
@@ -1,161 +0,0 @@
-package android.view;
-
-import android.app.IActivityManager;
-import android.content.ClipData;
-import android.content.Intent;
-import android.net.Uri;
-import android.os.Binder;
-import android.os.IBinder;
-import android.os.Parcel;
-import android.os.Parcelable;
-import android.os.RemoteException;
-import com.android.internal.view.IDropPermissionHolder;
-
-import java.util.ArrayList;
-
-public class DropPermissionHolder implements Parcelable {
-
-    IDropPermissionHolder mDropPermissionHolder;
-
-    /**
-     * Create a new DropPermissionHolder to be passed to the client with a DragEvent.
-     *
-     * @hide
-     */
-    public DropPermissionHolder(ClipData clipData, IActivityManager activityManager,
-            int sourceUid, String targetPackage, int mode, int sourceUserId, int targetUserId) {
-        mDropPermissionHolder = new LocalDropPermissionHolder(clipData, activityManager,
-                sourceUid, targetPackage, mode, sourceUserId, targetUserId);
-    }
-
-    private class LocalDropPermissionHolder extends IDropPermissionHolder.Stub {
-
-        private final IActivityManager mActivityManager;
-        private final int mSourceUid;
-        private final String mTargetPackage;
-        private final int mMode;
-        private final int mSourceUserId;
-        private final int mTargetUserId;
-
-        IBinder mPermissionOwner = null;
-
-        final private ArrayList<Uri> mUris = new ArrayList<Uri>();
-
-        LocalDropPermissionHolder(ClipData clipData, IActivityManager activityManager,
-                int sourceUid, String targetPackage, int mode, int sourceUserId, int targetUserId) {
-            mActivityManager = activityManager;
-            mSourceUid = sourceUid;
-            mTargetPackage = targetPackage;
-            mMode = mode;
-            mSourceUserId = sourceUserId;
-            mTargetUserId = targetUserId;
-
-            int N = clipData.getItemCount();
-            for (int i = 0; i != N; ++i) {
-                ClipData.Item item = clipData.getItemAt(i);
-
-                if (item.getUri() != null) {
-                    mUris.add(item.getUri());
-                }
-
-                Intent intent = item.getIntent();
-                if (intent != null && intent.getData() != null) {
-                    mUris.add(intent.getData());
-                }
-            }
-        }
-
-        @Override
-        public void grant() throws RemoteException {
-            if (mPermissionOwner != null) {
-                return;
-            }
-
-            mPermissionOwner = mActivityManager.newUriPermissionOwner("drop");
-
-            long origId = Binder.clearCallingIdentity();
-            try {
-                for (Uri mUri : mUris) {
-                    mActivityManager.grantUriPermissionFromOwner(
-                            mPermissionOwner, mSourceUid, mTargetPackage, mUri, mMode,
-                            mSourceUserId, mTargetUserId);
-                }
-            } finally {
-                Binder.restoreCallingIdentity(origId);
-            }
-
-        }
-
-        @Override
-        public void revoke() throws RemoteException {
-            if (mPermissionOwner == null) {
-                return;
-            }
-
-            for (Uri mUri : mUris) {
-                mActivityManager.revokeUriPermissionFromOwner(
-                        mPermissionOwner, mUri, mMode, mSourceUserId);
-            }
-
-            mPermissionOwner = null;
-        }
-    }
-
-    /**
-     * Request permissions granted by the activity which started the drag.
-     */
-    public void grant() {
-        try {
-            mDropPermissionHolder.grant();
-        } catch (RemoteException e) {
-        }
-    }
-
-    /**
-     * Revoke permissions granted by the {@link #grant()} call.
-     */
-    public void revoke() {
-        try {
-            mDropPermissionHolder.revoke();
-        } catch (RemoteException e) {
-        }
-    }
-
-    /**
-     * Returns information about the {@link android.os.Parcel} representation of this
-     * DropPermissionHolder object.
-     * @return Information about the {@link android.os.Parcel} representation.
-     */
-    @Override
-    public int describeContents() {
-        return 0;
-    }
-
-    /**
-     * Creates a {@link android.os.Parcel} object from this DropPermissionHolder object.
-     * @param dest A {@link android.os.Parcel} object in which to put the DropPermissionHolder
-     *             object.
-     * @param flags Flags to store in the Parcel.
-     */
-    @Override
-    public void writeToParcel(Parcel dest, int flags) {
-        dest.writeStrongBinder(mDropPermissionHolder.asBinder());
-    }
-
-    DropPermissionHolder(Parcel in) {
-        mDropPermissionHolder = IDropPermissionHolder.Stub.asInterface(in.readStrongBinder());
-    }
-
-    /**
-     * A container for creating a DropPermissionHolder from a Parcel.
-     */
-    public static final Parcelable.Creator<DropPermissionHolder> CREATOR
-            = new Parcelable.Creator<DropPermissionHolder>() {
-        public DropPermissionHolder createFromParcel(Parcel in) {
-            return new DropPermissionHolder(in);
-        }
-        public DropPermissionHolder[] newArray(int size) {
-            return new DropPermissionHolder[size];
-        }
-    };
-}
diff --git a/core/java/android/view/DropPermissions.java b/core/java/android/view/DropPermissions.java
new file mode 100644
index 0000000..780461f
--- /dev/null
+++ b/core/java/android/view/DropPermissions.java
@@ -0,0 +1,66 @@
+/*
+** Copyright 2015, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+package android.view;
+
+import android.os.RemoteException;
+import com.android.internal.view.IDropPermissions;
+import dalvik.system.CloseGuard;
+
+
+public final class DropPermissions {
+
+    private final IDropPermissions mDropPermissions;
+
+    private final CloseGuard mCloseGuard = CloseGuard.get();
+
+    /**
+     * Create a new DropPermissions object to be passed to the client with a DragEvent.
+     *
+     * @hide
+     */
+    DropPermissions(IDropPermissions dropPermissions) {
+        mDropPermissions = dropPermissions;
+        try {
+            mDropPermissions.take();
+        } catch (RemoteException e) {
+        }
+        mCloseGuard.open("release");
+    }
+
+    /**
+     * Revoke permissions taken by {@link DragEvent#requestDropPermissions()}.
+     */
+    public void release() {
+        try {
+            mDropPermissions.release();
+        } catch (RemoteException e) {
+        }
+        mCloseGuard.close();
+    }
+
+    @Override
+    protected void finalize() throws Throwable {
+        try {
+            if (mCloseGuard != null) {
+                mCloseGuard.warnIfOpen();
+            }
+            release();
+        } finally {
+            super.finalize();
+        }
+    }
+}
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index 2e884cc..251f4c8 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -1183,6 +1183,13 @@
         public static final int PRIVATE_FLAG_WILL_NOT_REPLACE_ON_RELAUNCH = 0x00008000;
 
         /**
+         * Flag to indicate that this child window should always be laid-out in the parent
+         * frame regardless of the current windowing mode configuration.
+         * @hide
+         */
+        public static final int PRIVATE_FLAG_LAYOUT_CHILD_WINDOW_IN_PARENT_FRAME = 0x00010000;
+
+        /**
          * Control flags that are private to the platform.
          * @hide
          */
diff --git a/core/java/android/webkit/WebViewFactory.java b/core/java/android/webkit/WebViewFactory.java
index 01d1566..6aa5e2f 100644
--- a/core/java/android/webkit/WebViewFactory.java
+++ b/core/java/android/webkit/WebViewFactory.java
@@ -134,6 +134,25 @@
     private static String TAG_SIGNATURE = "signature";
 
     /**
+     * Reads all signatures at the current depth (within the current provider) from the XML parser.
+     */
+    private static String[] readSignatures(XmlResourceParser parser) throws IOException,
+            XmlPullParserException {
+        List<String> signatures = new ArrayList<String>();
+        int outerDepth = parser.getDepth();
+        while(XmlUtils.nextElementWithin(parser, outerDepth)) {
+            if (parser.getName().equals(TAG_SIGNATURE)) {
+                // Parse the value within the signature tag
+                String signature = parser.nextText();
+                signatures.add(signature);
+            } else {
+                Log.e(LOGTAG, "Found an element in a webview provider that is not a signature");
+            }
+        }
+        return signatures.toArray(new String[signatures.size()]);
+    }
+
+    /**
      * Returns all packages declared in the framework resources as potential WebView providers.
      * @hide
      * */
@@ -161,9 +180,9 @@
                         throw new MissingWebViewPackageException(
                                 "WebView provider in framework resources missing description");
                     }
-                    String signature = parser.getAttributeValue(null, TAG_SIGNATURE);
                     webViewProviders.add(
-                            new WebViewProviderInfo(packageName, description, signature));
+                            new WebViewProviderInfo(packageName, description,
+                                readSignatures(parser)));
                 }
                 else {
                     Log.e(LOGTAG, "Found an element that is not a webview provider");
diff --git a/core/java/android/webkit/WebViewProviderInfo.java b/core/java/android/webkit/WebViewProviderInfo.java
index d5e3a230..7bad652 100644
--- a/core/java/android/webkit/WebViewProviderInfo.java
+++ b/core/java/android/webkit/WebViewProviderInfo.java
@@ -40,10 +40,10 @@
         public WebViewPackageNotFoundException(Exception e) { super(e); }
     }
 
-    public WebViewProviderInfo(String packageName, String description, String signature) {
+    public WebViewProviderInfo(String packageName, String description, String[] signatures) {
         this.packageName = packageName;
         this.description = description;
-        this.signature = signature;
+        this.signatures = signatures;
     }
 
     private boolean hasValidSignature() {
@@ -53,7 +53,7 @@
         try {
             // If no signature is declared, instead check whether the package is included in the
             // system.
-            if (signature == null)
+            if (signatures == null || signatures.length == 0)
                 return getPackageInfo().applicationInfo.isSystemApp();
 
             packageSignatures = getPackageInfo().signatures;
@@ -62,8 +62,15 @@
         }
         if (packageSignatures.length != 1)
             return false;
-        final byte[] releaseSignature = Base64.decode(signature, Base64.DEFAULT);
-        return Arrays.equals(releaseSignature, packageSignatures[0].toByteArray());
+
+        final byte[] packageSignature = packageSignatures[0].toByteArray();
+        // Return whether the package signature matches any of the valid signatures
+        for (String signature : signatures) {
+            final byte[] validSignature = Base64.decode(signature, Base64.DEFAULT);
+            if (Arrays.equals(packageSignature, validSignature))
+                return true;
+        }
+        return false;
     }
 
     /**
@@ -109,7 +116,7 @@
     private WebViewProviderInfo(Parcel in) {
         packageName = in.readString();
         description = in.readString();
-        signature = in.readString();
+        signatures = in.createStringArray();
         packageInfo = null;
     }
 
@@ -122,14 +129,14 @@
     public void writeToParcel(Parcel out, int flags) {
         out.writeString(packageName);
         out.writeString(description);
-        out.writeString(signature);
+        out.writeStringArray(signatures);
     }
 
     // fields read from framework resource
     public String packageName;
     public String description;
 
-    private String signature;
+    private String[] signatures;
 
     private PackageInfo packageInfo;
     // flags declaring we want extra info from the package manager
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index 2d1f855..15cea77 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -72,6 +72,7 @@
 import android.util.SparseArray;
 import android.view.ActionMode;
 import android.view.ActionMode.Callback;
+import android.view.ContextMenu;
 import android.view.DisplayListCanvas;
 import android.view.DragEvent;
 import android.view.Gravity;
@@ -135,13 +136,16 @@
     // Tag used when the Editor maintains its own separate UndoManager.
     private static final String UNDO_OWNER_TAG = "Editor";
 
-    // Ordering constants used to place the Action Mode items in their menu.
-    private static final int MENU_ITEM_ORDER_CUT = 1;
-    private static final int MENU_ITEM_ORDER_COPY = 2;
-    private static final int MENU_ITEM_ORDER_PASTE = 3;
-    private static final int MENU_ITEM_ORDER_SHARE = 4;
-    private static final int MENU_ITEM_ORDER_SELECT_ALL = 5;
-    private static final int MENU_ITEM_ORDER_REPLACE = 6;
+    // Ordering constants used to place the Action Mode or context menu items in their menu.
+    private static final int MENU_ITEM_ORDER_UNDO = 1;
+    private static final int MENU_ITEM_ORDER_REDO = 2;
+    private static final int MENU_ITEM_ORDER_CUT = 3;
+    private static final int MENU_ITEM_ORDER_COPY = 4;
+    private static final int MENU_ITEM_ORDER_PASTE = 5;
+    private static final int MENU_ITEM_ORDER_PASTE_AS_PLAIN_TEXT = 6;
+    private static final int MENU_ITEM_ORDER_SHARE = 7;
+    private static final int MENU_ITEM_ORDER_SELECT_ALL = 8;
+    private static final int MENU_ITEM_ORDER_REPLACE = 9;
     private static final int MENU_ITEM_ORDER_PROCESS_TEXT_INTENT_ACTIONS_START = 10;
 
     // Each Editor manages its own undo stack.
@@ -184,6 +188,7 @@
 
     boolean mDiscardNextActionUp;
     boolean mIgnoreActionUpEvent;
+    private boolean mIgnoreNextMouseActionUpOrDown;
 
     long mShowCursor;
     Blink mBlink;
@@ -209,6 +214,8 @@
     boolean mPreserveDetachedSelection;
     boolean mTemporaryDetach;
 
+    boolean mIsBeingLongClicked;
+
     SuggestionsPopupWindow mSuggestionsPopupWindow;
     SuggestionRangeSpan mSuggestionRangeSpan;
     Runnable mShowSuggestionRunnable;
@@ -224,6 +231,7 @@
     private PositionListener mPositionListener;
 
     float mLastDownPositionX, mLastDownPositionY;
+    private float mContextMenuAnchorX, mContextMenuAnchorY;
     Callback mCustomSelectionActionModeCallback;
     Callback mCustomInsertionActionModeCallback;
 
@@ -239,6 +247,9 @@
     // Only for mouse input.
     private static final int TAP_STATE_TRIPLE_CLICK = 3;
 
+    // The button state as of the last time #onTouchEvent is called.
+    private int mLastButtonState;
+
     private Runnable mInsertionActionModeRunnable;
 
     // The span controller helps monitoring the changes to which the Editor needs to react:
@@ -1314,7 +1325,33 @@
         }
     }
 
+    private boolean shouldFilterOutTouchEvent(MotionEvent event) {
+        if (!event.isFromSource(InputDevice.SOURCE_MOUSE)) {
+            return false;
+        }
+        final boolean primaryButtonStateChanged =
+                ((mLastButtonState ^ event.getButtonState()) & MotionEvent.BUTTON_PRIMARY) != 0;
+        final int action = event.getActionMasked();
+        if ((action == MotionEvent.ACTION_DOWN || action == MotionEvent.ACTION_UP)
+                && !primaryButtonStateChanged) {
+            return true;
+        }
+        if (action == MotionEvent.ACTION_MOVE
+                && !event.isButtonPressed(MotionEvent.BUTTON_PRIMARY)) {
+            return true;
+        }
+        return false;
+    }
+
     void onTouchEvent(MotionEvent event) {
+        final boolean filterOutEvent = shouldFilterOutTouchEvent(event);
+        mLastButtonState = event.getButtonState();
+        if (filterOutEvent) {
+            if (event.getActionMasked() == MotionEvent.ACTION_UP) {
+                mDiscardNextActionUp = true;
+            }
+            return;
+        }
         updateTapState(event);
         updateFloatingToolbarVisibility(event);
 
@@ -2318,6 +2355,84 @@
         text.setSpan(mSpanController, 0, textLength, Spanned.SPAN_INCLUSIVE_INCLUSIVE);
     }
 
+    void setContextMenuAnchor(float x, float y) {
+        mContextMenuAnchorX = x;
+        mContextMenuAnchorY = y;
+    }
+
+    void onCreateContextMenu(ContextMenu menu) {
+        if (mIsBeingLongClicked || Float.isNaN(mContextMenuAnchorX)
+                || Float.isNaN(mContextMenuAnchorY)) {
+            return;
+        }
+        final int offset = mTextView.getOffsetForPosition(mContextMenuAnchorX, mContextMenuAnchorY);
+        if (offset == -1) {
+            return;
+        }
+        final boolean isOnSelection = mTextView.hasSelection()
+                && offset >= mTextView.getSelectionStart() && offset <= mTextView.getSelectionEnd();
+        if (!isOnSelection) {
+            // Right clicked position is not on the selection. Remove the selection and move the
+            // cursor to the right clicked position.
+            stopTextActionMode();
+            Selection.setSelection((Spannable) mTextView.getText(), offset);
+        }
+
+        // TODO: Add suggestions in the context menu.
+
+        menu.add(Menu.NONE, TextView.ID_UNDO, MENU_ITEM_ORDER_UNDO,
+                com.android.internal.R.string.undo)
+                .setAlphabeticShortcut('z')
+                .setOnMenuItemClickListener(mOnContextMenuItemClickListener)
+                .setEnabled(mTextView.canUndo());
+        menu.add(Menu.NONE, TextView.ID_REDO, MENU_ITEM_ORDER_REDO,
+                com.android.internal.R.string.redo)
+                .setOnMenuItemClickListener(mOnContextMenuItemClickListener)
+                .setEnabled(mTextView.canRedo());
+
+        menu.add(Menu.NONE, TextView.ID_CUT, MENU_ITEM_ORDER_CUT,
+                com.android.internal.R.string.cut)
+                .setAlphabeticShortcut('x')
+                .setOnMenuItemClickListener(mOnContextMenuItemClickListener)
+                .setEnabled(mTextView.canCut());
+        menu.add(Menu.NONE, TextView.ID_COPY, MENU_ITEM_ORDER_COPY,
+                com.android.internal.R.string.copy)
+                .setAlphabeticShortcut('c')
+                .setOnMenuItemClickListener(mOnContextMenuItemClickListener)
+                .setEnabled(mTextView.canCopy());
+        menu.add(Menu.NONE, TextView.ID_PASTE, MENU_ITEM_ORDER_PASTE,
+                com.android.internal.R.string.paste)
+                .setAlphabeticShortcut('v')
+                .setEnabled(mTextView.canPaste())
+                .setOnMenuItemClickListener(mOnContextMenuItemClickListener);
+        menu.add(Menu.NONE, TextView.ID_PASTE, MENU_ITEM_ORDER_PASTE_AS_PLAIN_TEXT,
+                com.android.internal.R.string.paste_as_plain_text)
+                .setEnabled(mTextView.canPaste())
+                .setOnMenuItemClickListener(mOnContextMenuItemClickListener);
+        menu.add(Menu.NONE, TextView.ID_SHARE, MENU_ITEM_ORDER_SHARE,
+                com.android.internal.R.string.share)
+                .setEnabled(mTextView.canShare())
+                .setOnMenuItemClickListener(mOnContextMenuItemClickListener);
+        menu.add(Menu.NONE, TextView.ID_SELECT_ALL, MENU_ITEM_ORDER_SELECT_ALL,
+                com.android.internal.R.string.selectAll)
+                .setAlphabeticShortcut('a')
+                .setEnabled(mTextView.canSelectAllText())
+                .setOnMenuItemClickListener(mOnContextMenuItemClickListener);
+
+        mPreserveDetachedSelection = true;
+    }
+
+    private final MenuItem.OnMenuItemClickListener mOnContextMenuItemClickListener =
+            new MenuItem.OnMenuItemClickListener() {
+        @Override
+        public boolean onMenuItemClick(MenuItem item) {
+            if (mProcessTextIntentActionsHandler.performMenuItemAction(item)) {
+                return true;
+            }
+            return mTextView.onTextContextMenuItem(item.getItemId());
+        }
+    };
+
     /**
      * Controls the {@link EasyEditSpan} monitoring when it is added, and when the related
      * pop-up should be displayed.
@@ -2710,6 +2825,9 @@
         }
 
         public void hide() {
+            if (!isShowing()) {
+                return;
+            }
             mPopupWindow.dismiss();
             getPositionListener().removeSubscriber(this);
         }
@@ -2759,8 +2877,10 @@
 
             @Override
             public void dismiss() {
+                if (!isShowing()) {
+                    return;
+                }
                 super.dismiss();
-
                 getPositionListener().removeSubscriber(SuggestionsPopupWindow.this);
 
                 // Safe cast since show() checks that mTextView.getText() is an Editable
diff --git a/core/java/android/widget/LinearLayout.java b/core/java/android/widget/LinearLayout.java
index ad939be..f6e6186 100644
--- a/core/java/android/widget/LinearLayout.java
+++ b/core/java/android/widget/LinearLayout.java
@@ -359,7 +359,6 @@
         final int count = getVirtualChildCount();
         for (int i = 0; i < count; i++) {
             final View child = getVirtualChildAt(i);
-
             if (child != null && child.getVisibility() != GONE) {
                 if (hasDividerBeforeChildAt(i)) {
                     final LayoutParams lp = (LayoutParams) child.getLayoutParams();
@@ -388,7 +387,7 @@
      */
     private View getLastNonGoneChild() {
         for (int i = getVirtualChildCount() - 1; i >= 0; i--) {
-            View child = getVirtualChildAt(i);
+            final View child = getVirtualChildAt(i);
             if (child != null && child.getVisibility() != GONE) {
                 return child;
             }
@@ -401,7 +400,6 @@
         final boolean isLayoutRtl = isLayoutRtl();
         for (int i = 0; i < count; i++) {
             final View child = getVirtualChildAt(i);
-
             if (child != null && child.getVisibility() != GONE) {
                 if (hasDividerBeforeChildAt(i)) {
                     final LayoutParams lp = (LayoutParams) child.getLayoutParams();
@@ -588,8 +586,9 @@
      * for an example.</p>
      *
      * @param index the child's index
-     * @return the child at the specified index
+     * @return the child at the specified index, may be {@code null}
      */
+    @Nullable
     View getVirtualChildAt(int index) {
         return getChildAt(index);
     }
@@ -670,7 +669,7 @@
      */
     private boolean allViewsAreGoneBefore(int childIndex) {
         for (int i = childIndex - 1; i >= 0; i--) {
-            View child = getVirtualChildAt(i);
+            final View child = getVirtualChildAt(i);
             if (child != null && child.getVisibility() != GONE) {
                 return false;
             }
@@ -715,7 +714,6 @@
         // See how tall everyone is. Also remember max width.
         for (int i = 0; i < count; ++i) {
             final View child = getVirtualChildAt(i);
-
             if (child == null) {
                 mTotalLength += measureNullChild(i);
                 continue;
@@ -837,7 +835,6 @@
 
             for (int i = 0; i < count; ++i) {
                 final View child = getVirtualChildAt(i);
-
                 if (child == null) {
                     mTotalLength += measureNullChild(i);
                     continue;
@@ -943,7 +940,6 @@
             if (useLargestChild && heightMode != MeasureSpec.EXACTLY) {
                 for (int i = 0; i < count; i++) {
                     final View child = getVirtualChildAt(i);
-
                     if (child == null || child.getVisibility() == View.GONE) {
                         continue;
                     }
@@ -986,7 +982,7 @@
                 MeasureSpec.EXACTLY);
         for (int i = 0; i< count; ++i) {
            final View child = getVirtualChildAt(i);
-           if (child.getVisibility() != GONE) { 
+           if (child != null && child.getVisibility() != GONE) {
                LinearLayout.LayoutParams lp = ((LinearLayout.LayoutParams)child.getLayoutParams());
                
                if (lp.width == LayoutParams.MATCH_PARENT) {
@@ -1053,7 +1049,6 @@
         // See how wide everyone is. Also remember max height.
         for (int i = 0; i < count; ++i) {
             final View child = getVirtualChildAt(i);
-
             if (child == null) {
                 mTotalLength += measureNullChild(i);
                 continue;
@@ -1211,7 +1206,6 @@
 
             for (int i = 0; i < count; ++i) {
                 final View child = getVirtualChildAt(i);
-
                 if (child == null) {
                     mTotalLength += measureNullChild(i);
                     continue;
@@ -1357,7 +1351,6 @@
             if (useLargestChild && widthMode != MeasureSpec.EXACTLY) {
                 for (int i = 0; i < count; i++) {
                     final View child = getVirtualChildAt(i);
-
                     if (child == null || child.getVisibility() == View.GONE) {
                         continue;
                     }
@@ -1402,7 +1395,7 @@
                 MeasureSpec.EXACTLY);
         for (int i = 0; i < count; ++i) {
            final View child = getVirtualChildAt(i);
-           if (child.getVisibility() != GONE) { 
+           if (child != null && child.getVisibility() != GONE) {
                LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) child.getLayoutParams();
                
                if (lp.height == LayoutParams.MATCH_PARENT) {
@@ -1662,9 +1655,8 @@
         }
 
         for (int i = 0; i < count; i++) {
-            int childIndex = start + dir * i;
+            final int childIndex = start + dir * i;
             final View child = getVirtualChildAt(childIndex);
-
             if (child == null) {
                 childLeft += measureNullChild(childIndex);
             } else if (child.getVisibility() != GONE) {
diff --git a/core/java/android/widget/PopupWindow.java b/core/java/android/widget/PopupWindow.java
index f4c343a..19e290b 100644
--- a/core/java/android/widget/PopupWindow.java
+++ b/core/java/android/widget/PopupWindow.java
@@ -16,6 +16,7 @@
 
 package android.widget;
 
+import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_LAYOUT_CHILD_WINDOW_IN_PARENT_FRAME;
 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_WILL_NOT_REPLACE_ON_RELAUNCH;
 
 import com.android.internal.R;
@@ -1313,7 +1314,8 @@
             p.width = mLastWidth = mWidth;
         }
 
-        p.privateFlags = PRIVATE_FLAG_WILL_NOT_REPLACE_ON_RELAUNCH;
+        p.privateFlags = PRIVATE_FLAG_WILL_NOT_REPLACE_ON_RELAUNCH
+                | PRIVATE_FLAG_LAYOUT_CHILD_WINDOW_IN_PARENT_FRAME;
 
         // Used for debugging.
         p.setTitle("PopupWindow:" + Integer.toHexString(hashCode()));
diff --git a/core/java/android/widget/TableRow.java b/core/java/android/widget/TableRow.java
index f7f9c91..22931fc 100644
--- a/core/java/android/widget/TableRow.java
+++ b/core/java/android/widget/TableRow.java
@@ -98,7 +98,7 @@
      * {@hide}
      */
     void setColumnCollapsed(int columnIndex, boolean collapsed) {
-        View child = getVirtualChildAt(columnIndex);
+        final View child = getVirtualChildAt(columnIndex);
         if (child != null) {
             child.setVisibility(collapsed ? GONE : VISIBLE);
         }
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 17c803f..d46c6f9 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -111,6 +111,7 @@
 import android.view.AccessibilityIterators.TextSegmentIterator;
 import android.view.ActionMode;
 import android.view.Choreographer;
+import android.view.ContextMenu;
 import android.view.DragEvent;
 import android.view.Gravity;
 import android.view.HapticFeedbackConstants;
@@ -393,7 +394,17 @@
             mOverride = false;
         }
 
-        public void resolveWithLayoutDirection(int layoutDirection) {
+        /**
+         * Updates the list of displayed drawables to account for the current
+         * layout direction.
+         *
+         * @param layoutDirection the current layout direction
+         * @return {@code true} if the displayed drawables changed
+         */
+        public boolean resolveWithLayoutDirection(int layoutDirection) {
+            final Drawable previousLeft = mShowing[Drawables.LEFT];
+            final Drawable previousRight = mShowing[Drawables.RIGHT];
+
             // First reset "left" and "right" drawables to their initial values
             mShowing[Drawables.LEFT] = mDrawableLeftInitial;
             mShowing[Drawables.RIGHT] = mDrawableRightInitial;
@@ -441,16 +452,11 @@
                         break;
                 }
             }
-            applyErrorDrawableIfNeeded(layoutDirection);
-            updateDrawablesLayoutDirection(layoutDirection);
-        }
 
-        private void updateDrawablesLayoutDirection(int layoutDirection) {
-            for (Drawable dr : mShowing) {
-                if (dr != null) {
-                    dr.setLayoutDirection(layoutDirection);
-                }
-            }
+            applyErrorDrawableIfNeeded(layoutDirection);
+
+            return mShowing[Drawables.LEFT] != previousLeft
+                    || mShowing[Drawables.RIGHT] != previousRight;
         }
 
         public void setErrorDrawable(Drawable dr, TextView tv) {
@@ -5957,6 +5963,14 @@
 
     @Override
     public PointerIcon getPointerIcon(MotionEvent event, float x, float y) {
+        if (mText instanceof Spannable && mLinksClickable) {
+            final int offset = getOffsetForPosition(x, y);
+            final ClickableSpan[] clickables = ((Spannable) mText).getSpans(offset, offset,
+                    ClickableSpan.class);
+            if (clickables.length > 0) {
+                return PointerIcon.getSystemIcon(mContext, PointerIcon.STYLE_HAND);
+            }
+        }
         if (isTextSelectable() || isTextEditable()) {
             return PointerIcon.getSystemIcon(mContext, PointerIcon.STYLE_TEXT);
         }
@@ -8489,6 +8503,29 @@
         return super.onGenericMotionEvent(event);
     }
 
+    @Override
+    protected void onCreateContextMenu(ContextMenu menu) {
+        if (mEditor != null) {
+            mEditor.onCreateContextMenu(menu);
+        }
+    }
+
+    @Override
+    public boolean showContextMenu() {
+        if (mEditor != null) {
+            mEditor.setContextMenuAnchor(Float.NaN, Float.NaN);
+        }
+        return super.showContextMenu();
+    }
+
+    @Override
+    public boolean showContextMenu(float x, float y) {
+        if (mEditor != null) {
+            mEditor.setContextMenuAnchor(x, y);
+        }
+        return super.showContextMenu(x, y);
+    }
+
     /**
      * @return True iff this TextView contains a text that can be edited, or if this is
      * a selectable TextView.
@@ -9390,12 +9427,17 @@
     public boolean performLongClick() {
         boolean handled = false;
 
+        if (mEditor != null) {
+            mEditor.mIsBeingLongClicked = true;
+        }
+
         if (super.performLongClick()) {
             handled = true;
         }
 
         if (mEditor != null) {
             handled |= mEditor.performLongClick(handled);
+            mEditor.mIsBeingLongClicked = false;
         }
 
         if (handled) {
@@ -9809,7 +9851,30 @@
 
         // Resolve drawables
         if (mDrawables != null) {
-            mDrawables.resolveWithLayoutDirection(layoutDirection);
+            if (mDrawables.resolveWithLayoutDirection(layoutDirection)) {
+                prepareDrawableForDisplay(mDrawables.mShowing[Drawables.LEFT]);
+                prepareDrawableForDisplay(mDrawables.mShowing[Drawables.RIGHT]);
+                applyCompoundDrawableTint();
+            }
+        }
+    }
+
+    /**
+     * Prepares a drawable for display by propagating layout direction and
+     * drawable state.
+     *
+     * @param dr the drawable to prepare
+     */
+    private void prepareDrawableForDisplay(@Nullable Drawable dr) {
+        if (dr == null) {
+            return;
+        }
+
+        dr.setLayoutDirection(getLayoutDirection());
+
+        if (dr.isStateful()) {
+            dr.setState(getDrawableState());
+            dr.jumpToCurrentState();
         }
     }
 
diff --git a/core/java/com/android/internal/app/AlertController.java b/core/java/com/android/internal/app/AlertController.java
index 66374a6..27c3b72 100644
--- a/core/java/com/android/internal/app/AlertController.java
+++ b/core/java/com/android/internal/app/AlertController.java
@@ -378,6 +378,7 @@
 
         if (mIconView != null) {
             if (icon != null) {
+                mIconView.setVisibility(View.VISIBLE);
                 mIconView.setImageDrawable(icon);
             } else {
                 mIconView.setVisibility(View.GONE);
diff --git a/core/java/com/android/internal/app/ToolbarActionBar.java b/core/java/com/android/internal/app/ToolbarActionBar.java
index 9d12803..4b6e7e4 100644
--- a/core/java/com/android/internal/app/ToolbarActionBar.java
+++ b/core/java/com/android/internal/app/ToolbarActionBar.java
@@ -29,10 +29,14 @@
 import android.view.Menu;
 import android.view.MenuItem;
 import android.view.View;
+import android.view.View.OnFocusChangeListener;
+import android.view.ViewGroup;
+import android.view.ViewParent;
 import android.view.Window;
 import android.view.WindowCallbackWrapper;
 import android.widget.SpinnerAdapter;
 import android.widget.Toolbar;
+
 import com.android.internal.view.menu.MenuBuilder;
 import com.android.internal.view.menu.MenuPresenter;
 import com.android.internal.widget.DecorToolbar;
@@ -499,6 +503,12 @@
         }
     }
 
+    /** @hide */
+    @Override
+    public boolean requestFocus() {
+        return requestFocus(mDecorToolbar.getViewGroup());
+    }
+
     private class ToolbarCallbackWrapper extends WindowCallbackWrapper {
         public ToolbarCallbackWrapper(Window.Callback wrapped) {
             super(wrapped);
diff --git a/core/java/com/android/internal/app/WindowDecorActionBar.java b/core/java/com/android/internal/app/WindowDecorActionBar.java
index 05cfd81..c6bf1b4 100644
--- a/core/java/com/android/internal/app/WindowDecorActionBar.java
+++ b/core/java/com/android/internal/app/WindowDecorActionBar.java
@@ -18,6 +18,8 @@
 
 import android.animation.ValueAnimator;
 import android.content.res.TypedArray;
+import android.view.View.OnFocusChangeListener;
+import android.view.ViewGroup;
 import android.view.ViewParent;
 import android.widget.Toolbar;
 
@@ -950,6 +952,12 @@
         return false;
     }
 
+    /** @hide */
+    @Override
+    public boolean requestFocus() {
+        return requestFocus(mDecorToolbar.getViewGroup());
+    }
+
     /**
      * @hide
      */
diff --git a/core/java/com/android/internal/view/IDropPermissionHolder.aidl b/core/java/com/android/internal/view/IDropPermissions.aidl
similarity index 91%
rename from core/java/com/android/internal/view/IDropPermissionHolder.aidl
rename to core/java/com/android/internal/view/IDropPermissions.aidl
index e60ab0e..86d27e7 100644
--- a/core/java/com/android/internal/view/IDropPermissionHolder.aidl
+++ b/core/java/com/android/internal/view/IDropPermissions.aidl
@@ -20,7 +20,7 @@
  * Interface to allow a drop receiver to request permissions for URIs passed along with ClipData
  * contained in DragEvent.
  */
-interface IDropPermissionHolder {
-    void grant();
-    void revoke();
+interface IDropPermissions {
+    void take();
+    void release();
 }
diff --git a/core/jni/android/graphics/BitmapFactory.cpp b/core/jni/android/graphics/BitmapFactory.cpp
index 88b3769..4a0f3fc 100644
--- a/core/jni/android/graphics/BitmapFactory.cpp
+++ b/core/jni/android/graphics/BitmapFactory.cpp
@@ -265,7 +265,7 @@
     if (options != NULL) {
         jstring mimeType = encodedFormatToString(env, codec->getEncodedFormat());
         if (env->ExceptionCheck()) {
-            return nullObjectReturn("OOM in getEncodedFormat()");
+            return nullObjectReturn("OOM in encodedFormatToString()");
         }
         env->SetIntField(options, gOptions_widthFieldID, size.width());
         env->SetIntField(options, gOptions_heightFieldID, size.height());
diff --git a/core/jni/android/graphics/BitmapRegionDecoder.cpp b/core/jni/android/graphics/BitmapRegionDecoder.cpp
index 492d766..a1ba42e 100644
--- a/core/jni/android/graphics/BitmapRegionDecoder.cpp
+++ b/core/jni/android/graphics/BitmapRegionDecoder.cpp
@@ -136,9 +136,6 @@
         sampleSize = env->GetIntField(options, gOptions_sampleSizeFieldID);
         jobject jconfig = env->GetObjectField(options, gOptions_configFieldID);
         colorType = GraphicsJNI::getNativeBitmapColorType(env, jconfig);
-        if (kAlpha_8_SkColorType == colorType) {
-            colorType = kGray_8_SkColorType;
-        }
         requireUnpremul = !env->GetBooleanField(options, gOptions_premultipliedFieldID);
         javaBitmap = env->GetObjectField(options, gOptions_bitmapFieldID);
         // The Java options of ditherMode and preferQualityOverSpeed are deprecated.  We will
@@ -189,6 +186,9 @@
         env->SetIntField(options, gOptions_heightFieldID, bitmap.height());
         env->SetObjectField(options, gOptions_mimeFieldID,
                 encodedFormatToString(env, brd->getEncodedFormat()));
+        if (env->ExceptionCheck()) {
+            return nullObjectReturn("OOM in encodedFormatToString()");
+        }
     }
 
     // If we may have reused a bitmap, we need to indicate that the pixels have changed.
diff --git a/core/jni/android_view_DisplayListCanvas.cpp b/core/jni/android_view_DisplayListCanvas.cpp
index 9b41eb3..d2c99fd 100644
--- a/core/jni/android_view_DisplayListCanvas.cpp
+++ b/core/jni/android_view_DisplayListCanvas.cpp
@@ -27,7 +27,13 @@
 #include <SkBitmap.h>
 #include <SkRegion.h>
 
+#if HWUI_NEW_OPS
+#include <RecordingCanvas.h>
+typedef android::uirenderer::RecordingCanvas canvas_t;
+#else
 #include <DisplayListCanvas.h>
+typedef android::uirenderer::DisplayListCanvas canvas_t;
+#endif
 #include <Rect.h>
 #include <RenderNode.h>
 #include <CanvasProperty.h>
@@ -46,7 +52,7 @@
 
 static void android_view_DisplayListCanvas_insertReorderBarrier(JNIEnv* env, jobject clazz,
         jlong canvasPtr, jboolean reorderEnable) {
-    DisplayListCanvas* canvas = reinterpret_cast<DisplayListCanvas*>(canvasPtr);
+    canvas_t* canvas = reinterpret_cast<canvas_t*>(canvasPtr);
     canvas->insertReorderBarrier(reorderEnable);
 }
 
@@ -56,7 +62,7 @@
 
 static void android_view_DisplayListCanvas_callDrawGLFunction(JNIEnv* env, jobject clazz,
         jlong canvasPtr, jlong functorPtr) {
-    DisplayListCanvas* canvas = reinterpret_cast<DisplayListCanvas*>(canvasPtr);
+    canvas_t* canvas = reinterpret_cast<canvas_t*>(canvasPtr);
     Functor* functor = reinterpret_cast<Functor*>(functorPtr);
     canvas->callDrawGLFunction(functor);
 }
@@ -80,7 +86,7 @@
 static void android_view_DisplayListCanvas_drawRoundRectProps(JNIEnv* env, jobject clazz,
         jlong canvasPtr, jlong leftPropPtr, jlong topPropPtr, jlong rightPropPtr,
         jlong bottomPropPtr, jlong rxPropPtr, jlong ryPropPtr, jlong paintPropPtr) {
-    DisplayListCanvas* canvas = reinterpret_cast<DisplayListCanvas*>(canvasPtr);
+    canvas_t* canvas = reinterpret_cast<canvas_t*>(canvasPtr);
     CanvasPropertyPrimitive* leftProp = reinterpret_cast<CanvasPropertyPrimitive*>(leftPropPtr);
     CanvasPropertyPrimitive* topProp = reinterpret_cast<CanvasPropertyPrimitive*>(topPropPtr);
     CanvasPropertyPrimitive* rightProp = reinterpret_cast<CanvasPropertyPrimitive*>(rightPropPtr);
@@ -93,7 +99,7 @@
 
 static void android_view_DisplayListCanvas_drawCircleProps(JNIEnv* env, jobject clazz,
         jlong canvasPtr, jlong xPropPtr, jlong yPropPtr, jlong radiusPropPtr, jlong paintPropPtr) {
-    DisplayListCanvas* canvas = reinterpret_cast<DisplayListCanvas*>(canvasPtr);
+    canvas_t* canvas = reinterpret_cast<canvas_t*>(canvasPtr);
     CanvasPropertyPrimitive* xProp = reinterpret_cast<CanvasPropertyPrimitive*>(xPropPtr);
     CanvasPropertyPrimitive* yProp = reinterpret_cast<CanvasPropertyPrimitive*>(yPropPtr);
     CanvasPropertyPrimitive* radiusProp = reinterpret_cast<CanvasPropertyPrimitive*>(radiusPropPtr);
@@ -107,25 +113,25 @@
 
 static jlong android_view_DisplayListCanvas_finishRecording(JNIEnv* env,
         jobject clazz, jlong canvasPtr) {
-    DisplayListCanvas* canvas = reinterpret_cast<DisplayListCanvas*>(canvasPtr);
+    canvas_t* canvas = reinterpret_cast<canvas_t*>(canvasPtr);
     return reinterpret_cast<jlong>(canvas->finishRecording());
 }
 
 static jlong android_view_DisplayListCanvas_createDisplayListCanvas(JNIEnv* env, jobject clazz,
         jint width, jint height) {
-    return reinterpret_cast<jlong>(new DisplayListCanvas(width, height));
+    return reinterpret_cast<jlong>(new canvas_t(width, height));
 }
 
 static void android_view_DisplayListCanvas_resetDisplayListCanvas(JNIEnv* env, jobject clazz,
         jlong canvasPtr, jint width, jint height) {
-    DisplayListCanvas* canvas = reinterpret_cast<DisplayListCanvas*>(canvasPtr);
+    canvas_t* canvas = reinterpret_cast<canvas_t*>(canvasPtr);
     canvas->reset(width, height);
 }
 
 
 static void android_view_DisplayListCanvas_drawRenderNode(JNIEnv* env,
         jobject clazz, jlong canvasPtr, jlong renderNodePtr) {
-    DisplayListCanvas* canvas = reinterpret_cast<DisplayListCanvas*>(canvasPtr);
+    canvas_t* canvas = reinterpret_cast<canvas_t*>(canvasPtr);
     RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr);
     canvas->drawRenderNode(renderNode);
 }
@@ -136,7 +142,7 @@
 
 static void android_view_DisplayListCanvas_drawLayer(JNIEnv* env, jobject clazz,
         jlong canvasPtr, jlong layerPtr) {
-    DisplayListCanvas* canvas = reinterpret_cast<DisplayListCanvas*>(canvasPtr);
+    canvas_t* canvas = reinterpret_cast<canvas_t*>(canvasPtr);
     DeferredLayerUpdater* layer = reinterpret_cast<DeferredLayerUpdater*>(layerPtr);
     canvas->drawLayer(layer);
 }
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 313987a..c03d471 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -2370,7 +2370,7 @@
     <!-- Set initial MaxRetry value for operators -->
     <integer name="config_mdc_initial_max_retry">1</integer>
 
-    <!-- The OEM specified sensor type for the gesture to launch the camear app. -->
+    <!-- The OEM specified sensor type for the gesture to launch the camera app. -->
     <integer name="config_cameraLaunchGestureSensorType">-1</integer>
     <!-- The OEM specified sensor string type for the gesture to launch camera app, this value
          must match the value of config_cameraLaunchGestureSensorType in OEM's HAL -->
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 4d967ff..4843879 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -2453,6 +2453,9 @@
     <!-- Item on EditText context menu. This action is used to paste from the clipboard into the eidt field -->
     <string name="paste">Paste</string>
 
+    <!-- Item on EditText context menu. This action is used to paste from the clipboard into the eidt field without formatting -->
+    <string name="paste_as_plain_text">Paste as plain text</string>
+
     <!-- Item on EditText context menu. This action is used to replace the current word by other suggested words, suggested by the IME or the spell checker -->
     <string name="replace">Replace\u2026</string>
 
@@ -2465,6 +2468,12 @@
     <!-- Item on EditText context menu. Added only when the context menu is not empty, it enable selection context mode. [CHAR LIMIT=20] -->
     <string name="selectTextMode">Select text</string>
 
+    <!-- Item on EditText context menu. This action is used to undo a text edit operation. -->
+    <string name="undo">Undo</string>
+
+    <!-- Item on EditText context menu. This action is used to redo a text edit operation. -->
+    <string name="redo">Redo</string>
+
     <!-- Text selection contextual mode title, displayed in the CAB. [CHAR LIMIT=20] -->
     <string name="textSelectionCABTitle">Text selection</string>
 
@@ -3069,6 +3078,9 @@
     <string name="notification_listener_binding_label">Notification listener</string>
     <!-- Label to show for a service that is running because it is providing conditions. -->
     <string name="condition_provider_service_binding_label">Condition provider</string>
+    <!-- Label to show for a service that is running because it is observing and modifying the
+         importance of the user's notifications. -->
+    <string name="notification_assistant_binding_label">Notification assistant</string>
 
     <!-- Do Not Translate: Alternate eri.xml -->
     <string name="alternate_eri_file">/data/eri.xml</string>
diff --git a/core/res/res/values/styles.xml b/core/res/res/values/styles.xml
index 9a4016b..937d83d 100644
--- a/core/res/res/values/styles.xml
+++ b/core/res/res/values/styles.xml
@@ -1235,7 +1235,7 @@
         <item name="subtitleTextAppearance">@style/TextAppearance.Widget.Toolbar.Subtitle</item>
         <item name="minHeight">?attr/actionBarSize</item>
         <item name="titleMargin">4dp</item>
-        <item name="maxButtonHeight">56dp</item>
+        <item name="maxButtonHeight">@dimen/action_bar_default_height_material</item>
         <item name="buttonGravity">top</item>
         <item name="navigationButtonStyle">@style/Widget.Toolbar.Button.Navigation</item>
         <item name="collapseIcon">?attr/homeAsUpIndicator</item>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 517bb75..f257f14 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -456,7 +456,10 @@
   <java-symbol type="string" name="notification_title" />
   <java-symbol type="string" name="permission_request_notification_with_subtitle" />
   <java-symbol type="string" name="prepend_shortcut_label" />
+  <java-symbol type="string" name="paste_as_plain_text" />
   <java-symbol type="string" name="replace" />
+  <java-symbol type="string" name="undo" />
+  <java-symbol type="string" name="redo" />
   <java-symbol type="string" name="textSelectionCABTitle" />
   <java-symbol type="string" name="BaMmi" />
   <java-symbol type="string" name="CLIRDefaultOffNextCallOff" />
@@ -1807,6 +1810,7 @@
   <java-symbol type="string" name="low_internal_storage_view_title" />
   <java-symbol type="string" name="notification_listener_binding_label" />
   <java-symbol type="string" name="condition_provider_service_binding_label" />
+  <java-symbol type="string" name="notification_assistant_binding_label" />
   <java-symbol type="string" name="report" />
   <java-symbol type="string" name="select_input_method" />
   <java-symbol type="string" name="select_keyboard_layout_notification_title" />
diff --git a/core/res/res/xml/config_webview_packages.xml b/core/res/res/xml/config_webview_packages.xml
index 6f9c58d..fd443c1 100644
--- a/core/res/res/xml/config_webview_packages.xml
+++ b/core/res/res/xml/config_webview_packages.xml
@@ -16,5 +16,6 @@
 
 <webviewproviders>
     <!-- The default WebView implementation -->
-    <webviewprovider description="Android WebView" packageName="com.android.webview" />
+    <webviewprovider description="Android WebView" packageName="com.android.webview">
+    </webviewprovider>
 </webviewproviders>
diff --git a/core/tests/coretests/src/android/widget/TextViewActivityMouseTest.java b/core/tests/coretests/src/android/widget/TextViewActivityMouseTest.java
index 83a9e01..afd0bc4 100644
--- a/core/tests/coretests/src/android/widget/TextViewActivityMouseTest.java
+++ b/core/tests/coretests/src/android/widget/TextViewActivityMouseTest.java
@@ -26,8 +26,11 @@
 import static android.widget.espresso.TextViewActions.mouseLongClickAndDragOnText;
 import static android.widget.espresso.TextViewActions.mouseTripleClickAndDragOnText;
 import static android.widget.espresso.TextViewActions.mouseTripleClickOnTextAtIndex;
+import static android.widget.espresso.TextViewAssertions.hasInsertionPointerAtIndex;
 import static android.widget.espresso.TextViewAssertions.hasSelection;
+
 import static android.support.test.espresso.Espresso.onView;
+import static android.support.test.espresso.Espresso.pressBack;
 import static android.support.test.espresso.action.ViewActions.click;
 import static android.support.test.espresso.action.ViewActions.replaceText;
 import static android.support.test.espresso.action.ViewActions.typeTextIntoFocusedView;
@@ -37,8 +40,11 @@
 
 import com.android.frameworks.coretests.R;
 
+import android.support.test.espresso.Espresso;
 import android.test.ActivityInstrumentationTestCase2;
 import android.test.suitebuilder.annotation.SmallTest;
+import android.view.MotionEvent;
+import android.widget.espresso.ContextMenuUtils;
 
 /**
  * Tests mouse interaction of the TextView widget from an Activity
@@ -49,10 +55,13 @@
         super(TextViewActivity.class);
     }
 
+    @Override
+    public void setUp() {
+        getActivity();
+    }
+
     @SmallTest
     public void testSelectTextByDrag() throws Exception {
-        getActivity();
-
         final String helloWorld = "Hello world!";
         onView(withId(R.id.textview)).perform(click());
         onView(withId(R.id.textview)).perform(typeTextIntoFocusedView(helloWorld));
@@ -77,8 +86,6 @@
 
     @SmallTest
     public void testSelectTextByDrag_reverse() throws Exception {
-        getActivity();
-
         final String helloWorld = "Hello world!";
         onView(withId(R.id.textview)).perform(click());
         onView(withId(R.id.textview)).perform(typeTextIntoFocusedView(helloWorld));
@@ -89,9 +96,56 @@
     }
 
     @SmallTest
-    public void testSelectTextByLongClick() throws Exception {
-        getActivity();
+    public void testContextMenu() throws Exception {
+        final String text = "abc def ghi.";
+        onView(withId(R.id.textview)).perform(click());
+        onView(withId(R.id.textview)).perform(typeTextIntoFocusedView(text));
 
+        ContextMenuUtils.assertContextMenuIsNotDisplayed();
+
+        onView(withId(R.id.textview)).perform(
+                mouseClickOnTextAtIndex(text.indexOf("d"), MotionEvent.BUTTON_SECONDARY));
+
+        ContextMenuUtils.assertContextMenuContainsItemDisabled(
+                getActivity().getString(com.android.internal.R.string.copy));
+        ContextMenuUtils.assertContextMenuContainsItemEnabled(
+                getActivity().getString(com.android.internal.R.string.undo));
+
+        // Hide context menu.
+        pressBack();
+        ContextMenuUtils.assertContextMenuIsNotDisplayed();
+
+        onView(withId(R.id.textview)).perform(
+                mouseDragOnText(text.indexOf("c"), text.indexOf("h")));
+        onView(withId(R.id.textview)).perform(
+                mouseClickOnTextAtIndex(text.indexOf("d"), MotionEvent.BUTTON_SECONDARY));
+
+        ContextMenuUtils.assertContextMenuContainsItemEnabled(
+                getActivity().getString(com.android.internal.R.string.copy));
+        ContextMenuUtils.assertContextMenuContainsItemEnabled(
+                getActivity().getString(com.android.internal.R.string.undo));
+
+        // Hide context menu.
+        pressBack();
+
+        onView(withId(R.id.textview)).check(hasSelection("c def g"));
+
+        onView(withId(R.id.textview)).perform(
+                mouseClickOnTextAtIndex(text.indexOf("i"), MotionEvent.BUTTON_SECONDARY));
+        ContextMenuUtils.assertContextMenuContainsItemDisabled(
+                getActivity().getString(com.android.internal.R.string.copy));
+        ContextMenuUtils.assertContextMenuContainsItemEnabled(
+                getActivity().getString(com.android.internal.R.string.undo));
+
+        // Hide context menu.
+        pressBack();
+
+        onView(withId(R.id.textview)).check(hasSelection(""));
+        onView(withId(R.id.textview)).check(hasInsertionPointerAtIndex(text.indexOf("i")));
+    }
+
+    @SmallTest
+    public void testSelectTextByLongClick() throws Exception {
         final String helloWorld = "Hello world!";
         onView(withId(R.id.textview)).perform(click());
         onView(withId(R.id.textview)).perform(typeTextIntoFocusedView(helloWorld));
@@ -117,8 +171,6 @@
 
     @SmallTest
     public void testSelectTextByDoubleClick() throws Exception {
-        getActivity();
-
         final String helloWorld = "Hello world!";
         onView(withId(R.id.textview)).perform(click());
         onView(withId(R.id.textview)).perform(typeTextIntoFocusedView(helloWorld));
@@ -144,8 +196,6 @@
 
     @SmallTest
     public void testSelectTextByDoubleClickAndDrag() throws Exception {
-        getActivity();
-
         final String text = "abcd efg hijk lmn";
         onView(withId(R.id.textview)).perform(click());
         onView(withId(R.id.textview)).perform(typeTextIntoFocusedView(text));
@@ -157,8 +207,6 @@
 
     @SmallTest
     public void testSelectTextByDoubleClickAndDrag_reverse() throws Exception {
-        getActivity();
-
         final String text = "abcd efg hijk lmn";
         onView(withId(R.id.textview)).perform(click());
         onView(withId(R.id.textview)).perform(typeTextIntoFocusedView(text));
@@ -170,8 +218,6 @@
 
     @SmallTest
     public void testSelectTextByLongPressAndDrag() throws Exception {
-        getActivity();
-
         final String text = "abcd efg hijk lmn";
         onView(withId(R.id.textview)).perform(click());
         onView(withId(R.id.textview)).perform(typeTextIntoFocusedView(text));
@@ -183,8 +229,6 @@
 
     @SmallTest
     public void testSelectTextByLongPressAndDrag_reverse() throws Exception {
-        getActivity();
-
         final String text = "abcd efg hijk lmn";
         onView(withId(R.id.textview)).perform(click());
         onView(withId(R.id.textview)).perform(typeTextIntoFocusedView(text));
@@ -196,8 +240,6 @@
 
     @SmallTest
     public void testSelectTextByTripleClick() throws Exception {
-        getActivity();
-
         final StringBuilder builder = new StringBuilder();
         builder.append("First paragraph.\n");
         builder.append("Second paragraph.");
@@ -232,8 +274,6 @@
 
     @SmallTest
     public void testSelectTextByTripleClickAndDrag() throws Exception {
-        getActivity();
-
         final StringBuilder builder = new StringBuilder();
         builder.append("First paragraph.\n");
         builder.append("Second paragraph.");
@@ -263,8 +303,6 @@
 
     @SmallTest
     public void testSelectTextByTripleClickAndDrag_reverse() throws Exception {
-        getActivity();
-
         final StringBuilder builder = new StringBuilder();
         builder.append("First paragraph.\n");
         builder.append("Second paragraph.");
diff --git a/core/tests/coretests/src/android/widget/espresso/ContextMenuUtils.java b/core/tests/coretests/src/android/widget/espresso/ContextMenuUtils.java
new file mode 100644
index 0000000..c8218aa
--- /dev/null
+++ b/core/tests/coretests/src/android/widget/espresso/ContextMenuUtils.java
@@ -0,0 +1,110 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.widget.espresso;
+
+import static android.support.test.espresso.Espresso.onView;
+import static android.support.test.espresso.assertion.ViewAssertions.matches;
+import static android.support.test.espresso.matcher.RootMatchers.withDecorView;
+import static android.support.test.espresso.matcher.ViewMatchers.hasDescendant;
+import static android.support.test.espresso.matcher.ViewMatchers.hasFocus;
+import static android.support.test.espresso.matcher.ViewMatchers.isAssignableFrom;
+import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed;
+import static android.support.test.espresso.matcher.ViewMatchers.isEnabled;
+import static android.support.test.espresso.matcher.ViewMatchers.withText;
+import static org.hamcrest.Matchers.allOf;
+import static org.hamcrest.Matchers.not;
+
+import com.android.internal.view.menu.ListMenuItemView;
+
+import android.support.test.espresso.NoMatchingRootException;
+import android.support.test.espresso.NoMatchingViewException;
+import android.support.test.espresso.ViewInteraction;
+import android.support.test.espresso.matcher.ViewMatchers;
+import android.widget.MenuPopupWindow.MenuDropDownListView;
+
+/**
+ * Espresso utility methods for the context menu.
+ */
+public final class ContextMenuUtils {
+    private ContextMenuUtils() {}
+
+    private static ViewInteraction onContextMenu() {
+        // TODO: Have more reliable way to get context menu.
+        return onView(ViewMatchers.isAssignableFrom(MenuDropDownListView.class))
+                .inRoot(withDecorView(hasFocus()));
+    }
+
+    /**
+     * Asserts that the context menu is displayed
+     *
+     * @throws AssertionError if the assertion fails
+     */
+    private static void assertContextMenuIsDisplayed() {
+        onContextMenu().check(matches(isDisplayed()));
+    }
+
+    /**
+     * Asserts that the context menu is not displayed
+     *
+     * @throws AssertionError if the assertion fails
+     */
+    public static void assertContextMenuIsNotDisplayed() {
+        try {
+            assertContextMenuIsDisplayed();
+        } catch (NoMatchingRootException | NoMatchingViewException | AssertionError e) {
+            return;
+        }
+        throw new AssertionError("Context menu is displayed");
+    }
+
+    /**
+     * Asserts that the context menu contains the specified item and the item has specified enabled
+     *  state.
+     *
+     * @param itemLabel label of the item.
+     * @param enabled enabled state of the item.
+     * @throws AssertionError if the assertion fails
+     */
+    private static void asssertContextMenuContainsItemWithEnabledState(String itemLabel,
+            boolean enabled) {
+        onContextMenu().check(matches(
+                hasDescendant(allOf(
+                        isAssignableFrom(ListMenuItemView.class),
+                        enabled ? isEnabled() : not(isEnabled()),
+                        hasDescendant(withText(itemLabel))))));
+    }
+
+    /**
+     * Asserts that the context menu contains the specified item and the item is enabled.
+     *
+     * @param itemLabel label of the item.
+     * @throws AssertionError if the assertion fails
+     */
+    public static void assertContextMenuContainsItemEnabled(String itemLabel) {
+        asssertContextMenuContainsItemWithEnabledState(itemLabel, true);
+    }
+
+    /**
+     * Asserts that the context menu contains the specified item and the item is disabled.
+     *
+     * @param itemLabel label of the item.
+     * @throws AssertionError if the assertion fails
+     */
+    public static void assertContextMenuContainsItemDisabled(String itemLabel) {
+        asssertContextMenuContainsItemWithEnabledState(itemLabel, false);
+    }
+}
diff --git a/core/tests/coretests/src/android/widget/espresso/MouseClickAction.java b/core/tests/coretests/src/android/widget/espresso/MouseClickAction.java
index e51f2785..b8ea2de 100644
--- a/core/tests/coretests/src/android/widget/espresso/MouseClickAction.java
+++ b/core/tests/coretests/src/android/widget/espresso/MouseClickAction.java
@@ -24,9 +24,9 @@
 import android.support.test.espresso.action.GeneralClickAction;
 import android.support.test.espresso.action.MotionEvents;
 import android.support.test.espresso.action.MotionEvents.DownResultHolder;
-import android.support.test.espresso.action.PrecisionDescriber;
 import android.support.test.espresso.action.Press;
 import android.support.test.espresso.action.Tapper;
+import android.view.MotionEvent;
 import android.view.View;
 import android.view.ViewConfiguration;
 
@@ -35,6 +35,8 @@
  */
 public final class MouseClickAction implements ViewAction {
     private final GeneralClickAction mGeneralClickAction;
+    @MouseUiController.MouseButton
+    private final int mButton;
 
     public enum CLICK implements Tapper {
         TRIPLE {
@@ -86,8 +88,20 @@
     };
 
     public MouseClickAction(Tapper tapper, CoordinatesProvider coordinatesProvider) {
-        mGeneralClickAction = new GeneralClickAction(tapper, coordinatesProvider,
-                Press.PINPOINT);
+        this(tapper, coordinatesProvider, MotionEvent.BUTTON_PRIMARY);
+    }
+
+    /**
+     * Constructs MouseClickAction
+     *
+     * @param tapper the tapper
+     * @param coordinatesProvider the provider of the event coordinates
+     * @param button the mouse button used to send motion events
+     */
+    public MouseClickAction(Tapper tapper, CoordinatesProvider coordinatesProvider,
+            @MouseUiController.MouseButton int button) {
+        mGeneralClickAction = new GeneralClickAction(tapper, coordinatesProvider, Press.PINPOINT);
+        mButton = button;
     }
 
     @Override
@@ -102,7 +116,7 @@
 
     @Override
     public void perform(UiController uiController, View view) {
-        mGeneralClickAction.perform(new MouseUiController(uiController), view);
+        mGeneralClickAction.perform(new MouseUiController(uiController, mButton), view);
         long doubleTapTimeout = ViewConfiguration.getDoubleTapTimeout();
         if (0 < doubleTapTimeout) {
             // Wait to avoid false gesture detection. Without this wait, consecutive clicks can be
diff --git a/core/tests/coretests/src/android/widget/espresso/MouseUiController.java b/core/tests/coretests/src/android/widget/espresso/MouseUiController.java
index f1387f8..022be76 100644
--- a/core/tests/coretests/src/android/widget/espresso/MouseUiController.java
+++ b/core/tests/coretests/src/android/widget/espresso/MouseUiController.java
@@ -16,6 +16,12 @@
 
 package android.widget.espresso;
 
+import static com.android.internal.util.Preconditions.checkNotNull;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+import android.annotation.IntDef;
 import android.support.test.espresso.InjectEventSecurityException;
 import android.support.test.espresso.UiController;
 import android.view.InputDevice;
@@ -26,11 +32,28 @@
  * Class to wrap an UiController to overwrite source of motion events to SOURCE_MOUSE.
  * Note that this doesn't change the tool type.
  */
-public class MouseUiController implements UiController {
+public final class MouseUiController implements UiController {
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef({MotionEvent.BUTTON_PRIMARY, MotionEvent.BUTTON_SECONDARY, MotionEvent.BUTTON_TERTIARY})
+    public @interface MouseButton {}
+
     private final UiController mUiController;
+    @MouseButton
+    private final int mButton;
 
     public MouseUiController(UiController uiController) {
-        mUiController = uiController;
+        this(uiController, MotionEvent.BUTTON_PRIMARY);
+    }
+
+    /**
+     * Constructs MouseUiController.
+     *
+     * @param uiController the uiController to wrap
+     * @param button the button to be used for generating input events.
+     */
+    public MouseUiController(UiController uiController, @MouseButton int button) {
+        mUiController = checkNotNull(uiController);
+        mButton = button;
     }
 
     @Override
@@ -40,9 +63,11 @@
 
     @Override
     public boolean injectMotionEvent(MotionEvent event) throws InjectEventSecurityException {
-        // Modify the event to mimic mouse primary button event.
+        // Modify the event to mimic mouse event.
         event.setSource(InputDevice.SOURCE_MOUSE);
-        event.setButtonState(MotionEvent.BUTTON_PRIMARY);
+        if (event.getActionMasked() != MotionEvent.ACTION_UP) {
+            event.setButtonState(mButton);
+        }
         return mUiController.injectMotionEvent(event);
     }
 
diff --git a/core/tests/coretests/src/android/widget/espresso/TextViewActions.java b/core/tests/coretests/src/android/widget/espresso/TextViewActions.java
index 54d5823..1dd6e17 100644
--- a/core/tests/coretests/src/android/widget/espresso/TextViewActions.java
+++ b/core/tests/coretests/src/android/widget/espresso/TextViewActions.java
@@ -26,6 +26,7 @@
 import android.support.test.espresso.action.Tap;
 import android.support.test.espresso.util.HumanReadables;
 import android.text.Layout;
+import android.view.MotionEvent;
 import android.view.View;
 import android.widget.Editor;
 import android.widget.TextView;
@@ -63,8 +64,24 @@
      * @param index The index of the TextView's text to click on.
      */
     public static ViewAction mouseClickOnTextAtIndex(int index) {
+        return mouseClickOnTextAtIndex(index, MotionEvent.BUTTON_PRIMARY);
+    }
+
+    /**
+     * Returns an action that clicks by mouse on text at an index on the TextView.<br>
+     * <br>
+     * View constraints:
+     * <ul>
+     * <li>must be a TextView displayed on screen
+     * <ul>
+     *
+     * @param index The index of the TextView's text to click on.
+     * @param button the mouse button to use.
+     */
+    public static ViewAction mouseClickOnTextAtIndex(int index,
+            @MouseUiController.MouseButton int button) {
         return actionWithAssertions(
-                new MouseClickAction(Tap.SINGLE, new TextCoordinates(index)));
+                new MouseClickAction(Tap.SINGLE, new TextCoordinates(index), button));
     }
 
     /**
diff --git a/graphics/java/android/graphics/Canvas.java b/graphics/java/android/graphics/Canvas.java
index 5acc1a3..d5166ab 100644
--- a/graphics/java/android/graphics/Canvas.java
+++ b/graphics/java/android/graphics/Canvas.java
@@ -1079,11 +1079,12 @@
      *                 (count >> 2).
      * @param paint    The paint used to draw the points
      */
-    public void drawLines(@Size(min=4,multiple=2) float[] pts, int offset, int count, Paint paint) {
+    public void drawLines(@Size(multiple=4) @NonNull float[] pts, int offset, int count,
+            @NonNull Paint paint) {
         native_drawLines(mNativeCanvasWrapper, pts, offset, count, paint.getNativeInstance());
     }
 
-    public void drawLines(@Size(min=4,multiple=2) @NonNull float[] pts, @NonNull Paint paint) {
+    public void drawLines(@Size(multiple=4) @NonNull float[] pts, @NonNull Paint paint) {
         drawLines(pts, 0, pts.length, paint);
     }
 
diff --git a/libs/hwui/CanvasState.cpp b/libs/hwui/CanvasState.cpp
index 6a6cc42..cf76e6b 100644
--- a/libs/hwui/CanvasState.cpp
+++ b/libs/hwui/CanvasState.cpp
@@ -192,7 +192,7 @@
 ///////////////////////////////////////////////////////////////////////////////
 
 bool CanvasState::clipRect(float left, float top, float right, float bottom, SkRegion::Op op) {
-    mSnapshot->clip(left, top, right, bottom, op);
+    mSnapshot->clip(Rect(left, top, right, bottom), op);
     mDirtyClip = true;
     return !mSnapshot->clipIsEmpty();
 }
diff --git a/libs/hwui/ClipArea.cpp b/libs/hwui/ClipArea.cpp
index fd6f0b5..5f166ca 100644
--- a/libs/hwui/ClipArea.cpp
+++ b/libs/hwui/ClipArea.cpp
@@ -201,12 +201,6 @@
     mClipRegion.setEmpty();
 }
 
-void ClipArea::clipRectWithTransform(float left, float top, float right,
-        float bottom, const mat4* transform, SkRegion::Op op) {
-    Rect r(left, top, right, bottom);
-    clipRectWithTransform(r, transform, op);
-}
-
 void ClipArea::clipRectWithTransform(const Rect& r, const mat4* transform,
         SkRegion::Op op) {
     switch (mMode) {
@@ -274,13 +268,6 @@
     rectangleListModeClipRectWithTransform(r, transform, op);
 }
 
-void ClipArea::rectangleModeClipRectWithTransform(float left, float top,
-        float right, float bottom, const mat4* transform, SkRegion::Op op) {
-    Rect r(left, top, right, bottom);
-    rectangleModeClipRectWithTransform(r, transform, op);
-    mClipRect = mRectangleList.calculateBounds();
-}
-
 /*
  * RectangleList mode implementation
  */
@@ -303,12 +290,6 @@
     }
 }
 
-void ClipArea::rectangleListModeClipRectWithTransform(float left, float top,
-        float right, float bottom, const mat4* transform, SkRegion::Op op) {
-    Rect r(left, top, right, bottom);
-    rectangleListModeClipRectWithTransform(r, transform, op);
-}
-
 /*
  * Region mode implementation
  */
@@ -336,11 +317,6 @@
     onClipRegionUpdated();
 }
 
-void ClipArea::regionModeClipRectWithTransform(float left, float top,
-        float right, float bottom, const mat4* transform, SkRegion::Op op) {
-    regionModeClipRectWithTransform(Rect(left, top, right, bottom), transform, op);
-}
-
 void ClipArea::onClipRegionUpdated() {
     if (!mClipRegion.isEmpty()) {
         mClipRect.set(mClipRegion.getBounds());
diff --git a/libs/hwui/ClipArea.h b/libs/hwui/ClipArea.h
index f88fd92..268301c 100644
--- a/libs/hwui/ClipArea.h
+++ b/libs/hwui/ClipArea.h
@@ -98,8 +98,6 @@
 
     void setEmpty();
     void setClip(float left, float top, float right, float bottom);
-    void clipRectWithTransform(float left, float top, float right, float bottom,
-            const mat4* transform, SkRegion::Op op);
     void clipRectWithTransform(const Rect& r, const mat4* transform,
             SkRegion::Op op);
     void clipRegion(const SkRegion& region, SkRegion::Op op);
@@ -133,12 +131,8 @@
 private:
     void enterRectangleMode();
     void rectangleModeClipRectWithTransform(const Rect& r, const mat4* transform, SkRegion::Op op);
-    void rectangleModeClipRectWithTransform(float left, float top, float right,
-            float bottom, const mat4* transform, SkRegion::Op op);
 
     void enterRectangleListMode();
-    void rectangleListModeClipRectWithTransform(float left, float top,
-            float right, float bottom, const mat4* transform, SkRegion::Op op);
     void rectangleListModeClipRectWithTransform(const Rect& r,
             const mat4* transform, SkRegion::Op op);
 
@@ -147,8 +141,6 @@
     void enterRegionMode();
     void regionModeClipRectWithTransform(const Rect& r, const mat4* transform,
             SkRegion::Op op);
-    void regionModeClipRectWithTransform(float left, float top, float right,
-            float bottom, const mat4* transform, SkRegion::Op op);
 
     void ensureClipRegion();
     void onClipRegionUpdated();
diff --git a/libs/hwui/OpReorderer.cpp b/libs/hwui/OpReorderer.cpp
index bdc8f68..ad9559f 100644
--- a/libs/hwui/OpReorderer.cpp
+++ b/libs/hwui/OpReorderer.cpp
@@ -91,8 +91,7 @@
 
     MergingOpBatch(batchid_t batchId, BakedOpState* op)
             : BatchBase(batchId, op, true)
-            , mClipSideFlags(op->computedState.clipSideFlags)
-            , mClipRect(op->computedState.clipRect) {
+            , mClipSideFlags(op->computedState.clipSideFlags) {
     }
 
     /*
@@ -194,22 +193,17 @@
         mBounds.unionWith(op->computedState.clippedBounds);
         mOps.push_back(op);
 
-        const int newClipSideFlags = op->computedState.clipSideFlags;
-        mClipSideFlags |= newClipSideFlags;
-
-        const Rect& opClip = op->computedState.clipRect;
-        if (newClipSideFlags & OpClipSideFlags::Left) mClipRect.left = opClip.left;
-        if (newClipSideFlags & OpClipSideFlags::Top) mClipRect.top = opClip.top;
-        if (newClipSideFlags & OpClipSideFlags::Right) mClipRect.right = opClip.right;
-        if (newClipSideFlags & OpClipSideFlags::Bottom) mClipRect.bottom = opClip.bottom;
+        // Because a new op must have passed canMergeWith(), we know it's passed the clipping compat
+        // check, and doesn't extend past a side of the clip that's in use by the merged batch.
+        // Therefore it's safe to simply always merge flags, and use the bounds as the clip rect.
+        mClipSideFlags |= op->computedState.clipSideFlags;
     }
 
     int getClipSideFlags() const { return mClipSideFlags; }
-    const Rect& getClipRect() const { return mClipRect; }
+    const Rect& getClipRect() const { return mBounds; }
 
 private:
     int mClipSideFlags;
-    Rect mClipRect;
 };
 
 OpReorderer::LayerReorderer::LayerReorderer(uint32_t width, uint32_t height,
diff --git a/libs/hwui/RecordingCanvas.h b/libs/hwui/RecordingCanvas.h
index 48cc91a..470f9ec 100644
--- a/libs/hwui/RecordingCanvas.h
+++ b/libs/hwui/RecordingCanvas.h
@@ -39,7 +39,7 @@
 class DeferredLayerUpdater;
 struct RecordedOp;
 
-class RecordingCanvas: public Canvas, public CanvasStateClient {
+class ANDROID_API RecordingCanvas: public Canvas, public CanvasStateClient {
     enum class DeferredBarrierType {
         None,
         InOrder,
diff --git a/libs/hwui/Snapshot.cpp b/libs/hwui/Snapshot.cpp
index 2f535bb..c6d8977 100644
--- a/libs/hwui/Snapshot.cpp
+++ b/libs/hwui/Snapshot.cpp
@@ -88,9 +88,9 @@
     mClipArea->clipRegion(region, op);
 }
 
-void Snapshot::clip(float left, float top, float right, float bottom, SkRegion::Op op) {
+void Snapshot::clip(const Rect& localClip, SkRegion::Op op) {
     flags |= Snapshot::kFlagClipSet;
-    mClipArea->clipRectWithTransform(left, top, right, bottom, transform, op);
+    mClipArea->clipRectWithTransform(localClip, transform, op);
 }
 
 void Snapshot::clipPath(const SkPath& path, SkRegion::Op op) {
diff --git a/libs/hwui/Snapshot.h b/libs/hwui/Snapshot.h
index 4789b33..194aa57 100644
--- a/libs/hwui/Snapshot.h
+++ b/libs/hwui/Snapshot.h
@@ -124,7 +124,7 @@
      * the specified operation. The specified rectangle is transformed
      * by this snapshot's trasnformation.
      */
-    void clip(float left, float top, float right, float bottom, SkRegion::Op op);
+    void clip(const Rect& localClip, SkRegion::Op op);
 
     /**
      * Modifies the current clip with the new clip rectangle and
diff --git a/libs/hwui/tests/common/TestUtils.h b/libs/hwui/tests/common/TestUtils.h
index d37fad4..ac14fc8 100644
--- a/libs/hwui/tests/common/TestUtils.h
+++ b/libs/hwui/tests/common/TestUtils.h
@@ -98,7 +98,7 @@
 
     static std::unique_ptr<Snapshot> makeSnapshot(const Matrix4& transform, const Rect& clip) {
         std::unique_ptr<Snapshot> snapshot(new Snapshot());
-        snapshot->clip(clip.left, clip.top, clip.right, clip.bottom, SkRegion::kReplace_Op);
+        snapshot->clip(clip, SkRegion::kReplace_Op); // store clip first, so it isn't transformed
         *(snapshot->transform) = transform;
         return snapshot;
     }
diff --git a/libs/hwui/tests/unit/OpReordererTests.cpp b/libs/hwui/tests/unit/OpReordererTests.cpp
index 288f8af..b28e436 100644
--- a/libs/hwui/tests/unit/OpReordererTests.cpp
+++ b/libs/hwui/tests/unit/OpReordererTests.cpp
@@ -222,6 +222,46 @@
             << "Expect number of ops = 2 * loop count";
 }
 
+TEST(OpReorderer, clippedMerging) {
+    class ClippedMergingTestRenderer : public TestRendererBase {
+    public:
+        void onMergedBitmapOps(const MergedBakedOpList& opList) override {
+            EXPECT_EQ(0, mIndex);
+            mIndex += opList.count;
+            EXPECT_EQ(4u, opList.count);
+            EXPECT_EQ(Rect(10, 10, 90, 90), opList.clip);
+            EXPECT_EQ(OpClipSideFlags::Left | OpClipSideFlags::Top | OpClipSideFlags::Right,
+                    opList.clipSideFlags);
+        }
+    };
+    auto node = TestUtils::createNode(0, 0, 100, 100,
+            [](RenderProperties& props, TestCanvas& canvas) {
+        SkBitmap bitmap = TestUtils::createSkBitmap(20, 20);
+
+        // left side clipped (to inset left half)
+        canvas.clipRect(10, 0, 50, 100, SkRegion::kReplace_Op);
+        canvas.drawBitmap(bitmap, 0, 40, nullptr);
+
+        // top side clipped (to inset top half)
+        canvas.clipRect(0, 10, 100, 50, SkRegion::kReplace_Op);
+        canvas.drawBitmap(bitmap, 40, 0, nullptr);
+
+        // right side clipped (to inset right half)
+        canvas.clipRect(50, 0, 90, 100, SkRegion::kReplace_Op);
+        canvas.drawBitmap(bitmap, 80, 40, nullptr);
+
+        // bottom not clipped, just abutting (inset bottom half)
+        canvas.clipRect(0, 50, 100, 90, SkRegion::kReplace_Op);
+        canvas.drawBitmap(bitmap, 40, 70, nullptr);
+    });
+
+    OpReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 100), 100, 100,
+            createSyncedNodeList(node), sLightCenter);
+    ClippedMergingTestRenderer renderer;
+    reorderer.replayBakedOps<TestDispatcher>(renderer);
+    EXPECT_EQ(4, renderer.getIndex());
+}
+
 TEST(OpReorderer, textMerging) {
     class TextMergingTestRenderer : public TestRendererBase {
     public:
diff --git a/media/java/android/media/tv/TvInputManager.java b/media/java/android/media/tv/TvInputManager.java
index e9c94c0..6a13f82 100644
--- a/media/java/android/media/tv/TvInputManager.java
+++ b/media/java/android/media/tv/TvInputManager.java
@@ -129,9 +129,8 @@
      * The TV input is connected.
      *
      * <p>This state indicates that a source device is connected to the input port and is in the
-     * normal operation mode. It is mostly relevant to hardware inputs such as HDMI input. This is
-     * the default state for any hardware inputs where their states are unknown. Non-hardware inputs
-     * are considered connected all the time.
+     * normal operation mode. It is mostly relevant to hardware inputs such as HDMI input.
+     * Non-hardware inputs are considered connected all the time.
      *
      * @see #getInputState
      * @see TvInputManager.TvInputCallback#onInputStateChanged
@@ -141,7 +140,8 @@
      * The TV input is connected but in standby mode.
      *
      * <p>This state indicates that a source device is connected to the input port but is in standby
-     * mode. It is mostly relevant to hardware inputs such as HDMI input.
+     * or low power mode. It is mostly relevant to hardware inputs such as HDMI inputs and Component
+     * inputs.
      *
      * @see #getInputState
      * @see TvInputManager.TvInputCallback#onInputStateChanged
diff --git a/media/java/android/media/tv/TvStreamConfig.java b/media/java/android/media/tv/TvStreamConfig.java
index 0c2f3fe..eae83cf 100644
--- a/media/java/android/media/tv/TvStreamConfig.java
+++ b/media/java/android/media/tv/TvStreamConfig.java
@@ -28,8 +28,15 @@
 public class TvStreamConfig implements Parcelable {
     static final String TAG = TvStreamConfig.class.getSimpleName();
 
+    // Must be in sync with tv_input.h
     public final static int STREAM_TYPE_INDEPENDENT_VIDEO_SOURCE = 1;
     public final static int STREAM_TYPE_BUFFER_PRODUCER = 2;
+    /**
+     * A flag indicating whether the HAL is sure about signal at this stream. Note that
+     * value of 0 here does not necessarily mean no signal. It just means that it may not have
+     * signal and the underlying layer is not sure.
+     */
+    public static final int FLAG_MASK_SIGNAL_DETECTION = 0x1;
 
     private int mStreamId;
     private int mType;
@@ -41,6 +48,10 @@
      * via tv_input_device::get_stream_configurations().
      */
     private int mGeneration;
+    /**
+     * Flags for stream status. See FLAG_MASK_* for details.
+     */
+    private int mFlags;
 
     public static final Parcelable.Creator<TvStreamConfig> CREATOR =
             new Parcelable.Creator<TvStreamConfig>() {
@@ -52,7 +63,8 @@
                         type(source.readInt()).
                         maxWidth(source.readInt()).
                         maxHeight(source.readInt()).
-                        generation(source.readInt()).build();
+                        generation(source.readInt()).
+                        flags(source.readInt()).build();
             } catch (Exception e) {
                 Log.e(TAG, "Exception creating TvStreamConfig from parcel", e);
                 return null;
@@ -87,6 +99,10 @@
         return mGeneration;
     }
 
+    public int getFlags() {
+        return mFlags;
+    }
+
     @Override
     public String toString() {
         return "TvStreamConfig {mStreamId=" + mStreamId + ";" + "mType=" + mType + ";mGeneration="
@@ -106,6 +122,7 @@
         dest.writeInt(mMaxWidth);
         dest.writeInt(mMaxHeight);
         dest.writeInt(mGeneration);
+        dest.writeInt(mFlags);
     }
 
     /**
@@ -117,6 +134,7 @@
         private Integer mMaxWidth;
         private Integer mMaxHeight;
         private Integer mGeneration;
+        private int mFlags = 0;
 
         public Builder() {
         }
@@ -146,6 +164,11 @@
             return this;
         }
 
+        public Builder flags(int flag) {
+            mFlags = flag;
+            return this;
+        }
+
         public TvStreamConfig build() {
             if (mStreamId == null || mType == null || mMaxWidth == null || mMaxHeight == null
                     || mGeneration == null) {
@@ -158,6 +181,7 @@
             config.mMaxWidth = mMaxWidth;
             config.mMaxHeight = mMaxHeight;
             config.mGeneration = mGeneration;
+            config.mFlags = mFlags;
             return config;
         }
     }
@@ -172,6 +196,7 @@
             && config.mStreamId == mStreamId
             && config.mType == mType
             && config.mMaxWidth == mMaxWidth
-            && config.mMaxHeight == mMaxHeight;
+            && config.mMaxHeight == mMaxHeight
+            && config.mFlags == mFlags;
     }
 }
diff --git a/packages/DocumentsUI/AndroidManifest.xml b/packages/DocumentsUI/AndroidManifest.xml
index 8cc79a4c..5e634a4 100644
--- a/packages/DocumentsUI/AndroidManifest.xml
+++ b/packages/DocumentsUI/AndroidManifest.xml
@@ -40,7 +40,7 @@
 
         <activity
             android:name=".DownloadsActivity"
-            android:theme="@style/DocumentsFullScreenTheme"
+            android:theme="@style/DocumentsTheme"
             android:label="@string/downloads_label"
             android:icon="@drawable/ic_doc_text">
             <intent-filter>
@@ -64,7 +64,7 @@
 
         <activity
             android:name=".FilesActivity"
-            android:theme="@style/DocumentsFullScreenTheme"
+            android:theme="@style/DocumentsTheme"
             android:icon="@drawable/ic_files_app"
             android:label="@string/files_label"
             android:documentLaunchMode="intoExisting">
diff --git a/packages/DocumentsUI/res/values-sw720dp/dimens.xml b/packages/DocumentsUI/res/values-sw720dp/dimens.xml
index f393d88..83ceb55 100644
--- a/packages/DocumentsUI/res/values-sw720dp/dimens.xml
+++ b/packages/DocumentsUI/res/values-sw720dp/dimens.xml
@@ -15,10 +15,6 @@
 -->
 
 <resources>
-    <bool name="show_as_dialog">true</bool>
-
-    <item type="dimen" name="dialog_width">85%</item>
-
     <dimen name="grid_padding_horiz">16dp</dimen>
     <dimen name="grid_padding_vert">16dp</dimen>
 
diff --git a/packages/DocumentsUI/res/values-sw720dp/layouts.xml b/packages/DocumentsUI/res/values-sw720dp/layouts.xml
deleted file mode 100644
index 7d28f9c..0000000
--- a/packages/DocumentsUI/res/values-sw720dp/layouts.xml
+++ /dev/null
@@ -1,19 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2015 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-
-<resources>
-    <item name="docs_activity" type="layout">@layout/fixed_layout</item>
-</resources>
diff --git a/packages/DocumentsUI/res/values-sw720dp/styles.xml b/packages/DocumentsUI/res/values-sw720dp/styles.xml
deleted file mode 100644
index a8dcbb0..0000000
--- a/packages/DocumentsUI/res/values-sw720dp/styles.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2014 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android">
-
-    <style name="DocumentsBaseTheme" parent="@style/Theme.AppCompat.Light.Dialog">
-        <!-- We do not specify width of window here because the max size of
-             floating window specified by windowFixedWidthis is limited. -->
-        <item name="*android:windowFixedHeightMajor">80%</item>
-        <item name="*android:windowFixedHeightMinor">90%</item>
-    </style>
-
-</resources>
diff --git a/packages/DocumentsUI/res/values/attrs.xml b/packages/DocumentsUI/res/values/attrs.xml
index 0afc3a2..9e13001 100644
--- a/packages/DocumentsUI/res/values/attrs.xml
+++ b/packages/DocumentsUI/res/values/attrs.xml
@@ -14,7 +14,7 @@
      limitations under the License.
 -->
 <resources>
-    <declare-styleable name="DocumentsBaseTheme">
+    <declare-styleable name="DocumentsTheme">
         <attr name="colorActionMode" format="color"/>
     </declare-styleable>
 </resources>
diff --git a/packages/DocumentsUI/res/values/dimens.xml b/packages/DocumentsUI/res/values/dimens.xml
index f94a00e..060871d 100644
--- a/packages/DocumentsUI/res/values/dimens.xml
+++ b/packages/DocumentsUI/res/values/dimens.xml
@@ -35,7 +35,6 @@
     <dimen name="list_divider_inset">72dp</dimen>
     <bool name="list_divider_inset_left">true</bool>
 
-    <bool name="show_as_dialog">false</bool>
     <bool name="always_show_summary">false</bool>
 
     <dimen name="dir_elevation">8dp</dimen>
diff --git a/packages/DocumentsUI/res/values/styles.xml b/packages/DocumentsUI/res/values/styles.xml
index 6712e2d..d14631d 100644
--- a/packages/DocumentsUI/res/values/styles.xml
+++ b/packages/DocumentsUI/res/values/styles.xml
@@ -16,31 +16,10 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android">
 
-    <style name="DocumentsBaseTheme" parent="@style/Theme.AppCompat.Light.DarkActionBar" />
     <style name="ActionBarTheme" parent="@*android:style/ThemeOverlay.Material.Dark.ActionBar" />
     <style name="ActionBarPopupTheme" parent="@*android:style/ThemeOverlay.Material.Light" />
 
-    <style name="DocumentsTheme" parent="@style/DocumentsBaseTheme">
-        <item name="actionBarWidgetTheme">@null</item>
-        <item name="actionBarTheme">@style/ActionBarTheme</item>
-        <item name="actionBarPopupTheme">@style/ActionBarPopupTheme</item>
-
-        <item name="android:windowBackground">@color/window_background</item>
-        <item name="android:colorPrimaryDark">@color/primary_dark</item>
-        <item name="android:colorPrimary">@color/primary</item>
-        <item name="android:colorAccent">@color/accent</item>
-        <item name="colorActionMode">@color/action_mode</item>
-
-        <item name="android:listDivider">@*android:drawable/list_divider_material</item>
-
-        <item name="android:windowActionBar">false</item>
-        <item name="android:windowActionModeOverlay">true</item>
-        <item name="android:windowNoTitle">true</item>
-
-        <item name="android:windowSoftInputMode">stateUnspecified|adjustUnspecified</item>
-    </style>
-
-    <style name="DocumentsFullScreenTheme" parent="@style/Theme.AppCompat.Light.DarkActionBar">
+    <style name="DocumentsTheme" parent="@style/Theme.AppCompat.Light.DarkActionBar">
         <item name="actionBarWidgetTheme">@null</item>
         <item name="actionBarTheme">@style/ActionBarTheme</item>
         <item name="actionBarPopupTheme">@style/ActionBarPopupTheme</item>
diff --git a/packages/DocumentsUI/src/com/android/documentsui/CopyService.java b/packages/DocumentsUI/src/com/android/documentsui/CopyService.java
index fd8b56a..6a5911b 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/CopyService.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/CopyService.java
@@ -45,6 +45,7 @@
 import android.support.design.widget.Snackbar;
 import android.text.format.DateUtils;
 import android.util.Log;
+import android.webkit.MimeTypeMap;
 
 import com.android.documentsui.model.DocumentInfo;
 import com.android.documentsui.model.DocumentStack;
@@ -495,10 +496,41 @@
             }
         }
 
+        final String dstMimeType;
+        final String dstDisplayName;
+
+        // If the file is virtual, but can be converted to another format, then try to copy it
+        // as such format. Also, append an extension for the target mime type (if known).
+        if (srcInfo.isVirtualDocument()) {
+            if (!srcInfo.isTypedDocument()) {
+                // Impossible to copy a file which is virtual, but not typed.
+                mFailedFiles.add(srcInfo);
+                return false;
+            }
+            final String[] streamTypes = getContentResolver().getStreamTypes(
+                    srcInfo.derivedUri, "*/*");
+            if (streamTypes != null && streamTypes.length > 0) {
+                dstMimeType = streamTypes[0];
+                final String extension = MimeTypeMap.getSingleton().
+                        getExtensionFromMimeType(dstMimeType);
+                dstDisplayName = srcInfo.displayName +
+                        (extension != null ? "." + extension : srcInfo.displayName);
+            } else {
+                // The provider says that it supports typed documents, but doesn't say
+                // anything about available formats.
+                // TODO: Log failures. b/26192412
+                mFailedFiles.add(srcInfo);
+                return false;
+            }
+        } else {
+            dstMimeType = srcInfo.mimeType;
+            dstDisplayName = srcInfo.displayName;
+        }
+
         // Create the target document (either a file or a directory), then copy recursively the
         // contents (bytes or children).
         final Uri dstUri = DocumentsContract.createDocument(mDstClient,
-                dstDirInfo.derivedUri, srcInfo.mimeType, srcInfo.displayName);
+                dstDirInfo.derivedUri, dstMimeType, dstDisplayName);
         if (dstUri == null) {
             // If this is a directory, the entire subdir will not be copied over.
             mFailedFiles.add(srcInfo);
@@ -517,7 +549,7 @@
         if (Document.MIME_TYPE_DIR.equals(srcInfo.mimeType)) {
             success = copyDirectoryHelper(srcInfo, dstInfo, mode);
         } else {
-            success = copyFileHelper(srcInfo, dstInfo, mode);
+            success = copyFileHelper(srcInfo, dstInfo, dstMimeType, mode);
         }
 
         if (mode == TRANSFER_MODE_MOVE && success) {
@@ -593,11 +625,12 @@
      *
      * @param srcUriInfo Info of the file to copy from.
      * @param dstUriInfo Info of the *file* to copy to. Must be created beforehand.
+     * @param mimeType Mime type for the target. Can be different than source for virtual files.
      * @return True on success, false on error.
      * @throws RemoteException
      */
-    private boolean copyFileHelper(DocumentInfo srcInfo, DocumentInfo dstInfo, int mode)
-            throws RemoteException {
+    private boolean copyFileHelper(DocumentInfo srcInfo, DocumentInfo dstInfo, String mimeType,
+            int mode) throws RemoteException {
         // Copy an individual file.
         CancellationSignal canceller = new CancellationSignal();
         ParcelFileDescriptor srcFile = null;
@@ -610,19 +643,11 @@
             // If the file is virtual, but can be converted to another format, then try to copy it
             // as such format.
             if (srcInfo.isVirtualDocument() && srcInfo.isTypedDocument()) {
-                final String[] streamTypes = mSrcClient.getStreamTypes(
-                        srcInfo.derivedUri, "*/*");
-                if (streamTypes.length > 0) {
-                    // Pick the first streamable format.
-                    final AssetFileDescriptor srcFileAsAsset =
-                            mSrcClient.openTypedAssetFileDescriptor(
-                                    srcInfo.derivedUri, streamTypes[0], null, canceller);
-                    srcFile = srcFileAsAsset.getParcelFileDescriptor();
-                    src = new AssetFileDescriptor.AutoCloseInputStream(srcFileAsAsset);
-                } else {
-                    // TODO: Log failures. b/26192412
-                    mFailedFiles.add(srcInfo);
-                }
+                final AssetFileDescriptor srcFileAsAsset =
+                        mSrcClient.openTypedAssetFileDescriptor(
+                                srcInfo.derivedUri, mimeType, null, canceller);
+                srcFile = srcFileAsAsset.getParcelFileDescriptor();
+                src = new AssetFileDescriptor.AutoCloseInputStream(srcFileAsAsset);
             } else {
                 srcFile = mSrcClient.openFile(srcInfo.derivedUri, "r", canceller);
                 src = new ParcelFileDescriptor.AutoCloseInputStream(srcFile);
diff --git a/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java b/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java
index 313d303..8ca2cfb 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java
@@ -34,7 +34,6 @@
 import android.content.Intent;
 import android.content.pm.ResolveInfo;
 import android.content.res.Resources;
-import android.graphics.Point;
 import android.net.Uri;
 import android.os.AsyncTask;
 import android.os.Bundle;
@@ -45,7 +44,6 @@
 import android.view.Menu;
 import android.view.MenuItem;
 import android.view.View;
-import android.view.WindowManager;
 import android.widget.BaseAdapter;
 import android.widget.Spinner;
 import android.widget.Toolbar;
@@ -64,8 +62,6 @@
     private static final int CODE_FORWARD = 42;
     private static final String TAG = "DocumentsActivity";
 
-    private boolean mShowAsDialog;
-
     private Toolbar mToolbar;
     private Spinner mToolbarStack;
 
@@ -83,29 +79,8 @@
         super.onCreate(icicle);
 
         final Resources res = getResources();
-        mShowAsDialog = res.getBoolean(R.bool.show_as_dialog);
 
-        if (!mShowAsDialog) {
-            setTheme(R.style.DocumentsFullScreenTheme);
-        }
-
-        if (mShowAsDialog) {
-            mDrawer = DrawerController.createDummy();
-
-            // Strongly define our horizontal dimension; we leave vertical as
-            // WRAP_CONTENT so that system resizes us when IME is showing.
-            final WindowManager.LayoutParams a = getWindow().getAttributes();
-
-            final Point size = new Point();
-            getWindowManager().getDefaultDisplay().getSize(size);
-            a.width = (int) res.getFraction(R.dimen.dialog_width, size.x, size.x);
-
-            getWindow().setAttributes(a);
-
-        } else {
-            mDrawer = DrawerController.create(this);
-        }
-
+        mDrawer = DrawerController.create(this);
         mToolbar = (Toolbar) findViewById(R.id.toolbar);
 
         mStackAdapter = new StackAdapter();
@@ -267,15 +242,16 @@
             }
         }
 
-        if (!mShowAsDialog && mDrawer.isUnlocked()) {
+        if (mDrawer.isUnlocked()) {
             mToolbar.setNavigationIcon(R.drawable.ic_hamburger);
             mToolbar.setNavigationContentDescription(R.string.drawer_open);
-            mToolbar.setNavigationOnClickListener(new View.OnClickListener() {
-                @Override
-                public void onClick(View v) {
-                    setRootsDrawerOpen(true);
-                }
-            });
+            mToolbar.setNavigationOnClickListener(
+                    new View.OnClickListener() {
+                        @Override
+                        public void onClick(View v) {
+                            setRootsDrawerOpen(true);
+                        }
+                    });
         } else {
             mToolbar.setNavigationIcon(null);
             mToolbar.setNavigationContentDescription(R.string.drawer_open);
@@ -306,10 +282,7 @@
     public boolean onCreateOptionsMenu(Menu menu) {
         boolean showMenu = super.onCreateOptionsMenu(menu);
 
-        // Most actions are visible when showing as dialog
-        if (mShowAsDialog) {
-            expandMenus(menu);
-        }
+        expandMenus(menu);
         return showMenu;
     }
 
diff --git a/packages/DocumentsUI/tests/src/com/android/documentsui/CopyServiceTest.java b/packages/DocumentsUI/tests/src/com/android/documentsui/CopyServiceTest.java
index 079d599..24a8113 100644
--- a/packages/DocumentsUI/tests/src/com/android/documentsui/CopyServiceTest.java
+++ b/packages/DocumentsUI/tests/src/com/android/documentsui/CopyServiceTest.java
@@ -97,7 +97,7 @@
 
     public void testCopyFile() throws Exception {
         String srcPath = "/test0.txt";
-        Uri testFile = mStorage.createFile(SRC_ROOT, srcPath, "text/plain",
+        Uri testFile = mStorage.createRegularFile(SRC_ROOT, srcPath, "text/plain",
                 "The five boxing wizards jump quickly".getBytes());
 
         startService(createCopyIntent(Lists.newArrayList(testFile)));
@@ -110,10 +110,33 @@
         assertCopied(srcPath);
     }
 
+    public void testCopyVirtualTypedFile() throws Exception {
+        String srcPath = "/virtual.sth";
+        String expectedDstPath = "/virtual.sth.pdf";
+        ArrayList<String> streamTypes = new ArrayList<>();
+        streamTypes.add("application/pdf");
+        streamTypes.add("text/html");
+        String testContent = "I love fruit cakes!";
+        Uri testFile = mStorage.createVirtualFile(SRC_ROOT, srcPath, "virtual/mime-type",
+                streamTypes, testContent.getBytes());
+
+        startService(createCopyIntent(Lists.newArrayList(testFile)));
+
+        // 2 operations: file creation, then writing data.
+        mResolver.waitForChanges(2);
+
+        // Verify that one file was copied.
+        assertDestFileCount(1);
+
+        byte[] dstContent = readFile(DST_ROOT, expectedDstPath);
+        MoreAsserts.assertEquals("Moved file contents differ", testContent.getBytes(), dstContent);
+    }
+
     public void testMoveFile() throws Exception {
         String srcPath = "/test0.txt";
         String testContent = "The five boxing wizards jump quickly";
-        Uri testFile = mStorage.createFile(SRC_ROOT, srcPath, "text/plain", testContent.getBytes());
+        Uri testFile = mStorage.createRegularFile(SRC_ROOT, srcPath, "text/plain",
+                testContent.getBytes());
 
         Intent moveIntent = createCopyIntent(Lists.newArrayList(testFile));
         moveIntent.putExtra(CopyService.EXTRA_TRANSFER_MODE, CopyService.TRANSFER_MODE_MOVE);
@@ -142,9 +165,12 @@
                 "/test2.txt"
         };
         List<Uri> testFiles = Lists.newArrayList(
-                mStorage.createFile(SRC_ROOT, srcPaths[0], "text/plain", testContent[0].getBytes()),
-                mStorage.createFile(SRC_ROOT, srcPaths[1], "text/plain", testContent[1].getBytes()),
-                mStorage.createFile(SRC_ROOT, srcPaths[2], "text/plain", testContent[2].getBytes()));
+                mStorage.createRegularFile(SRC_ROOT, srcPaths[0], "text/plain",
+                        testContent[0].getBytes()),
+                mStorage.createRegularFile(SRC_ROOT, srcPaths[1], "text/plain",
+                        testContent[1].getBytes()),
+                mStorage.createRegularFile(SRC_ROOT, srcPaths[2], "text/plain",
+                        testContent[2].getBytes()));
 
         // Copy all the test files.
         startService(createCopyIntent(testFiles));
@@ -195,7 +221,6 @@
 
         Intent intent = createCopyIntent(Lists.newArrayList(testDir), descDir);
         startService(intent);
-
         getService().addFinishedListener(mListener);
 
         mListener.waitForFinished();
@@ -240,9 +265,9 @@
         };
         // Create test dir; put some files in it.
         Uri testDir = createTestDirectory(srcDir);
-        mStorage.createFile(SRC_ROOT, srcFiles[0], "text/plain", testContent[0].getBytes());
-        mStorage.createFile(SRC_ROOT, srcFiles[1], "text/plain", testContent[1].getBytes());
-        mStorage.createFile(SRC_ROOT, srcFiles[2], "text/plain", testContent[2].getBytes());
+        mStorage.createRegularFile(SRC_ROOT, srcFiles[0], "text/plain", testContent[0].getBytes());
+        mStorage.createRegularFile(SRC_ROOT, srcFiles[1], "text/plain", testContent[1].getBytes());
+        mStorage.createRegularFile(SRC_ROOT, srcFiles[2], "text/plain", testContent[2].getBytes());
 
         Intent moveIntent = createCopyIntent(Lists.newArrayList(testDir));
         moveIntent.putExtra(CopyService.EXTRA_TRANSFER_MODE, CopyService.TRANSFER_MODE_MOVE);
@@ -270,7 +295,7 @@
 
     public void testCopyFileWithReadErrors() throws Exception {
         String srcPath = "/test0.txt";
-        Uri testFile = mStorage.createFile(SRC_ROOT, srcPath, "text/plain",
+        Uri testFile = mStorage.createRegularFile(SRC_ROOT, srcPath, "text/plain",
                 "The five boxing wizards jump quickly".getBytes());
 
         mStorage.simulateReadErrorsForFile(testFile);
@@ -284,9 +309,26 @@
         assertDestFileCount(0);
     }
 
+    public void testCopyVirtualNonTypedFile() throws Exception {
+        String srcPath = "/non-typed.sth";
+        // Empty stream types causes the FLAG_SUPPORTS_TYPED_DOCUMENT to be not set.
+        ArrayList<String> streamTypes = new ArrayList<>();
+        Uri testFile = mStorage.createVirtualFile(SRC_ROOT, srcPath, "virtual/mime-type",
+                streamTypes, "I love Tokyo!".getBytes());
+
+        Intent intent = createCopyIntent(Lists.newArrayList(testFile));
+        startService(intent);
+        getService().addFinishedListener(mListener);
+
+        mListener.waitForFinished();
+        mListener.assertFailedCount(1);
+        mListener.assertFileFailed("non-typed.sth");
+        assertDestFileCount(0);
+    }
+
     public void testMoveFileWithReadErrors() throws Exception {
         String srcPath = "/test0.txt";
-        Uri testFile = mStorage.createFile(SRC_ROOT, srcPath, "text/plain",
+        Uri testFile = mStorage.createRegularFile(SRC_ROOT, srcPath, "text/plain",
                 "The five boxing wizards jump quickly".getBytes());
 
         mStorage.simulateReadErrorsForFile(testFile);
@@ -326,10 +368,10 @@
         };
         // Create test dir; put some files in it.
         Uri testDir = createTestDirectory(srcDir);
-        mStorage.createFile(SRC_ROOT, srcFiles[0], "text/plain", testContent[0].getBytes());
+        mStorage.createRegularFile(SRC_ROOT, srcFiles[0], "text/plain", testContent[0].getBytes());
         Uri errFile = mStorage
-                .createFile(SRC_ROOT, srcFiles[1], "text/plain", testContent[1].getBytes());
-        mStorage.createFile(SRC_ROOT, srcFiles[2], "text/plain", testContent[2].getBytes());
+                .createRegularFile(SRC_ROOT, srcFiles[1], "text/plain", testContent[1].getBytes());
+        mStorage.createRegularFile(SRC_ROOT, srcFiles[2], "text/plain", testContent[2].getBytes());
 
         mStorage.simulateReadErrorsForFile(errFile);
 
@@ -363,7 +405,7 @@
     }
 
     private Uri createTestDirectory(String dir) throws IOException {
-        return mStorage.createFile(
+        return mStorage.createRegularFile(
                 SRC_ROOT, dir, DocumentsContract.Document.MIME_TYPE_DIR, null);
     }
 
@@ -473,6 +515,7 @@
 
         final CountDownLatch latch = new CountDownLatch(1);
         final List<DocumentInfo> failedDocs = new ArrayList<>();
+
         @Override
         public void onFinished(List<DocumentInfo> failed) {
             failedDocs.addAll(failed);
diff --git a/packages/DocumentsUI/tests/src/com/android/documentsui/StubProvider.java b/packages/DocumentsUI/tests/src/com/android/documentsui/StubProvider.java
index 7a75503..2c311a7 100644
--- a/packages/DocumentsUI/tests/src/com/android/documentsui/StubProvider.java
+++ b/packages/DocumentsUI/tests/src/com/android/documentsui/StubProvider.java
@@ -44,9 +44,11 @@
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
+import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
 import java.util.HashMap;
+import java.util.List;
 import java.util.Map;
 
 public class StubProvider extends DocumentsProvider {
@@ -59,7 +61,7 @@
     private static final String EXTRA_SIZE = "com.android.documentsui.stubprovider.SIZE";
     private static final String EXTRA_ROOT = "com.android.documentsui.stubprovider.ROOT";
     private static final String STORAGE_SIZE_KEY = "documentsui.stubprovider.size";
-    private static int DEFAULT_ROOT_SIZE = 1024 * 1024 * 100; // 100 MB.
+    private static int DEFAULT_ROOT_SIZE = 1024 * 1024 * 100;  // 100 MB.
 
     private static final String[] DEFAULT_ROOT_PROJECTION = new String[] {
             Root.COLUMN_ROOT_ID, Root.COLUMN_FLAGS, Root.COLUMN_TITLE, Root.COLUMN_DOCUMENT_ID,
@@ -105,7 +107,13 @@
 
         mRoots.clear();
         for (String rootId : rootIds) {
-            final RootInfo rootInfo = new RootInfo(rootId, getSize(rootId));
+            // Make a subdir in the cache dir for each root.
+            final File file = new File(getContext().getCacheDir(), rootId);
+            if (file.mkdir()) {
+                Log.i(TAG, "Created new root directory @ " + file.getPath());
+            }
+            final RootInfo rootInfo = new RootInfo(file, getSize(rootId));
+            mStorage.put(rootInfo.document.documentId, rootInfo.document);
             mRoots.put(rootId, rootInfo);
         }
     }
@@ -188,7 +196,7 @@
                 created = file.createNewFile();
             } catch (IOException e) {
                 // We'll throw an FNF exception later :)
-                Log.e(TAG, "createnewFile operation failed for file: " + file, e);
+                Log.e(TAG, "createNewFile operation failed for file: " + file, e);
             }
             if (!created) {
                 throw new FileNotFoundException(
@@ -197,7 +205,8 @@
             Log.i(TAG, "Created new file: " + file);
         }
 
-        final StubDocument document = new StubDocument(file, mimeType, parent);
+        final StubDocument document = StubDocument.createRegularDocument(file, mimeType, parent);
+        mStorage.put(document.documentId, document);
         Log.d(TAG, "Created document " + document.documentId);
         notifyParentChanged(document.parentId);
         getContext().getContentResolver().notifyChange(
@@ -264,14 +273,18 @@
     public ParcelFileDescriptor openDocument(String docId, String mode, CancellationSignal signal)
             throws FileNotFoundException {
         final StubDocument document = mStorage.get(docId);
-        if (document == null || !document.file.isFile())
+        if (document == null || !document.file.isFile()) {
             throw new FileNotFoundException();
+        }
+        if ((document.flags & Document.FLAG_VIRTUAL_DOCUMENT) != 0) {
+            throw new IllegalStateException("Tried to open a virtual file.");
+        }
 
         if ("r".equals(mode)) {
-            ParcelFileDescriptor pfd = ParcelFileDescriptor.open(document.file,
-                    ParcelFileDescriptor.MODE_READ_ONLY);
+            final ParcelFileDescriptor pfd = ParcelFileDescriptor.open(document.file,
+                        ParcelFileDescriptor.MODE_READ_ONLY);
             if (docId.equals(mSimulateReadErrors)) {
-                pfd = new ParcelFileDescriptor(pfd) {
+                return new ParcelFileDescriptor(pfd) {
                     @Override
                     public void checkError() throws IOException {
                         throw new IOException("Test error");
@@ -298,6 +311,54 @@
         throw new FileNotFoundException();
     }
 
+    @Override
+    public AssetFileDescriptor openTypedDocument(
+            String documentId, String mimeTypeFilter, Bundle opts, CancellationSignal signal)
+            throws FileNotFoundException {
+        final StubDocument document = mStorage.get(documentId);
+        if (document == null || !document.file.isFile()) {
+            throw new FileNotFoundException();
+        }
+        if ((document.flags & Document.FLAG_SUPPORTS_TYPED_DOCUMENT) == 0) {
+            throw new IllegalStateException("Tried to open a non-typed document as typed.");
+        }
+        for (final String mimeType : document.streamTypes) {
+            // Strict compare won't accept wildcards, but that's OK for tests, as DocumentsUI
+            // doesn't use them for getStreamTypes nor openTypedDocument.
+            if (mimeType.equals(mimeTypeFilter)) {
+                ParcelFileDescriptor pfd = ParcelFileDescriptor.open(
+                            document.file, ParcelFileDescriptor.MODE_READ_ONLY);
+                if (documentId.equals(mSimulateReadErrors)) {
+                    pfd = new ParcelFileDescriptor(pfd) {
+                        @Override
+                        public void checkError() throws IOException {
+                            throw new IOException("Test error");
+                        }
+                    };
+                }
+                return new AssetFileDescriptor(pfd, 0, document.file.length());
+            }
+        }
+        throw new IllegalArgumentException("Invalid MIME type filter for openTypedDocument().");
+    }
+
+    @Override
+    public String[] getStreamTypes(Uri uri, String mimeTypeFilter) {
+        final StubDocument document = mStorage.get(DocumentsContract.getDocumentId(uri));
+        if (document == null) {
+            throw new IllegalArgumentException(
+                    "The provided Uri is incorrect, or the file is gone.");
+        }
+        if ((document.flags & Document.FLAG_SUPPORTS_TYPED_DOCUMENT) == 0) {
+            return null;
+        }
+        if (!"*/*".equals(mimeTypeFilter)) {
+            // Not used by DocumentsUI, so don't bother implementing it.
+            throw new UnsupportedOperationException();
+        }
+        return document.streamTypes.toArray(new String[document.streamTypes.size()]);
+    }
+
     private ParcelFileDescriptor startWrite(final StubDocument document)
             throws FileNotFoundException {
         ParcelFileDescriptor[] pipe;
@@ -398,14 +459,7 @@
         row.add(Document.COLUMN_DISPLAY_NAME, document.file.getName());
         row.add(Document.COLUMN_SIZE, document.file.length());
         row.add(Document.COLUMN_MIME_TYPE, document.mimeType);
-        int flags = Document.FLAG_SUPPORTS_DELETE;
-        // TODO: Add support for renaming.
-        if (document.file.isDirectory()) {
-            flags |= Document.FLAG_DIR_SUPPORTS_CREATE;
-        } else {
-            flags |= Document.FLAG_SUPPORTS_WRITE;
-        }
-        row.add(Document.COLUMN_FLAGS, flags);
+        row.add(Document.COLUMN_FLAGS, document.flags);
         row.add(Document.COLUMN_LAST_MODIFIED, document.file.lastModified());
     }
 
@@ -439,37 +493,30 @@
     }
 
     @VisibleForTesting
-    public Uri createFile(String rootId, String path, String mimeType, byte[] content)
+    public Uri createRegularFile(String rootId, String path, String mimeType, byte[] content)
             throws FileNotFoundException, IOException {
-        Log.d(TAG, "Creating test file " + rootId + ":" + path);
-        StubDocument root = mRoots.get(rootId).document;
-        if (root == null) {
-            throw new FileNotFoundException("No roots with the ID " + rootId + " were found");
-        }
-        File file = new File(root.file, path.substring(1));
-        StubDocument parent = mStorage.get(getDocumentIdForFile(file.getParentFile()));
+        final File file = createFile(rootId, path, mimeType, content);
+        final StubDocument parent = mStorage.get(getDocumentIdForFile(file.getParentFile()));
         if (parent == null) {
-            parent = mStorage.get(createFile(rootId, file.getParentFile().getPath(),
-                    DocumentsContract.Document.MIME_TYPE_DIR, null));
-            Log.d(TAG, "Created parent " + parent.documentId);
-        } else {
-            Log.d(TAG, "Found parent " + parent.documentId);
+            throw new FileNotFoundException("Parent not found.");
         }
+        final StubDocument document = StubDocument.createRegularDocument(file, mimeType, parent);
+        mStorage.put(document.documentId, document);
+        return DocumentsContract.buildDocumentUri(mAuthority,  document.documentId);
+    }
 
-        if (DocumentsContract.Document.MIME_TYPE_DIR.equals(mimeType)) {
-            if (!file.mkdirs()) {
-                throw new FileNotFoundException("Couldn't create directory " + file.getPath());
-            }
-        } else {
-            if (!file.createNewFile()) {
-                throw new FileNotFoundException("Couldn't create file " + file.getPath());
-            }
-            // Add content to the file.
-            FileOutputStream fout = new FileOutputStream(file);
-            fout.write(content);
-            fout.close();
+    @VisibleForTesting
+    public Uri createVirtualFile(
+            String rootId, String path, String mimeType, List<String> streamTypes, byte[] content)
+            throws FileNotFoundException, IOException {
+        final File file = createFile(rootId, path, mimeType, content);
+        final StubDocument parent = mStorage.get(getDocumentIdForFile(file.getParentFile()));
+        if (parent == null) {
+            throw new FileNotFoundException("Parent not found.");
         }
-        final StubDocument document = new StubDocument(file, mimeType, parent);
+        final StubDocument document = StubDocument.createVirtualDocument(
+                file, mimeType, streamTypes, parent);
+        mStorage.put(document.documentId, document);
         return DocumentsContract.buildDocumentUri(mAuthority,  document.documentId);
     }
 
@@ -489,21 +536,39 @@
         return found.file;
     }
 
-    final class RootInfo {
+    private File createFile(String rootId, String path, String mimeType, byte[] content)
+            throws FileNotFoundException, IOException {
+        Log.d(TAG, "Creating test file " + rootId + ":" + path);
+        StubDocument root = mRoots.get(rootId).document;
+        if (root == null) {
+            throw new FileNotFoundException("No roots with the ID " + rootId + " were found");
+        }
+        final File file = new File(root.file, path.substring(1));
+        if (DocumentsContract.Document.MIME_TYPE_DIR.equals(mimeType)) {
+            if (!file.mkdirs()) {
+                throw new FileNotFoundException("Couldn't create directory " + file.getPath());
+            }
+        } else {
+            if (!file.createNewFile()) {
+                throw new FileNotFoundException("Couldn't create file " + file.getPath());
+            }
+            try (final FileOutputStream fout = new FileOutputStream(file)) {
+                fout.write(content);
+            }
+        }
+        return file;
+    }
+
+    final static class RootInfo {
         public final String name;
         public final StubDocument document;
         public long capacity;
         public long size;
 
-        RootInfo(String name, long capacity) {
-            this.name = name;
+        RootInfo(File file, long capacity) {
+            this.name = file.getName();
             this.capacity = 1024 * 1024;
-            // Make a subdir in the cache dir for each root.
-            File file = new File(getContext().getCacheDir(), name);
-            if (file.mkdir()) {
-                Log.i(TAG, "Created new root directory @ " + file.getPath());
-            }
-            this.document = new StubDocument(file, Document.MIME_TYPE_DIR, this);
+            this.document = StubDocument.createRootDocument(file, this);
             this.capacity = capacity;
             this.size = 0;
         }
@@ -513,38 +578,72 @@
         }
     }
 
-    final class StubDocument {
+    final static class StubDocument {
         public final File file;
-        public final String mimeType;
         public final String documentId;
+        public final String mimeType;
+        public final List<String> streamTypes;
+        public final int flags;
         public final String parentId;
         public final RootInfo rootInfo;
 
-        StubDocument(File file, String mimeType, StubDocument parent) {
+        private StubDocument(
+                 File file, String mimeType, List<String> streamTypes, int flags,
+                 StubDocument parent) {
             this.file = file;
-            this.mimeType = mimeType;
             this.documentId = getDocumentIdForFile(file);
+            this.mimeType = mimeType;
+            this.streamTypes = streamTypes;
+            this.flags = flags;
             this.parentId = parent.documentId;
             this.rootInfo = parent.rootInfo;
-            mStorage.put(this.documentId, this);
         }
 
-        StubDocument(File file, String mimeType, RootInfo rootInfo) {
+        private StubDocument(File file, RootInfo rootInfo) {
             this.file = file;
-            this.mimeType = mimeType;
             this.documentId = getDocumentIdForFile(file);
+            this.mimeType = Document.MIME_TYPE_DIR;
+            this.streamTypes = new ArrayList<String>();
+            this.flags = Document.FLAG_DIR_SUPPORTS_CREATE;
             this.parentId = null;
             this.rootInfo = rootInfo;
-            mStorage.put(this.documentId, this);
         }
+
+        public static StubDocument createRootDocument(File file, RootInfo rootInfo) {
+            return new StubDocument(file, rootInfo);
+        }
+
+        public static StubDocument createRegularDocument(
+                File file, String mimeType, StubDocument parent) {
+            int flags = Document.FLAG_SUPPORTS_DELETE;
+            if (file.isDirectory()) {
+                flags |= Document.FLAG_DIR_SUPPORTS_CREATE;
+            } else {
+                flags |= Document.FLAG_SUPPORTS_WRITE;
+            }
+            return new StubDocument(file, mimeType, new ArrayList<String>(), flags, parent);
+        }
+
+        public static StubDocument createVirtualDocument(
+                File file, String mimeType, List<String> streamTypes, StubDocument parent) {
+            int flags = Document.FLAG_SUPPORTS_DELETE | Document.FLAG_SUPPORTS_WRITE
+                    | Document.FLAG_VIRTUAL_DOCUMENT;
+            if (streamTypes.size() > 0) {
+                flags |= Document.FLAG_SUPPORTS_TYPED_DOCUMENT;
+            }
+            return new StubDocument(file, mimeType, streamTypes, flags, parent);
+        }
+
         @Override
         public String toString() {
             return "StubDocument{"
                     + "path:" + file.getPath()
-                    + ", mimeType:" + mimeType
-                    + ", rootInfo:" + rootInfo
                     + ", documentId:" + documentId
+                    + ", mimeType:" + mimeType
+                    + ", streamTypes:" + streamTypes.toString()
+                    + ", flags:" + flags
                     + ", parentId:" + parentId
+                    + ", rootInfo:" + rootInfo
                     + "}";
         }
     }
diff --git a/packages/MtpDocumentsProvider/src/com/android/mtp/Mapper.java b/packages/MtpDocumentsProvider/src/com/android/mtp/Mapper.java
index cb49535e..ac47067 100644
--- a/packages/MtpDocumentsProvider/src/com/android/mtp/Mapper.java
+++ b/packages/MtpDocumentsProvider/src/com/android/mtp/Mapper.java
@@ -66,10 +66,13 @@
         database.beginTransaction();
         try {
             final ContentValues[] valuesList = new ContentValues[1];
+            final ContentValues[] extraValuesList = new ContentValues[1];
             valuesList[0] = new ContentValues();
-            MtpDatabase.getDeviceDocumentValues(valuesList[0], device);
+            extraValuesList[0] = new ContentValues();
+            MtpDatabase.getDeviceDocumentValues(valuesList[0], extraValuesList[0], device);
             final boolean changed = putDocuments(
                     valuesList,
+                    extraValuesList,
                     COLUMN_PARENT_DOCUMENT_ID + " IS NULL",
                     EMPTY_ARGS,
                     /* heuristic */ false,
@@ -88,7 +91,7 @@
      * @param roots List of root information.
      * @return If roots are added or removed from the database.
      */
-    synchronized boolean putRootDocuments(
+    synchronized boolean putStorageDocuments(
             String parentDocumentId, Resources resources, MtpRoot[] roots) {
         final SQLiteDatabase database = mDatabase.getSQLiteDatabase();
         database.beginTransaction();
@@ -109,36 +112,21 @@
                     throw new Error("Unexpected map mode.");
             }
             final ContentValues[] valuesList = new ContentValues[roots.length];
+            final ContentValues[] extraValuesList = new ContentValues[roots.length];
             for (int i = 0; i < roots.length; i++) {
                 valuesList[i] = new ContentValues();
+                extraValuesList[i] = new ContentValues();
                 MtpDatabase.getStorageDocumentValues(
-                        valuesList[i], resources, parentDocumentId, roots[i]);
+                        valuesList[i], extraValuesList[i], resources, parentDocumentId, roots[i]);
             }
             final boolean changed = putDocuments(
                     valuesList,
+                    extraValuesList,
                     COLUMN_PARENT_DOCUMENT_ID + "=?",
                     strings(parentDocumentId),
                     heuristic,
                     mapColumn);
-            final ContentValues values = new ContentValues();
-            int i = 0;
-            for (final MtpRoot root : roots) {
-                // Use the same value for the root ID and the corresponding document ID.
-                final String documentId = valuesList[i++].getAsString(Document.COLUMN_DOCUMENT_ID);
-                // If it fails to insert/update documents, the document ID will be set with -1.
-                // In this case we don't insert/update root extra information neither.
-                if (documentId == null) {
-                    continue;
-                }
-                values.put(Root.COLUMN_ROOT_ID, documentId);
-                values.put(
-                        Root.COLUMN_FLAGS,
-                        Root.FLAG_SUPPORTS_IS_CHILD | Root.FLAG_SUPPORTS_CREATE);
-                values.put(Root.COLUMN_AVAILABLE_BYTES, root.mFreeSpace);
-                values.put(Root.COLUMN_CAPACITY_BYTES, root.mMaxCapacity);
-                values.put(Root.COLUMN_MIME_TYPES, "");
-                database.replace(TABLE_ROOT_EXTRA, null, values);
-            }
+
             database.setTransactionSuccessful();
             return changed;
         } finally {
@@ -176,6 +164,7 @@
         }
         putDocuments(
                 valuesList,
+                null,
                 COLUMN_PARENT_DOCUMENT_ID + "=?",
                 strings(parentId),
                 heuristic,
@@ -257,6 +246,7 @@
      * rows. If the methods adds rows to database, it updates valueList with correct document ID.
      *
      * @param valuesList Values for documents to be stored in the database.
+     * @param rootExtraValuesList Values for root extra to be stored in the database.
      * @param selection SQL where closure to select rows that shares the same parent.
      * @param args Argument for selection SQL.
      * @param heuristic Whether the mapping mode is heuristic.
@@ -264,6 +254,7 @@
      */
     private boolean putDocuments(
             ContentValues[] valuesList,
+            @Nullable ContentValues[] rootExtraValuesList,
             String selection,
             String[] args,
             boolean heuristic,
@@ -272,7 +263,14 @@
         boolean added = false;
         database.beginTransaction();
         try {
-            for (final ContentValues values : valuesList) {
+            for (int i = 0; i < valuesList.length; i++) {
+                final ContentValues values = valuesList[i];
+                final ContentValues rootExtraValues;
+                if (rootExtraValuesList != null) {
+                    rootExtraValues = rootExtraValuesList[i];
+                } else {
+                    rootExtraValues = null;
+                }
                 final Cursor candidateCursor = database.query(
                         TABLE_DOCUMENTS,
                         strings(Document.COLUMN_DOCUMENT_ID),
@@ -290,25 +288,26 @@
                     final long rowId;
                     if (candidateCursor.getCount() == 0) {
                         rowId = database.insert(TABLE_DOCUMENTS, null, values);
-                        if (rowId == -1) {
-                            throw new SQLiteException("Failed to put a document into database.");
-                        }
                         added = true;
                     } else if (!heuristic) {
                         candidateCursor.moveToNext();
-                        final String documentId = candidateCursor.getString(0);
-                        rowId = database.update(
+                        rowId = candidateCursor.getLong(0);
+                        database.update(
                                 TABLE_DOCUMENTS,
                                 values,
                                 SELECTION_DOCUMENT_ID,
-                                strings(documentId));
+                                strings(rowId));
                     } else {
                         values.put(COLUMN_ROW_STATE, ROW_STATE_PENDING);
-                        rowId = database.insert(TABLE_DOCUMENTS, null, values);
+                        rowId = database.insertOrThrow(TABLE_DOCUMENTS, null, values);
                     }
                     // Document ID is a primary integer key of the table. So the returned row
                     // IDs should be same with the document ID.
                     values.put(Document.COLUMN_DOCUMENT_ID, rowId);
+                    if (rootExtraValues != null) {
+                        rootExtraValues.put(Root.COLUMN_ROOT_ID, rowId);
+                        database.replace(TABLE_ROOT_EXTRA, null, rootExtraValues);
+                    }
                 } finally {
                     candidateCursor.close();
                 }
diff --git a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDatabase.java b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDatabase.java
index 10941eb..112914e 100644
--- a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDatabase.java
+++ b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDatabase.java
@@ -23,6 +23,9 @@
 import android.content.Context;
 import android.content.res.Resources;
 import android.database.Cursor;
+import android.database.DatabaseUtils;
+import android.database.MatrixCursor;
+import android.database.MatrixCursor.RowBuilder;
 import android.database.sqlite.SQLiteDatabase;
 import android.database.sqlite.SQLiteOpenHelper;
 import android.database.sqlite.SQLiteQueryBuilder;
@@ -106,17 +109,93 @@
      * @return Database cursor.
      */
     Cursor queryRoots(String[] columnNames) {
-        final SQLiteQueryBuilder builder = new SQLiteQueryBuilder();
-        builder.setTables(JOIN_ROOTS);
-        builder.setProjectionMap(COLUMN_MAP_ROOTS);
-        return builder.query(
-                mDatabase,
-                columnNames,
-                COLUMN_ROW_STATE + " IN (?, ?) AND " + COLUMN_DOCUMENT_TYPE + " = ?",
-                strings(ROW_STATE_VALID, ROW_STATE_INVALIDATED, DOCUMENT_TYPE_STORAGE),
+        final String selection =
+                COLUMN_ROW_STATE + " IN (?, ?) AND " + COLUMN_DOCUMENT_TYPE + " = ?";
+        final Cursor deviceCursor = mDatabase.query(
+                TABLE_DOCUMENTS,
+                strings(COLUMN_DEVICE_ID),
+                selection,
+                strings(ROW_STATE_VALID, ROW_STATE_INVALIDATED, DOCUMENT_TYPE_DEVICE),
+                COLUMN_DEVICE_ID,
                 null,
                 null,
                 null);
+
+        try {
+            final SQLiteQueryBuilder builder = new SQLiteQueryBuilder();
+            builder.setTables(JOIN_ROOTS);
+            builder.setProjectionMap(COLUMN_MAP_ROOTS);
+            final MatrixCursor result = new MatrixCursor(columnNames);
+            final ContentValues values = new ContentValues();
+
+            while (deviceCursor.moveToNext()) {
+                final int deviceId = deviceCursor.getInt(0);
+                final Cursor storageCursor = builder.query(
+                        mDatabase,
+                        columnNames,
+                        selection + " AND " + COLUMN_DEVICE_ID + " = ?",
+                        strings(ROW_STATE_VALID,
+                                ROW_STATE_INVALIDATED,
+                                DOCUMENT_TYPE_STORAGE,
+                                deviceId),
+                        null,
+                        null,
+                        null);
+                try {
+                    values.clear();
+                    if (storageCursor.getCount() == 1) {
+                        storageCursor.moveToNext();
+                        DatabaseUtils.cursorRowToContentValues(storageCursor, values);
+                    } else {
+                        final Cursor cursor = builder.query(
+                                mDatabase,
+                                columnNames,
+                                selection + " AND " + COLUMN_DEVICE_ID + " = ?",
+                                strings(ROW_STATE_VALID,
+                                        ROW_STATE_INVALIDATED,
+                                        DOCUMENT_TYPE_DEVICE,
+                                        deviceId),
+                                null,
+                                null,
+                                null);
+                        try {
+                            cursor.moveToNext();
+                            DatabaseUtils.cursorRowToContentValues(cursor, values);
+                        } finally {
+                            cursor.close();
+                        }
+
+                        long capacityBytes = 0;
+                        long availableBytes = 0;
+                        int capacityIndex = cursor.getColumnIndex(Root.COLUMN_CAPACITY_BYTES);
+                        int availableIndex = cursor.getColumnIndex(Root.COLUMN_AVAILABLE_BYTES);
+                        while (storageCursor.moveToNext()) {
+                            // If requested columnNames does not include COLUMN_XXX_BYTES, we don't
+                            // calculate corresponding values.
+                            if (capacityIndex != -1) {
+                                capacityBytes += cursor.getLong(capacityIndex);
+                            }
+                            if (availableIndex != -1) {
+                                availableBytes += cursor.getLong(availableIndex);
+                            }
+                        }
+                        values.put(Root.COLUMN_CAPACITY_BYTES, capacityBytes);
+                        values.put(Root.COLUMN_AVAILABLE_BYTES, availableBytes);
+                    }
+                } finally {
+                    storageCursor.close();
+                }
+
+                final RowBuilder row = result.newRow();
+                for (final String key : values.keySet()) {
+                    row.add(key, values.get(key));
+                }
+            }
+
+            return result;
+        } finally {
+            deviceCursor.close();
+        }
     }
 
     /**
@@ -380,7 +459,10 @@
         context.deleteDatabase(DATABASE_NAME);
     }
 
-    static void getDeviceDocumentValues(ContentValues values, MtpDeviceRecord device) {
+    static void getDeviceDocumentValues(
+            ContentValues values,
+            ContentValues extraValues,
+            MtpDeviceRecord device) {
         values.clear();
         values.put(COLUMN_DEVICE_ID, device.deviceId);
         values.putNull(COLUMN_STORAGE_ID);
@@ -394,11 +476,15 @@
         values.putNull(Document.COLUMN_LAST_MODIFIED);
         values.put(Document.COLUMN_ICON, R.drawable.ic_root_mtp);
         values.put(Document.COLUMN_FLAGS, 0);
-        long size = 0;
-        for (final MtpRoot root : device.roots) {
-            size += root.mMaxCapacity - root.mFreeSpace;
-        }
-        values.put(Document.COLUMN_SIZE, size);
+        values.putNull(Document.COLUMN_SIZE);
+
+        extraValues.clear();
+        extraValues.put(
+                Root.COLUMN_FLAGS,
+                Root.FLAG_SUPPORTS_IS_CHILD | Root.FLAG_SUPPORTS_CREATE);
+        extraValues.putNull(Root.COLUMN_AVAILABLE_BYTES);
+        extraValues.putNull(Root.COLUMN_CAPACITY_BYTES);
+        extraValues.put(Root.COLUMN_MIME_TYPES, "");
     }
 
     /**
@@ -408,7 +494,11 @@
      * @param root Root to be converted {@link ContentValues}.
      */
     static void getStorageDocumentValues(
-            ContentValues values, Resources resources, String parentDocumentId, MtpRoot root) {
+            ContentValues values,
+            ContentValues extraValues,
+            Resources resources,
+            String parentDocumentId,
+            MtpRoot root) {
         values.clear();
         values.put(COLUMN_DEVICE_ID, root.mDeviceId);
         values.put(COLUMN_STORAGE_ID, root.mStorageId);
@@ -424,6 +514,13 @@
         values.put(Document.COLUMN_FLAGS, 0);
         values.put(Document.COLUMN_SIZE,
                 (int) Math.min(root.mMaxCapacity - root.mFreeSpace, Integer.MAX_VALUE));
+
+        extraValues.put(
+                Root.COLUMN_FLAGS,
+                Root.FLAG_SUPPORTS_IS_CHILD | Root.FLAG_SUPPORTS_CREATE);
+        extraValues.put(Root.COLUMN_AVAILABLE_BYTES, root.mFreeSpace);
+        extraValues.put(Root.COLUMN_CAPACITY_BYTES, root.mMaxCapacity);
+        extraValues.put(Root.COLUMN_MIME_TYPES, "");
     }
 
     /**
diff --git a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDatabaseConstants.java b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDatabaseConstants.java
index a233589..f252e0f 100644
--- a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDatabaseConstants.java
+++ b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDatabaseConstants.java
@@ -126,14 +126,14 @@
             Document.COLUMN_LAST_MODIFIED + " INTEGER," +
             Document.COLUMN_ICON + " INTEGER," +
             Document.COLUMN_FLAGS + " INTEGER NOT NULL," +
-            Document.COLUMN_SIZE + " INTEGER NOT NULL);";
+            Document.COLUMN_SIZE + " INTEGER);";
 
     static final String QUERY_CREATE_ROOT_EXTRA =
             "CREATE TABLE " + TABLE_ROOT_EXTRA + " (" +
             Root.COLUMN_ROOT_ID + " INTEGER PRIMARY KEY," +
             Root.COLUMN_FLAGS + " INTEGER NOT NULL," +
-            Root.COLUMN_AVAILABLE_BYTES + " INTEGER NOT NULL," +
-            Root.COLUMN_CAPACITY_BYTES + " INTEGER NOT NULL," +
+            Root.COLUMN_AVAILABLE_BYTES + " INTEGER," +
+            Root.COLUMN_CAPACITY_BYTES + " INTEGER," +
             Root.COLUMN_MIME_TYPES + " TEXT NOT NULL);";
 
     /**
@@ -145,18 +145,26 @@
         COLUMN_MAP_ROOTS = new HashMap<>();
         COLUMN_MAP_ROOTS.put(Root.COLUMN_ROOT_ID, TABLE_ROOT_EXTRA + "." + Root.COLUMN_ROOT_ID);
         COLUMN_MAP_ROOTS.put(Root.COLUMN_FLAGS, TABLE_ROOT_EXTRA + "." + Root.COLUMN_FLAGS);
-        COLUMN_MAP_ROOTS.put(Root.COLUMN_ICON, TABLE_DOCUMENTS + "." + Document.COLUMN_ICON);
         COLUMN_MAP_ROOTS.put(
-                Root.COLUMN_TITLE, TABLE_DOCUMENTS + "." + Document.COLUMN_DISPLAY_NAME);
-        COLUMN_MAP_ROOTS.put(Root.COLUMN_SUMMARY, TABLE_DOCUMENTS + "." + Document.COLUMN_SUMMARY);
+                Root.COLUMN_ICON,
+                TABLE_DOCUMENTS + "." + Document.COLUMN_ICON + " AS " + Root.COLUMN_ICON);
         COLUMN_MAP_ROOTS.put(
-                Root.COLUMN_DOCUMENT_ID, TABLE_DOCUMENTS + "." + Document.COLUMN_DOCUMENT_ID);
+                Root.COLUMN_TITLE,
+                TABLE_DOCUMENTS + "." + Document.COLUMN_DISPLAY_NAME + " AS " + Root.COLUMN_TITLE);
+        COLUMN_MAP_ROOTS.put(
+                Root.COLUMN_SUMMARY,
+                TABLE_DOCUMENTS + "." + Document.COLUMN_SUMMARY + " AS " + Root.COLUMN_SUMMARY);
+        COLUMN_MAP_ROOTS.put(
+                Root.COLUMN_DOCUMENT_ID,
+                TABLE_DOCUMENTS + "." + Document.COLUMN_DOCUMENT_ID +
+                " AS " + Root.COLUMN_DOCUMENT_ID);
         COLUMN_MAP_ROOTS.put(
                 Root.COLUMN_AVAILABLE_BYTES, TABLE_ROOT_EXTRA + "." + Root.COLUMN_AVAILABLE_BYTES);
         COLUMN_MAP_ROOTS.put(
                 Root.COLUMN_CAPACITY_BYTES, TABLE_ROOT_EXTRA + "." + Root.COLUMN_CAPACITY_BYTES);
         COLUMN_MAP_ROOTS.put(
                 Root.COLUMN_MIME_TYPES, TABLE_ROOT_EXTRA + "." + Root.COLUMN_MIME_TYPES);
+        COLUMN_MAP_ROOTS.put(COLUMN_DEVICE_ID, COLUMN_DEVICE_ID);
     }
 
     private static String createJoinFromClosure(
diff --git a/packages/MtpDocumentsProvider/src/com/android/mtp/RootScanner.java b/packages/MtpDocumentsProvider/src/com/android/mtp/RootScanner.java
index e6c2726..619ef54 100644
--- a/packages/MtpDocumentsProvider/src/com/android/mtp/RootScanner.java
+++ b/packages/MtpDocumentsProvider/src/com/android/mtp/RootScanner.java
@@ -127,7 +127,7 @@
                         continue;
                     }
                     mDatabase.getMapper().startAddingDocuments(documentId);
-                    if (mDatabase.getMapper().putRootDocuments(
+                    if (mDatabase.getMapper().putStorageDocuments(
                             documentId, mResources, device.roots)) {
                         changed = true;
                     }
diff --git a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/DocumentLoaderTest.java b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/DocumentLoaderTest.java
index 7bd9a17..f37a55c 100644
--- a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/DocumentLoaderTest.java
+++ b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/DocumentLoaderTest.java
@@ -41,7 +41,7 @@
     public void setUp() {
         mDatabase = new MtpDatabase(getContext(), MtpDatabaseConstants.FLAG_DATABASE_IN_MEMORY);
         mDatabase.getMapper().startAddingDocuments("deviceDocId");
-        mDatabase.getMapper().putRootDocuments("deviceDocId", new TestResources(), new MtpRoot[] {
+        mDatabase.getMapper().putStorageDocuments("deviceDocId", new TestResources(), new MtpRoot[] {
                 new MtpRoot(0, 0, "Device", "Storage", 1000, 1000, "")
         });
         mDatabase.getMapper().stopAddingDocuments("deviceDocId");
diff --git a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDatabaseTest.java b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDatabaseTest.java
index b745175..1e1ea0a 100644
--- a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDatabaseTest.java
+++ b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDatabaseTest.java
@@ -74,9 +74,72 @@
         return cursor.getString(cursor.getColumnIndex(columnName));
     }
 
-    public void testPutRootDocuments() throws Exception {
+    public void testPutSingleStorageDocuments() throws Exception {
+        mDatabase.getMapper().startAddingDocuments(null);
+        mDatabase.getMapper().putDeviceDocument(
+                new MtpDeviceRecord(0, "Device", true, new MtpRoot[0]));
+        mDatabase.getMapper().stopAddingDocuments(null);
+
+        mDatabase.getMapper().startAddingDocuments("1");
+        mDatabase.getMapper().putStorageDocuments("1", resources, new MtpRoot[] {
+                new MtpRoot(0, 1, "Device", "Storage", 1000, 2000, "")
+        });
+        mDatabase.getMapper().stopAddingDocuments("1");
+
+        {
+            final Cursor cursor = mDatabase.queryRootDocuments(COLUMN_NAMES);
+            assertEquals(1, cursor.getCount());
+
+            cursor.moveToNext();
+            assertEquals(2, getInt(cursor, COLUMN_DOCUMENT_ID));
+            assertEquals(0, getInt(cursor, COLUMN_DEVICE_ID));
+            assertEquals(1, getInt(cursor, COLUMN_STORAGE_ID));
+            assertTrue(isNull(cursor, COLUMN_OBJECT_HANDLE));
+            assertEquals(DocumentsContract.Document.MIME_TYPE_DIR, getString(cursor, COLUMN_MIME_TYPE));
+            assertEquals("Device Storage", getString(cursor, COLUMN_DISPLAY_NAME));
+            assertTrue(isNull(cursor, COLUMN_SUMMARY));
+            assertTrue(isNull(cursor, COLUMN_LAST_MODIFIED));
+            assertEquals(R.drawable.ic_root_mtp, getInt(cursor, COLUMN_ICON));
+            assertEquals(0, getInt(cursor, COLUMN_FLAGS));
+            assertEquals(1000, getInt(cursor, COLUMN_SIZE));
+            assertEquals(
+                    MtpDatabaseConstants.DOCUMENT_TYPE_STORAGE, getInt(cursor, COLUMN_DOCUMENT_TYPE));
+
+            cursor.close();
+        }
+
+        {
+            final Cursor cursor = mDatabase.queryRoots(new String [] {
+                    Root.COLUMN_ROOT_ID,
+                    Root.COLUMN_FLAGS,
+                    Root.COLUMN_ICON,
+                    Root.COLUMN_TITLE,
+                    Root.COLUMN_SUMMARY,
+                    Root.COLUMN_DOCUMENT_ID,
+                    Root.COLUMN_AVAILABLE_BYTES,
+                    Root.COLUMN_CAPACITY_BYTES
+            });
+            assertEquals(1, cursor.getCount());
+
+            cursor.moveToNext();
+            assertEquals(2, getInt(cursor, Root.COLUMN_ROOT_ID));
+            assertEquals(
+                    Root.FLAG_SUPPORTS_IS_CHILD | Root.FLAG_SUPPORTS_CREATE,
+                    getInt(cursor, Root.COLUMN_FLAGS));
+            assertEquals(R.drawable.ic_root_mtp, getInt(cursor, Root.COLUMN_ICON));
+            assertEquals("Device Storage", getString(cursor, Root.COLUMN_TITLE));
+            assertTrue(isNull(cursor, Root.COLUMN_SUMMARY));
+            assertEquals(2, getInt(cursor, Root.COLUMN_DOCUMENT_ID));
+            assertEquals(1000, getInt(cursor, Root.COLUMN_AVAILABLE_BYTES));
+            assertEquals(2000, getInt(cursor, Root.COLUMN_CAPACITY_BYTES));
+
+            cursor.close();
+        }
+    }
+
+    public void testPutStorageDocuments() throws Exception {
         mDatabase.getMapper().startAddingDocuments("deviceDocId");
-        mDatabase.getMapper().putRootDocuments("deviceDocId", resources, new MtpRoot[] {
+        mDatabase.getMapper().putStorageDocuments("deviceDocId", resources, new MtpRoot[] {
                 new MtpRoot(0, 1, "Device", "Storage", 1000, 2000, ""),
                 new MtpRoot(0, 2, "Device", "Storage", 2000, 4000, ""),
                 new MtpRoot(0, 3, "Device", "/@#%&<>Storage", 3000, 6000,"")
@@ -111,52 +174,6 @@
 
             cursor.close();
         }
-
-        {
-            final Cursor cursor = mDatabase.queryRoots(new String [] {
-                    Root.COLUMN_ROOT_ID,
-                    Root.COLUMN_FLAGS,
-                    Root.COLUMN_ICON,
-                    Root.COLUMN_TITLE,
-                    Root.COLUMN_SUMMARY,
-                    Root.COLUMN_DOCUMENT_ID,
-                    Root.COLUMN_AVAILABLE_BYTES,
-                    Root.COLUMN_CAPACITY_BYTES
-            });
-            assertEquals(3, cursor.getCount());
-
-            cursor.moveToNext();
-            assertEquals(1, cursor.getInt(0));
-            assertEquals(Root.FLAG_SUPPORTS_IS_CHILD | Root.FLAG_SUPPORTS_CREATE, cursor.getInt(1));
-            assertEquals(R.drawable.ic_root_mtp, cursor.getInt(2));
-            assertEquals("Device Storage", cursor.getString(3));
-            assertTrue(cursor.isNull(4));
-            assertEquals(1, cursor.getInt(5));
-            assertEquals(1000, cursor.getInt(6));
-            assertEquals(2000, cursor.getInt(7));
-
-            cursor.moveToNext();
-            assertEquals(2, cursor.getInt(0));
-            assertEquals(Root.FLAG_SUPPORTS_IS_CHILD | Root.FLAG_SUPPORTS_CREATE, cursor.getInt(1));
-            assertEquals(R.drawable.ic_root_mtp, cursor.getInt(2));
-            assertEquals("Device Storage", cursor.getString(3));
-            assertTrue(cursor.isNull(4));
-            assertEquals(2, cursor.getInt(5));
-            assertEquals(2000, cursor.getInt(6));
-            assertEquals(4000, cursor.getInt(7));
-
-            cursor.moveToNext();
-            assertEquals(3, cursor.getInt(0));
-            assertEquals(Root.FLAG_SUPPORTS_IS_CHILD | Root.FLAG_SUPPORTS_CREATE, cursor.getInt(1));
-            assertEquals(R.drawable.ic_root_mtp, cursor.getInt(2));
-            assertEquals("Device /@#%&<>Storage", cursor.getString(3));
-            assertTrue(cursor.isNull(4));
-            assertEquals(3, cursor.getInt(5));
-            assertEquals(3000, cursor.getInt(6));
-            assertEquals(6000, cursor.getInt(7));
-
-            cursor.close();
-        }
     }
 
     private MtpObjectInfo createDocument(int objectHandle, String name, int format, int size) {
@@ -245,13 +262,9 @@
                 MtpDatabaseConstants.COLUMN_STORAGE_ID,
                 DocumentsContract.Document.COLUMN_DISPLAY_NAME
         };
-        final String[] rootColumns = new String[] {
-                Root.COLUMN_ROOT_ID,
-                Root.COLUMN_AVAILABLE_BYTES
-        };
 
         mDatabase.getMapper().startAddingDocuments("deviceDocId");
-        mDatabase.getMapper().putRootDocuments("deviceDocId", resources, new MtpRoot[] {
+        mDatabase.getMapper().putStorageDocuments("deviceDocId", resources, new MtpRoot[] {
                 new MtpRoot(0, 100, "Device", "Storage A", 1000, 0, ""),
                 new MtpRoot(0, 101, "Device", "Storage B", 1001, 0, "")
         });
@@ -270,18 +283,6 @@
             cursor.close();
         }
 
-        {
-            final Cursor cursor = mDatabase.queryRoots(rootColumns);
-            assertEquals(2, cursor.getCount());
-            cursor.moveToNext();
-            assertEquals(1, getInt(cursor, Root.COLUMN_ROOT_ID));
-            assertEquals(1000, getInt(cursor, Root.COLUMN_AVAILABLE_BYTES));
-            cursor.moveToNext();
-            assertEquals(2, getInt(cursor, Root.COLUMN_ROOT_ID));
-            assertEquals(1001, getInt(cursor, Root.COLUMN_AVAILABLE_BYTES));
-            cursor.close();
-        }
-
         mDatabase.getMapper().clearMapping();
 
         {
@@ -298,20 +299,8 @@
             cursor.close();
         }
 
-        {
-            final Cursor cursor = mDatabase.queryRoots(rootColumns);
-            assertEquals(2, cursor.getCount());
-            cursor.moveToNext();
-            assertEquals(1, getInt(cursor, Root.COLUMN_ROOT_ID));
-            assertEquals(1000, getInt(cursor, Root.COLUMN_AVAILABLE_BYTES));
-            cursor.moveToNext();
-            assertEquals(2, getInt(cursor, Root.COLUMN_ROOT_ID));
-            assertEquals(1001, getInt(cursor, Root.COLUMN_AVAILABLE_BYTES));
-            cursor.close();
-        }
-
         mDatabase.getMapper().startAddingDocuments("deviceDocId");
-        mDatabase.getMapper().putRootDocuments("deviceDocId", resources, new MtpRoot[] {
+        mDatabase.getMapper().putStorageDocuments("deviceDocId", resources, new MtpRoot[] {
                 new MtpRoot(0, 200, "Device", "Storage A", 2000, 0, ""),
                 new MtpRoot(0, 202, "Device", "Storage C", 2002, 0, "")
         });
@@ -334,21 +323,6 @@
             cursor.close();
         }
 
-        {
-            final Cursor cursor = mDatabase.queryRoots(rootColumns);
-            assertEquals(3, cursor.getCount());
-            cursor.moveToNext();
-            assertEquals(1, getInt(cursor, Root.COLUMN_ROOT_ID));
-            assertEquals(1000, getInt(cursor, Root.COLUMN_AVAILABLE_BYTES));
-            cursor.moveToNext();
-            assertEquals(2, getInt(cursor, Root.COLUMN_ROOT_ID));
-            assertEquals(1001, getInt(cursor, Root.COLUMN_AVAILABLE_BYTES));
-            cursor.moveToNext();
-            assertEquals(4, getInt(cursor, Root.COLUMN_ROOT_ID));
-            assertEquals(2002, getInt(cursor, Root.COLUMN_AVAILABLE_BYTES));
-            cursor.close();
-        }
-
         mDatabase.getMapper().stopAddingDocuments("deviceDocId");
 
         {
@@ -364,18 +338,6 @@
             assertEquals("Device Storage C", getString(cursor, COLUMN_DISPLAY_NAME));
             cursor.close();
         }
-
-        {
-            final Cursor cursor = mDatabase.queryRoots(rootColumns);
-            assertEquals(2, cursor.getCount());
-            cursor.moveToNext();
-            assertEquals(1, getInt(cursor, Root.COLUMN_ROOT_ID));
-            assertEquals(2000, getInt(cursor, Root.COLUMN_AVAILABLE_BYTES));
-            cursor.moveToNext();
-            assertEquals(4, getInt(cursor, Root.COLUMN_ROOT_ID));
-            assertEquals(2002, getInt(cursor, Root.COLUMN_AVAILABLE_BYTES));
-            cursor.close();
-        }
     }
 
     public void testRestoreIdForChildDocuments() throws Exception {
@@ -461,26 +423,33 @@
                 Root.COLUMN_ROOT_ID,
                 Root.COLUMN_AVAILABLE_BYTES
         };
-        mDatabase.getMapper().startAddingDocuments("deviceDocIdA");
-        mDatabase.getMapper().startAddingDocuments("deviceDocIdB");
-        mDatabase.getMapper().putRootDocuments("deviceDocIdA", resources, new MtpRoot[] {
-                new MtpRoot(0, 100, "Device", "Storage", 0, 0, "")
+        mDatabase.getMapper().startAddingDocuments(null);
+        mDatabase.getMapper().putDeviceDocument(
+                new MtpDeviceRecord(0, "Device A", true, new MtpRoot[0]));
+        mDatabase.getMapper().putDeviceDocument(
+                new MtpDeviceRecord(1, "Device B", true, new MtpRoot[0]));
+        mDatabase.getMapper().stopAddingDocuments(null);
+
+        mDatabase.getMapper().startAddingDocuments("1");
+        mDatabase.getMapper().startAddingDocuments("2");
+        mDatabase.getMapper().putStorageDocuments("1", resources, new MtpRoot[] {
+                new MtpRoot(0, 100, "Device A", "Storage", 0, 0, "")
         });
-        mDatabase.getMapper().putRootDocuments("deviceDocIdB", resources, new MtpRoot[] {
-                new MtpRoot(1, 100, "Device", "Storage", 0, 0, "")
+        mDatabase.getMapper().putStorageDocuments("2", resources, new MtpRoot[] {
+                new MtpRoot(1, 100, "Device B", "Storage", 0, 0, "")
         });
 
         {
             final Cursor cursor = mDatabase.queryRootDocuments(columns);
             assertEquals(2, cursor.getCount());
             cursor.moveToNext();
-            assertEquals(1, getInt(cursor, COLUMN_DOCUMENT_ID));
+            assertEquals(3, getInt(cursor, COLUMN_DOCUMENT_ID));
             assertEquals(100, getInt(cursor, COLUMN_STORAGE_ID));
-            assertEquals("Device Storage", getString(cursor, COLUMN_DISPLAY_NAME));
+            assertEquals("Device A Storage", getString(cursor, COLUMN_DISPLAY_NAME));
             cursor.moveToNext();
-            assertEquals(2, getInt(cursor, COLUMN_DOCUMENT_ID));
+            assertEquals(4, getInt(cursor, COLUMN_DOCUMENT_ID));
             assertEquals(100, getInt(cursor, COLUMN_STORAGE_ID));
-            assertEquals("Device Storage", getString(cursor, COLUMN_DISPLAY_NAME));
+            assertEquals("Device B Storage", getString(cursor, COLUMN_DISPLAY_NAME));
             cursor.close();
         }
 
@@ -488,36 +457,36 @@
             final Cursor cursor = mDatabase.queryRoots(rootColumns);
             assertEquals(2, cursor.getCount());
             cursor.moveToNext();
-            assertEquals(1, getInt(cursor, Root.COLUMN_ROOT_ID));
+            assertEquals(3, getInt(cursor, Root.COLUMN_ROOT_ID));
             assertEquals(0, getInt(cursor, Root.COLUMN_AVAILABLE_BYTES));
             cursor.moveToNext();
-            assertEquals(2, getInt(cursor, Root.COLUMN_ROOT_ID));
+            assertEquals(4, getInt(cursor, Root.COLUMN_ROOT_ID));
             assertEquals(0, getInt(cursor, Root.COLUMN_AVAILABLE_BYTES));
             cursor.close();
         }
 
         mDatabase.getMapper().clearMapping();
 
-        mDatabase.getMapper().startAddingDocuments("deviceDocIdA");
-        mDatabase.getMapper().startAddingDocuments("deviceDocIdB");
-        mDatabase.getMapper().putRootDocuments("deviceDocIdA", resources, new MtpRoot[] {
+        mDatabase.getMapper().startAddingDocuments("1");
+        mDatabase.getMapper().startAddingDocuments("2");
+        mDatabase.getMapper().putStorageDocuments("1", resources, new MtpRoot[] {
                 new MtpRoot(0, 200, "Device", "Storage", 2000, 0, "")
         });
-        mDatabase.getMapper().putRootDocuments("deviceDocIdB", resources, new MtpRoot[] {
+        mDatabase.getMapper().putStorageDocuments("2", resources, new MtpRoot[] {
                 new MtpRoot(1, 300, "Device", "Storage", 3000, 0, "")
         });
-        mDatabase.getMapper().stopAddingDocuments("deviceDocIdA");
-        mDatabase.getMapper().stopAddingDocuments("deviceDocIdB");
+        mDatabase.getMapper().stopAddingDocuments("1");
+        mDatabase.getMapper().stopAddingDocuments("2");
 
         {
             final Cursor cursor = mDatabase.queryRootDocuments(columns);
             assertEquals(2, cursor.getCount());
             cursor.moveToNext();
-            assertEquals(1, getInt(cursor, COLUMN_DOCUMENT_ID));
+            assertEquals(5, getInt(cursor, COLUMN_DOCUMENT_ID));
             assertEquals(200, getInt(cursor, COLUMN_STORAGE_ID));
             assertEquals("Device Storage", getString(cursor, COLUMN_DISPLAY_NAME));
             cursor.moveToNext();
-            assertEquals(2, getInt(cursor, COLUMN_DOCUMENT_ID));
+            assertEquals(6, getInt(cursor, COLUMN_DOCUMENT_ID));
             assertEquals(300, getInt(cursor, COLUMN_STORAGE_ID));
             assertEquals("Device Storage", getString(cursor, COLUMN_DISPLAY_NAME));
             cursor.close();
@@ -527,10 +496,10 @@
             final Cursor cursor = mDatabase.queryRoots(rootColumns);
             assertEquals(2, cursor.getCount());
             cursor.moveToNext();
-            assertEquals(1, getInt(cursor, Root.COLUMN_ROOT_ID));
+            assertEquals(5, getInt(cursor, Root.COLUMN_ROOT_ID));
             assertEquals(2000, getInt(cursor, Root.COLUMN_AVAILABLE_BYTES));
             cursor.moveToNext();
-            assertEquals(2, getInt(cursor, Root.COLUMN_ROOT_ID));
+            assertEquals(6, getInt(cursor, Root.COLUMN_ROOT_ID));
             assertEquals(3000, getInt(cursor, Root.COLUMN_AVAILABLE_BYTES));
             cursor.close();
         }
@@ -591,29 +560,34 @@
                 Root.COLUMN_AVAILABLE_BYTES
         };
 
-        mDatabase.getMapper().startAddingDocuments("deviceDocId");
-        mDatabase.getMapper().putRootDocuments("deviceDocId", resources, new MtpRoot[] {
+        mDatabase.getMapper().startAddingDocuments(null);
+        mDatabase.getMapper().putDeviceDocument(
+                new MtpDeviceRecord(0, "Device",  false,  new MtpRoot[0]));
+        mDatabase.getMapper().stopAddingDocuments(null);
+
+        mDatabase.getMapper().startAddingDocuments("1");
+        mDatabase.getMapper().putStorageDocuments("1", resources, new MtpRoot[] {
                 new MtpRoot(0, 100, "Device", "Storage", 0, 0, ""),
         });
         mDatabase.getMapper().clearMapping();
 
-        mDatabase.getMapper().startAddingDocuments("deviceDocId");
-        mDatabase.getMapper().putRootDocuments("deviceDocId", resources, new MtpRoot[] {
+        mDatabase.getMapper().startAddingDocuments("1");
+        mDatabase.getMapper().putStorageDocuments("1", resources, new MtpRoot[] {
                 new MtpRoot(0, 200, "Device", "Storage", 2000, 0, ""),
         });
         mDatabase.getMapper().clearMapping();
 
-        mDatabase.getMapper().startAddingDocuments("deviceDocId");
-        mDatabase.getMapper().putRootDocuments("deviceDocId", resources, new MtpRoot[] {
+        mDatabase.getMapper().startAddingDocuments("1");
+        mDatabase.getMapper().putStorageDocuments("1", resources, new MtpRoot[] {
                 new MtpRoot(0, 300, "Device", "Storage", 3000, 0, ""),
         });
-        mDatabase.getMapper().stopAddingDocuments("deviceDocId");
+        mDatabase.getMapper().stopAddingDocuments("1");
 
         {
             final Cursor cursor = mDatabase.queryRootDocuments(columns);
             assertEquals(1, cursor.getCount());
             cursor.moveToNext();
-            assertEquals(1, getInt(cursor, COLUMN_DOCUMENT_ID));
+            assertEquals(2, getInt(cursor, COLUMN_DOCUMENT_ID));
             assertEquals(300, getInt(cursor, COLUMN_STORAGE_ID));
             assertEquals("Device Storage", getString(cursor, COLUMN_DISPLAY_NAME));
             cursor.close();
@@ -622,7 +596,7 @@
             final Cursor cursor = mDatabase.queryRoots(rootColumns);
             assertEquals(1, cursor.getCount());
             cursor.moveToNext();
-            assertEquals(1, getInt(cursor, Root.COLUMN_ROOT_ID));
+            assertEquals(2, getInt(cursor, Root.COLUMN_ROOT_ID));
             assertEquals(3000, getInt(cursor, Root.COLUMN_AVAILABLE_BYTES));
             cursor.close();
         }
@@ -634,19 +608,15 @@
                 MtpDatabaseConstants.COLUMN_STORAGE_ID,
                 DocumentsContract.Document.COLUMN_DISPLAY_NAME
         };
-        final String[] rootColumns = new String[] {
-                Root.COLUMN_ROOT_ID,
-                Root.COLUMN_AVAILABLE_BYTES
-        };
 
         mDatabase.getMapper().startAddingDocuments("deviceDocId");
-        mDatabase.getMapper().putRootDocuments("deviceDocId", resources, new MtpRoot[] {
+        mDatabase.getMapper().putStorageDocuments("deviceDocId", resources, new MtpRoot[] {
                 new MtpRoot(0, 100, "Device", "Storage", 0, 0, ""),
         });
         mDatabase.getMapper().clearMapping();
 
         mDatabase.getMapper().startAddingDocuments("deviceDocId");
-        mDatabase.getMapper().putRootDocuments("deviceDocId", resources, new MtpRoot[] {
+        mDatabase.getMapper().putStorageDocuments("deviceDocId", resources, new MtpRoot[] {
                 new MtpRoot(0, 200, "Device", "Storage", 2000, 0, ""),
                 new MtpRoot(0, 201, "Device", "Storage", 2001, 0, ""),
         });
@@ -665,33 +635,27 @@
             assertEquals("Device Storage", getString(cursor, COLUMN_DISPLAY_NAME));
             cursor.close();
         }
-        {
-            final Cursor cursor = mDatabase.queryRoots(rootColumns);
-            assertEquals(2, cursor.getCount());
-            cursor.moveToNext();
-            assertEquals(2, getInt(cursor, Root.COLUMN_ROOT_ID));
-            assertEquals(2000, getInt(cursor, Root.COLUMN_AVAILABLE_BYTES));
-            cursor.moveToNext();
-            assertEquals(3, getInt(cursor, Root.COLUMN_ROOT_ID));
-            assertEquals(2001, getInt(cursor, Root.COLUMN_AVAILABLE_BYTES));
-            cursor.close();
-        }
     }
 
     public void testReplaceExistingRoots() {
+        mDatabase.getMapper().startAddingDocuments(null);
+        mDatabase.getMapper().putDeviceDocument(
+                new MtpDeviceRecord(0, "Device", true, new MtpRoot[0]));
+        mDatabase.getMapper().stopAddingDocuments(null);
+
         // The client code should be able to replace existing rows with new information.
         // Add one.
-        mDatabase.getMapper().startAddingDocuments("deviceDocId");
-        mDatabase.getMapper().putRootDocuments("deviceDocId", resources, new MtpRoot[] {
+        mDatabase.getMapper().startAddingDocuments("1");
+        mDatabase.getMapper().putStorageDocuments("1", resources, new MtpRoot[] {
                 new MtpRoot(0, 100, "Device", "Storage A", 0, 0, ""),
         });
-        mDatabase.getMapper().stopAddingDocuments("deviceDocId");
+        mDatabase.getMapper().stopAddingDocuments("1");
         // Replace it.
-        mDatabase.getMapper().startAddingDocuments("deviceDocId");
-        mDatabase.getMapper().putRootDocuments("deviceDocId", resources, new MtpRoot[] {
+        mDatabase.getMapper().startAddingDocuments("1");
+        mDatabase.getMapper().putStorageDocuments("1", resources, new MtpRoot[] {
                 new MtpRoot(0, 100, "Device", "Storage B", 1000, 1000, ""),
         });
-        mDatabase.getMapper().stopAddingDocuments("deviceDocId");
+        mDatabase.getMapper().stopAddingDocuments("1");
         {
             final String[] columns = new String[] {
                     DocumentsContract.Document.COLUMN_DOCUMENT_ID,
@@ -701,7 +665,7 @@
             final Cursor cursor = mDatabase.queryRootDocuments(columns);
             assertEquals(1, cursor.getCount());
             cursor.moveToNext();
-            assertEquals(1, getInt(cursor, COLUMN_DOCUMENT_ID));
+            assertEquals(2, getInt(cursor, COLUMN_DOCUMENT_ID));
             assertEquals(100, getInt(cursor, COLUMN_STORAGE_ID));
             assertEquals("Device Storage B", getString(cursor, COLUMN_DISPLAY_NAME));
             cursor.close();
@@ -709,12 +673,14 @@
         {
             final String[] columns = new String[] {
                     Root.COLUMN_ROOT_ID,
+                    Root.COLUMN_TITLE,
                     Root.COLUMN_AVAILABLE_BYTES
             };
             final Cursor cursor = mDatabase.queryRoots(columns);
             assertEquals(1, cursor.getCount());
             cursor.moveToNext();
-            assertEquals(1, getInt(cursor, Root.COLUMN_ROOT_ID));
+            assertEquals(2, getInt(cursor, Root.COLUMN_ROOT_ID));
+            assertEquals("Device Storage B", getString(cursor, Root.COLUMN_TITLE));
             assertEquals(1000, getInt(cursor, Root.COLUMN_AVAILABLE_BYTES));
             cursor.close();
         }
@@ -723,8 +689,13 @@
     public void testFailToReplaceExisitingUnmappedRoots() {
         // The client code should not be able to replace rows before resolving 'unmapped' rows.
         // Add one.
-        mDatabase.getMapper().startAddingDocuments("deviceDocId");
-        mDatabase.getMapper().putRootDocuments("deviceDocId", resources, new MtpRoot[] {
+        mDatabase.getMapper().startAddingDocuments(null);
+        mDatabase.getMapper().putDeviceDocument(
+                new MtpDeviceRecord(0, "Device", true, new MtpRoot[0]));
+        mDatabase.getMapper().stopAddingDocuments(null);
+
+        mDatabase.getMapper().startAddingDocuments("1");
+        mDatabase.getMapper().putStorageDocuments("1", resources, new MtpRoot[] {
                 new MtpRoot(0, 100, "Device", "Storage A", 0, 0, ""),
         });
         mDatabase.getMapper().clearMapping();
@@ -732,18 +703,19 @@
         assertEquals(1, oldCursor.getCount());
 
         // Add one.
-        mDatabase.getMapper().startAddingDocuments("deviceDocId");
-        mDatabase.getMapper().putRootDocuments("deviceDocId", resources, new MtpRoot[] {
+        mDatabase.getMapper().startAddingDocuments("1");
+        mDatabase.getMapper().putStorageDocuments("1", resources, new MtpRoot[] {
                 new MtpRoot(0, 101, "Device", "Storage B", 1000, 1000, ""),
         });
         // Add one more before resolving unmapped documents.
-        mDatabase.getMapper().putRootDocuments("deviceDocId", resources, new MtpRoot[] {
+        mDatabase.getMapper().putStorageDocuments("1", resources, new MtpRoot[] {
                 new MtpRoot(0, 102, "Device", "Storage B", 1000, 1000, ""),
         });
-        mDatabase.getMapper().stopAddingDocuments("deviceDocId");
+        mDatabase.getMapper().stopAddingDocuments("1");
 
         // Because the roots shares the same name, the roots should have new IDs.
-        final Cursor newCursor = mDatabase.queryRoots(strings(Root.COLUMN_ROOT_ID));
+        final Cursor newCursor = mDatabase.queryChildDocuments(
+                strings(Document.COLUMN_DOCUMENT_ID), "1");
         assertEquals(2, newCursor.getCount());
         oldCursor.moveToNext();
         newCursor.moveToNext();
@@ -755,9 +727,9 @@
         newCursor.close();
     }
 
-    public void testQueryDocument() {
+    public void testQueryDocuments() {
         mDatabase.getMapper().startAddingDocuments("deviceDocId");
-        mDatabase.getMapper().putRootDocuments("deviceDocId", resources, new MtpRoot[] {
+        mDatabase.getMapper().putStorageDocuments("deviceDocId", resources, new MtpRoot[] {
                 new MtpRoot(0, 100, "Device", "Storage A", 0, 0, ""),
         });
         mDatabase.getMapper().stopAddingDocuments("deviceDocId");
@@ -765,13 +737,61 @@
         final Cursor cursor = mDatabase.queryDocument("1", strings(Document.COLUMN_DISPLAY_NAME));
         assertEquals(1, cursor.getCount());
         cursor.moveToNext();
-        assertEquals("Device Storage A", cursor.getString(0));
+        assertEquals("Device Storage A", getString(cursor, Document.COLUMN_DISPLAY_NAME));
         cursor.close();
     }
 
+    public void testQueryRoots() {
+        // Add device document.
+        mDatabase.getMapper().startAddingDocuments(null);
+        mDatabase.getMapper().putDeviceDocument(
+                new MtpDeviceRecord(0, "Device", false, new MtpRoot[0]));
+        mDatabase.getMapper().stopAddingDocuments(null);
+
+        // It the device does not have storages, it shows a device root.
+        {
+            final Cursor cursor = mDatabase.queryRoots(strings(Root.COLUMN_TITLE));
+            assertEquals(1, cursor.getCount());
+            cursor.moveToNext();
+            assertEquals("Device", cursor.getString(0));
+            cursor.close();
+        }
+
+        mDatabase.getMapper().startAddingDocuments("1");
+        mDatabase.getMapper().putStorageDocuments("1", resources, new MtpRoot[] {
+                new MtpRoot(0, 100, "Device", "Storage A", 0, 0, "")
+        });
+        mDatabase.getMapper().stopAddingDocuments("1");
+
+        // It the device has single storage, it shows a storage root.
+        {
+            final Cursor cursor = mDatabase.queryRoots(strings(Root.COLUMN_TITLE));
+            assertEquals(1, cursor.getCount());
+            cursor.moveToNext();
+            assertEquals("Device Storage A", cursor.getString(0));
+            cursor.close();
+        }
+
+        mDatabase.getMapper().startAddingDocuments("1");
+        mDatabase.getMapper().putStorageDocuments("1", resources, new MtpRoot[] {
+                new MtpRoot(0, 100, "Device", "Storage A", 0, 0, ""),
+                new MtpRoot(0, 101, "Device", "Storage B", 0, 0, "")
+        });
+        mDatabase.getMapper().stopAddingDocuments("1");
+
+        // It the device has multiple storages, it shows a device root.
+        {
+            final Cursor cursor = mDatabase.queryRoots(strings(Root.COLUMN_TITLE));
+            assertEquals(1, cursor.getCount());
+            cursor.moveToNext();
+            assertEquals("Device", cursor.getString(0));
+            cursor.close();
+        }
+    }
+
     public void testGetParentId() throws FileNotFoundException {
         mDatabase.getMapper().startAddingDocuments("deviceDocId");
-        mDatabase.getMapper().putRootDocuments("deviceDocId", resources, new MtpRoot[] {
+        mDatabase.getMapper().putStorageDocuments("deviceDocId", resources, new MtpRoot[] {
                 new MtpRoot(0, 100, "Device", "Storage A", 0, 0, ""),
         });
         mDatabase.getMapper().stopAddingDocuments("deviceDocId");
@@ -790,7 +810,7 @@
 
     public void testDeleteDocument() {
         mDatabase.getMapper().startAddingDocuments("deviceDocId");
-        mDatabase.getMapper().putRootDocuments("deviceDocId", resources, new MtpRoot[] {
+        mDatabase.getMapper().putStorageDocuments("deviceDocId", resources, new MtpRoot[] {
                 new MtpRoot(0, 100, "Device", "Storage A", 0, 0, ""),
         });
         mDatabase.getMapper().stopAddingDocuments("deviceDocId");
@@ -834,7 +854,7 @@
 
     public void testPutNewDocument() {
         mDatabase.getMapper().startAddingDocuments("deviceDocId");
-        mDatabase.getMapper().putRootDocuments("deviceDocId", resources, new MtpRoot[] {
+        mDatabase.getMapper().putStorageDocuments("deviceDocId", resources, new MtpRoot[] {
                 new MtpRoot(0, 100, "Device", "Storage A", 0, 0, ""),
         });
         mDatabase.getMapper().stopAddingDocuments("deviceDocId");
diff --git a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDocumentsProviderTest.java b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDocumentsProviderTest.java
index 884b1e2..71c4897 100644
--- a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDocumentsProviderTest.java
+++ b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDocumentsProviderTest.java
@@ -147,7 +147,7 @@
             mProvider.openDevice(0);
             mResolver.waitForNotification(ROOTS_URI, 1);
             final Cursor cursor = mProvider.queryRoots(null);
-            assertEquals(1, cursor.getCount());
+            assertEquals(2, cursor.getCount());
             cursor.moveToNext();
             assertEquals("3", cursor.getString(0));
             assertEquals(Root.FLAG_SUPPORTS_IS_CHILD | Root.FLAG_SUPPORTS_CREATE, cursor.getInt(1));
@@ -176,7 +176,7 @@
     public void testQueryRoots_error() throws Exception {
         setupProvider(MtpDatabaseConstants.FLAG_DATABASE_IN_MEMORY);
         mMtpManager.addValidDevice(
-                new MtpDeviceRecord(0, "Device", false /* unopened */, new MtpRoot[0]));
+                new MtpDeviceRecord(0, "Device A", false /* unopened */, new MtpRoot[0]));
         mMtpManager.addValidDevice(new MtpDeviceRecord(
                 1,
                 "Device",
@@ -197,7 +197,16 @@
             mResolver.waitForNotification(ROOTS_URI, 1);
 
             final Cursor cursor = mProvider.queryRoots(null);
-            assertEquals(1, cursor.getCount());
+            assertEquals(2, cursor.getCount());
+
+            cursor.moveToNext();
+            assertEquals("1", cursor.getString(0));
+            assertEquals(Root.FLAG_SUPPORTS_IS_CHILD | Root.FLAG_SUPPORTS_CREATE, cursor.getInt(1));
+            assertEquals(R.drawable.ic_root_mtp, cursor.getInt(2));
+            assertEquals("Device A", cursor.getString(3));
+            assertEquals("1", cursor.getString(4));
+            assertEquals(0, cursor.getInt(5));
+
             cursor.moveToNext();
             assertEquals("3", cursor.getString(0));
             assertEquals(Root.FLAG_SUPPORTS_IS_CHILD | Root.FLAG_SUPPORTS_CREATE, cursor.getInt(1));
diff --git a/packages/SettingsProvider/res/values/defaults.xml b/packages/SettingsProvider/res/values/defaults.xml
index 6680d88..51d8ca0 100644
--- a/packages/SettingsProvider/res/values/defaults.xml
+++ b/packages/SettingsProvider/res/values/defaults.xml
@@ -25,6 +25,7 @@
     <!-- Comma-separated list of bluetooth, wifi, and cell. -->
     <string name="def_airplane_mode_radios" translatable="false">cell,bluetooth,wifi,nfc,wimax</string>
     <string name="airplane_mode_toggleable_radios" translatable="false">bluetooth,wifi,nfc</string>
+    <string name="def_bluetooth_disabled_profiles" translatable="false">0</string>
     <bool name="def_auto_time">true</bool>
     <bool name="def_auto_time_zone">true</bool>
     <bool name="def_accelerometer_rotation">true</bool>
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
index d4e428e..7338a9c 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
@@ -116,6 +116,12 @@
             // cleaned up automatically when the user is deleted.
             File databaseFile = new File(
                     Environment.getUserSystemDirectory(userHandle), DATABASE_NAME);
+            // If databaseFile doesn't exist, database can be kept in memory. It's safe because the
+            // database will be migrated and disposed of immediately after onCreate finishes
+            if (!databaseFile.exists()) {
+                Log.i(TAG, "No previous database file exists - running in in-memory mode");
+                return null;
+            }
             return databaseFile.getPath();
         }
     }
@@ -130,8 +136,16 @@
         return mValidTables.contains(name);
     }
 
+    private boolean isInMemory() {
+        return getDatabaseName() == null;
+    }
+
     public void dropDatabase() {
         close();
+        // No need to remove files if db is in memory
+        if (isInMemory()) {
+            return;
+        }
         File databaseFile = mContext.getDatabasePath(getDatabaseName());
         if (databaseFile.exists()) {
             databaseFile.delete();
@@ -145,6 +159,10 @@
 
     public void backupDatabase() {
         close();
+        // No need to backup files if db is in memory
+        if (isInMemory()) {
+            return;
+        }
         File databaseFile = mContext.getDatabasePath(getDatabaseName());
         if (!databaseFile.exists()) {
             return;
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index 7365e66..bcb459a 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -1890,7 +1890,7 @@
         }
 
         private final class UpgradeController {
-            private static final int SETTINGS_VERSION = 123;
+            private static final int SETTINGS_VERSION = 124;
 
             private final int mUserId;
 
@@ -2060,6 +2060,16 @@
                     }
                     currentVersion = 123;
                 }
+
+                if (currentVersion == 123) {
+                    final SettingsState globalSettings = getGlobalSettingsLocked();
+                    String defaultDisabledProfiles = (getContext().getResources().getString(
+                            R.string.def_bluetooth_disabled_profiles));
+                    globalSettings.insertSettingLocked(Settings.Global.BLUETOOTH_DISABLED_PROFILES,
+                            defaultDisabledProfiles, SettingsState.SYSTEM_PACKAGE_NAME);
+                    currentVersion = 124;
+                }
+
                 // vXXX: Add new settings above this point.
 
                 // Return the current version.
diff --git a/packages/SystemUI/res/layout/notification_guts.xml b/packages/SystemUI/res/layout/notification_guts.xml
index c9dbc79..f15c97e 100644
--- a/packages/SystemUI/res/layout/notification_guts.xml
+++ b/packages/SystemUI/res/layout/notification_guts.xml
@@ -24,35 +24,23 @@
         android:clickable="true"
         android:gravity="top|start"
         android:orientation="vertical"
-        android:paddingEnd="8dp"
+        android:paddingStart="@*android:dimen/notification_content_margin_start"
+        android:paddingEnd="@*android:dimen/notification_content_margin_end"
         android:background="@color/notification_guts_text_color" >
 
     <!-- header -->
     <FrameLayout
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
-            android:paddingStart="@*android:dimen/notification_content_margin_start"
-            android:paddingTop="8dp"
-            android:paddingBottom="16dp" >
+            android:paddingBottom="8dp" >
 
         <LinearLayout
                 android:layout_width="wrap_content"
                 android:layout_height="wrap_content"
                 android:id="@+id/notification_guts_header"
-                android:orientation="vertical"
+                android:orientation="horizontal"
                 android:layout_gravity="center_vertical|start"
                 android:layout_marginEnd="52dp">
-
-                <LinearLayout
-                        android:id="@+id/notification_guts_app_details"
-                        android:orientation="horizontal"
-                        android:layout_width="match_parent"
-                        android:layout_height="wrap_content"
-                        android:clipChildren="false"
-                        android:layout_gravity="start|top"
-                        android:gravity="center_vertical"
-                        >
-
                     <ImageView
                             android:id="@android:id/icon"
                             android:layout_width="18dp"
@@ -76,22 +64,12 @@
                             android:layout_gravity="bottom|start"
                             android:visibility="gone"
                             android:textColor="#ffffff" />
-                </LinearLayout>
-
-                <TextView
-                        android:id="@+id/topic_details"
-                        android:layout_width="match_parent"
-                        android:layout_height="wrap_content"
-                        android:textAppearance="@android:style/TextAppearance.Material.Notification.Title"
-                        android:textColor="@color/notification_guts_text_color"
-                        android:layout_alignParentBottom="true"
-                        android:layout_alignParentStart="true" />
-            </LinearLayout>
+        </LinearLayout>
 
         <ImageButton style="@android:style/Widget.Material.Light.Button.Borderless.Small"
                 android:id="@+id/notification_inspect_item"
                 android:layout_width="52dp"
-                android:layout_height="match_parent"
+                android:layout_height="wrap_content"
                 android:layout_weight="0"
                 android:gravity="center"
                 android:layout_gravity="center_vertical|end"
@@ -103,7 +81,6 @@
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
             android:gravity="center_vertical"
-            android:paddingStart="@*android:dimen/notification_content_margin_start"
             android:orientation="vertical"
             android:clickable="false"
             android:focusable="false"
@@ -116,8 +93,7 @@
                 android:textAppearance="@android:style/TextAppearance.Material.Subhead"
                 android:textColor="@color/notification_guts_text_color"
                 android:ellipsize="marquee"
-                android:fadingEdge="horizontal"
-                android:text="@*android:string/notification_importance_title"/>
+                android:fadingEdge="horizontal"/>
 
         <TextView
                 android:id="@+id/summary"
@@ -133,7 +109,7 @@
         <FrameLayout
                 android:layout_width="match_parent"
                 android:layout_height="wrap_content"
-                android:paddingTop="6dp" >
+                android:paddingTop="8dp" >
 
             <ImageView
                     android:id="@+id/low_importance"
@@ -162,5 +138,21 @@
                     android:layout_height="24dp"/>
 
         </FrameLayout>
+
+        <RadioGroup android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:paddingTop="8dp">
+            <RadioButton android:id="@+id/apply_to_topic"
+                         android:layout_width="wrap_content"
+                         android:layout_height="wrap_content"
+                         android:textColor="@color/notification_guts_text_color"
+                         android:visibility="gone"/>
+            <RadioButton android:id="@+id/apply_to_app"
+                         android:layout_width="wrap_content"
+                         android:layout_height="wrap_content"
+                         android:text="@string/apply_to_app"
+                         android:textColor="@color/notification_guts_text_color"
+                         android:visibility="gone"/>
+        </RadioGroup>
     </LinearLayout>
 </com.android.systemui.statusbar.NotificationGuts>
diff --git a/packages/SystemUI/res/layout/recents_task_view_header.xml b/packages/SystemUI/res/layout/recents_task_view_header.xml
index b8caf23..04f18c5 100644
--- a/packages/SystemUI/res/layout/recents_task_view_header.xml
+++ b/packages/SystemUI/res/layout/recents_task_view_header.xml
@@ -20,7 +20,7 @@
     android:layout_height="@dimen/recents_task_bar_height"
     android:layout_gravity="top|center_horizontal">
     <com.android.systemui.recents.views.FixedSizeImageView
-        android:id="@+id/application_icon"
+        android:id="@+id/icon"
         android:contentDescription="@string/recents_app_info_button_label"
         android:layout_width="@dimen/recents_task_view_application_icon_size"
         android:layout_height="@dimen/recents_task_view_application_icon_size"
@@ -29,7 +29,7 @@
         android:padding="8dp"
         android:background="@drawable/recents_button_bg" />
     <TextView
-        android:id="@+id/activity_description"
+        android:id="@+id/title"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
         android:layout_gravity="center_vertical|start"
diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml
index 76c08f6..0ccc236 100644
--- a/packages/SystemUI/res/values/colors.xml
+++ b/packages/SystemUI/res/values/colors.xml
@@ -69,7 +69,7 @@
     <!-- The lock to task button foreground color. -->
     <color name="recents_task_view_lock_to_app_button_color">#ff666666</color>
     <!-- The background color for the freeform workspace. -->
-    <color name="recents_freeform_workspace_bg_color">#66000000</color>
+    <color name="recents_freeform_workspace_bg_color">#33FFFFFF</color>
 
     <color name="keyguard_affordance">#ffffffff</color>
 
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index f71a71a..50e0661 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -1201,4 +1201,34 @@
     <!-- Bluetooth enablement ok text [CHAR LIMIT=40] -->
     <string name="enable_bluetooth_confirmation_ok">Turn on</string>
 
+    <!-- Apply notification importance setting to a topic [CHAR LIMIT=NONE] -->
+    <string name="apply_to_topic">Apply to <xliff:g id="topic_name" example="Friend Request">%1$s</xliff:g> notifications</string>
+    <!-- Apply notification importance setting to an app [CHAR LIMIT=NONE] -->
+    <string name="apply_to_app">Apply to all notifications from this app</string>
+    <!-- Notification importance title, blocked status-->
+    <string name="blocked_importance">Blocked</string>
+    <!-- Notification importance title, low status-->
+    <string name="low_importance">Low importance</string>
+    <!-- Notification importance title, normal status-->
+    <string name="default_importance">Normal importance</string>
+    <!-- Notification importance title, high status-->
+    <string name="high_importance">High importance</string>
+    <!-- Notification importance title, max status-->
+    <string name="max_importance">Urgent importance</string>
+
+    <!-- [CHAR LIMIT=100] Notification Importance slider: blocked importance level description -->
+    <string name="notification_importance_blocked">Never show these notifications</string>
+
+    <!-- [CHAR LIMIT=100] Notification Importance slider: low importance level description -->
+    <string name="notification_importance_low">Silently show at the bottom of the notification list</string>
+
+    <!-- [CHAR LIMIT=100] Notification Importance slider: normal importance level description -->
+    <string name="notification_importance_default">Silently show these notifications</string>
+
+    <!-- [CHAR LIMIT=100] Notification Importance slider: high importance level description -->
+    <string name="notification_importance_high">Show at the top of the notifications list and make sound</string>
+
+    <!-- [CHAR LIMIT=100] Notification Importance slider: max importance level description -->
+    <string name="notification_importance_max">Peek onto the screen and make sound</string>
+
 </resources>
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
index b3ce4a6..ffcc805 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
@@ -246,10 +246,7 @@
     boolean dismissHistory() {
         // Try and hide the history view first
         if (mHistoryView != null && mHistoryView.isVisible()) {
-            ReferenceCountedTrigger t = new ReferenceCountedTrigger(this);
-            t.increment();
-            EventBus.getDefault().send(new HideHistoryEvent(true /* animate */, t));
-            t.decrement();
+            EventBus.getDefault().send(new HideHistoryEvent(true /* animate */));
             return true;
         }
         return false;
@@ -301,8 +298,8 @@
      */
     void dismissRecentsToHome(boolean animated) {
         if (animated) {
-            ReferenceCountedTrigger exitTrigger = new ReferenceCountedTrigger(this,
-                    null, mFinishLaunchHomeRunnable, null);
+            ReferenceCountedTrigger exitTrigger = new ReferenceCountedTrigger(null,
+                    mFinishLaunchHomeRunnable, null);
             mRecentsView.startExitToHomeAnimation(
                     new ViewAnimation.TaskViewExitContext(exitTrigger));
         } else {
@@ -439,10 +436,7 @@
         // Reset some states
         mIgnoreAltTabRelease = false;
         if (mHistoryView != null) {
-            ReferenceCountedTrigger t = new ReferenceCountedTrigger(this);
-            t.increment();
-            EventBus.getDefault().send(new HideHistoryEvent(false /* animate */, t));
-            t.decrement();
+            EventBus.getDefault().send(new HideHistoryEvent(false /* animate */));
         }
 
         // Notify that recents is now hidden
@@ -511,11 +505,7 @@
     protected void onRestoreInstanceState(Bundle savedInstanceState) {
         super.onRestoreInstanceState(savedInstanceState);
         if (savedInstanceState.getBoolean(KEY_SAVED_STATE_HISTORY_VISIBLE, false)) {
-            ReferenceCountedTrigger postHideStackAnimationTrigger =
-                    new ReferenceCountedTrigger(this);
-            postHideStackAnimationTrigger.increment();
-            EventBus.getDefault().send(new ShowHistoryEvent(postHideStackAnimationTrigger));
-            postHideStackAnimationTrigger.decrement();
+            EventBus.getDefault().send(new ShowHistoryEvent());
         }
     }
 
@@ -644,16 +634,14 @@
         } else if (event.triggeredFromHomeKey) {
             // Otherwise, dismiss Recents to Home
             if (mHistoryView != null && mHistoryView.isVisible()) {
-                ReferenceCountedTrigger t = new ReferenceCountedTrigger(this);
-                t.increment();
-                t.addLastDecrementRunnable(new Runnable() {
+                HideHistoryEvent hideEvent = new HideHistoryEvent(true /* animate */);
+                hideEvent.addPostAnimationCallback(new Runnable() {
                     @Override
                     public void run() {
                         dismissRecentsToHome(true /* animated */);
                     }
                 });
-                EventBus.getDefault().send(new HideHistoryEvent(true, t));
-                t.decrement();
+                EventBus.getDefault().send(hideEvent);
 
             } else {
                 dismissRecentsToHome(true /* animated */);
@@ -665,7 +653,7 @@
 
     public final void onBusEvent(EnterRecentsWindowAnimationCompletedEvent event) {
         // Try and start the enter animation (or restart it on configuration changed)
-        ReferenceCountedTrigger t = new ReferenceCountedTrigger(this);
+        ReferenceCountedTrigger t = new ReferenceCountedTrigger();
         ViewAnimation.TaskViewEnterContext ctx = new ViewAnimation.TaskViewEnterContext(t);
         ctx.postAnimationTrigger.increment();
         if (mSearchWidgetInfo != null) {
@@ -784,11 +772,11 @@
             // provided.
             mHistoryView.setSystemInsets(mRecentsView.getSystemInsets());
         }
-        mHistoryView.show(mRecentsView.getTaskStack(), event.postHideStackAnimationTrigger);
+        mHistoryView.show(mRecentsView.getTaskStack(), event.getAnimationTrigger());
     }
 
     public final void onBusEvent(HideHistoryEvent event) {
-        mHistoryView.hide(event.animate, event.postHideHistoryAnimationTrigger);
+        mHistoryView.hide(event.animate, event.getAnimationTrigger());
     }
 
     private void refreshSearchWidgetView() {
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
index 8a0a043..949fb86 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
@@ -207,7 +207,7 @@
         RecentsTaskLoadPlan plan = loader.createLoadPlan(mContext);
         loader.preloadTasks(plan, true /* isTopTaskHome */);
         RecentsTaskLoadPlan.Options launchOpts = new RecentsTaskLoadPlan.Options();
-        launchOpts.numVisibleTasks = loader.getApplicationIconCacheSize();
+        launchOpts.numVisibleTasks = loader.getIconCacheSize();
         launchOpts.numVisibleTaskThumbnails = loader.getThumbnailCacheSize();
         launchOpts.onlyLoadForCache = true;
         loader.loadTasks(mContext, plan, launchOpts);
@@ -452,7 +452,7 @@
         }
 
         // Launch the task
-        ssp.startActivityFromRecents(mContext, toTask.key.id, toTask.activityLabel, launchOpts);
+        ssp.startActivityFromRecents(mContext, toTask.key.id, toTask.title, launchOpts);
     }
 
     /**
@@ -524,7 +524,7 @@
         MetricsLogger.count(mContext, "overview_affiliated_task_launch", 1);
 
         // Launch the task
-        ssp.startActivityFromRecents(mContext, toTask.key.id, toTask.activityLabel, launchOpts);
+        ssp.startActivityFromRecents(mContext, toTask.key.id, toTask.title, launchOpts);
     }
 
     public void showNextAffiliatedTask() {
diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/EventBus.java b/packages/SystemUI/src/com/android/systemui/recents/events/EventBus.java
index d72218f..5c49ac3 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/events/EventBus.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/events/EventBus.java
@@ -28,6 +28,8 @@
 import android.util.Log;
 import android.util.MutableBoolean;
 
+import com.android.systemui.recents.misc.ReferenceCountedTrigger;
+
 import java.lang.ref.WeakReference;
 import java.lang.reflect.Constructor;
 import java.lang.reflect.InvocationTargetException;
@@ -220,6 +222,20 @@
         // Only accessible from derived events
         protected Event() {}
 
+        /**
+         * Called by the EventBus prior to dispatching this event to any subscriber of this event.
+         */
+        void onPreDispatch() {
+            // Do nothing
+        }
+
+        /**
+         * Called by the EventBus after dispatching this event to every subscriber of this event.
+         */
+        void onPostDispatch() {
+            // Do nothing
+        }
+
         @Override
         protected Object clone() throws CloneNotSupportedException {
             Event evt = (Event) super.clone();
@@ -230,6 +246,51 @@
     }
 
     /**
+     * An event that represents an animated state change, which allows subscribers to coordinate
+     * callbacks which happen after the animation has taken place.
+     *
+     * Internally, it is guaranteed that increment() and decrement() will be called before and the
+     * after the event is dispatched.
+     */
+    public static class AnimatedEvent extends Event {
+
+        private final ReferenceCountedTrigger mTrigger = new ReferenceCountedTrigger();
+
+        // Only accessible from derived events
+        protected AnimatedEvent() {}
+
+        /**
+         * Returns the reference counted trigger that coordinates the animations for this event.
+         */
+        public ReferenceCountedTrigger getAnimationTrigger() {
+            return mTrigger;
+        }
+
+        /**
+         * Adds a callback that is guaranteed to be called after the state has changed regardless of
+         * whether an actual animation took place.
+         */
+        public void addPostAnimationCallback(Runnable r) {
+            mTrigger.addLastDecrementRunnable(r);
+        }
+
+        @Override
+        void onPreDispatch() {
+            mTrigger.increment();
+        }
+
+        @Override
+        void onPostDispatch() {
+            mTrigger.decrement();
+        }
+
+        @Override
+        protected Object clone() throws CloneNotSupportedException {
+            throw new CloneNotSupportedException();
+        }
+    }
+
+    /**
      * An inter-process event super class that allows us to track user state across subscriber
      * invocations.
      */
@@ -706,6 +767,11 @@
         if (eventHandlers == null) {
             return;
         }
+
+        // Prepare this event
+        boolean hasPostedEvent = false;
+        event.onPreDispatch();
+
         // We need to clone the list in case a subscriber unregisters itself during traversal
         eventHandlers = (ArrayList<EventHandler>) eventHandlers.clone();
         for (final EventHandler eventHandler : eventHandlers) {
@@ -717,11 +783,24 @@
                             processEvent(eventHandler, event);
                         }
                     });
+                    hasPostedEvent = true;
                 } else {
                     processEvent(eventHandler, event);
                 }
             }
         }
+
+        // Clean up after this event, deferring until all subscribers have been called
+        if (hasPostedEvent) {
+            mHandler.post(new Runnable() {
+                @Override
+                public void run() {
+                    event.onPostDispatch();
+                }
+            });
+        } else {
+            event.onPostDispatch();
+        }
     }
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/activity/HideHistoryEvent.java b/packages/SystemUI/src/com/android/systemui/recents/events/activity/HideHistoryEvent.java
index 3412852..e85dea3 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/events/activity/HideHistoryEvent.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/events/activity/HideHistoryEvent.java
@@ -22,17 +22,11 @@
 /**
  * This is sent when the history view will be closed.
  */
-public class HideHistoryEvent extends EventBus.Event {
+public class HideHistoryEvent extends EventBus.AnimatedEvent {
 
     public final boolean animate;
-    public final ReferenceCountedTrigger postHideHistoryAnimationTrigger;
 
-    /**
-     * @param postHideHistoryAnimationTrigger the trigger that gets called when all the history animations are finished
-     *                                        when transitioning from the history view
-     */
-    public HideHistoryEvent(boolean animate, ReferenceCountedTrigger postHideHistoryAnimationTrigger) {
+    public HideHistoryEvent(boolean animate) {
         this.animate = animate;
-        this.postHideHistoryAnimationTrigger = postHideHistoryAnimationTrigger;
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/activity/ShowHistoryEvent.java b/packages/SystemUI/src/com/android/systemui/recents/events/activity/ShowHistoryEvent.java
index c91752e..94e5a97 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/events/activity/ShowHistoryEvent.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/events/activity/ShowHistoryEvent.java
@@ -22,15 +22,8 @@
 /**
  * This is sent when the history view button is clicked.
  */
-public class ShowHistoryEvent extends EventBus.Event {
+public class ShowHistoryEvent extends EventBus.AnimatedEvent {
 
-    public final ReferenceCountedTrigger postHideStackAnimationTrigger;
+    // Simple event
 
-    /**
-     * @param postHideStackAnimationTrigger the trigger that gets called when all the task animations are finished when
-     *                                      transitioning to the history view
-     */
-    public ShowHistoryEvent(ReferenceCountedTrigger postHideStackAnimationTrigger) {
-        this.postHideStackAnimationTrigger = postHideStackAnimationTrigger;
-    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/ui/dragndrop/DragEndEvent.java b/packages/SystemUI/src/com/android/systemui/recents/events/ui/dragndrop/DragEndEvent.java
index 3deeb47..8aa4631 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/events/ui/dragndrop/DragEndEvent.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/events/ui/dragndrop/DragEndEvent.java
@@ -25,18 +25,15 @@
 /**
  * This event is sent whenever a drag ends.
  */
-public class DragEndEvent extends EventBus.Event {
+public class DragEndEvent extends EventBus.AnimatedEvent {
 
     public final Task task;
     public final TaskView taskView;
     public final DropTarget dropTarget;
-    public final ReferenceCountedTrigger postAnimationTrigger;
 
-    public DragEndEvent(Task task, TaskView taskView, DropTarget dropTarget,
-            ReferenceCountedTrigger postAnimationTrigger) {
+    public DragEndEvent(Task task, TaskView taskView, DropTarget dropTarget) {
         this.task = task;
         this.taskView = taskView;
         this.dropTarget = dropTarget;
-        this.postAnimationTrigger = postAnimationTrigger;
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/recents/history/RecentsHistoryAdapter.java b/packages/SystemUI/src/com/android/systemui/recents/history/RecentsHistoryAdapter.java
index 76439c0..72ec7b7 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/history/RecentsHistoryAdapter.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/history/RecentsHistoryAdapter.java
@@ -31,7 +31,6 @@
 import com.android.systemui.recents.events.EventBus;
 import com.android.systemui.recents.events.activity.HideHistoryButtonEvent;
 import com.android.systemui.recents.events.activity.HideHistoryEvent;
-import com.android.systemui.recents.misc.ReferenceCountedTrigger;
 import com.android.systemui.recents.misc.SystemServicesProxy;
 import com.android.systemui.recents.model.RecentsTaskLoader;
 import com.android.systemui.recents.model.Task;
@@ -72,7 +71,7 @@
         public void onTaskDataLoaded(Task task) {
             // This callback is only made for TaskRow view holders
             ImageView iv = (ImageView) content.findViewById(R.id.icon);
-            iv.setImageDrawable(task.applicationIcon);
+            iv.setImageDrawable(task.icon);
         }
 
         @Override
@@ -128,7 +127,7 @@
         @Override
         public void onClick(View v) {
             SystemServicesProxy ssp = Recents.getSystemServices();
-            ssp.startActivityFromRecents(v.getContext(), task.key.id, task.activityLabel,
+            ssp.startActivityFromRecents(v.getContext(), task.key.id, task.title,
                     ActivityOptions.makeBasic());
         }
 
@@ -240,7 +239,7 @@
                 TaskRow taskRow = (TaskRow) row;
                 taskRow.task.addCallback(holder);
                 TextView tv = (TextView) holder.content.findViewById(R.id.description);
-                tv.setText(taskRow.task.activityLabel);
+                tv.setText(taskRow.task.title);
                 holder.content.setOnClickListener(taskRow);
                 loader.loadTaskData(taskRow.task);
                 break;
@@ -313,10 +312,7 @@
      * Dismisses history back to the stack view.
      */
     private void dismissHistory() {
-        ReferenceCountedTrigger t = new ReferenceCountedTrigger(mContext);
-        t.increment();
-        EventBus.getDefault().send(new HideHistoryEvent(true /* animate */, t));
-        t.decrement();
+        EventBus.getDefault().send(new HideHistoryEvent(true /* animate */));
         EventBus.getDefault().send(new HideHistoryButtonEvent());
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/recents/misc/ReferenceCountedTrigger.java b/packages/SystemUI/src/com/android/systemui/recents/misc/ReferenceCountedTrigger.java
index b06539a..367f2e2 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/misc/ReferenceCountedTrigger.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/misc/ReferenceCountedTrigger.java
@@ -29,9 +29,6 @@
  */
 public class ReferenceCountedTrigger {
 
-    private static final String TAG = "ReferenceCountedTrigger";
-
-    Context mContext;
     int mCount;
     ArrayList<Runnable> mFirstIncRunnables = new ArrayList<Runnable>();
     ArrayList<Runnable> mLastDecRunnables = new ArrayList<Runnable>();
@@ -51,13 +48,12 @@
         }
     };
 
-    public ReferenceCountedTrigger(Context context) {
-        this(context, null, null, null);
+    public ReferenceCountedTrigger() {
+        this(null, null, null);
     }
 
-    public ReferenceCountedTrigger(Context context, Runnable firstIncRunnable,
-                                   Runnable lastDecRunnable, Runnable errorRunanable) {
-        mContext = context;
+    public ReferenceCountedTrigger(Runnable firstIncRunnable, Runnable lastDecRunnable,
+            Runnable errorRunanable) {
         if (firstIncRunnable != null) mFirstIncRunnables.add(firstIncRunnable);
         if (lastDecRunnable != null) mLastDecRunnables.add(lastDecRunnable);
         mErrorRunnable = errorRunanable;
@@ -81,22 +77,14 @@
 
     /** Adds a runnable to the last-decrement runnables list. */
     public void addLastDecrementRunnable(Runnable r) {
-        // To ensure that the last decrement always calls, we increment and decrement after setting
-        // the last decrement runnable
-        boolean ensureLastDecrement = (mCount == 0);
-        if (ensureLastDecrement) increment();
         mLastDecRunnables.add(r);
-        if (ensureLastDecrement) decrement();
     }
 
     /** Decrements the ref count */
     public void decrement() {
         mCount--;
-        if (mCount == 0 && !mLastDecRunnables.isEmpty()) {
-            int numRunnables = mLastDecRunnables.size();
-            for (int i = 0; i < numRunnables; i++) {
-                mLastDecRunnables.get(i).run();
-            }
+        if (mCount == 0) {
+            flushLastDecrementRunnables();
         } else if (mCount < 0) {
             if (mErrorRunnable != null) {
                 mErrorRunnable.run();
@@ -106,6 +94,19 @@
         }
     }
 
+    /**
+     * Runs and clears all the last-decrement runnables now.
+     */
+    public void flushLastDecrementRunnables() {
+        if (!mLastDecRunnables.isEmpty()) {
+            int numRunnables = mLastDecRunnables.size();
+            for (int i = 0; i < numRunnables; i++) {
+                mLastDecRunnables.get(i).run();
+            }
+        }
+        mLastDecRunnables.clear();
+    }
+
     /** Convenience method to decrement this trigger as a runnable. */
     public Runnable decrementAsRunnable() {
         return mDecrementRunnable;
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 5888b30..35e53f6 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
@@ -43,6 +43,7 @@
 import android.graphics.PorterDuff;
 import android.graphics.PorterDuffXfermode;
 import android.graphics.Rect;
+import android.graphics.drawable.BitmapDrawable;
 import android.graphics.drawable.ColorDrawable;
 import android.graphics.drawable.Drawable;
 import android.os.Bundle;
@@ -193,6 +194,8 @@
         int numTasksToQuery = Math.max(minNumTasksToQuery, numLatestTasks);
         List<ActivityManager.RecentTaskInfo> tasks = mAm.getRecentTasksForUser(numTasksToQuery,
                 ActivityManager.RECENT_IGNORE_HOME_STACK_TASKS |
+                ActivityManager.RECENT_INGORE_DOCKED_STACK_TASKS |
+                ActivityManager.RECENT_INGORE_PINNED_STACK_TASKS |
                 ActivityManager.RECENT_IGNORE_UNAVAILABLE |
                 ActivityManager.RECENT_INCLUDE_PROFILES |
                 ActivityManager.RECENT_WITH_EXCLUDED, userId);
@@ -572,7 +575,7 @@
      * Returns the activity icon for the ActivityInfo for a user, badging if
      * necessary.
      */
-    public Drawable getActivityIcon(ActivityInfo info, int userId) {
+    public Drawable getBadgedActivityIcon(ActivityInfo info, int userId) {
         if (mPm == null) return null;
 
         // If we are mocking, then return a mock label
@@ -585,9 +588,31 @@
     }
 
     /**
+     * Returns the task description icon, loading and badging it if it necessary.
+     */
+    public Drawable getBadgedTaskDescriptionIcon(ActivityManager.TaskDescription taskDescription,
+            int userId, Resources res) {
+
+        // If we are mocking, then return a mock label
+        if (RecentsDebugFlags.Static.EnableSystemServicesProxy) {
+            return new ColorDrawable(0xFF666666);
+        }
+
+        Bitmap tdIcon = taskDescription.getInMemoryIcon();
+        if (tdIcon == null) {
+            tdIcon = ActivityManager.TaskDescription.loadTaskDescriptionIcon(
+                    taskDescription.getIconFilename(), userId);
+        }
+        if (tdIcon != null) {
+            return getBadgedIcon(new BitmapDrawable(res, tdIcon), userId);
+        }
+        return null;
+    }
+
+    /**
      * Returns the given icon for a user, badging if necessary.
      */
-    public Drawable getBadgedIcon(Drawable icon, int userId) {
+    private Drawable getBadgedIcon(Drawable icon, int userId) {
         if (userId != UserHandle.myUserId()) {
             icon = mPm.getUserBadgedIcon(icon, new UserHandle(userId));
         }
@@ -597,7 +622,7 @@
     /**
      * Returns the given label for a user, badging if necessary.
      */
-    public String getBadgedLabel(String label, int userId) {
+    private String getBadgedLabel(String label, int userId) {
         if (userId != UserHandle.myUserId()) {
             label = mPm.getUserBadgedLabel(label, new UserHandle(userId)).toString();
         }
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 1d18087..7a92b2a 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoadPlan.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoadPlan.java
@@ -29,7 +29,6 @@
 import com.android.systemui.Prefs;
 import com.android.systemui.recents.Recents;
 import com.android.systemui.recents.RecentsConfiguration;
-import com.android.systemui.recents.RecentsDebugFlags;
 import com.android.systemui.recents.misc.SystemServicesProxy;
 
 import java.util.ArrayList;
@@ -47,9 +46,6 @@
  */
 public class RecentsTaskLoadPlan {
 
-    private static String TAG = "RecentsTaskLoadPlan";
-    private static boolean DEBUG = false;
-
     private static int MIN_NUM_TASKS = 5;
     private static int SESSION_BEGIN_TIME = 1000 /* ms/s */ * 60 /* s/min */ * 60 /* min/hr */ *
             6 /* hrs */;
@@ -107,13 +103,6 @@
 
         // Since the raw tasks are given in most-recent to least-recent order, we need to reverse it
         Collections.reverse(mRawTasks);
-
-        if (DEBUG) {
-            Log.d(TAG, "preloadRawTasks, tasks: " + mRawTasks.size());
-            for (ActivityManager.RecentTaskInfo info : mRawTasks) {
-                Log.d(TAG, "  " + info.baseIntent + ", " + info.lastActiveTime);
-            }
-        }
     }
 
     /**
@@ -126,8 +115,6 @@
      * - least-recent to most-recent freeform tasks
      */
     public synchronized void preloadPlan(RecentsTaskLoader loader, boolean isTopTaskHome) {
-        if (DEBUG) Log.d(TAG, "preloadPlan");
-
         RecentsConfiguration config = Recents.getConfiguration();
         SystemServicesProxy ssp = Recents.getSystemServices();
         Resources res = mContext.getResources();
@@ -149,38 +136,26 @@
 
             // This task is only shown in the stack if it statisfies the historical time or min
             // number of tasks constraints. Freeform tasks are also always shown.
-            boolean isStackTask = true;
             boolean isFreeformTask = SystemServicesProxy.isFreeformStack(t.stackId);
-            isStackTask = isFreeformTask || (!isHistoricalTask(t) ||
-                    (t.lastActiveTime >= lastStackActiveTime &&
-                            i >= (taskCount - MIN_NUM_TASKS)));
+            boolean isStackTask = isFreeformTask || (!isHistoricalTask(t) ||
+                    (t.lastActiveTime >= lastStackActiveTime && i >= (taskCount - MIN_NUM_TASKS)));
             if (isStackTask && newLastStackActiveTime < 0) {
                 newLastStackActiveTime = t.lastActiveTime;
             }
 
-            // Load the label, icon, and color
-            String activityLabel = loader.getAndUpdateActivityLabel(taskKey, t.taskDescription,
-                    ssp);
-            String contentDescription = loader.getAndUpdateContentDescription(taskKey,
-                    activityLabel, ssp, res);
-            Drawable activityIcon = isStackTask
-                    ? loader.getAndUpdateActivityIcon(taskKey, t.taskDescription, ssp, res, false)
+            // Load the title, icon, and color
+            String title = loader.getAndUpdateActivityTitle(taskKey, t.taskDescription);
+            String contentDescription = loader.getAndUpdateContentDescription(taskKey, title, res);
+            Drawable icon = isStackTask
+                    ? loader.getAndUpdateActivityIcon(taskKey, t.taskDescription, res, false)
                     : null;
+            Bitmap thumbnail = loader.getAndUpdateThumbnail(taskKey, false);
             int activityColor = loader.getActivityPrimaryColor(t.taskDescription);
 
-            Bitmap icon = t.taskDescription != null
-                    ? t.taskDescription.getInMemoryIcon() : null;
-            String iconFilename = t.taskDescription != null
-                    ? t.taskDescription.getIconFilename() : null;
-
             // Add the task to the stack
-            Task task = new Task(taskKey, t.affiliatedTaskId, t.affiliatedTaskColor, activityLabel,
-                    contentDescription, activityIcon, activityColor, (i == (taskCount - 1)),
-                    config.lockToAppEnabled, !isStackTask, icon, iconFilename, t.bounds);
-            task.thumbnail = loader.getAndUpdateThumbnail(taskKey, ssp, false);
-            if (DEBUG) {
-                Log.d(TAG, activityLabel + " bounds: " + t.bounds);
-            }
+            Task task = new Task(taskKey, t.affiliatedTaskId, t.affiliatedTaskColor, icon,
+                    thumbnail, title, contentDescription, activityColor, !isStackTask,
+                    (i == (taskCount - 1)), config.lockToAppEnabled, t.bounds, t.taskDescription);
 
             allTasks.add(task);
         }
@@ -200,19 +175,13 @@
      */
     public synchronized void executePlan(Options opts, RecentsTaskLoader loader,
             TaskResourceLoadQueue loadQueue) {
-        if (DEBUG) Log.d(TAG, "executePlan, # tasks: " + opts.numVisibleTasks +
-                ", # thumbnails: " + opts.numVisibleTaskThumbnails +
-                ", running task id: " + opts.runningTaskId);
-
         RecentsConfiguration config = Recents.getConfiguration();
-        SystemServicesProxy ssp = Recents.getSystemServices();
         Resources res = mContext.getResources();
 
         // Iterate through each of the tasks and load them according to the load conditions.
         ArrayList<Task> tasks = mStack.getStackTasks();
         int taskCount = tasks.size();
         for (int i = 0; i < taskCount; i++) {
-            ActivityManager.RecentTaskInfo t = mRawTasks.get(i);
             Task task = tasks.get(i);
             Task.TaskKey taskKey = task.key;
 
@@ -226,17 +195,15 @@
             }
 
             if (opts.loadIcons && (isRunningTask || isVisibleTask)) {
-                if (task.activityIcon == null) {
-                    if (DEBUG) Log.d(TAG, "\tLoading icon: " + taskKey);
-                    task.activityIcon = loader.getAndUpdateActivityIcon(taskKey, t.taskDescription,
-                            ssp, res, true);
+                if (task.icon == null) {
+                    task.icon = loader.getAndUpdateActivityIcon(taskKey, task.taskDescription,
+                            res, true);
                 }
             }
             if (opts.loadThumbnails && (isRunningTask || isVisibleThumbnail)) {
                 if (task.thumbnail == null || isRunningTask) {
-                    if (DEBUG) Log.d(TAG, "\tLoading thumbnail: " + taskKey);
                     if (config.svelteLevel <= RecentsConfiguration.SVELTE_LIMIT_CACHE) {
-                        task.thumbnail = loader.getAndUpdateThumbnail(taskKey, ssp, true);
+                        task.thumbnail = loader.getAndUpdateThumbnail(taskKey, true);
                     } else if (config.svelteLevel == RecentsConfiguration.SVELTE_DISABLE_CACHE) {
                         loadQueue.addTask(task);
                     }
diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoader.java b/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoader.java
index c72d166..28338d83 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoader.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoader.java
@@ -93,23 +93,23 @@
     Handler mMainThreadHandler;
 
     TaskResourceLoadQueue mLoadQueue;
-    TaskKeyLruCache<Drawable> mApplicationIconCache;
+    TaskKeyLruCache<Drawable> mIconCache;
     TaskKeyLruCache<Bitmap> mThumbnailCache;
     Bitmap mDefaultThumbnail;
-    BitmapDrawable mDefaultApplicationIcon;
+    BitmapDrawable mDefaultIcon;
 
     boolean mCancelled;
     boolean mWaitingOnLoadQueue;
 
     /** Constructor, creates a new loading thread that loads task resources in the background */
     public BackgroundTaskLoader(TaskResourceLoadQueue loadQueue,
-            TaskKeyLruCache<Drawable> applicationIconCache, TaskKeyLruCache<Bitmap> thumbnailCache,
-            Bitmap defaultThumbnail, BitmapDrawable defaultApplicationIcon) {
+            TaskKeyLruCache<Drawable> iconCache, TaskKeyLruCache<Bitmap> thumbnailCache,
+            Bitmap defaultThumbnail, BitmapDrawable defaultIcon) {
         mLoadQueue = loadQueue;
-        mApplicationIconCache = applicationIconCache;
+        mIconCache = iconCache;
         mThumbnailCache = thumbnailCache;
         mDefaultThumbnail = defaultThumbnail;
-        mDefaultApplicationIcon = defaultApplicationIcon;
+        mDefaultIcon = defaultIcon;
         mMainThreadHandler = new Handler();
         mLoadThread = new HandlerThread("Recents-TaskResourceLoader",
                 android.os.Process.THREAD_PRIORITY_BACKGROUND);
@@ -163,30 +163,30 @@
                     // Load the next item from the queue
                     final Task t = mLoadQueue.nextTask();
                     if (t != null) {
-                        Drawable cachedIcon = mApplicationIconCache.get(t.key);
+                        Drawable cachedIcon = mIconCache.get(t.key);
                         Bitmap cachedThumbnail = mThumbnailCache.get(t.key);
 
-                        // Load the application icon if it is stale or we haven't cached one yet
+                        // Load the icon if it is stale or we haven't cached one yet
                         if (cachedIcon == null) {
-                            cachedIcon = getTaskDescriptionIcon(t.key, t.icon, t.iconFilename, ssp,
-                                    mContext.getResources());
+                            cachedIcon = ssp.getBadgedTaskDescriptionIcon(t.taskDescription,
+                                    t.key.userId, mContext.getResources());
 
                             if (cachedIcon == null) {
                                 ActivityInfo info = ssp.getActivityInfo(
                                         t.key.getComponent(), t.key.userId);
                                 if (info != null) {
                                     if (DEBUG) Log.d(TAG, "Loading icon: " + t.key);
-                                    cachedIcon = ssp.getActivityIcon(info, t.key.userId);
+                                    cachedIcon = ssp.getBadgedActivityIcon(info, t.key.userId);
                                 }
                             }
 
                             if (cachedIcon == null) {
-                                cachedIcon = mDefaultApplicationIcon;
+                                cachedIcon = mDefaultIcon;
                             }
 
                             // At this point, even if we can't load the icon, we will set the
                             // default icon.
-                            mApplicationIconCache.put(t.key, cachedIcon);
+                            mIconCache.put(t.key, cachedIcon);
                         }
                         // Load the thumbnail if it is stale or we haven't cached one yet
                         if (cachedThumbnail == null) {
@@ -234,25 +234,6 @@
             }
         }
     }
-
-    Drawable getTaskDescriptionIcon(Task.TaskKey taskKey, Bitmap iconBitmap, String iconFilename,
-            SystemServicesProxy ssp, Resources res) {
-        Bitmap tdIcon = null;
-        if (iconBitmap != null) {
-            tdIcon = iconBitmap;
-        } else {
-            try {
-                tdIcon = ActivityManager.TaskDescription.loadTaskDescriptionIcon(iconFilename,
-                        taskKey.userId);
-            } catch (Exception e) {
-                // TODO: Investigate for b/26221779
-            }
-        }
-        if (tdIcon != null) {
-            return ssp.getBadgedIcon(new BitmapDrawable(res, tdIcon), taskKey.userId);
-        }
-        return null;
-    }
 }
 
 /**
@@ -269,7 +250,7 @@
     // active time.  Instead, we rely on the RecentsPackageMonitor to keep us informed whenever a
     // package in the cache has been updated, so that we may remove it.
     private final LruCache<ComponentName, ActivityInfo> mActivityInfoCache;
-    private final TaskKeyLruCache<Drawable> mApplicationIconCache;
+    private final TaskKeyLruCache<Drawable> mIconCache;
     private final TaskKeyLruCache<Bitmap> mThumbnailCache;
     private final TaskKeyLruCache<String> mActivityLabelCache;
     private final TaskKeyLruCache<String> mContentDescriptionCache;
@@ -282,7 +263,7 @@
     private int mNumVisibleThumbnailsLoaded;
 
     int mDefaultTaskBarBackgroundColor;
-    BitmapDrawable mDefaultApplicationIcon;
+    BitmapDrawable mDefaultIcon;
     Bitmap mDefaultThumbnail;
 
     public RecentsTaskLoader(Context context) {
@@ -302,22 +283,22 @@
         mDefaultThumbnail = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888);
         mDefaultThumbnail.setHasAlpha(false);
         mDefaultThumbnail.eraseColor(0xFFffffff);
-        mDefaultApplicationIcon = new BitmapDrawable(context.getResources(), icon);
+        mDefaultIcon = new BitmapDrawable(context.getResources(), icon);
 
         // Initialize the proxy, cache and loaders
         int numRecentTasks = ActivityManager.getMaxRecentTasksStatic();
         mLoadQueue = new TaskResourceLoadQueue();
-        mApplicationIconCache = new TaskKeyLruCache<>(iconCacheSize);
+        mIconCache = new TaskKeyLruCache<>(iconCacheSize);
         mThumbnailCache = new TaskKeyLruCache<>(thumbnailCacheSize);
         mActivityLabelCache = new TaskKeyLruCache<>(numRecentTasks);
         mContentDescriptionCache = new TaskKeyLruCache<>(numRecentTasks);
         mActivityInfoCache = new LruCache(numRecentTasks);
-        mLoader = new BackgroundTaskLoader(mLoadQueue, mApplicationIconCache, mThumbnailCache,
-                mDefaultThumbnail, mDefaultApplicationIcon);
+        mLoader = new BackgroundTaskLoader(mLoadQueue, mIconCache, mThumbnailCache,
+                mDefaultThumbnail, mDefaultIcon);
     }
 
     /** Returns the size of the app icon cache. */
-    public int getApplicationIconCacheSize() {
+    public int getIconCacheSize() {
         return mMaxIconCacheSize;
     }
 
@@ -355,33 +336,33 @@
 
     /** Acquires the task resource data directly from the pool. */
     public void loadTaskData(Task t) {
-        Drawable applicationIcon = mApplicationIconCache.getAndInvalidateIfModified(t.key);
+        Drawable icon = mIconCache.getAndInvalidateIfModified(t.key);
         Bitmap thumbnail = mThumbnailCache.getAndInvalidateIfModified(t.key);
 
         // Grab the thumbnail/icon from the cache, if either don't exist, then trigger a reload and
         // use the default assets in their place until they load
-        boolean requiresLoad = (applicationIcon == null) || (thumbnail == null);
-        applicationIcon = applicationIcon != null ? applicationIcon : mDefaultApplicationIcon;
+        boolean requiresLoad = (icon == null) || (thumbnail == null);
+        icon = icon != null ? icon : mDefaultIcon;
         if (requiresLoad) {
             mLoadQueue.addTask(t);
         }
-        t.notifyTaskDataLoaded(thumbnail == mDefaultThumbnail ? null : thumbnail, applicationIcon);
+        t.notifyTaskDataLoaded(thumbnail == mDefaultThumbnail ? null : thumbnail, icon);
     }
 
     /** Releases the task resource data back into the pool. */
     public void unloadTaskData(Task t) {
         mLoadQueue.removeTask(t);
-        t.notifyTaskDataUnloaded(null, mDefaultApplicationIcon);
+        t.notifyTaskDataUnloaded(null, mDefaultIcon);
     }
 
     /** Completely removes the resource data from the pool. */
     public void deleteTaskData(Task t, boolean notifyTaskDataUnloaded) {
         mLoadQueue.removeTask(t);
         mThumbnailCache.remove(t.key);
-        mApplicationIconCache.remove(t.key);
+        mIconCache.remove(t.key);
         mActivityInfoCache.remove(t.key.getComponent());
         if (notifyTaskDataUnloaded) {
-            t.notifyTaskDataUnloaded(null, mDefaultApplicationIcon);
+            t.notifyTaskDataUnloaded(null, mDefaultIcon);
         }
     }
 
@@ -403,14 +384,14 @@
                 } else if (config.svelteLevel >= RecentsConfiguration.SVELTE_DISABLE_CACHE) {
                     mThumbnailCache.evictAll();
                 }
-                mApplicationIconCache.trimToSize(Math.max(mNumVisibleTasksLoaded,
+                mIconCache.trimToSize(Math.max(mNumVisibleTasksLoaded,
                         mMaxIconCacheSize / 2));
                 break;
             case ComponentCallbacks2.TRIM_MEMORY_RUNNING_MODERATE:
             case ComponentCallbacks2.TRIM_MEMORY_BACKGROUND:
                 // We are leaving recents, so trim the data a bit
                 mThumbnailCache.trimToSize(Math.max(1, mMaxThumbnailCacheSize / 2));
-                mApplicationIconCache.trimToSize(Math.max(1, mMaxIconCacheSize / 2));
+                mIconCache.trimToSize(Math.max(1, mMaxIconCacheSize / 2));
                 mActivityInfoCache.trimToSize(Math.max(1,
                         ActivityManager.getMaxRecentTasksStatic() / 2));
                 break;
@@ -418,7 +399,7 @@
             case ComponentCallbacks2.TRIM_MEMORY_MODERATE:
                 // We are going to be low on memory
                 mThumbnailCache.trimToSize(Math.max(1, mMaxThumbnailCacheSize / 4));
-                mApplicationIconCache.trimToSize(Math.max(1, mMaxIconCacheSize / 4));
+                mIconCache.trimToSize(Math.max(1, mMaxIconCacheSize / 4));
                 mActivityInfoCache.trimToSize(Math.max(1,
                         ActivityManager.getMaxRecentTasksStatic() / 4));
                 break;
@@ -426,7 +407,7 @@
             case ComponentCallbacks2.TRIM_MEMORY_COMPLETE:
                 // We are low on memory, so release everything
                 mThumbnailCache.evictAll();
-                mApplicationIconCache.evictAll();
+                mIconCache.evictAll();
                 mActivityInfoCache.evictAll();
                 // The cache is small, only clear the label cache when we are critical
                 mActivityLabelCache.evictAll();
@@ -440,8 +421,9 @@
     /**
      * Returns the cached task label if the task key is not expired, updating the cache if it is.
      */
-    String getAndUpdateActivityLabel(Task.TaskKey taskKey, ActivityManager.TaskDescription td,
-                                     SystemServicesProxy ssp) {
+    String getAndUpdateActivityTitle(Task.TaskKey taskKey, ActivityManager.TaskDescription td) {
+        SystemServicesProxy ssp = Recents.getSystemServices();
+
         // Return the task description label if it exists
         if (td != null && td.getLabel() != null) {
             return td.getLabel();
@@ -452,7 +434,7 @@
             return label;
         }
         // All short paths failed, load the label from the activity info and cache it
-        ActivityInfo activityInfo = getAndUpdateActivityInfo(taskKey, ssp);
+        ActivityInfo activityInfo = getAndUpdateActivityInfo(taskKey);
         if (activityInfo != null) {
             label = ssp.getActivityLabel(activityInfo);
             mActivityLabelCache.put(taskKey, label);
@@ -468,7 +450,9 @@
      * cache if it is.
      */
     String getAndUpdateContentDescription(Task.TaskKey taskKey, String activityLabel,
-            SystemServicesProxy ssp, Resources res) {
+            Resources res) {
+        SystemServicesProxy ssp = Recents.getSystemServices();
+
         // Return the cached content description if it exists
         String label = mContentDescriptionCache.getAndInvalidateIfModified(taskKey);
         if (label != null) {
@@ -493,28 +477,29 @@
      * Returns the cached task icon if the task key is not expired, updating the cache if it is.
      */
     Drawable getAndUpdateActivityIcon(Task.TaskKey taskKey, ActivityManager.TaskDescription td,
-            SystemServicesProxy ssp, Resources res, boolean loadIfNotCached) {
+            Resources res, boolean loadIfNotCached) {
+        SystemServicesProxy ssp = Recents.getSystemServices();
+
         // Return the cached activity icon if it exists
-        Drawable icon = mApplicationIconCache.getAndInvalidateIfModified(taskKey);
+        Drawable icon = mIconCache.getAndInvalidateIfModified(taskKey);
         if (icon != null) {
             return icon;
         }
 
         if (loadIfNotCached) {
             // Return and cache the task description icon if it exists
-            icon = mLoader.getTaskDescriptionIcon(taskKey, td.getInMemoryIcon(),
-                    td.getIconFilename(), ssp, res);
+            icon = ssp.getBadgedTaskDescriptionIcon(td, taskKey.userId, res);
             if (icon != null) {
-                mApplicationIconCache.put(taskKey, icon);
+                mIconCache.put(taskKey, icon);
                 return icon;
             }
 
             // Load the icon from the activity info and cache it
-            ActivityInfo activityInfo = getAndUpdateActivityInfo(taskKey, ssp);
+            ActivityInfo activityInfo = getAndUpdateActivityInfo(taskKey);
             if (activityInfo != null) {
-                icon = ssp.getActivityIcon(activityInfo, taskKey.userId);
+                icon = ssp.getBadgedActivityIcon(activityInfo, taskKey.userId);
                 if (icon != null) {
-                    mApplicationIconCache.put(taskKey, icon);
+                    mIconCache.put(taskKey, icon);
                     return icon;
                 }
             }
@@ -526,8 +511,9 @@
     /**
      * Returns the cached thumbnail if the task key is not expired, updating the cache if it is.
      */
-    Bitmap getAndUpdateThumbnail(Task.TaskKey taskKey, SystemServicesProxy ssp,
-            boolean loadIfNotCached) {
+    Bitmap getAndUpdateThumbnail(Task.TaskKey taskKey, boolean loadIfNotCached) {
+        SystemServicesProxy ssp = Recents.getSystemServices();
+
         // Return the cached thumbnail if it exists
         Bitmap thumbnail = mThumbnailCache.getAndInvalidateIfModified(taskKey);
         if (thumbnail != null) {
@@ -550,7 +536,8 @@
     }
 
     /**
-     * Returns the task's primary color.
+     * Returns the task's primary color if possible, defaulting to the default color if there is
+     * no specified primary color.
      */
     int getActivityPrimaryColor(ActivityManager.TaskDescription td) {
         if (td != null && td.getPrimaryColor() != 0) {
@@ -563,7 +550,8 @@
      * Returns the activity info for the given task key, retrieving one from the system if the
      * task key is expired.
      */
-    private ActivityInfo getAndUpdateActivityInfo(Task.TaskKey taskKey, SystemServicesProxy ssp) {
+    private ActivityInfo getAndUpdateActivityInfo(Task.TaskKey taskKey) {
+        SystemServicesProxy ssp = Recents.getSystemServices();
         ComponentName cn = taskKey.getComponent();
         ActivityInfo activityInfo = mActivityInfoCache.get(cn);
         if (activityInfo == null) {
diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/Task.java b/packages/SystemUI/src/com/android/systemui/recents/model/Task.java
index 73c0adb..34a0e52 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/model/Task.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/model/Task.java
@@ -16,6 +16,7 @@
 
 package com.android.systemui.recents.model;
 
+import android.app.ActivityManager;
 import android.content.ComponentName;
 import android.content.Intent;
 import android.graphics.Bitmap;
@@ -92,24 +93,46 @@
     }
 
     public TaskKey key;
+
+    /**
+     * The group will be computed separately from the initialization of the task
+     */
     public TaskGrouping group;
-    // The taskAffiliationId is the task id of the parent task or itself if it is not affiliated with any task
-    public int taskAffiliationId;
-    public int taskAffiliationColor;
-    public boolean isLaunchTarget;
-    public Drawable applicationIcon;
-    public Drawable activityIcon;
+    /**
+     * The affiliationTaskId is the task id of the parent task or itself if it is not affiliated
+     * with any task.
+     */
+    public int affiliationTaskId;
+    public int affiliationColor;
+
+    /**
+     * The icon is the task description icon (if provided), which falls back to the activity icon,
+     * which can then fall back to the application icon.
+     */
+    public Drawable icon;
+    public Bitmap thumbnail;
+    public String title;
     public String contentDescription;
-    public String activityLabel;
     public int colorPrimary;
     public boolean useLightOnPrimaryColor;
-    public Bitmap thumbnail;
+
+    /**
+     * The bounds of the task, used only if it is a freeform task.
+     */
+    public Rect bounds;
+
+    /**
+     * The task description for this task, only used to reload task icons.
+     */
+    public ActivityManager.TaskDescription taskDescription;
+
+    /**
+     * The state isLaunchTarget will be set for the correct task upon launching Recents.
+     */
+    public boolean isLaunchTarget;
+    public boolean isHistorical;
     public boolean lockToThisTask;
     public boolean lockToTaskEnabled;
-    public boolean isHistorical;
-    public Bitmap icon;
-    public String iconFilename;
-    public Rect bounds;
 
     private ArrayList<TaskCallbacks> mCallbacks = new ArrayList<>();
 
@@ -117,45 +140,46 @@
         // Do nothing
     }
 
-    public Task(TaskKey key, int taskAffiliation, int taskAffiliationColor,
-                String activityTitle, String contentDescription, Drawable activityIcon,
-                int colorPrimary, boolean lockToThisTask, boolean lockToTaskEnabled,
-                boolean isHistorical, Bitmap icon, String iconFilename, Rect bounds) {
-        boolean isInAffiliationGroup = (taskAffiliation != key.id);
-        boolean hasAffiliationGroupColor = isInAffiliationGroup && (taskAffiliationColor != 0);
+    public Task(TaskKey key, int affiliationTaskId, int affiliationColor, Drawable icon,
+                Bitmap thumbnail, String title, String contentDescription, int colorPrimary,
+                boolean isHistorical, boolean lockToThisTask, boolean lockToTaskEnabled,
+                Rect bounds, ActivityManager.TaskDescription taskDescription) {
+        boolean isInAffiliationGroup = (affiliationTaskId != key.id);
+        boolean hasAffiliationGroupColor = isInAffiliationGroup && (affiliationColor != 0);
         this.key = key;
-        this.taskAffiliationId = taskAffiliation;
-        this.taskAffiliationColor = taskAffiliationColor;
-        this.activityLabel = activityTitle;
+        this.affiliationTaskId = affiliationTaskId;
+        this.affiliationColor = affiliationColor;
+        this.icon = icon;
+        this.thumbnail = thumbnail;
+        this.title = title;
         this.contentDescription = contentDescription;
-        this.activityIcon = activityIcon;
-        this.colorPrimary = hasAffiliationGroupColor ? taskAffiliationColor : colorPrimary;
+        this.colorPrimary = hasAffiliationGroupColor ? affiliationColor : colorPrimary;
         this.useLightOnPrimaryColor = Utilities.computeContrastBetweenColors(this.colorPrimary,
                 Color.WHITE) > 3f;
+        this.bounds = bounds;
+        this.taskDescription = taskDescription;
+        this.isHistorical = isHistorical;
         this.lockToThisTask = lockToTaskEnabled && lockToThisTask;
         this.lockToTaskEnabled = lockToTaskEnabled;
-        this.isHistorical = isHistorical;
-        this.icon = icon;
-        this.iconFilename = iconFilename;
-        this.bounds = bounds;
     }
 
     /** Copies the other task. */
     public void copyFrom(Task o) {
         this.key = o.key;
-        this.taskAffiliationId = o.taskAffiliationId;
-        this.taskAffiliationColor = o.taskAffiliationColor;
-        this.activityLabel = o.activityLabel;
+        this.group = o.group;
+        this.affiliationTaskId = o.affiliationTaskId;
+        this.affiliationColor = o.affiliationColor;
+        this.icon = o.icon;
+        this.thumbnail = o.thumbnail;
+        this.title = o.title;
         this.contentDescription = o.contentDescription;
-        this.activityIcon = o.activityIcon;
         this.colorPrimary = o.colorPrimary;
         this.useLightOnPrimaryColor = o.useLightOnPrimaryColor;
+        this.bounds = o.bounds;
+        this.isLaunchTarget = o.isLaunchTarget;
+        this.isHistorical = o.isHistorical;
         this.lockToThisTask = o.lockToThisTask;
         this.lockToTaskEnabled = o.lockToTaskEnabled;
-        this.isHistorical = o.isHistorical;
-        this.icon = o.icon;
-        this.iconFilename = o.iconFilename;
-        this.bounds = o.bounds;
     }
 
     /**
@@ -200,7 +224,7 @@
 
     /** Notifies the callback listeners that this task has been loaded */
     public void notifyTaskDataLoaded(Bitmap thumbnail, Drawable applicationIcon) {
-        this.applicationIcon = applicationIcon;
+        this.icon = applicationIcon;
         this.thumbnail = thumbnail;
         int callbackCount = mCallbacks.size();
         for (int i = 0; i < callbackCount; i++) {
@@ -210,7 +234,7 @@
 
     /** Notifies the callback listeners that this task has been unloaded */
     public void notifyTaskDataUnloaded(Bitmap defaultThumbnail, Drawable defaultApplicationIcon) {
-        applicationIcon = defaultApplicationIcon;
+        icon = defaultApplicationIcon;
         thumbnail = defaultThumbnail;
         int callbackCount = mCallbacks.size();
         for (int i = 0; i < callbackCount; i++) {
@@ -222,7 +246,7 @@
      * Returns whether this task is affiliated with another task.
      */
     public boolean isAffiliatedTask() {
-        return key.id != taskAffiliationId;
+        return key.id != affiliationTaskId;
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/TaskStack.java b/packages/SystemUI/src/com/android/systemui/recents/model/TaskStack.java
index 3484c38..6f003ab 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/model/TaskStack.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/model/TaskStack.java
@@ -232,16 +232,16 @@
         public static final DockState NONE = new DockState(-1, 96, null, null);
         public static final DockState LEFT = new DockState(
                 DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT, DOCK_AREA_ALPHA,
-                new RectF(0, 0, 0.25f, 1), new RectF(0, 0, 0.25f, 1));
+                new RectF(0, 0, 0.15f, 1), new RectF(0, 0, 0.15f, 1));
         public static final DockState TOP = new DockState(
                 DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT, DOCK_AREA_ALPHA,
-                new RectF(0, 0, 1, 0.25f), new RectF(0, 0, 1, 0.25f));
+                new RectF(0, 0, 1, 0.15f), new RectF(0, 0, 1, 0.15f));
         public static final DockState RIGHT = new DockState(
                 DOCKED_STACK_CREATE_MODE_BOTTOM_OR_RIGHT, DOCK_AREA_ALPHA,
-                new RectF(0.75f, 0, 1, 1), new RectF(0.75f, 0, 1, 1));
+                new RectF(0.85f, 0, 1, 1), new RectF(0.85f, 0, 1, 1));
         public static final DockState BOTTOM = new DockState(
                 DOCKED_STACK_CREATE_MODE_BOTTOM_OR_RIGHT, DOCK_AREA_ALPHA,
-                new RectF(0, 0.75f, 1, 1), new RectF(0, 0.75f, 1, 1));
+                new RectF(0, 0.85f, 1, 1), new RectF(0, 0.85f, 1, 1));
 
         @Override
         public boolean acceptsDrop(int x, int y, int width, int height) {
@@ -352,28 +352,28 @@
             @Override
             public boolean acceptTask(SparseArray<Task> taskIdMap, Task t, int index) {
                 if (t.isAffiliatedTask()) {
-                    // If this task is affiliated with another parent in the stack, then the historical state of this
-                    // task depends on the state of the parent task
-                    Task parentTask = taskIdMap.get(t.taskAffiliationId);
+                    // If this task is affiliated with another parent in the stack, then the
+                    // historical state of this task depends on the state of the parent task
+                    Task parentTask = taskIdMap.get(t.affiliationTaskId);
                     if (parentTask != null) {
                         t = parentTask;
                     }
                 }
-                return !t.isHistorical && !SystemServicesProxy.isDockedStack(t.key.stackId);
+                return !t.isHistorical;
             }
         });
         mHistoryTaskList.setFilter(new TaskFilter() {
             @Override
             public boolean acceptTask(SparseArray<Task> taskIdMap, Task t, int index) {
                 if (t.isAffiliatedTask()) {
-                    // If this task is affiliated with another parent in the stack, then the historical state of this
-                    // task depends on the state of the parent task
-                    Task parentTask = taskIdMap.get(t.taskAffiliationId);
+                    // If this task is affiliated with another parent in the stack, then the
+                    // historical state of this task depends on the state of the parent task
+                    Task parentTask = taskIdMap.get(t.affiliationTaskId);
                     if (parentTask != null) {
                         t = parentTask;
                     }
                 }
-                return t.isHistorical && !SystemServicesProxy.isDockedStack(t.key.stackId);
+                return t.isHistorical;
             }
         });
     }
@@ -716,7 +716,7 @@
             for (int i = 0; i < taskCount; i++) {
                 Task t = tasks.get(i);
                 TaskGrouping group;
-                int affiliation = t.taskAffiliationId > 0 ? t.taskAffiliationId :
+                int affiliation = t.affiliationTaskId > 0 ? t.affiliationTaskId :
                         IndividualTaskIdOffset + t.key.id;
                 if (mAffinitiesGroups.containsKey(affiliation)) {
                     group = getGroupWithAffiliation(affiliation);
@@ -737,7 +737,7 @@
                 // Ignore the groups that only have one task
                 if (taskCount <= 1) continue;
                 // Calculate the group color distribution
-                int affiliationColor = tasksMap.get(group.mTaskKeys.get(0)).taskAffiliationColor;
+                int affiliationColor = tasksMap.get(group.mTaskKeys.get(0)).affiliationColor;
                 float alphaStep = (1f - minAlpha) / taskCount;
                 float alpha = 1f;
                 for (int j = 0; j < taskCount; j++) {
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsTransitionHelper.java b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsTransitionHelper.java
index 135f0f9..0af7c1e 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsTransitionHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsTransitionHelper.java
@@ -157,7 +157,7 @@
             ActivityOptions opts, IAppTransitionAnimationSpecsFuture transitionFuture,
             final ActivityOptions.OnAnimationStartedListener animStartedListener) {
         SystemServicesProxy ssp = Recents.getSystemServices();
-        if (ssp.startActivityFromRecents(mContext, task.key.id, task.activityLabel, opts)) {
+        if (ssp.startActivityFromRecents(mContext, task.key.id, task.title, opts)) {
             // Keep track of the index of the task launch
             int taskIndexFromFront = 0;
             int taskIndex = stack.indexOfStackTask(task);
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
index 53c02cb..9b1315a 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
@@ -132,10 +132,7 @@
         mHistoryButton.setOnClickListener(new View.OnClickListener() {
             @Override
             public void onClick(View v) {
-                ReferenceCountedTrigger postHideStackAnimationTrigger = new ReferenceCountedTrigger(v.getContext());
-                postHideStackAnimationTrigger.increment();
-                EventBus.getDefault().send(new ShowHistoryEvent(postHideStackAnimationTrigger));
-                postHideStackAnimationTrigger.decrement();
+                EventBus.getDefault().send(new ShowHistoryEvent());
             }
         });
         addView(mHistoryButton);
@@ -576,8 +573,8 @@
     public final void onBusEvent(ShowHistoryEvent event) {
         // Hide the history button when the history view is shown
         hideHistoryButton(getResources().getInteger(R.integer.recents_history_transition_duration),
-                event.postHideStackAnimationTrigger);
-        event.postHideStackAnimationTrigger.addLastDecrementRunnable(new Runnable() {
+                event.getAnimationTrigger());
+        event.addPostAnimationCallback(new Runnable() {
             @Override
             public void run() {
                 setAlpha(0f);
@@ -589,7 +586,7 @@
         // Show the history button when the history view is hidden
         setAlpha(1f);
         showHistoryButton(getResources().getInteger(R.integer.recents_history_transition_duration),
-                event.postHideHistoryAnimationTrigger);
+                event.getAnimationTrigger());
     }
 
     public final void onBusEvent(ShowHistoryButtonEvent event) {
@@ -609,10 +606,9 @@
      * Shows the history button.
      */
     private void showHistoryButton(final int duration) {
-        ReferenceCountedTrigger postAnimationTrigger = new ReferenceCountedTrigger(getContext());
-        postAnimationTrigger.increment();
+        ReferenceCountedTrigger postAnimationTrigger = new ReferenceCountedTrigger();
         showHistoryButton(duration, postAnimationTrigger);
-        postAnimationTrigger.decrement();
+        postAnimationTrigger.flushLastDecrementRunnables();
     }
 
     private void showHistoryButton(final int duration,
@@ -638,10 +634,9 @@
      * Hides the history button.
      */
     private void hideHistoryButton(int duration) {
-        ReferenceCountedTrigger postAnimationTrigger = new ReferenceCountedTrigger(getContext());
-        postAnimationTrigger.increment();
+        ReferenceCountedTrigger postAnimationTrigger = new ReferenceCountedTrigger();
         hideHistoryButton(duration, postAnimationTrigger);
-        postAnimationTrigger.decrement();
+        postAnimationTrigger.flushLastDecrementRunnables();
     }
 
     private void hideHistoryButton(int duration,
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsViewTouchHandler.java b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsViewTouchHandler.java
index 37a0194..318801d 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsViewTouchHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsViewTouchHandler.java
@@ -188,12 +188,8 @@
             case MotionEvent.ACTION_UP:
             case MotionEvent.ACTION_CANCEL: {
                 if (mDragging) {
-                    ReferenceCountedTrigger postAnimationTrigger = new ReferenceCountedTrigger(
-                            mRv.getContext());
-                    postAnimationTrigger.increment();
                     EventBus.getDefault().send(new DragEndEvent(mDragTask, mTaskView,
-                            mLastDropTarget, postAnimationTrigger));
-                    postAnimationTrigger.decrement();
+                            mLastDropTarget));
                     break;
                 }
             }
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackLayoutAlgorithm.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackLayoutAlgorithm.java
index 9625e5d..10df156 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackLayoutAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackLayoutAlgorithm.java
@@ -118,7 +118,7 @@
      */
     public static class StackState {
 
-        public static final StackState FREEFORM_ONLY = new StackState(1f, 0);
+        public static final StackState FREEFORM_ONLY = new StackState(1f, 255);
         public static final StackState STACK_ONLY = new StackState(0f, 0);
         public static final StackState SPLIT = new StackState(0.5f, 255);
 
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
index b9ca9fd..830d607 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
@@ -44,7 +44,6 @@
 import com.android.systemui.recents.RecentsActivity;
 import com.android.systemui.recents.RecentsActivityLaunchState;
 import com.android.systemui.recents.RecentsConfiguration;
-import com.android.systemui.recents.RecentsDebugFlags;
 import com.android.systemui.recents.events.EventBus;
 import com.android.systemui.recents.events.activity.CancelEnterRecentsWindowAnimationEvent;
 import com.android.systemui.recents.events.activity.EnterRecentsWindowAnimationCompletedEvent;
@@ -216,7 +215,8 @@
                 R.drawable.recents_freeform_workspace_bg);
         mFreeformWorkspaceBackground.setCallback(this);
         if (ssp.hasFreeformWorkspaceSupport()) {
-            setBackgroundColor(getContext().getColor(R.color.recents_freeform_workspace_bg_color));
+            mFreeformWorkspaceBackground.setColor(
+                    getContext().getColor(R.color.recents_freeform_workspace_bg_color));
         }
     }
 
@@ -797,7 +797,7 @@
             TaskView frontMostTask = taskViews.get(taskViewCount - 1);
             event.setFromIndex(mStack.indexOfStackTask(backMostTask.getTask()));
             event.setToIndex(mStack.indexOfStackTask(frontMostTask.getTask()));
-            event.setContentDescription(frontMostTask.getTask().activityLabel);
+            event.setContentDescription(frontMostTask.getTask().title);
         }
         event.setItemCount(mStack.getStackTaskCount());
         event.setScrollY(mStackScroller.mScroller.getCurrY());
@@ -1021,8 +1021,7 @@
             if (launchTargetTask != null) {
                 occludesLaunchTarget = launchTargetTask.group.isTaskAboveTask(task,
                         launchTargetTask);
-                hideTask = SystemServicesProxy.isFreeformStack(launchTargetTask.key.stackId) &&
-                        SystemServicesProxy.isFreeformStack(task.key.stackId);
+                hideTask = launchTargetTask.isFreeformTask() && task.isFreeformTask();
             }
             tv.prepareEnterRecentsAnimation(task.isLaunchTarget, hideTask, occludesLaunchTarget,
                     offscreenY);
@@ -1494,7 +1493,6 @@
                 (!isFreeformTask && event.dropTarget == mFreeformWorkspaceDropTarget) ||
                         (isFreeformTask && event.dropTarget == mStackDropTarget);
 
-        event.postAnimationTrigger.increment();
         if (hasChangedStacks) {
             // Move the task to the right position in the stack (ie. the front of the stack if
             // freeform or the front of the stack if fullscreen).  Note, we MUST move the tasks
@@ -1507,7 +1505,7 @@
             updateLayout(true);
 
             // Move the task to the new stack in the system after the animation completes
-            event.postAnimationTrigger.addLastDecrementRunnable(new Runnable() {
+            event.addPostAnimationCallback(new Runnable() {
                 @Override
                 public void run() {
                     SystemServicesProxy ssp = Recents.getSystemServices();
@@ -1515,8 +1513,8 @@
                 }
             });
         }
-        event.taskView.animate()
-                .withEndAction(event.postAnimationTrigger.decrementAsRunnable());
+        event.getAnimationTrigger().increment();
+        event.taskView.animate().withEndAction(event.getAnimationTrigger().decrementAsRunnable());
 
         // We translated the view but we need to animate it back from the current layout-space rect
         // to its final layout-space rect
@@ -1556,7 +1554,7 @@
         for (int i = 0; i < taskViewCount; i++) {
             TaskView tv = taskViews.get(i);
             Task task = tv.getTask();
-            if (SystemServicesProxy.isFreeformStack(task.key.stackId)) {
+            if (task.isFreeformTask()) {
                 tv.setVisibility(event.visible ? View.VISIBLE : View.INVISIBLE);
             }
         }
@@ -1577,9 +1575,9 @@
                     .setUpdateListener(null)
                     .setListener(null)
                     .withLayer()
-                    .withEndAction(event.postHideStackAnimationTrigger.decrementAsRunnable())
+                    .withEndAction(event.getAnimationTrigger().decrementAsRunnable())
                     .start();
-            event.postHideStackAnimationTrigger.increment();
+            event.getAnimationTrigger().increment();
         }
     }
 
@@ -1592,7 +1590,7 @@
         int taskViewCount = taskViews.size();
         for (int i = taskViewCount - 1; i >= 0; i--) {
             final TaskView tv = taskViews.get(i);
-            event.postHideHistoryAnimationTrigger.addLastDecrementRunnable(new Runnable() {
+            event.addPostAnimationCallback(new Runnable() {
                 @Override
                 public void run() {
                     tv.animate()
@@ -1616,7 +1614,7 @@
 
         // Announce for accessibility
         tv.announceForAccessibility(getContext().getString(
-                R.string.accessibility_recents_item_dismissed, tv.getTask().activityLabel));
+                R.string.accessibility_recents_item_dismissed, tv.getTask().title));
 
         // Remove the task from the stack
         mStack.removeTask(task);
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
index df7b9a6..a3e8b2d 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
@@ -651,7 +651,7 @@
      */
     public void setFocusedState(boolean isFocused, boolean animated, boolean requestViewFocus) {
         if (DEBUG) {
-            Log.d(TAG, "setFocusedState: " + mTask.activityLabel + " focused: " + isFocused +
+            Log.d(TAG, "setFocusedState: " + mTask.title + " focused: " + isFocused +
                     " animated: " + animated + " requestViewFocus: " + requestViewFocus +
                     " isFocused(): " + isFocused() +
                     " isAccessibilityFocused(): " + isAccessibilityFocused());
@@ -771,7 +771,7 @@
 
     public final void onBusEvent(DragEndEvent event) {
         if (!(event.dropTarget instanceof TaskStack.DockState)) {
-            event.postAnimationTrigger.addLastDecrementRunnable(new Runnable() {
+            event.addPostAnimationCallback(new Runnable() {
                 @Override
                 public void run() {
                     // Animate the drag view back from where it is, to the view location, then after
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java
index 0271ccd..9a2ffe7 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java
@@ -60,8 +60,8 @@
     // Header views
     ImageView mMoveTaskButton;
     ImageView mDismissButton;
-    ImageView mApplicationIcon;
-    TextView mActivityDescription;
+    ImageView mIconView;
+    TextView mTitleView;
     int mMoveTaskTargetStackId = INVALID_STACK_ID;
 
     // Header drawables
@@ -128,16 +128,16 @@
     @Override
     protected void onFinishInflate() {
         // Initialize the icon and description views
-        mApplicationIcon = (ImageView) findViewById(R.id.application_icon);
-        mApplicationIcon.setOnLongClickListener(this);
-        mActivityDescription = (TextView) findViewById(R.id.activity_description);
+        mIconView = (ImageView) findViewById(R.id.icon);
+        mIconView.setOnLongClickListener(this);
+        mTitleView = (TextView) findViewById(R.id.title);
         mDismissButton = (ImageView) findViewById(R.id.dismiss_task);
         mDismissButton.setOnClickListener(this);
         mMoveTaskButton = (ImageView) findViewById(R.id.move_task);
 
         // Hide the backgrounds if they are ripple drawables
-        if (mApplicationIcon.getBackground() instanceof RippleDrawable) {
-            mApplicationIcon.setBackground(null);
+        if (mIconView.getBackground() instanceof RippleDrawable) {
+            mIconView.setBackground(null);
         }
 
         mBackgroundColorDrawable = (GradientDrawable) getContext().getDrawable(
@@ -158,8 +158,8 @@
     public void onTaskViewSizeChanged(int width, int height) {
         mTaskViewRect.set(0, 0, width, height);
         boolean updateMoveTaskButton = mMoveTaskButton.getVisibility() != View.GONE;
-        int appIconWidth = mApplicationIcon.getMeasuredWidth();
-        int activityDescWidth = mActivityDescription.getMeasuredWidth();
+        int appIconWidth = mIconView.getMeasuredWidth();
+        int activityDescWidth = mTitleView.getMeasuredWidth();
         int dismissIconWidth = mDismissButton.getMeasuredWidth();
         int moveTaskIconWidth = mMoveTaskButton.getVisibility() == View.VISIBLE
                 ? mMoveTaskButton.getMeasuredWidth()
@@ -168,26 +168,26 @@
         // Priority-wise, we show the activity icon first, the dismiss icon if there is room, the
         // move-task icon if there is room, and then finally, the activity label if there is room
         if (width < (appIconWidth + dismissIconWidth)) {
-            mActivityDescription.setVisibility(View.INVISIBLE);
+            mTitleView.setVisibility(View.INVISIBLE);
             if (updateMoveTaskButton) {
                 mMoveTaskButton.setVisibility(View.INVISIBLE);
             }
             mDismissButton.setVisibility(View.INVISIBLE);
         } else if (width < (appIconWidth + dismissIconWidth + moveTaskIconWidth)) {
-            mActivityDescription.setVisibility(View.INVISIBLE);
+            mTitleView.setVisibility(View.INVISIBLE);
             if (updateMoveTaskButton) {
                 mMoveTaskButton.setVisibility(View.INVISIBLE);
             }
             mDismissButton.setVisibility(View.VISIBLE);
         } else if (width < (appIconWidth + dismissIconWidth + moveTaskIconWidth +
                 activityDescWidth)) {
-            mActivityDescription.setVisibility(View.INVISIBLE);
+            mTitleView.setVisibility(View.INVISIBLE);
             if (updateMoveTaskButton) {
                 mMoveTaskButton.setVisibility(View.VISIBLE);
             }
             mDismissButton.setVisibility(View.VISIBLE);
         } else {
-            mActivityDescription.setVisibility(View.VISIBLE);
+            mTitleView.setVisibility(View.VISIBLE);
             if (updateMoveTaskButton) {
                 mMoveTaskButton.setVisibility(View.VISIBLE);
             }
@@ -233,15 +233,13 @@
 
         // If an activity icon is defined, then we use that as the primary icon to show in the bar,
         // otherwise, we fall back to the application icon
-        if (t.activityIcon != null) {
-            mApplicationIcon.setImageDrawable(t.activityIcon);
-        } else if (t.applicationIcon != null) {
-            mApplicationIcon.setImageDrawable(t.applicationIcon);
+        if (t.icon != null) {
+            mIconView.setImageDrawable(t.icon);
         }
-        if (!mActivityDescription.getText().toString().equals(t.activityLabel)) {
-            mActivityDescription.setText(t.activityLabel);
+        if (!mTitleView.getText().toString().equals(t.title)) {
+            mTitleView.setText(t.title);
         }
-        mActivityDescription.setContentDescription(t.contentDescription);
+        mTitleView.setContentDescription(t.contentDescription);
 
         // Try and apply the system ui tint
         int existingBgColor = (getBackground() instanceof ColorDrawable) ?
@@ -254,7 +252,7 @@
                 R.color.recents_task_bar_light_text_color);
         int taskBarViewDarkTextColor = getResources().getColor(
                 R.color.recents_task_bar_dark_text_color);
-        mActivityDescription.setTextColor(t.useLightOnPrimaryColor ?
+        mTitleView.setTextColor(t.useLightOnPrimaryColor ?
                 taskBarViewLightTextColor : taskBarViewDarkTextColor);
         mDismissButton.setImageDrawable(t.useLightOnPrimaryColor ?
                 mLightDismissDrawable : mDarkDismissDrawable);
@@ -281,15 +279,15 @@
 
         // In accessibility, a single click on the focused app info button will show it
         if (ssp.isTouchExplorationEnabled()) {
-            mApplicationIcon.setOnClickListener(this);
+            mIconView.setOnClickListener(this);
         }
     }
 
     /** Unbinds the bar view from the task */
     void unbindFromTask() {
         mTask = null;
-        mApplicationIcon.setImageDrawable(null);
-        mApplicationIcon.setOnClickListener(null);
+        mIconView.setImageDrawable(null);
+        mIconView.setOnClickListener(null);
         mMoveTaskButton.setOnClickListener(null);
     }
 
@@ -357,7 +355,7 @@
 
     @Override
     public void onClick(View v) {
-        if (v == mApplicationIcon) {
+        if (v == mIconView) {
             // In accessibility, a single click on the focused app info button will show it
             EventBus.getDefault().send(new ShowApplicationInfoEvent(mTask));
         } else if (v == mDismissButton) {
@@ -379,7 +377,7 @@
 
     @Override
     public boolean onLongClick(View v) {
-        if (v == mApplicationIcon) {
+        if (v == mIconView) {
             EventBus.getDefault().send(new ShowApplicationInfoEvent(mTask));
             return true;
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
index 6d4dc872..a6ca50a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
@@ -947,7 +947,7 @@
         final StatusBarNotification sbn = row.getStatusBarNotification();
         PackageManager pmUser = getPackageManagerForUser(mContext, sbn.getUser().getIdentifier());
         row.setTag(sbn.getPackageName());
-        final View guts = row.getGuts();
+        final NotificationGuts guts = row.getGuts();
         final String pkg = sbn.getPackageName();
         String appname = pkg;
         Drawable pkgicon = null;
@@ -969,8 +969,6 @@
         ((ImageView) row.findViewById(android.R.id.icon)).setImageDrawable(pkgicon);
         ((TextView) row.findViewById(R.id.pkgname)).setText(appname);
 
-        bindTopicImportance(sbn, row);
-
         final View settingsButton = guts.findViewById(R.id.notification_inspect_item);
         if (appUid >= 0) {
             final int appUidF = appUid;
@@ -983,69 +981,8 @@
         } else {
             settingsButton.setVisibility(View.GONE);
         }
-    }
 
-    private void bindTopicImportance(final StatusBarNotification sbn,
-            ExpandableNotificationRow row) {
-        final INotificationManager sINM = INotificationManager.Stub.asInterface(
-                ServiceManager.getService(Context.NOTIFICATION_SERVICE));
-        final Notification.Topic topic = sbn.getNotification().getTopic() == null
-                ? new Notification.Topic(Notification.TOPIC_DEFAULT, mContext.getString(
-                        com.android.internal.R.string.default_notification_topic_label))
-                : sbn.getNotification().getTopic();
-
-        ((TextView) row.findViewById(R.id.topic_details)).setText(topic.getLabel());
-        final TextView topicSummary = ((TextView) row.findViewById(R.id.summary));
-        int importance = mNotificationData.getImportance(sbn.getKey());
-        SeekBar seekBar = (SeekBar) row.findViewById(R.id.seekbar);
-        seekBar.setMax(4);
-        seekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
-            @Override
-            public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
-                topicSummary.setText(getProgressSummary(progress));
-                if (fromUser) {
-                    try {
-                        sINM.setTopicImportance(sbn.getPackageName(), sbn.getUid(), topic,
-                                progress);
-                    } catch (RemoteException e) {
-                        // :(
-                    }
-                }
-            }
-
-            @Override
-            public void onStartTrackingTouch(SeekBar seekBar) {
-                // no-op
-            }
-
-            @Override
-            public void onStopTrackingTouch(SeekBar seekBar) {
-                // no-op
-            }
-
-            private String getProgressSummary(int progress) {
-                switch (progress) {
-                    case NotificationListenerService.Ranking.IMPORTANCE_NONE:
-                        return mContext.getString(
-                                com.android.internal.R.string.notification_importance_blocked);
-                    case NotificationListenerService.Ranking.IMPORTANCE_LOW:
-                        return mContext.getString(
-                                com.android.internal.R.string.notification_importance_low);
-                    case NotificationListenerService.Ranking.IMPORTANCE_DEFAULT:
-                        return mContext.getString(
-                                com.android.internal.R.string.notification_importance_default);
-                    case NotificationListenerService.Ranking.IMPORTANCE_HIGH:
-                        return mContext.getString(
-                                com.android.internal.R.string.notification_importance_high);
-                    case NotificationListenerService.Ranking.IMPORTANCE_MAX:
-                        return mContext.getString(
-                                com.android.internal.R.string.notification_importance_max);
-                    default:
-                        return "";
-                }
-            }
-        });
-        seekBar.setProgress(importance);
+        guts.bindImportance(sbn, row, mNotificationData.getImportance(sbn.getKey()));
     }
 
     protected SwipeHelper.LongPressListener getNotificationLongClicker() {
@@ -1612,8 +1549,14 @@
 
             for (int i = 0; i < numActions; i++) {
                 Notification.Action action = actions.get(i);
+                if (action == null) {
+                    continue;
+                }
                 RemoteInput[] remoteInputs = action.getRemoteInputs();
-                for (RemoteInput ri : action.getRemoteInputs()) {
+                if (remoteInputs == null) {
+                    continue;
+                }
+                for (RemoteInput ri : remoteInputs) {
                     if (ri.getAllowFreeFormInput()) {
                         viableAction = action;
                         break;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGuts.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGuts.java
index 0081496..57db80a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGuts.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGuts.java
@@ -16,12 +16,22 @@
 
 package com.android.systemui.statusbar;
 
+import android.app.INotificationManager;
+import android.app.Notification;
 import android.content.Context;
 import android.graphics.Canvas;
 import android.graphics.drawable.Drawable;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.service.notification.NotificationListenerService;
+import android.service.notification.StatusBarNotification;
 import android.util.AttributeSet;
+import android.view.View;
 import android.widget.FrameLayout;
 import android.widget.LinearLayout;
+import android.widget.RadioButton;
+import android.widget.SeekBar;
+import android.widget.TextView;
 
 import com.android.systemui.R;
 
@@ -83,6 +93,88 @@
         }
     }
 
+    void bindImportance(final StatusBarNotification sbn, final ExpandableNotificationRow row,
+            final int importance) {
+        final INotificationManager sINM = INotificationManager.Stub.asInterface(
+                ServiceManager.getService(Context.NOTIFICATION_SERVICE));
+        final Notification.Topic topic = sbn.getNotification().getTopic() == null
+                ? new Notification.Topic(Notification.TOPIC_DEFAULT, mContext.getString(
+                com.android.internal.R.string.default_notification_topic_label))
+                : sbn.getNotification().getTopic();
+
+        final RadioButton applyToTopic = (RadioButton) row.findViewById(R.id.apply_to_topic);
+        if (sbn.getNotification().getTopic() != null) {
+            applyToTopic.setVisibility(View.VISIBLE);
+            applyToTopic.setChecked(true);
+            applyToTopic.setText(mContext.getString(R.string.apply_to_topic, topic.getLabel()));
+            row.findViewById(R.id.apply_to_app).setVisibility(View.VISIBLE);
+        }
+
+        final TextView topicSummary = ((TextView) row.findViewById(R.id.summary));
+        final TextView topicTitle = ((TextView) row.findViewById(R.id.title));
+        SeekBar seekBar = (SeekBar) row.findViewById(R.id.seekbar);
+        seekBar.setMax(4);
+        seekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
+            @Override
+            public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
+                updateTitleAndSummary(progress);
+                if (fromUser) {
+                    try {
+                        if (applyToTopic.isChecked()) {
+                            sINM.setTopicImportance(sbn.getPackageName(), sbn.getUid(), topic,
+                                    progress);
+                        } else {
+                            sINM.setAppImportance(sbn.getPackageName(), sbn.getUid(), progress);
+                        }
+                    } catch (RemoteException e) {
+                        // :(
+                    }
+                }
+            }
+
+            @Override
+            public void onStartTrackingTouch(SeekBar seekBar) {
+                // no-op
+            }
+
+            @Override
+            public void onStopTrackingTouch(SeekBar seekBar) {
+                // no-op
+            }
+
+            private void updateTitleAndSummary(int progress) {
+                switch (progress) {
+                    case NotificationListenerService.Ranking.IMPORTANCE_NONE:
+                        topicSummary.setText(mContext.getString(
+                                R.string.notification_importance_blocked));
+                        topicTitle.setText(mContext.getString(R.string.blocked_importance));
+                        break;
+                    case NotificationListenerService.Ranking.IMPORTANCE_LOW:
+                        topicSummary.setText(mContext.getString(
+                                R.string.notification_importance_low));
+                        topicTitle.setText(mContext.getString(R.string.low_importance));
+                        break;
+                    case NotificationListenerService.Ranking.IMPORTANCE_DEFAULT:
+                        topicSummary.setText(mContext.getString(
+                                R.string.notification_importance_default));
+                        topicTitle.setText(mContext.getString(R.string.default_importance));
+                        break;
+                    case NotificationListenerService.Ranking.IMPORTANCE_HIGH:
+                        topicSummary.setText(mContext.getString(
+                                R.string.notification_importance_high));
+                        topicTitle.setText(mContext.getString(R.string.high_importance));
+                        break;
+                    case NotificationListenerService.Ranking.IMPORTANCE_MAX:
+                        topicSummary.setText(mContext.getString(
+                                R.string.notification_importance_max));
+                        topicTitle.setText(mContext.getString(R.string.max_importance));
+                        break;
+                }
+            }
+        });
+        seekBar.setProgress(importance);
+    }
+
     public void setActualHeight(int actualHeight) {
         mActualHeight = actualHeight;
         invalidate();
diff --git a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
index df18d3e..6f8f8eb 100644
--- a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
+++ b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
@@ -16,6 +16,7 @@
 
 package com.android.server.appwidget;
 
+import android.app.ActivityManager;
 import android.app.AlarmManager;
 import android.app.AppGlobals;
 import android.app.AppOpsManager;
@@ -76,6 +77,7 @@
 import android.view.WindowManager;
 import android.widget.RemoteViews;
 
+import com.android.internal.R;
 import com.android.internal.appwidget.IAppWidgetHost;
 import com.android.internal.appwidget.IAppWidgetService;
 import com.android.internal.os.BackgroundThread;
@@ -83,7 +85,6 @@
 import com.android.internal.util.FastXmlSerializer;
 import com.android.internal.widget.IRemoteViewsAdapterConnection;
 import com.android.internal.widget.IRemoteViewsFactory;
-import com.android.internal.R;
 import com.android.server.LocalServices;
 import com.android.server.WidgetBackupProvider;
 
@@ -139,8 +140,10 @@
     private static final int CURRENT_VERSION = 1;
 
     private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
+        @Override
         public void onReceive(Context context, Intent intent) {
-            String action = intent.getAction();
+            final String action = intent.getAction();
+            final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL);
 
             if (DEBUG) {
                 Slog.i(TAG, "Received broadcast: " + action);
@@ -148,23 +151,16 @@
 
             if (Intent.ACTION_CONFIGURATION_CHANGED.equals(action)) {
                 onConfigurationChanged();
-            } else if (Intent.ACTION_USER_STARTED.equals(action)) {
-                onUserStarted(intent.getIntExtra(Intent.EXTRA_USER_HANDLE,
-                        UserHandle.USER_NULL));
+            } else if (Intent.ACTION_USER_UNLOCKED.equals(action)) {
+                onUserUnlocked(userId);
             } else if (Intent.ACTION_USER_STOPPED.equals(action)) {
-                onUserStopped(intent.getIntExtra(Intent.EXTRA_USER_HANDLE,
-                        UserHandle.USER_NULL));
+                onUserStopped(userId);
             } else if (Intent.ACTION_USER_SWITCHED.equals(action)) {
-                refreshProfileWidgetsMaskedState(intent.getIntExtra(Intent.EXTRA_USER_HANDLE,
-                        UserHandle.USER_NULL));
+                refreshProfileWidgetsMaskedState(userId);
             } else if (Intent.ACTION_MANAGED_PROFILE_AVAILABILITY_CHANGED.equals(action)) {
-                UserHandle profile = (UserHandle)intent.getParcelableExtra(Intent.EXTRA_USER);
-                if (profile != null) {
-                    refreshWidgetMaskedState(profile.getIdentifier());
-                }
+                refreshWidgetMaskedState(userId);
             } else {
-                onPackageBroadcastReceived(intent, intent.getIntExtra(
-                        Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL));
+                onPackageBroadcastReceived(intent, userId);
             }
         }
     };
@@ -263,7 +259,7 @@
                 sdFilter, null, null);
 
         IntentFilter userFilter = new IntentFilter();
-        userFilter.addAction(Intent.ACTION_USER_STARTED);
+        userFilter.addAction(Intent.ACTION_USER_UNLOCKED);
         userFilter.addAction(Intent.ACTION_USER_STOPPED);
         userFilter.addAction(Intent.ACTION_USER_SWITCHED);
         mContext.registerReceiverAsUser(mBroadcastReceiver, UserHandle.ALL,
@@ -340,6 +336,8 @@
     }
 
     private void onPackageBroadcastReceived(Intent intent, int userId) {
+        if (!mUserManager.isUserUnlocked(userId)) return;
+
         final String action = intent.getAction();
         boolean added = false;
         boolean changed = false;
@@ -419,9 +417,7 @@
      * Refresh the masked state for all profiles under the given user.
      */
     private void refreshProfileWidgetsMaskedState(int userId) {
-        if (userId == UserHandle.USER_NULL) {
-            return;
-        }
+        if (!mUserManager.isUserUnlocked(userId)) return;
         List<UserInfo> profiles = mUserManager.getEnabledProfiles(userId);
         if (profiles != null) {
             for (int i = 0; i < profiles.size(); i++) {
@@ -435,6 +431,7 @@
      * Mask/unmask widgets in the given profile, depending on the quiet state of the profile.
      */
     private void refreshWidgetMaskedState(int profileId) {
+        if (!mUserManager.isUserUnlocked(profileId)) return;
         final long identity = Binder.clearCallingIdentity();
         try {
             UserInfo user  = mUserManager.getUserInfo(profileId);
@@ -484,6 +481,11 @@
     }
 
     private void ensureGroupStateLoadedLocked(int userId) {
+        if (!mUserManager.isUserUnlocked(userId)) {
+            throw new IllegalStateException(
+                    "User " + userId + " must be unlocked for widgets to be available");
+        }
+
         final int[] profileIds = mSecurityPolicy.getEnabledGroupProfileIds(userId);
 
         // Careful lad, we may have already loaded the state for some
@@ -2321,7 +2323,7 @@
         }
     }
 
-    private void onUserStarted(int userId) {
+    private void onUserUnlocked(int userId) {
         synchronized (mLock) {
             ensureGroupStateLoadedLocked(userId);
 
diff --git a/services/core/java/com/android/server/SystemConfig.java b/services/core/java/com/android/server/SystemConfig.java
index 86183af..4dc46ac 100644
--- a/services/core/java/com/android/server/SystemConfig.java
+++ b/services/core/java/com/android/server/SystemConfig.java
@@ -48,6 +48,13 @@
 
     static SystemConfig sInstance;
 
+    // permission flag, determines which types of configuration are allowed to be read
+    private static final int ALLOW_FEATURES = 0x01;
+    private static final int ALLOW_LIBS = 0x02;
+    private static final int ALLOW_PERMISSIONS = 0x04;
+    private static final int ALLOW_APP_CONFIGS = 0x08;
+    private static final int ALLOW_ALL = ~0;
+
     // Group-ids that are given to all packages as read from etc/permissions/*.xml.
     int[] mGlobalGids;
 
@@ -161,21 +168,27 @@
     SystemConfig() {
         // Read configuration from system
         readPermissions(Environment.buildPath(
-                Environment.getRootDirectory(), "etc", "sysconfig"), false);
+                Environment.getRootDirectory(), "etc", "sysconfig"), ALLOW_ALL);
         // Read configuration from the old permissions dir
         readPermissions(Environment.buildPath(
-                Environment.getRootDirectory(), "etc", "permissions"), false);
-        // Only read features from OEM config
+                Environment.getRootDirectory(), "etc", "permissions"), ALLOW_ALL);
+        // Allow ODM to customize system configs around libs, features and apps
+        int odmPermissionFlag = ALLOW_LIBS | ALLOW_FEATURES | ALLOW_APP_CONFIGS;
         readPermissions(Environment.buildPath(
-                Environment.getOemDirectory(), "etc", "sysconfig"), true);
+                Environment.getOdmDirectory(), "etc", "sysconfig"), odmPermissionFlag);
         readPermissions(Environment.buildPath(
-                Environment.getOemDirectory(), "etc", "permissions"), true);
+                Environment.getOdmDirectory(), "etc", "permissions"), odmPermissionFlag);
+        // Only allow OEM to customize features
+        readPermissions(Environment.buildPath(
+                Environment.getOemDirectory(), "etc", "sysconfig"), ALLOW_FEATURES);
+        readPermissions(Environment.buildPath(
+                Environment.getOemDirectory(), "etc", "permissions"), ALLOW_FEATURES);
     }
 
-    void readPermissions(File libraryDir, boolean onlyFeatures) {
+    void readPermissions(File libraryDir, int permissionFlag) {
         // Read permissions from given directory.
         if (!libraryDir.exists() || !libraryDir.isDirectory()) {
-            if (!onlyFeatures) {
+            if (permissionFlag == ALLOW_ALL) {
                 Slog.w(TAG, "No directory " + libraryDir + ", skipping");
             }
             return;
@@ -203,16 +216,16 @@
                 continue;
             }
 
-            readPermissionsFromXml(f, onlyFeatures);
+            readPermissionsFromXml(f, permissionFlag);
         }
 
         // Read platform permissions last so it will take precedence
         if (platformFile != null) {
-            readPermissionsFromXml(platformFile, onlyFeatures);
+            readPermissionsFromXml(platformFile, permissionFlag);
         }
     }
 
-    private void readPermissionsFromXml(File permFile, boolean onlyFeatures) {
+    private void readPermissionsFromXml(File permFile, int permissionFlag) {
         FileReader permReader = null;
         try {
             permReader = new FileReader(permFile);
@@ -242,6 +255,11 @@
                         + ": found " + parser.getName() + ", expected 'permissions' or 'config'");
             }
 
+            boolean allowAll = permissionFlag == ALLOW_ALL;
+            boolean allowLibs = (permissionFlag & ALLOW_LIBS) != 0;
+            boolean allowFeatures = (permissionFlag & ALLOW_FEATURES) != 0;
+            boolean allowPermissions = (permissionFlag & ALLOW_PERMISSIONS) != 0;
+            boolean allowAppConfigs = (permissionFlag & ALLOW_APP_CONFIGS) != 0;
             while (true) {
                 XmlUtils.nextElement(parser);
                 if (parser.getEventType() == XmlPullParser.END_DOCUMENT) {
@@ -249,7 +267,7 @@
                 }
 
                 String name = parser.getName();
-                if ("group".equals(name) && !onlyFeatures) {
+                if ("group".equals(name) && allowAll) {
                     String gidStr = parser.getAttributeValue(null, "gid");
                     if (gidStr != null) {
                         int gid = android.os.Process.getGidForName(gidStr);
@@ -261,7 +279,7 @@
 
                     XmlUtils.skipCurrentTag(parser);
                     continue;
-                } else if ("permission".equals(name) && !onlyFeatures) {
+                } else if ("permission".equals(name) && allowPermissions) {
                     String perm = parser.getAttributeValue(null, "name");
                     if (perm == null) {
                         Slog.w(TAG, "<permission> without name in " + permFile + " at "
@@ -272,7 +290,7 @@
                     perm = perm.intern();
                     readPermission(parser, perm);
 
-                } else if ("assign-permission".equals(name) && !onlyFeatures) {
+                } else if ("assign-permission".equals(name) && allowPermissions) {
                     String perm = parser.getAttributeValue(null, "name");
                     if (perm == null) {
                         Slog.w(TAG, "<assign-permission> without name in " + permFile + " at "
@@ -304,7 +322,7 @@
                     perms.add(perm);
                     XmlUtils.skipCurrentTag(parser);
 
-                } else if ("library".equals(name) && !onlyFeatures) {
+                } else if ("library".equals(name) && allowLibs) {
                     String lname = parser.getAttributeValue(null, "name");
                     String lfile = parser.getAttributeValue(null, "file");
                     if (lname == null) {
@@ -320,7 +338,7 @@
                     XmlUtils.skipCurrentTag(parser);
                     continue;
 
-                } else if ("feature".equals(name)) {
+                } else if ("feature".equals(name) && allowFeatures) {
                     String fname = parser.getAttributeValue(null, "name");
                     boolean allowed;
                     if (!lowRam) {
@@ -341,7 +359,7 @@
                     XmlUtils.skipCurrentTag(parser);
                     continue;
 
-                } else if ("unavailable-feature".equals(name)) {
+                } else if ("unavailable-feature".equals(name) && allowFeatures) {
                     String fname = parser.getAttributeValue(null, "name");
                     if (fname == null) {
                         Slog.w(TAG, "<unavailable-feature> without name in " + permFile + " at "
@@ -352,7 +370,7 @@
                     XmlUtils.skipCurrentTag(parser);
                     continue;
 
-                } else if ("allow-in-power-save-except-idle".equals(name) && !onlyFeatures) {
+                } else if ("allow-in-power-save-except-idle".equals(name) && allowAll) {
                     String pkgname = parser.getAttributeValue(null, "package");
                     if (pkgname == null) {
                         Slog.w(TAG, "<allow-in-power-save-except-idle> without package in "
@@ -363,7 +381,7 @@
                     XmlUtils.skipCurrentTag(parser);
                     continue;
 
-                } else if ("allow-in-power-save".equals(name) && !onlyFeatures) {
+                } else if ("allow-in-power-save".equals(name) && allowAll) {
                     String pkgname = parser.getAttributeValue(null, "package");
                     if (pkgname == null) {
                         Slog.w(TAG, "<allow-in-power-save> without package in " + permFile + " at "
@@ -374,7 +392,7 @@
                     XmlUtils.skipCurrentTag(parser);
                     continue;
 
-                } else if ("fixed-ime-app".equals(name) && !onlyFeatures) {
+                } else if ("fixed-ime-app".equals(name) && allowAll) {
                     String pkgname = parser.getAttributeValue(null, "package");
                     if (pkgname == null) {
                         Slog.w(TAG, "<fixed-ime-app> without package in " + permFile + " at "
@@ -385,7 +403,7 @@
                     XmlUtils.skipCurrentTag(parser);
                     continue;
 
-                } else if ("app-link".equals(name)) {
+                } else if ("app-link".equals(name) && allowAppConfigs) {
                     String pkgname = parser.getAttributeValue(null, "package");
                     if (pkgname == null) {
                         Slog.w(TAG, "<app-link> without package in " + permFile + " at "
@@ -394,7 +412,7 @@
                         mLinkedApps.add(pkgname);
                     }
                     XmlUtils.skipCurrentTag(parser);
-                } else if ("system-user-whitelisted-app".equals(name)) {
+                } else if ("system-user-whitelisted-app".equals(name) && allowAppConfigs) {
                     String pkgname = parser.getAttributeValue(null, "package");
                     if (pkgname == null) {
                         Slog.w(TAG, "<system-user-whitelisted-app> without package in " + permFile
@@ -403,7 +421,7 @@
                         mSystemUserWhitelistedApps.add(pkgname);
                     }
                     XmlUtils.skipCurrentTag(parser);
-                } else if ("system-user-blacklisted-app".equals(name)) {
+                } else if ("system-user-blacklisted-app".equals(name) && allowAppConfigs) {
                     String pkgname = parser.getAttributeValue(null, "package");
                     if (pkgname == null) {
                         Slog.w(TAG, "<system-user-blacklisted-app without package in " + permFile
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 70f3c05..de0a23a 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -324,6 +324,7 @@
 import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_VISIBLE_BEHIND;
 import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
 import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
+import static com.android.server.am.ActivityStackSupervisor.ActivityContainer.FORCE_NEW_TASK_FLAGS;
 import static com.android.server.am.ActivityStackSupervisor.FORCE_FOCUS;
 import static com.android.server.am.ActivityStackSupervisor.ON_TOP;
 import static com.android.server.am.ActivityStackSupervisor.PRESERVE_WINDOWS;
@@ -511,6 +512,8 @@
     /** Run all ActivityStacks through this */
     ActivityStackSupervisor mStackSupervisor;
 
+    ActivityStarter mActivityStarter;
+
     /** Task stack change listeners. */
     private RemoteCallbackList<ITaskStackListener> mTaskStackListeners =
             new RemoteCallbackList<ITaskStackListener>();
@@ -1788,7 +1791,7 @@
             } break;
             case DO_PENDING_ACTIVITY_LAUNCHES_MSG: {
                 synchronized (ActivityManagerService.this) {
-                    mStackSupervisor.doPendingActivityLaunchesLocked(true);
+                    mActivityStarter.doPendingActivityLaunchesLocked(true);
                 }
             } break;
             case KILL_APPLICATION_MSG: {
@@ -2296,6 +2299,7 @@
     public void setWindowManager(WindowManagerService wm) {
         mWindowManager = wm;
         mStackSupervisor.setWindowManager(wm);
+        mActivityStarter.setWindowManager(wm);
     }
 
     public void setUsageStatsManager(UsageStatsManagerInternal usageStatsManager) {
@@ -2483,6 +2487,7 @@
         mIntentFirewall = new IntentFirewall(new IntentFirewallInterface(), mHandler);
         mRecentTasks = new RecentTasks(this);
         mStackSupervisor = new ActivityStackSupervisor(this, mRecentTasks);
+        mActivityStarter = new ActivityStarter(this, mStackSupervisor);
         mTaskPersister = new TaskPersister(systemDir, mStackSupervisor, mRecentTasks);
 
         mProcessCpuThread = new Thread("CpuTracker") {
@@ -3613,7 +3618,7 @@
                     aInfo.applicationInfo.uid, true);
             if (app == null || app.instrumentationClass == null) {
                 intent.setFlags(intent.getFlags() | Intent.FLAG_ACTIVITY_NEW_TASK);
-                mStackSupervisor.startHomeActivity(intent, aInfo, reason);
+                mActivityStarter.startHomeActivityLocked(intent, aInfo, reason);
             }
         }
 
@@ -3691,7 +3696,7 @@
                     intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                     intent.setComponent(new ComponentName(
                             ri.activityInfo.packageName, ri.activityInfo.name));
-                    mStackSupervisor.startActivityLocked(null, intent, null /*ephemeralIntent*/,
+                    mActivityStarter.startActivityLocked(null, intent, null /*ephemeralIntent*/,
                             null, ri.activityInfo, null /*rInfo*/, null, null, null, null, 0, 0, 0,
                             null, 0, 0, 0, null, false, false, null, null, null);
                 }
@@ -4018,6 +4023,25 @@
                 UserHandle.getCallingUserId());
     }
 
+    final int startActivity(Intent intent, ActivityStackSupervisor.ActivityContainer container) {
+        enforceNotIsolatedCaller("ActivityContainer.startActivity");
+        final int userId = mUserController.handleIncomingUser(Binder.getCallingPid(),
+                Binder.getCallingUid(), mStackSupervisor.mCurrentUser, false,
+                ActivityManagerService.ALLOW_FULL_ONLY, "ActivityContainer", null);
+
+        // TODO: Switch to user app stacks here.
+        String mimeType = intent.getType();
+        final Uri data = intent.getData();
+        if (mimeType == null && data != null && "content".equals(data.getScheme())) {
+            mimeType = getProviderMimeType(data, userId);
+        }
+        container.checkEmbeddedAllowedInner(userId, intent, mimeType);
+
+        intent.addFlags(FORCE_NEW_TASK_FLAGS);
+        return mActivityStarter.startActivityMayWait(null, -1, null, intent, mimeType, null, null, null,
+                null, 0, 0, null, null, null, null, false, userId, container, null);
+    }
+
     @Override
     public final int startActivityAsUser(IApplicationThread caller, String callingPackage,
             Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode,
@@ -4026,7 +4050,7 @@
         userId = mUserController.handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(),
                 userId, false, ALLOW_FULL_ONLY, "startActivity", null);
         // TODO: Switch to user app stacks here.
-        return mStackSupervisor.startActivityMayWait(caller, -1, callingPackage, intent,
+        return mActivityStarter.startActivityMayWait(caller, -1, callingPackage, intent,
                 resolvedType, null, null, resultTo, resultWho, requestCode, startFlags,
                 profilerInfo, null, null, bOptions, false, userId, null, null);
     }
@@ -4089,7 +4113,7 @@
 
         // TODO: Switch to user app stacks here.
         try {
-            int ret = mStackSupervisor.startActivityMayWait(null, targetUid, targetPackage, intent,
+            int ret = mActivityStarter.startActivityMayWait(null, targetUid, targetPackage, intent,
                     resolvedType, null, null, resultTo, resultWho, requestCode, startFlags, null,
                     null, null, bOptions, ignoreTargetSecurity, userId, null, null);
             return ret;
@@ -4118,7 +4142,7 @@
                 userId, false, ALLOW_FULL_ONLY, "startActivityAndWait", null);
         WaitResult res = new WaitResult();
         // TODO: Switch to user app stacks here.
-        mStackSupervisor.startActivityMayWait(caller, -1, callingPackage, intent, resolvedType,
+        mActivityStarter.startActivityMayWait(caller, -1, callingPackage, intent, resolvedType,
                 null, null, resultTo, resultWho, requestCode, startFlags, profilerInfo, res, null,
                 bOptions, false, userId, null, null);
         return res;
@@ -4132,7 +4156,7 @@
         userId = mUserController.handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(),
                 userId, false, ALLOW_FULL_ONLY, "startActivityWithConfig", null);
         // TODO: Switch to user app stacks here.
-        int ret = mStackSupervisor.startActivityMayWait(caller, -1, callingPackage, intent,
+        int ret = mActivityStarter.startActivityMayWait(caller, -1, callingPackage, intent,
                 resolvedType, null, null, resultTo, resultWho, requestCode, startFlags,
                 null, null, config, bOptions, false, userId, null, null);
         return ret;
@@ -4190,7 +4214,7 @@
         userId = mUserController.handleIncomingUser(callingPid, callingUid, userId, false,
                 ALLOW_FULL_ONLY, "startVoiceActivity", null);
         // TODO: Switch to user app stacks here.
-        return mStackSupervisor.startActivityMayWait(null, callingUid, callingPackage, intent,
+        return mActivityStarter.startActivityMayWait(null, callingUid, callingPackage, intent,
                 resolvedType, session, interactor, null, null, 0, startFlags, profilerInfo, null,
                 null, bOptions, false, userId, null, null);
     }
@@ -4301,7 +4325,7 @@
             }
 
             final long origId = Binder.clearCallingIdentity();
-            int res = mStackSupervisor.startActivityLocked(r.app.thread, intent,
+            int res = mActivityStarter.startActivityLocked(r.app.thread, intent,
                     null /*ephemeralIntent*/, r.resolvedType, aInfo, null /*rInfo*/, null,
                     null, resultTo != null ? resultTo.appToken : null, resultWho, requestCode, -1,
                     r.launchedFromUid, r.launchedFromPackage, -1, r.launchedFromUid, 0, options,
@@ -4386,7 +4410,7 @@
                 userId, false, ALLOW_FULL_ONLY, "startActivityInPackage", null);
 
         // TODO: Switch to user app stacks here.
-        int ret = mStackSupervisor.startActivityMayWait(null, uid, callingPackage, intent,
+        int ret = mActivityStarter.startActivityMayWait(null, uid, callingPackage, intent,
                 resolvedType, null, null, resultTo, resultWho, requestCode, startFlags,
                 null, null, null, bOptions, false, userId, container, inTask);
         return ret;
@@ -4400,7 +4424,7 @@
         userId = mUserController.handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(),
                 userId, false, ALLOW_FULL_ONLY, "startActivity", null);
         // TODO: Switch to user app stacks here.
-        int ret = mStackSupervisor.startActivities(caller, -1, callingPackage, intents,
+        int ret = mActivityStarter.startActivities(caller, -1, callingPackage, intents,
                 resolvedTypes, resultTo, bOptions, userId);
         return ret;
     }
@@ -4412,7 +4436,7 @@
         userId = mUserController.handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(),
                 userId, false, ALLOW_FULL_ONLY, "startActivityInPackage", null);
         // TODO: Switch to user app stacks here.
-        int ret = mStackSupervisor.startActivities(null, uid, callingPackage, intents, resolvedTypes,
+        int ret = mActivityStarter.startActivities(null, uid, callingPackage, intents, resolvedTypes,
                 resultTo, bOptions, userId);
         return ret;
     }
@@ -8780,6 +8804,20 @@
                             continue;
                         }
                     }
+                    if ((flags & ActivityManager.RECENT_INGORE_DOCKED_STACK_TASKS) != 0) {
+                        if (tr.stack != null && tr.stack.isDockedStack()) {
+                            if (DEBUG_RECENTS) Slog.d(TAG_RECENTS,
+                                    "Skipping, docked stack task: " + tr);
+                            continue;
+                        }
+                    }
+                    if ((flags & ActivityManager.RECENT_INGORE_PINNED_STACK_TASKS) != 0) {
+                        if (tr.stack != null && tr.stack.isPinnedStack()) {
+                            if (DEBUG_RECENTS) Slog.d(TAG_RECENTS,
+                                    "Skipping, pinned stack task: " + tr);
+                            continue;
+                        }
+                    }
                     if (tr.autoRemoveRecents && tr.getTopActivity() == null) {
                         // Don't include auto remove tasks that are finished or finishing.
                         if (DEBUG_RECENTS) Slog.d(TAG_RECENTS,
@@ -17093,6 +17131,8 @@
             }
         }
 
+        // Verify that protected broadcasts are only being sent by system code,
+        // and that system code is only sending protected broadcasts.
         final String action = intent.getAction();
         final boolean isProtectedBroadcast;
         try {
@@ -17102,35 +17142,47 @@
             return ActivityManager.BROADCAST_SUCCESS;
         }
 
-        /*
-         * Prevent non-system code (defined here to be non-persistent
-         * processes) from sending protected broadcasts.
-         */
-        int callingAppId = UserHandle.getAppId(callingUid);
-        if (callingAppId == Process.SYSTEM_UID || callingAppId == Process.PHONE_UID
-            || callingAppId == Process.SHELL_UID || callingAppId == Process.BLUETOOTH_UID
-            || callingAppId == Process.NFC_UID || callingUid == 0) {
-            // Always okay.
+        final boolean isCallerSystem;
+        switch (UserHandle.getAppId(callingUid)) {
+            case Process.ROOT_UID:
+            case Process.SYSTEM_UID:
+            case Process.PHONE_UID:
+            case Process.SHELL_UID:
+            case Process.BLUETOOTH_UID:
+            case Process.NFC_UID:
+                isCallerSystem = true;
+                break;
+            default:
+                isCallerSystem = (callerApp != null) && callerApp.persistent;
+                break;
+        }
 
-            // Yell if the system is trying to send a non-protected broadcast.
-            // The vast majority of broadcasts sent from system callers should
-            // be protected to avoid security holes, so exceptions here should
-            // be incredibly rare.
-            if (!isProtectedBroadcast
-                    && !Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(action)) {
-                // TODO: eventually switch over to hard throw
+        if (isCallerSystem) {
+            if (isProtectedBroadcast
+                    || Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(action)
+                    || Intent.ACTION_MEDIA_SCANNER_SCAN_FILE.equals(action)
+                    || AppWidgetManager.ACTION_APPWIDGET_CONFIGURE.equals(action)
+                    || AppWidgetManager.ACTION_APPWIDGET_UPDATE.equals(action)) {
+                // Broadcast is either protected, or it's a public action that
+                // we've relaxed, so it's fine for system internals to send.
+            } else {
+                // The vast majority of broadcasts sent from system internals
+                // should be protected to avoid security holes, so yell loudly
+                // to ensure we examine these cases.
                 Log.wtf(TAG, "Sending non-protected broadcast " + action
                         + " from system", new Throwable());
             }
 
-        } else if (callerApp == null || !callerApp.persistent) {
+        } else {
             if (isProtectedBroadcast) {
                 String msg = "Permission Denial: not allowed to send broadcast "
                         + action + " from pid="
                         + callingPid + ", uid=" + callingUid;
                 Slog.w(TAG, msg);
                 throw new SecurityException(msg);
-            } else if (AppWidgetManager.ACTION_APPWIDGET_CONFIGURE.equals(action)) {
+
+            } else if (AppWidgetManager.ACTION_APPWIDGET_CONFIGURE.equals(action)
+                    || AppWidgetManager.ACTION_APPWIDGET_UPDATE.equals(action)) {
                 // Special case for compatibility: we don't want apps to send this,
                 // but historically it has not been protected and apps may be using it
                 // to poke their own app widget.  So, instead of making it protected,
@@ -20841,7 +20893,7 @@
                     throw new IllegalArgumentException("Bad app thread " + appThread);
                 }
             }
-            return mStackSupervisor.startActivityMayWait(appThread, -1, callingPackage, intent,
+            return mActivityStarter.startActivityMayWait(appThread, -1, callingPackage, intent,
                     resolvedType, null, null, null, null, 0, 0, null, null,
                     null, bOptions, false, callingUser, null, tr);
         }
diff --git a/services/core/java/com/android/server/am/ActivityRecord.java b/services/core/java/com/android/server/am/ActivityRecord.java
index b64aa0e..4d9120b 100755
--- a/services/core/java/com/android/server/am/ActivityRecord.java
+++ b/services/core/java/com/android/server/am/ActivityRecord.java
@@ -16,22 +16,21 @@
 
 package com.android.server.am;
 
-import static com.android.server.am.ActivityManagerDebugConfig.*;
+import static android.app.ActivityManager.StackId.FREEFORM_WORKSPACE_STACK_ID;
+import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_CONFIGURATION;
+import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_SAVED_STATE;
+import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_SWITCH;
+import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_THUMBNAILS;
+import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_STATES;
+import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_SWITCH;
+import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_THUMBNAILS;
+import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
+import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
 import static com.android.server.am.TaskRecord.INVALID_TASK_ID;
 
 import android.app.ActivityManager.TaskDescription;
-import android.app.PendingIntent;
-import android.os.PersistableBundle;
-import android.os.Trace;
-
-import com.android.internal.app.ResolverActivity;
-import com.android.internal.content.ReferrerIntent;
-import com.android.internal.util.XmlUtils;
-import com.android.server.AttributeCache;
-import com.android.server.am.ActivityStack.ActivityState;
-import com.android.server.am.ActivityStackSupervisor.ActivityContainer;
-
 import android.app.ActivityOptions;
+import android.app.PendingIntent;
 import android.app.ResultInfo;
 import android.content.ComponentName;
 import android.content.Intent;
@@ -45,9 +44,11 @@
 import android.os.Bundle;
 import android.os.IBinder;
 import android.os.Message;
+import android.os.PersistableBundle;
 import android.os.Process;
 import android.os.RemoteException;
 import android.os.SystemClock;
+import android.os.Trace;
 import android.os.UserHandle;
 import android.util.EventLog;
 import android.util.Log;
@@ -57,9 +58,12 @@
 import android.view.IApplicationToken;
 import android.view.WindowManager;
 
-import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlPullParserException;
-import org.xmlpull.v1.XmlSerializer;
+import com.android.internal.app.ResolverActivity;
+import com.android.internal.content.ReferrerIntent;
+import com.android.internal.util.XmlUtils;
+import com.android.server.AttributeCache;
+import com.android.server.am.ActivityStack.ActivityState;
+import com.android.server.am.ActivityStackSupervisor.ActivityContainer;
 
 import java.io.File;
 import java.io.IOException;
@@ -69,6 +73,10 @@
 import java.util.HashSet;
 import java.util.Objects;
 
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlSerializer;
+
 /**
  * An entry in the history stack, representing an activity.
  */
@@ -400,6 +408,11 @@
         }
     }
 
+    boolean isFreeform() {
+        return task != null && task.stack != null
+                && task.stack.mStackId == FREEFORM_WORKSPACE_STACK_ID;
+    }
+
     static class Token extends IApplicationToken.Stub {
         private final WeakReference<ActivityRecord> weakActivity;
         private final ActivityManagerService mService;
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index bfd17b2..1492e23 100644
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -28,6 +28,7 @@
 import static com.android.server.am.ActivityRecord.HOME_ACTIVITY_TYPE;
 import static com.android.server.am.ActivityRecord.APPLICATION_ACTIVITY_TYPE;
 
+import static com.android.server.am.ActivityStackSupervisor.FindTaskResult;
 import static com.android.server.am.ActivityStackSupervisor.MOVING;
 import static com.android.server.am.ActivityStackSupervisor.PRESERVE_WINDOWS;
 
@@ -74,7 +75,6 @@
 import android.os.UserHandle;
 import android.service.voice.IVoiceInteractionSession;
 import android.util.EventLog;
-import android.util.Log;
 import android.util.Slog;
 import android.view.Display;
 
@@ -519,6 +519,14 @@
         return mStackId == HOME_STACK_ID;
     }
 
+    final boolean isDockedStack() {
+        return mStackId == DOCKED_STACK_ID;
+    }
+
+    final boolean isPinnedStack() {
+        return mStackId == PINNED_STACK_ID;
+    }
+
     final boolean isOnHomeDisplay() {
         return isAttached() &&
                 mActivityContainer.mActivityDisplay.mDisplayId == Display.DEFAULT_DISPLAY;
@@ -569,10 +577,10 @@
     }
 
     /**
-     * Returns the top activity in any existing task matching the given
-     * Intent.  Returns null if no such task is found.
+     * Returns the top activity in any existing task matching the given Intent in the input result.
+     * Returns null if no such task is found.
      */
-    ActivityRecord findTaskLocked(ActivityRecord target) {
+    void findTaskLocked(ActivityRecord target, FindTaskResult result) {
         Intent intent = target.intent;
         ActivityInfo info = target.info;
         ComponentName cls = intent.getComponent();
@@ -627,10 +635,15 @@
                     + taskIntent.getComponent().flattenToShortString()
                     + "/aff=" + r.task.rootAffinity + " to new cls="
                     + intent.getComponent().flattenToShortString() + "/aff=" + info.taskAffinity);
-            if (!isDocument && !taskIsDocument && task.rootAffinity != null) {
+            if (!isDocument && !taskIsDocument
+                    && result.r == null && task.canMatchRootAffinity()) {
                 if (task.rootAffinity.equals(target.taskAffinity)) {
-                    if (DEBUG_TASKS) Slog.d(TAG_TASKS, "Found matching affinity!");
-                    return r;
+                    if (DEBUG_TASKS) Slog.d(TAG_TASKS, "Found matching affinity candidate!");
+                    // It is possible for multiple tasks to have the same root affinity especially
+                    // if they are in separate stacks. We save off this candidate, but keep looking
+                    // to see if there is a better candidate.
+                    result.r = r;
+                    result.matchedByRootAffinity = true;
                 }
             } else if (taskIntent != null && taskIntent.getComponent() != null &&
                     taskIntent.getComponent().compareTo(cls) == 0 &&
@@ -639,7 +652,9 @@
                 //dump();
                 if (DEBUG_TASKS) Slog.d(TAG_TASKS,
                         "For Intent " + intent + " bringing to top: " + r.intent);
-                return r;
+                result.r = r;
+                result.matchedByRootAffinity = false;
+                break;
             } else if (affinityIntent != null && affinityIntent.getComponent() != null &&
                     affinityIntent.getComponent().compareTo(cls) == 0 &&
                     Objects.equals(documentData, taskDocumentData)) {
@@ -647,11 +662,11 @@
                 //dump();
                 if (DEBUG_TASKS) Slog.d(TAG_TASKS,
                         "For Intent " + intent + " bringing to top: " + r.intent);
-                return r;
+                result.r = r;
+                result.matchedByRootAffinity = false;
+                break;
             } else if (DEBUG_TASKS) Slog.d(TAG_TASKS, "Not a match: " + task);
         }
-
-        return null;
     }
 
     /**
@@ -813,10 +828,9 @@
 
         if (hasVisibleBehindActivity()) {
             // Stop visible behind activity before going to sleep.
-            final ActivityRecord r = mActivityContainer.mActivityDisplay.mVisibleBehindActivity;
+            final ActivityRecord r = getVisibleBehindActivity();
             mStackSupervisor.mStoppingActivities.add(r);
-            if (DEBUG_STATES) Slog.v(TAG_STATES,
-                    "Sleep still waiting to stop visible behind " + r);
+            if (DEBUG_STATES) Slog.v(TAG_STATES, "Sleep still waiting to stop visible behind " + r);
             return true;
         }
 
@@ -1053,7 +1067,7 @@
             mHandler.removeMessages(STOP_TIMEOUT_MSG, r);
             r.stopped = true;
             r.state = ActivityState.STOPPED;
-            if (mActivityContainer.mActivityDisplay.mVisibleBehindActivity == r) {
+            if (getVisibleBehindActivity() == r) {
                 mStackSupervisor.requestVisibleBehindLocked(r, false);
             }
             if (r.finishing) {
@@ -1214,9 +1228,9 @@
 
         next.returningOptions = null;
 
-        if (mActivityContainer.mActivityDisplay.mVisibleBehindActivity == next) {
+        if (getVisibleBehindActivity() == next) {
             // When resuming an activity, require it to call requestVisibleBehind() again.
-            mActivityContainer.mActivityDisplay.setVisibleBehindActivity(null);
+            setVisibleBehindActivity(null);
         }
     }
 
@@ -3348,7 +3362,7 @@
                 try {
                     ActivityInfo aInfo = AppGlobals.getPackageManager().getActivityInfo(
                             destIntent.getComponent(), 0, srec.userId);
-                    int res = mStackSupervisor.startActivityLocked(srec.app.thread, destIntent,
+                    int res = mService.mActivityStarter.startActivityLocked(srec.app.thread, destIntent,
                             null /*ephemeralIntent*/, null, aInfo, null /*rInfo*/, null, null,
                             parent.appToken, null, 0, -1, parent.launchedFromUid,
                             parent.launchedFromPackage, -1, parent.launchedFromUid, 0, null,
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index 80d531e..32671acf 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -31,51 +31,37 @@
 import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
 import static android.app.ActivityManager.StackId.LAST_STATIC_STACK_ID;
 import static android.app.ActivityManager.StackId.PINNED_STACK_ID;
-import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TASK;
-import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TOP;
-import static android.content.Intent.FLAG_ACTIVITY_LAUNCH_TO_SIDE;
 import static android.content.Intent.FLAG_ACTIVITY_MULTIPLE_TASK;
 import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
-import static android.content.Intent.FLAG_ACTIVITY_TASK_ON_HOME;
 import static android.content.pm.ActivityInfo.FLAG_SHOW_FOR_ALL_USERS;
 import static android.content.pm.PackageManager.PERMISSION_GRANTED;
 import static android.os.Trace.TRACE_TAG_ACTIVITY_MANAGER;
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_ALL;
-import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_CONFIGURATION;
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_CONTAINERS;
-import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_FOCUS;
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_IDLE;
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_LOCKSCREEN;
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_LOCKTASK;
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_PAUSE;
-import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_PERMISSIONS_REVIEW;
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_RECENTS;
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_RELEASE;
-import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_RESULTS;
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_STACK;
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_STATES;
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_SWITCH;
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_TASKS;
-import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_USER_LEAVING;
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_VISIBLE_BEHIND;
-import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_CONFIGURATION;
 import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_CONTAINERS;
-import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_FOCUS;
 import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_IDLE;
 import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_LOCKTASK;
 import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_PAUSE;
 import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_RECENTS;
 import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_RELEASE;
-import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_RESULTS;
 import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_STACK;
 import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_STATES;
 import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_SWITCH;
 import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_TASKS;
-import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_USER_LEAVING;
 import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_VISIBLE_BEHIND;
 import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
 import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
-import static com.android.server.am.ActivityManagerService.ANIMATE;
 import static com.android.server.am.ActivityManagerService.FIRST_SUPERVISOR_STACK_MSG;
 import static com.android.server.am.ActivityRecord.APPLICATION_ACTIVITY_TYPE;
 import static com.android.server.am.ActivityRecord.HOME_ACTIVITY_TYPE;
@@ -107,9 +93,6 @@
 import android.app.IActivityContainerCallback;
 import android.app.IActivityManager;
 import android.app.IActivityManager.WaitResult;
-import android.app.IApplicationThread;
-import android.app.KeyguardManager;
-import android.app.PendingIntent;
 import android.app.ProfilerInfo;
 import android.app.ResultInfo;
 import android.app.StatusBarManager;
@@ -118,7 +101,6 @@
 import android.content.Context;
 import android.content.IIntentSender;
 import android.content.Intent;
-import android.content.IntentSender;
 import android.content.pm.ActivityInfo;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageInfo;
@@ -133,10 +115,7 @@
 import android.hardware.display.VirtualDisplay;
 import android.hardware.input.InputManager;
 import android.hardware.input.InputManagerInternal;
-import android.net.Uri;
 import android.os.Binder;
-import android.os.Build;
-import android.os.Bundle;
 import android.os.Debug;
 import android.os.Handler;
 import android.os.IBinder;
@@ -168,8 +147,6 @@
 import android.view.InputEvent;
 import android.view.Surface;
 
-import com.android.internal.app.HeavyWeightSwitcherActivity;
-import com.android.internal.app.IVoiceInteractor;
 import com.android.internal.content.ReferrerIntent;
 import com.android.internal.os.TransferPipe;
 import com.android.internal.statusbar.IStatusBarService;
@@ -189,21 +166,17 @@
 
 public final class ActivityStackSupervisor implements DisplayListener {
     private static final String TAG = TAG_WITH_CLASS_NAME ? "ActivityStackSupervisor" : TAG_AM;
-    private static final String TAG_CONFIGURATION = TAG + POSTFIX_CONFIGURATION;
     private static final String TAG_CONTAINERS = TAG + POSTFIX_CONTAINERS;
-    private static final String TAG_FOCUS = TAG + POSTFIX_FOCUS;
     private static final String TAG_IDLE = TAG + POSTFIX_IDLE;
     private static final String TAG_LOCKTASK = TAG + POSTFIX_LOCKTASK;
     private static final String TAG_PAUSE = TAG + POSTFIX_PAUSE;
     private static final String TAG_RECENTS = TAG + POSTFIX_RECENTS;
     private static final String TAG_RELEASE = TAG + POSTFIX_RELEASE;
-    private static final String TAG_RESULTS = TAG + POSTFIX_RESULTS;
     private static final String TAG_STACK = TAG + POSTFIX_STACK;
     private static final String TAG_STATES = TAG + POSTFIX_STATES;
     private static final String TAG_SWITCH = TAG + POSTFIX_SWITCH;
-    private static final String TAG_TASKS = TAG + POSTFIX_TASKS;
+    static final String TAG_TASKS = TAG + POSTFIX_TASKS;
     private static final String TAG_VISIBLE_BEHIND = TAG + POSTFIX_VISIBLE_BEHIND;
-    private static final String TAG_USER_LEAVING = TAG + POSTFIX_USER_LEAVING;
 
     /** How long we wait until giving up on the last activity telling us it is idle. */
     static final int IDLE_TIMEOUT = 10 * 1000;
@@ -235,7 +208,7 @@
 
     // Used to indicate if an object (e.g. stack) that we are trying to get
     // should be created if it doesn't exist already.
-    private static final boolean CREATE_IF_NEEDED = true;
+    static final boolean CREATE_IF_NEEDED = true;
 
     // Used to indicate that windows of activities should be preserved during the resize.
     static final boolean PRESERVE_WINDOWS = true;
@@ -308,14 +281,14 @@
     private int mCurTaskId = 0;
 
     /** The current user */
-    private int mCurrentUser;
+    int mCurrentUser;
 
     /** The stack containing the launcher app. Assumed to always be attached to
      * Display.DEFAULT_DISPLAY. */
-    private ActivityStack mHomeStack;
+    ActivityStack mHomeStack;
 
     /** The stack currently receiving input or launching the next activity. */
-    private ActivityStack mFocusedStack;
+    ActivityStack mFocusedStack;
 
     /** If this is the same as mFocusedStack then the activity on the top of the focused stack has
      * been resumed. If stacks are changing position this will hold the old stack until the new
@@ -397,8 +370,6 @@
      */
     private LockTaskNotify mLockTaskNotify;
 
-    final ArrayList<PendingActivityLaunch> mPendingActivityLaunches = new ArrayList<>();
-
     /** Used to keep resumeTopActivityLocked() from being entered recursively */
     boolean inResumeTopActivity;
 
@@ -418,6 +389,12 @@
 
     private final ActivityMetricsLogger mActivityMetricsLogger;
 
+    static class FindTaskResult {
+        ActivityRecord r;
+        boolean matchedByRootAffinity;
+    }
+    private final FindTaskResult mTmpFindTaskResult = new FindTaskResult();
+
     /**
      * Description of a request to start a new activity, which has been held
      * due to app switches being disabled.
@@ -1011,270 +988,6 @@
         return resolveActivity(intent, rInfo, startFlags, profilerInfo);
     }
 
-    void startHomeActivity(Intent intent, ActivityInfo aInfo, String reason) {
-        moveHomeStackTaskToTop(HOME_ACTIVITY_TYPE, reason);
-        startActivityLocked(null /*caller*/, intent, null /*ephemeralIntent*/,
-                null /*resolvedType*/, aInfo, null /*rInfo*/, null /*voiceSession*/,
-                null /*voiceInteractor*/, null /*resultTo*/, null /*resultWho*/,
-                0 /*requestCode*/, 0 /*callingPid*/, 0 /*callingUid*/, null /*callingPackage*/,
-                0 /*realCallingPid*/, 0 /*realCallingUid*/, 0 /*startFlags*/, null /*options*/,
-                false /*ignoreTargetSecurity*/, false /*componentSpecified*/,  null /*outActivity*/,
-                null /*container*/, null /*inTask*/);
-        if (inResumeTopActivity) {
-            // If we are in resume section already, home activity will be initialized, but not
-            // resumed (to avoid recursive resume) and will stay that way until something pokes it
-            // again. We need to schedule another resume.
-            scheduleResumeTopActivities();
-        }
-    }
-
-    final int startActivityMayWait(IApplicationThread caller, int callingUid,
-            String callingPackage, Intent intent, String resolvedType,
-            IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
-            IBinder resultTo, String resultWho, int requestCode, int startFlags,
-            ProfilerInfo profilerInfo, WaitResult outResult, Configuration config,
-            Bundle bOptions, boolean ignoreTargetSecurity, int userId,
-            IActivityContainer iContainer, TaskRecord inTask) {
-        // Refuse possible leaked file descriptors
-        if (intent != null && intent.hasFileDescriptors()) {
-            throw new IllegalArgumentException("File descriptors passed in Intent");
-        }
-        boolean componentSpecified = intent.getComponent() != null;
-
-        // Save a copy in case ephemeral needs it
-        final Intent ephemeralIntent = new Intent(intent);
-        // Don't modify the client's object!
-        intent = new Intent(intent);
-
-        ResolveInfo rInfo = resolveIntent(intent, resolvedType, userId);
-        // Collect information about the target of the Intent.
-        ActivityInfo aInfo = resolveActivity(intent, rInfo, startFlags, profilerInfo);
-
-        ActivityOptions options = ActivityOptions.fromBundle(bOptions);
-        ActivityContainer container = (ActivityContainer)iContainer;
-        synchronized (mService) {
-            if (container != null && container.mParentActivity != null &&
-                    container.mParentActivity.state != RESUMED) {
-                // Cannot start a child activity if the parent is not resumed.
-                return ActivityManager.START_CANCELED;
-            }
-            final int realCallingPid = Binder.getCallingPid();
-            final int realCallingUid = Binder.getCallingUid();
-            int callingPid;
-            if (callingUid >= 0) {
-                callingPid = -1;
-            } else if (caller == null) {
-                callingPid = realCallingPid;
-                callingUid = realCallingUid;
-            } else {
-                callingPid = callingUid = -1;
-            }
-
-            final ActivityStack stack;
-            if (container == null || container.mStack.isOnHomeDisplay()) {
-                stack = mFocusedStack;
-            } else {
-                stack = container.mStack;
-            }
-            stack.mConfigWillChange = config != null && mService.mConfiguration.diff(config) != 0;
-            if (DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION,
-                    "Starting activity when config will change = " + stack.mConfigWillChange);
-
-            final long origId = Binder.clearCallingIdentity();
-
-            if (aInfo != null &&
-                    (aInfo.applicationInfo.privateFlags
-                            &ApplicationInfo.PRIVATE_FLAG_CANT_SAVE_STATE) != 0) {
-                // This may be a heavy-weight process!  Check to see if we already
-                // have another, different heavy-weight process running.
-                if (aInfo.processName.equals(aInfo.applicationInfo.packageName)) {
-                    if (mService.mHeavyWeightProcess != null &&
-                            (mService.mHeavyWeightProcess.info.uid != aInfo.applicationInfo.uid ||
-                            !mService.mHeavyWeightProcess.processName.equals(aInfo.processName))) {
-                        int appCallingUid = callingUid;
-                        if (caller != null) {
-                            ProcessRecord callerApp = mService.getRecordForAppLocked(caller);
-                            if (callerApp != null) {
-                                appCallingUid = callerApp.info.uid;
-                            } else {
-                                Slog.w(TAG, "Unable to find app for caller " + caller
-                                      + " (pid=" + callingPid + ") when starting: "
-                                      + intent.toString());
-                                ActivityOptions.abort(options);
-                                return ActivityManager.START_PERMISSION_DENIED;
-                            }
-                        }
-
-                        IIntentSender target = mService.getIntentSenderLocked(
-                                ActivityManager.INTENT_SENDER_ACTIVITY, "android",
-                                appCallingUid, userId, null, null, 0, new Intent[] { intent },
-                                new String[] { resolvedType }, PendingIntent.FLAG_CANCEL_CURRENT
-                                | PendingIntent.FLAG_ONE_SHOT, null);
-
-                        Intent newIntent = new Intent();
-                        if (requestCode >= 0) {
-                            // Caller is requesting a result.
-                            newIntent.putExtra(HeavyWeightSwitcherActivity.KEY_HAS_RESULT, true);
-                        }
-                        newIntent.putExtra(HeavyWeightSwitcherActivity.KEY_INTENT,
-                                new IntentSender(target));
-                        if (mService.mHeavyWeightProcess.activities.size() > 0) {
-                            ActivityRecord hist = mService.mHeavyWeightProcess.activities.get(0);
-                            newIntent.putExtra(HeavyWeightSwitcherActivity.KEY_CUR_APP,
-                                    hist.packageName);
-                            newIntent.putExtra(HeavyWeightSwitcherActivity.KEY_CUR_TASK,
-                                    hist.task.taskId);
-                        }
-                        newIntent.putExtra(HeavyWeightSwitcherActivity.KEY_NEW_APP,
-                                aInfo.packageName);
-                        newIntent.setFlags(intent.getFlags());
-                        newIntent.setClassName("android",
-                                HeavyWeightSwitcherActivity.class.getName());
-                        intent = newIntent;
-                        resolvedType = null;
-                        caller = null;
-                        callingUid = Binder.getCallingUid();
-                        callingPid = Binder.getCallingPid();
-                        componentSpecified = true;
-                        rInfo = resolveIntent(intent, null /*resolvedType*/, userId);
-                        aInfo = rInfo != null ? rInfo.activityInfo : null;
-                        if (aInfo != null) {
-                            aInfo = mService.getActivityInfoForUser(aInfo, userId);
-                        }
-                    }
-                }
-            }
-
-            int res = startActivityLocked(caller, intent, ephemeralIntent, resolvedType,
-                    aInfo, rInfo, voiceSession, voiceInteractor,
-                    resultTo, resultWho, requestCode, callingPid,
-                    callingUid, callingPackage, realCallingPid, realCallingUid, startFlags,
-                    options, ignoreTargetSecurity, componentSpecified, null, container, inTask);
-
-            Binder.restoreCallingIdentity(origId);
-
-            if (stack.mConfigWillChange) {
-                // If the caller also wants to switch to a new configuration,
-                // do so now.  This allows a clean switch, as we are waiting
-                // for the current activity to pause (so we will not destroy
-                // it), and have not yet started the next activity.
-                mService.enforceCallingPermission(android.Manifest.permission.CHANGE_CONFIGURATION,
-                        "updateConfiguration()");
-                stack.mConfigWillChange = false;
-                if (DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION,
-                        "Updating to new configuration after starting activity.");
-                mService.updateConfigurationLocked(config, null, false);
-            }
-
-            if (outResult != null) {
-                outResult.result = res;
-                if (res == ActivityManager.START_SUCCESS) {
-                    mWaitingActivityLaunched.add(outResult);
-                    do {
-                        try {
-                            mService.wait();
-                        } catch (InterruptedException e) {
-                        }
-                    } while (!outResult.timeout && outResult.who == null);
-                } else if (res == ActivityManager.START_TASK_TO_FRONT) {
-                    ActivityRecord r = stack.topRunningActivityLocked();
-                    if (r.nowVisible && r.state == RESUMED) {
-                        outResult.timeout = false;
-                        outResult.who = new ComponentName(r.info.packageName, r.info.name);
-                        outResult.totalTime = 0;
-                        outResult.thisTime = 0;
-                    } else {
-                        outResult.thisTime = SystemClock.uptimeMillis();
-                        mWaitingActivityVisible.add(outResult);
-                        do {
-                            try {
-                                mService.wait();
-                            } catch (InterruptedException e) {
-                            }
-                        } while (!outResult.timeout && outResult.who == null);
-                    }
-                }
-            }
-
-            return res;
-        }
-    }
-
-    final int startActivities(IApplicationThread caller, int callingUid, String callingPackage,
-            Intent[] intents, String[] resolvedTypes, IBinder resultTo,
-            Bundle bOptions, int userId) {
-        if (intents == null) {
-            throw new NullPointerException("intents is null");
-        }
-        if (resolvedTypes == null) {
-            throw new NullPointerException("resolvedTypes is null");
-        }
-        if (intents.length != resolvedTypes.length) {
-            throw new IllegalArgumentException("intents are length different than resolvedTypes");
-        }
-
-
-        int callingPid;
-        if (callingUid >= 0) {
-            callingPid = -1;
-        } else if (caller == null) {
-            callingPid = Binder.getCallingPid();
-            callingUid = Binder.getCallingUid();
-        } else {
-            callingPid = callingUid = -1;
-        }
-        final long origId = Binder.clearCallingIdentity();
-        try {
-            synchronized (mService) {
-                ActivityRecord[] outActivity = new ActivityRecord[1];
-                for (int i=0; i<intents.length; i++) {
-                    Intent intent = intents[i];
-                    if (intent == null) {
-                        continue;
-                    }
-
-                    // Refuse possible leaked file descriptors
-                    if (intent != null && intent.hasFileDescriptors()) {
-                        throw new IllegalArgumentException("File descriptors passed in Intent");
-                    }
-
-                    boolean componentSpecified = intent.getComponent() != null;
-
-                    // Don't modify the client's object!
-                    intent = new Intent(intent);
-
-                    // Collect information about the target of the Intent.
-                    ActivityInfo aInfo = resolveActivity(intent, resolvedTypes[i], 0, null, userId);
-                    // TODO: New, check if this is correct
-                    aInfo = mService.getActivityInfoForUser(aInfo, userId);
-
-                    if (aInfo != null &&
-                            (aInfo.applicationInfo.privateFlags
-                                    & ApplicationInfo.PRIVATE_FLAG_CANT_SAVE_STATE)  != 0) {
-                        throw new IllegalArgumentException(
-                                "FLAG_CANT_SAVE_STATE not supported here");
-                    }
-
-                    ActivityOptions options = ActivityOptions.fromBundle(
-                            i == intents.length - 1 ? bOptions : null);
-                    int res = startActivityLocked(caller, intent, null /*ephemeralIntent*/,
-                            resolvedTypes[i], aInfo, null /*rInfo*/, null, null, resultTo, null, -1,
-                            callingPid, callingUid, callingPackage, callingPid, callingUid, 0,
-                            options, false, componentSpecified, outActivity, null, null);
-                    if (res < 0) {
-                        return res;
-                    }
-
-                    resultTo = outActivity[0] != null ? outActivity[0].appToken : null;
-                }
-            }
-        } finally {
-            Binder.restoreCallingIdentity(origId);
-        }
-
-        return ActivityManager.START_SUCCESS;
-    }
-
     final boolean realStartActivityLocked(ActivityRecord r,
             ProcessRecord app, boolean andResume, boolean checkConfig)
             throws RemoteException {
@@ -1494,348 +1207,7 @@
                 "activity", r.intent.getComponent(), false, false, true);
     }
 
-    final int startActivityLocked(IApplicationThread caller, Intent intent, Intent ephemeralIntent,
-            String resolvedType, ActivityInfo aInfo, ResolveInfo rInfo,
-            IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
-            IBinder resultTo, String resultWho, int requestCode, int callingPid, int callingUid,
-            String callingPackage, int realCallingPid, int realCallingUid, int startFlags,
-            ActivityOptions options, boolean ignoreTargetSecurity, boolean componentSpecified,
-            ActivityRecord[] outActivity, ActivityContainer container, TaskRecord inTask) {
-        int err = ActivityManager.START_SUCCESS;
-
-        ProcessRecord callerApp = null;
-        if (caller != null) {
-            callerApp = mService.getRecordForAppLocked(caller);
-            if (callerApp != null) {
-                callingPid = callerApp.pid;
-                callingUid = callerApp.info.uid;
-            } else {
-                Slog.w(TAG, "Unable to find app for caller " + caller
-                      + " (pid=" + callingPid + ") when starting: "
-                      + intent.toString());
-                err = ActivityManager.START_PERMISSION_DENIED;
-            }
-        }
-
-        final int userId = aInfo != null ? UserHandle.getUserId(aInfo.applicationInfo.uid) : 0;
-
-        if (err == ActivityManager.START_SUCCESS) {
-            Slog.i(TAG, "START u" + userId + " {" + intent.toShortString(true, true, true, false)
-                    + "} from uid " + callingUid
-                    + " on display " + (container == null ? (mFocusedStack == null ?
-                            Display.DEFAULT_DISPLAY : mFocusedStack.mDisplayId) :
-                            (container.mActivityDisplay == null ? Display.DEFAULT_DISPLAY :
-                                    container.mActivityDisplay.mDisplayId)));
-        }
-
-        ActivityRecord sourceRecord = null;
-        ActivityRecord resultRecord = null;
-        if (resultTo != null) {
-            sourceRecord = isInAnyStackLocked(resultTo);
-            if (DEBUG_RESULTS) Slog.v(TAG_RESULTS,
-                    "Will send result to " + resultTo + " " + sourceRecord);
-            if (sourceRecord != null) {
-                if (requestCode >= 0 && !sourceRecord.finishing) {
-                    resultRecord = sourceRecord;
-                }
-            }
-        }
-
-        final int launchFlags = intent.getFlags();
-
-        if ((launchFlags & Intent.FLAG_ACTIVITY_FORWARD_RESULT) != 0 && sourceRecord != null) {
-            // Transfer the result target from the source activity to the new
-            // one being started, including any failures.
-            if (requestCode >= 0) {
-                ActivityOptions.abort(options);
-                return ActivityManager.START_FORWARD_AND_REQUEST_CONFLICT;
-            }
-            resultRecord = sourceRecord.resultTo;
-            if (resultRecord != null && !resultRecord.isInStackLocked()) {
-                resultRecord = null;
-            }
-            resultWho = sourceRecord.resultWho;
-            requestCode = sourceRecord.requestCode;
-            sourceRecord.resultTo = null;
-            if (resultRecord != null) {
-                resultRecord.removeResultsLocked(sourceRecord, resultWho, requestCode);
-            }
-            if (sourceRecord.launchedFromUid == callingUid) {
-                // The new activity is being launched from the same uid as the previous
-                // activity in the flow, and asking to forward its result back to the
-                // previous.  In this case the activity is serving as a trampoline between
-                // the two, so we also want to update its launchedFromPackage to be the
-                // same as the previous activity.  Note that this is safe, since we know
-                // these two packages come from the same uid; the caller could just as
-                // well have supplied that same package name itself.  This specifially
-                // deals with the case of an intent picker/chooser being launched in the app
-                // flow to redirect to an activity picked by the user, where we want the final
-                // activity to consider it to have been launched by the previous app activity.
-                callingPackage = sourceRecord.launchedFromPackage;
-            }
-        }
-
-        if (err == ActivityManager.START_SUCCESS && intent.getComponent() == null) {
-            // We couldn't find a class that can handle the given Intent.
-            // That's the end of that!
-            err = ActivityManager.START_INTENT_NOT_RESOLVED;
-        }
-
-        if (err == ActivityManager.START_SUCCESS && aInfo == null) {
-            // We couldn't find the specific class specified in the Intent.
-            // Also the end of the line.
-            err = ActivityManager.START_CLASS_NOT_FOUND;
-        }
-
-        if (err == ActivityManager.START_SUCCESS && sourceRecord != null
-                && sourceRecord.task.voiceSession != null) {
-            // If this activity is being launched as part of a voice session, we need
-            // to ensure that it is safe to do so.  If the upcoming activity will also
-            // be part of the voice session, we can only launch it if it has explicitly
-            // said it supports the VOICE category, or it is a part of the calling app.
-            if ((launchFlags & FLAG_ACTIVITY_NEW_TASK) == 0
-                    && sourceRecord.info.applicationInfo.uid != aInfo.applicationInfo.uid) {
-                try {
-                    intent.addCategory(Intent.CATEGORY_VOICE);
-                    if (!AppGlobals.getPackageManager().activitySupportsIntent(
-                            intent.getComponent(), intent, resolvedType)) {
-                        Slog.w(TAG,
-                                "Activity being started in current voice task does not support voice: "
-                                + intent);
-                        err = ActivityManager.START_NOT_VOICE_COMPATIBLE;
-                    }
-                } catch (RemoteException e) {
-                    Slog.w(TAG, "Failure checking voice capabilities", e);
-                    err = ActivityManager.START_NOT_VOICE_COMPATIBLE;
-                }
-            }
-        }
-
-        if (err == ActivityManager.START_SUCCESS && voiceSession != null) {
-            // If the caller is starting a new voice session, just make sure the target
-            // is actually allowing it to run this way.
-            try {
-                if (!AppGlobals.getPackageManager().activitySupportsIntent(intent.getComponent(),
-                        intent, resolvedType)) {
-                    Slog.w(TAG,
-                            "Activity being started in new voice task does not support: "
-                            + intent);
-                    err = ActivityManager.START_NOT_VOICE_COMPATIBLE;
-                }
-            } catch (RemoteException e) {
-                Slog.w(TAG, "Failure checking voice capabilities", e);
-                err = ActivityManager.START_NOT_VOICE_COMPATIBLE;
-            }
-        }
-
-        final ActivityStack resultStack = resultRecord == null ? null : resultRecord.task.stack;
-
-        if (err != ActivityManager.START_SUCCESS) {
-            if (resultRecord != null) {
-                resultStack.sendActivityResultLocked(-1,
-                    resultRecord, resultWho, requestCode,
-                    Activity.RESULT_CANCELED, null);
-            }
-            ActivityOptions.abort(options);
-            return err;
-        }
-
-        boolean abort = !checkStartAnyActivityPermission(intent, aInfo, resultWho, requestCode,
-                callingPid, callingUid, callingPackage, ignoreTargetSecurity, callerApp,
-                resultRecord, resultStack);
-        abort |= !mService.mIntentFirewall.checkStartActivity(intent, callingUid,
-                callingPid, resolvedType, aInfo.applicationInfo);
-
-        if (mService.mController != null) {
-            try {
-                // The Intent we give to the watcher has the extra data
-                // stripped off, since it can contain private information.
-                Intent watchIntent = intent.cloneFilter();
-                abort |= !mService.mController.activityStarting(watchIntent,
-                        aInfo.applicationInfo.packageName);
-            } catch (RemoteException e) {
-                mService.mController = null;
-            }
-        }
-
-        UserInfo user = getUserInfo(userId);
-        KeyguardManager km = (KeyguardManager) mService.mContext
-                .getSystemService(Context.KEYGUARD_SERVICE);
-        if (user.isManagedProfile()
-                && LockPatternUtils.isSeparateWorkChallengeEnabled()
-                && km.isDeviceLocked(userId)) {
-            IIntentSender target = mService.getIntentSenderLocked(
-                    ActivityManager.INTENT_SENDER_ACTIVITY, callingPackage,
-                    Binder.getCallingUid(), userId, null, null, 0, new Intent[]{ intent },
-                    new String[]{ resolvedType },
-                    PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_ONE_SHOT
-                            | PendingIntent.FLAG_IMMUTABLE, null);
-            final int flags = intent.getFlags();
-            final Intent newIntent = km.createConfirmDeviceCredentialIntent(null, null, user.id);
-            if (newIntent != null) {
-                intent = newIntent;
-                intent.setFlags(flags
-                        | Intent.FLAG_ACTIVITY_NEW_TASK
-                        | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
-                intent.putExtra(Intent.EXTRA_PACKAGE_NAME, aInfo.packageName);
-                intent.putExtra(Intent.EXTRA_INTENT, new IntentSender(target));
-
-                resolvedType = null;
-                callingUid = realCallingUid;
-                callingPid = realCallingPid;
-
-                UserInfo parent = UserManager.get(mService.mContext).getProfileParent(userId);
-                rInfo = resolveIntent(intent, resolvedType, parent.id);
-                aInfo = resolveActivity(intent, rInfo, startFlags, null /*profilerInfo*/);
-            }
-        }
-
-        if (abort) {
-            if (resultRecord != null) {
-                resultStack.sendActivityResultLocked(-1, resultRecord, resultWho, requestCode,
-                        Activity.RESULT_CANCELED, null);
-            }
-            // We pretend to the caller that it was really started, but
-            // they will just get a cancel result.
-            ActivityOptions.abort(options);
-            return ActivityManager.START_SUCCESS;
-        }
-
-        // If permissions need a review before any of the app components can run, we
-        // launch the review activity and pass a pending intent to start the activity
-        // we are to launching now after the review is completed.
-        if (Build.PERMISSIONS_REVIEW_REQUIRED && aInfo != null) {
-            if (mService.getPackageManagerInternalLocked().isPermissionsReviewRequired(
-                    aInfo.packageName, userId)) {
-                IIntentSender target = mService.getIntentSenderLocked(
-                        ActivityManager.INTENT_SENDER_ACTIVITY, callingPackage,
-                        callingUid, userId, null, null, 0, new Intent[]{intent},
-                        new String[]{resolvedType}, PendingIntent.FLAG_CANCEL_CURRENT
-                                | PendingIntent.FLAG_ONE_SHOT, null);
-
-                final int flags = intent.getFlags();
-                Intent newIntent = new Intent(Intent.ACTION_REVIEW_PERMISSIONS);
-                newIntent.setFlags(flags
-                        | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
-                newIntent.putExtra(Intent.EXTRA_PACKAGE_NAME, aInfo.packageName);
-                newIntent.putExtra(Intent.EXTRA_INTENT, new IntentSender(target));
-                if (resultRecord != null) {
-                    newIntent.putExtra(Intent.EXTRA_RESULT_NEEDED, true);
-                }
-                intent = newIntent;
-
-                resolvedType = null;
-                callingUid = realCallingUid;
-                callingPid = realCallingPid;
-
-                rInfo = resolveIntent(intent, resolvedType, userId);
-                aInfo = resolveActivity(intent, rInfo, startFlags, null /*profilerInfo*/);
-
-                if (DEBUG_PERMISSIONS_REVIEW) {
-                    Slog.i(TAG, "START u" + userId + " {" + intent.toShortString(true, true,
-                            true, false) + "} from uid " + callingUid + " on display "
-                            + (container == null ? (mFocusedStack == null ?
-                            Display.DEFAULT_DISPLAY : mFocusedStack.mDisplayId) :
-                            (container.mActivityDisplay == null ? Display.DEFAULT_DISPLAY :
-                                    container.mActivityDisplay.mDisplayId)));
-                }
-            }
-        }
-
-        // If we have an ephemeral app, abort the process of launching the resolved intent.
-        // Instead, launch the ephemeral installer. Once the installer is finished, it
-        // starts either the intent we resolved here [on install error] or the ephemeral
-        // app [on install success].
-        if (rInfo != null && rInfo.ephemeralResolveInfo != null) {
-            // Create a pending intent to start the intent resolved here.
-            final IIntentSender failureTarget = mService.getIntentSenderLocked(
-                    ActivityManager.INTENT_SENDER_ACTIVITY, callingPackage,
-                    Binder.getCallingUid(), userId, null, null, 0, new Intent[]{ intent },
-                    new String[]{ resolvedType },
-                    PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_ONE_SHOT
-                            | PendingIntent.FLAG_IMMUTABLE, null);
-
-            // Create a pending intent to start the ephemeral application; force it to be
-            // directed to the ephemeral package.
-            ephemeralIntent.setPackage(rInfo.ephemeralResolveInfo.getPackageName());
-            final IIntentSender ephemeralTarget = mService.getIntentSenderLocked(
-                    ActivityManager.INTENT_SENDER_ACTIVITY, callingPackage,
-                    Binder.getCallingUid(), userId, null, null, 0, new Intent[]{ ephemeralIntent },
-                    new String[]{ resolvedType },
-                    PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_ONE_SHOT
-                    | PendingIntent.FLAG_IMMUTABLE, null);
-
-            int flags = intent.getFlags();
-            intent = new Intent();
-            intent.setFlags(flags
-                    | Intent.FLAG_ACTIVITY_NEW_TASK
-                    | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
-            intent.putExtra(Intent.EXTRA_PACKAGE_NAME,
-                    rInfo.ephemeralResolveInfo.getPackageName());
-            intent.putExtra(Intent.EXTRA_EPHEMERAL_FAILURE, new IntentSender(failureTarget));
-            intent.putExtra(Intent.EXTRA_EPHEMERAL_SUCCESS, new IntentSender(ephemeralTarget));
-
-            resolvedType = null;
-            callingUid = realCallingUid;
-            callingPid = realCallingPid;
-
-            rInfo = rInfo.ephemeralInstaller;
-            aInfo = resolveActivity(intent, rInfo, startFlags, null /*profilerInfo*/);
-        }
-
-        ActivityRecord r = new ActivityRecord(mService, callerApp, callingUid, callingPackage,
-                intent, resolvedType, aInfo, mService.mConfiguration, resultRecord, resultWho,
-                requestCode, componentSpecified, voiceSession != null, this, container, options);
-        if (outActivity != null) {
-            outActivity[0] = r;
-        }
-
-        if (r.appTimeTracker == null && sourceRecord != null) {
-            // If the caller didn't specify an explicit time tracker, we want to continue
-            // tracking under any it has.
-            r.appTimeTracker = sourceRecord.appTimeTracker;
-        }
-
-        final ActivityStack stack = mFocusedStack;
-        if (voiceSession == null && (stack.mResumedActivity == null
-                || stack.mResumedActivity.info.applicationInfo.uid != callingUid)) {
-            if (!mService.checkAppSwitchAllowedLocked(callingPid, callingUid,
-                    realCallingPid, realCallingUid, "Activity start")) {
-                PendingActivityLaunch pal =  new PendingActivityLaunch(r,
-                        sourceRecord, startFlags, stack, callerApp);
-                mPendingActivityLaunches.add(pal);
-                ActivityOptions.abort(options);
-                return ActivityManager.START_SWITCHES_CANCELED;
-            }
-        }
-
-        if (mService.mDidAppSwitch) {
-            // This is the second allowed switch since we stopped switches,
-            // so now just generally allow switches.  Use case: user presses
-            // home (switches disabled, switch to home, mDidAppSwitch now true);
-            // user taps a home icon (coming from home so allowed, we hit here
-            // and now allow anyone to switch again).
-            mService.mAppSwitchesAllowedTime = 0;
-        } else {
-            mService.mDidAppSwitch = true;
-        }
-
-        doPendingActivityLaunchesLocked(false);
-
-        err = startActivityUncheckedLocked(r, sourceRecord, voiceSession, voiceInteractor,
-                startFlags, true, options, inTask);
-
-        if (err < 0) {
-            // If someone asked to have the keyguard dismissed on the next
-            // activity start, but we are not actually doing an activity
-            // switch...  just dismiss the keyguard now, because we
-            // probably want to see whatever is behind it.
-            notifyActivityDrawnForKeyguard();
-        }
-        return err;
-    }
-
-    private boolean checkStartAnyActivityPermission(Intent intent, ActivityInfo aInfo,
+    boolean checkStartAnyActivityPermission(Intent intent, ActivityInfo aInfo,
             String resultWho, int requestCode, int callingPid, int callingUid,
             String callingPackage, boolean ignoreTargetSecurity, ProcessRecord callerApp,
             ActivityRecord resultRecord, ActivityStack resultStack) {
@@ -1895,7 +1267,7 @@
         return true;
     }
 
-    private UserInfo getUserInfo(int userId) {
+    UserInfo getUserInfo(int userId) {
         final long identity = Binder.clearCallingIdentity();
         try {
             return UserManager.get(mService.mContext).getUserInfo(userId);
@@ -1973,104 +1345,6 @@
         return ACTIVITY_RESTRICTION_NONE;
     }
 
-    private ActivityStack computeStackFocus(ActivityRecord r, boolean newTask, Rect bounds,
-            int launchFlags) {
-        final TaskRecord task = r.task;
-        if (!(r.isApplicationActivity() || (task != null && task.isApplicationTask()))) {
-            return mHomeStack;
-        }
-
-        ActivityStack stack = getLaunchToSideStack(r, launchFlags, task);
-        if (stack != null) {
-            return stack;
-        }
-
-        if (task != null && task.stack != null) {
-            stack = task.stack;
-            if (stack.isOnHomeDisplay()) {
-                if (mFocusedStack != stack) {
-                    if (DEBUG_FOCUS || DEBUG_STACK) Slog.d(TAG_FOCUS,
-                            "computeStackFocus: Setting " + "focused stack to r=" + r
-                                    + " task=" + task);
-                } else {
-                    if (DEBUG_FOCUS || DEBUG_STACK) Slog.d(TAG_FOCUS,
-                            "computeStackFocus: Focused stack already=" + mFocusedStack);
-                }
-            }
-            return stack;
-        }
-
-        final ActivityContainer container = r.mInitialActivityContainer;
-        if (container != null) {
-            // The first time put it on the desired stack, after this put on task stack.
-            r.mInitialActivityContainer = null;
-            return container.mStack;
-        }
-
-        // The fullscreen stack can contain any task regardless of if the task is resizeable
-        // or not. So, we let the task go in the fullscreen task if it is the focus stack.
-        // If the freeform or docked stack has focus, and the activity to be launched is resizeable,
-        // we can also put it in the focused stack.
-        final int focusedStackId = mFocusedStack.mStackId;
-        final boolean canUseFocusedStack =
-                focusedStackId == FULLSCREEN_WORKSPACE_STACK_ID
-                || focusedStackId == DOCKED_STACK_ID
-                || (focusedStackId == FREEFORM_WORKSPACE_STACK_ID && r.info.resizeable);
-        if (canUseFocusedStack
-                && (!newTask || mFocusedStack.mActivityContainer.isEligibleForNewTasks())) {
-            if (DEBUG_FOCUS || DEBUG_STACK) Slog.d(TAG_FOCUS,
-                    "computeStackFocus: Have a focused stack=" + mFocusedStack);
-            return mFocusedStack;
-        }
-
-        // We first try to put the task in the first dynamic stack.
-        final ArrayList<ActivityStack> homeDisplayStacks = mHomeStack.mStacks;
-        for (int stackNdx = homeDisplayStacks.size() - 1; stackNdx >= 0; --stackNdx) {
-            stack = homeDisplayStacks.get(stackNdx);
-            if (!StackId.isStaticStack(stack.mStackId)) {
-                if (DEBUG_FOCUS || DEBUG_STACK) Slog.d(TAG_FOCUS,
-                        "computeStackFocus: Setting focused stack=" + stack);
-                return stack;
-            }
-        }
-
-        // If there is no suitable dynamic stack then we figure out which static stack to use.
-        final int stackId = task != null ? task.getLaunchStackId() :
-                    bounds != null ? FREEFORM_WORKSPACE_STACK_ID :
-                                     FULLSCREEN_WORKSPACE_STACK_ID;
-        stack = getStack(stackId, CREATE_IF_NEEDED, ON_TOP);
-        if (DEBUG_FOCUS || DEBUG_STACK) Slog.d(TAG_FOCUS, "computeStackFocus: New stack r="
-                + r + " stackId=" + stack.mStackId);
-        return stack;
-    }
-
-    private ActivityStack getLaunchToSideStack(ActivityRecord r, int launchFlags, TaskRecord task) {
-        if ((launchFlags & FLAG_ACTIVITY_LAUNCH_TO_SIDE) == 0) {
-            return null;
-        }
-        // The parent activity doesn't want to launch the activity on top of itself, but
-        // instead tries to put it onto other side in side-by-side mode.
-        final ActivityStack parentStack = task != null ? task.stack
-                : r.mInitialActivityContainer != null ? r.mInitialActivityContainer.mStack
-                : mFocusedStack;
-        if (parentStack != null && parentStack.mStackId == DOCKED_STACK_ID) {
-            // If parent was in docked stack, the natural place to launch another activity
-            // will be fullscreen, so it can appear alongside the docked window.
-            return getStack(FULLSCREEN_WORKSPACE_STACK_ID, CREATE_IF_NEEDED, ON_TOP);
-        } else {
-            // If the parent is not in the docked stack, we check if there is docked window
-            // and if yes, we will launch into that stack. If not, we just put the new
-            // activity into parent's stack, because we can't find a better place.
-            final ActivityStack stack = getStack(DOCKED_STACK_ID);
-            if (stack != null && !stack.isStackVisibleLocked()) {
-                // There is a docked stack, but it isn't visible, so we can't launch into that.
-                return null;
-            } else {
-                return stack;
-            }
-        }
-    }
-
     boolean setFocusedStack(ActivityRecord r, String reason) {
         if (r == null) {
             // Not sure what you are trying to do, but it is not going to work...
@@ -2085,734 +1359,14 @@
         return true;
     }
 
-    final int startActivityUncheckedLocked(final ActivityRecord r, ActivityRecord sourceRecord,
-            IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor, int startFlags,
-            boolean doResume, ActivityOptions options, TaskRecord inTask) {
-        final Intent intent = r.intent;
-        final int callingUid = r.launchedFromUid;
-
-        boolean overrideBounds = false;
+    Rect getOverrideBounds(ActivityRecord r, ActivityOptions options, TaskRecord inTask) {
         Rect newBounds = null;
         if (options != null && (r.info.resizeable || (inTask != null && inTask.mResizeable))) {
             if (canUseActivityOptionsLaunchBounds(options)) {
-                overrideBounds = true;
                 newBounds = options.getLaunchBounds();
             }
         }
-
-        // In some flows in to this function, we retrieve the task record and hold on to it
-        // without a lock before calling back in to here...  so the task at this point may
-        // not actually be in recents.  Check for that, and if it isn't in recents just
-        // consider it invalid.
-        if (inTask != null && !inTask.inRecents) {
-            Slog.w(TAG, "Starting activity in task not in recents: " + inTask);
-            inTask = null;
-        }
-
-        final boolean launchSingleTop = r.launchMode == ActivityInfo.LAUNCH_SINGLE_TOP;
-        final boolean launchSingleInstance = r.launchMode == ActivityInfo.LAUNCH_SINGLE_INSTANCE;
-        final boolean launchSingleTask = r.launchMode == ActivityInfo.LAUNCH_SINGLE_TASK;
-
-        int launchFlags = intent.getFlags();
-        if ((launchFlags & Intent.FLAG_ACTIVITY_NEW_DOCUMENT) != 0 &&
-                (launchSingleInstance || launchSingleTask)) {
-            // We have a conflict between the Intent and the Activity manifest, manifest wins.
-            Slog.i(TAG, "Ignoring FLAG_ACTIVITY_NEW_DOCUMENT, launchMode is " +
-                    "\"singleInstance\" or \"singleTask\"");
-            launchFlags &=
-                    ~(Intent.FLAG_ACTIVITY_NEW_DOCUMENT | FLAG_ACTIVITY_MULTIPLE_TASK);
-        } else {
-            switch (r.info.documentLaunchMode) {
-                case ActivityInfo.DOCUMENT_LAUNCH_NONE:
-                    break;
-                case ActivityInfo.DOCUMENT_LAUNCH_INTO_EXISTING:
-                    launchFlags |= Intent.FLAG_ACTIVITY_NEW_DOCUMENT;
-                    break;
-                case ActivityInfo.DOCUMENT_LAUNCH_ALWAYS:
-                    launchFlags |= Intent.FLAG_ACTIVITY_NEW_DOCUMENT;
-                    break;
-                case ActivityInfo.DOCUMENT_LAUNCH_NEVER:
-                    launchFlags &= ~FLAG_ACTIVITY_MULTIPLE_TASK;
-                    break;
-            }
-        }
-
-        final boolean launchTaskBehind = r.mLaunchTaskBehind
-                && !launchSingleTask && !launchSingleInstance
-                && (launchFlags & Intent.FLAG_ACTIVITY_NEW_DOCUMENT) != 0;
-
-        if (r.resultTo != null && (launchFlags & FLAG_ACTIVITY_NEW_TASK) != 0
-                && r.resultTo.task.stack != null) {
-            // For whatever reason this activity is being launched into a new
-            // task...  yet the caller has requested a result back.  Well, that
-            // is pretty messed up, so instead immediately send back a cancel
-            // and let the new task continue launched as normal without a
-            // dependency on its originator.
-            Slog.w(TAG, "Activity is launching as a new task, so cancelling activity result.");
-            r.resultTo.task.stack.sendActivityResultLocked(-1,
-                    r.resultTo, r.resultWho, r.requestCode,
-                    Activity.RESULT_CANCELED, null);
-            r.resultTo = null;
-        }
-
-        if ((launchFlags & Intent.FLAG_ACTIVITY_NEW_DOCUMENT) != 0 && r.resultTo == null) {
-            launchFlags |= FLAG_ACTIVITY_NEW_TASK;
-        }
-
-        // If we are actually going to launch in to a new task, there are some cases where
-        // we further want to do multiple task.
-        if ((launchFlags & FLAG_ACTIVITY_NEW_TASK) != 0) {
-            if (launchTaskBehind
-                    || r.info.documentLaunchMode == ActivityInfo.DOCUMENT_LAUNCH_ALWAYS) {
-                launchFlags |= FLAG_ACTIVITY_MULTIPLE_TASK;
-            }
-        }
-
-        // We'll invoke onUserLeaving before onPause only if the launching
-        // activity did not explicitly state that this is an automated launch.
-        mUserLeaving = (launchFlags & Intent.FLAG_ACTIVITY_NO_USER_ACTION) == 0;
-        if (DEBUG_USER_LEAVING) Slog.v(TAG_USER_LEAVING,
-                "startActivity() => mUserLeaving=" + mUserLeaving);
-
-        // If the caller has asked not to resume at this point, we make note
-        // of this in the record so that we can skip it when trying to find
-        // the top running activity.
-        if (!doResume || !okToShowLocked(r)) {
-            r.delayedResume = true;
-            doResume = false;
-        }
-
-        ActivityRecord notTop =
-                (launchFlags & Intent.FLAG_ACTIVITY_PREVIOUS_IS_TOP) != 0 ? r : null;
-
-        // If the onlyIfNeeded flag is set, then we can do this if the activity
-        // being launched is the same as the one making the call...  or, as
-        // a special case, if we do not know the caller then we count the
-        // current top activity as the caller.
-        if ((startFlags&ActivityManager.START_FLAG_ONLY_IF_NEEDED) != 0) {
-            ActivityRecord checkedCaller = sourceRecord;
-            if (checkedCaller == null) {
-                checkedCaller = mFocusedStack.topRunningNonDelayedActivityLocked(notTop);
-            }
-            if (!checkedCaller.realActivity.equals(r.realActivity)) {
-                // Caller is not the same as launcher, so always needed.
-                startFlags &= ~ActivityManager.START_FLAG_ONLY_IF_NEEDED;
-            }
-        }
-
-        boolean addingToTask = false;
-        TaskRecord reuseTask = null;
-
-        // If the caller is not coming from another activity, but has given us an
-        // explicit task into which they would like us to launch the new activity,
-        // then let's see about doing that.
-        if (sourceRecord == null && inTask != null && inTask.stack != null) {
-            final Intent baseIntent = inTask.getBaseIntent();
-            final ActivityRecord root = inTask.getRootActivity();
-            if (baseIntent == null) {
-                ActivityOptions.abort(options);
-                throw new IllegalArgumentException("Launching into task without base intent: "
-                        + inTask);
-            }
-
-            // If this task is empty, then we are adding the first activity -- it
-            // determines the root, and must be launching as a NEW_TASK.
-            if (launchSingleInstance || launchSingleTask) {
-                if (!baseIntent.getComponent().equals(r.intent.getComponent())) {
-                    ActivityOptions.abort(options);
-                    throw new IllegalArgumentException("Trying to launch singleInstance/Task "
-                            + r + " into different task " + inTask);
-                }
-                if (root != null) {
-                    ActivityOptions.abort(options);
-                    throw new IllegalArgumentException("Caller with inTask " + inTask
-                            + " has root " + root + " but target is singleInstance/Task");
-                }
-            }
-
-            // If task is empty, then adopt the interesting intent launch flags in to the
-            // activity being started.
-            if (root == null) {
-                final int flagsOfInterest = FLAG_ACTIVITY_NEW_TASK
-                        | FLAG_ACTIVITY_MULTIPLE_TASK | Intent.FLAG_ACTIVITY_NEW_DOCUMENT
-                        | Intent.FLAG_ACTIVITY_RETAIN_IN_RECENTS;
-                launchFlags = (launchFlags&~flagsOfInterest)
-                        | (baseIntent.getFlags()&flagsOfInterest);
-                intent.setFlags(launchFlags);
-                inTask.setIntent(r);
-                addingToTask = true;
-
-            // If the task is not empty and the caller is asking to start it as the root
-            // of a new task, then we don't actually want to start this on the task.  We
-            // will bring the task to the front, and possibly give it a new intent.
-            } else if ((launchFlags & FLAG_ACTIVITY_NEW_TASK) != 0) {
-                addingToTask = false;
-
-            } else {
-                addingToTask = true;
-            }
-
-            reuseTask = inTask;
-        } else {
-            inTask = null;
-            // Launch ResolverActivity in the source task, so that it stays in the task
-            // bounds when in freeform workspace.
-            // Also put noDisplay activities in the source task. These by itself can
-            // be placed in any task/stack, however it could launch other activities
-            // like ResolverActivity, and we want those to stay in the original task.
-            if (r.isResolverActivity() || r.noDisplay) {
-                addingToTask = true;
-            }
-        }
-
-        if (inTask == null) {
-            if (sourceRecord == null) {
-                // This activity is not being started from another...  in this
-                // case we -always- start a new task.
-                if ((launchFlags & FLAG_ACTIVITY_NEW_TASK) == 0 && inTask == null) {
-                    Slog.w(TAG, "startActivity called from non-Activity context; forcing " +
-                            "Intent.FLAG_ACTIVITY_NEW_TASK for: " + intent);
-                    launchFlags |= FLAG_ACTIVITY_NEW_TASK;
-                }
-            } else if (sourceRecord.launchMode == ActivityInfo.LAUNCH_SINGLE_INSTANCE) {
-                // The original activity who is starting us is running as a single
-                // instance...  this new activity it is starting must go on its
-                // own task.
-                launchFlags |= FLAG_ACTIVITY_NEW_TASK;
-            } else if (launchSingleInstance || launchSingleTask) {
-                // The activity being started is a single instance...  it always
-                // gets launched into its own task.
-                launchFlags |= FLAG_ACTIVITY_NEW_TASK;
-            }
-        }
-
-        ActivityInfo newTaskInfo = null;
-        Intent newTaskIntent = null;
-        final ActivityStack sourceStack;
-        if (sourceRecord != null) {
-            if (sourceRecord.finishing) {
-                // If the source is finishing, we can't further count it as our source.  This
-                // is because the task it is associated with may now be empty and on its way out,
-                // so we don't want to blindly throw it in to that task.  Instead we will take
-                // the NEW_TASK flow and try to find a task for it. But save the task information
-                // so it can be used when creating the new task.
-                if ((launchFlags & FLAG_ACTIVITY_NEW_TASK) == 0) {
-                    Slog.w(TAG, "startActivity called from finishing " + sourceRecord
-                            + "; forcing " + "Intent.FLAG_ACTIVITY_NEW_TASK for: " + intent);
-                    launchFlags |= FLAG_ACTIVITY_NEW_TASK;
-                    newTaskInfo = sourceRecord.info;
-                    newTaskIntent = sourceRecord.task.intent;
-                }
-                sourceRecord = null;
-                sourceStack = null;
-            } else {
-                sourceStack = sourceRecord.task.stack;
-            }
-        } else {
-            sourceStack = null;
-        }
-
-        boolean movedHome = false;
-        ActivityStack targetStack;
-
-        intent.setFlags(launchFlags);
-        final boolean noAnimation = (launchFlags & Intent.FLAG_ACTIVITY_NO_ANIMATION) != 0;
-
-        ActivityRecord intentActivity = getReusableIntentActivity(r, inTask, intent,
-                launchSingleInstance, launchSingleTask, launchFlags);
-        if (intentActivity != null) {
-            // When the flags NEW_TASK and CLEAR_TASK are set, then the task gets reused
-            // but still needs to be a lock task mode violation since the task gets
-            // cleared out and the device would otherwise leave the locked task.
-            if (isLockTaskModeViolation(intentActivity.task,
-                    (launchFlags & (FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK))
-                    == (FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK))) {
-                showLockTaskToast();
-                Slog.e(TAG, "startActivityUnchecked: Attempt to violate Lock Task Mode");
-                return ActivityManager.START_RETURN_LOCK_TASK_MODE_VIOLATION;
-            }
-            if (r.task == null) {
-                r.task = intentActivity.task;
-            }
-            if (intentActivity.task.intent == null) {
-                // This task was started because of movement of the activity based on affinity...
-                // Now that we are actually launching it, we can assign the base intent.
-                intentActivity.task.setIntent(r);
-            }
-
-            targetStack = intentActivity.task.stack;
-            targetStack.mLastPausedActivity = null;
-            // If the target task is not in the front, then we need
-            // to bring it to the front...  except...  well, with
-            // SINGLE_TASK_LAUNCH it's not entirely clear.  We'd like
-            // to have the same behavior as if a new instance was
-            // being started, which means not bringing it to the front
-            // if the caller is not itself in the front.
-            final ActivityStack focusStack = getFocusedStack();
-            ActivityRecord curTop = (focusStack == null)
-                    ? null : focusStack.topRunningNonDelayedActivityLocked(notTop);
-            boolean movedToFront = false;
-            if (curTop != null && (curTop.task != intentActivity.task ||
-                    curTop.task != focusStack.topTask())) {
-                r.intent.addFlags(Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT);
-                if (sourceRecord == null || (sourceStack.topActivity() != null &&
-                        sourceStack.topActivity().task == sourceRecord.task)) {
-                    // We really do want to push this one into the user's face, right now.
-                    if (launchTaskBehind && sourceRecord != null) {
-                        intentActivity.setTaskToAffiliateWith(sourceRecord.task);
-                    }
-                    movedHome = true;
-                    final ActivityStack sideStack = getLaunchToSideStack(r, launchFlags, r.task);
-                    if (sideStack == null || sideStack == targetStack) {
-                        // We only want to move to the front, if we aren't going to launch on a
-                        // different stack. If we launch on a different stack, we will put the
-                        // task on top there.
-                        targetStack.moveTaskToFrontLocked(intentActivity.task, noAnimation,
-                        options, r.appTimeTracker, "bringingFoundTaskToFront");
-                        movedToFront = true;
-                    }
-                    if ((launchFlags & (FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_TASK_ON_HOME))
-                            == (FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_TASK_ON_HOME)) {
-                        // Caller wants to appear on home activity.
-                        intentActivity.task.setTaskToReturnTo(HOME_ACTIVITY_TYPE);
-                    }
-                    options = null;
-                }
-            }
-            if (!movedToFront && doResume) {
-                if (DEBUG_TASKS) Slog.d(TAG_TASKS, "Bring to front target: " + targetStack
-                        + " from " + intentActivity);
-                targetStack.moveToFront("intentActivityFound");
-            }
-
-            // If the caller has requested that the target task be
-            // reset, then do so.
-            if ((launchFlags & Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED) != 0) {
-                intentActivity = targetStack.resetTaskIfNeededLocked(intentActivity, r);
-            }
-            if ((startFlags & ActivityManager.START_FLAG_ONLY_IF_NEEDED) != 0) {
-                // We don't need to start a new activity, and
-                // the client said not to do anything if that
-                // is the case, so this is it!  And for paranoia, make
-                // sure we have correctly resumed the top activity.
-                if (doResume) {
-                    resumeTopActivitiesLocked(targetStack, null, options);
-
-                    // Make sure to notify Keyguard as well if we are not running an app
-                    // transition later.
-                    if (!movedToFront) {
-                        notifyActivityDrawnForKeyguard();
-                    }
-                } else {
-                    ActivityOptions.abort(options);
-                }
-                updateUserStackLocked(r.userId, targetStack);
-                return ActivityManager.START_RETURN_INTENT_TO_CALLER;
-            }
-            if ((launchFlags & (FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK))
-                    == (FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK)) {
-                // The caller has requested to completely replace any
-                // existing task with its new activity.  Well that should
-                // not be too hard...
-                reuseTask = intentActivity.task;
-                reuseTask.performClearTaskLocked();
-                reuseTask.setIntent(r);
-            } else if ((launchFlags & FLAG_ACTIVITY_CLEAR_TOP) != 0
-                    || launchSingleInstance || launchSingleTask) {
-                // In this situation we want to remove all activities
-                // from the task up to the one being started.  In most
-                // cases this means we are resetting the task to its
-                // initial state.
-                ActivityRecord top = intentActivity.task.performClearTaskLocked(r, launchFlags);
-                if (top != null) {
-                    if (top.frontOfTask) {
-                        // Activity aliases may mean we use different
-                        // intents for the top activity, so make sure
-                        // the task now has the identity of the new
-                        // intent.
-                        top.task.setIntent(r);
-                    }
-                    ActivityStack.logStartActivity(EventLogTags.AM_NEW_INTENT, r, top.task);
-                    top.deliverNewIntentLocked(callingUid, r.intent, r.launchedFromPackage);
-                } else {
-                    // A special case: we need to start the activity because it is not
-                    // currently running, and the caller has asked to clear the current
-                    // task to have this activity at the top.
-                    addingToTask = true;
-                    // Now pretend like this activity is being started by the top of its
-                    // task, so it is put in the right place.
-                    sourceRecord = intentActivity;
-                    TaskRecord task = sourceRecord.task;
-                    if (task != null && task.stack == null) {
-                        // Target stack got cleared when we all activities were removed
-                        // above. Go ahead and reset it.
-                        targetStack = computeStackFocus(
-                                sourceRecord, false /* newTask */, null /* bounds */, launchFlags);
-                        targetStack.addTask(task,
-                                !launchTaskBehind /* toTop */, "startActivityUnchecked");
-                    }
-
-                }
-            } else if (r.realActivity.equals(intentActivity.task.realActivity)) {
-                // In this case the top activity on the task is the
-                // same as the one being launched, so we take that
-                // as a request to bring the task to the foreground.
-                // If the top activity in the task is the root
-                // activity, deliver this new intent to it if it
-                // desires.
-                if (((launchFlags&Intent.FLAG_ACTIVITY_SINGLE_TOP) != 0 || launchSingleTop)
-                        && intentActivity.realActivity.equals(r.realActivity)) {
-                    ActivityStack.logStartActivity(EventLogTags.AM_NEW_INTENT, r,
-                            intentActivity.task);
-                    if (intentActivity.frontOfTask) {
-                        intentActivity.task.setIntent(r);
-                    }
-                    intentActivity.deliverNewIntentLocked(callingUid, r.intent,
-                            r.launchedFromPackage);
-                } else if (!r.intent.filterEquals(intentActivity.task.intent)) {
-                    // In this case we are launching the root activity
-                    // of the task, but with a different intent.  We
-                    // should start a new instance on top.
-                    addingToTask = true;
-                    sourceRecord = intentActivity;
-                }
-            } else if ((launchFlags&Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED) == 0) {
-                // In this case an activity is being launched in to an
-                // existing task, without resetting that task.  This
-                // is typically the situation of launching an activity
-                // from a notification or shortcut.  We want to place
-                // the new activity on top of the current task.
-                addingToTask = true;
-                sourceRecord = intentActivity;
-            } else if (!intentActivity.task.rootWasReset) {
-                // In this case we are launching in to an existing task
-                // that has not yet been started from its front door.
-                // The current task has been brought to the front.
-                // Ideally, we'd probably like to place this new task
-                // at the bottom of its stack, but that's a little hard
-                // to do with the current organization of the code so
-                // for now we'll just drop it.
-                intentActivity.task.setIntent(r);
-            }
-            if (!addingToTask && reuseTask == null) {
-                // We didn't do anything...  but it was needed (a.k.a., client
-                // don't use that intent!)  And for paranoia, make
-                // sure we have correctly resumed the top activity.
-                if (doResume) {
-                    targetStack.resumeTopActivityLocked(null, options);
-                    if (!movedToFront) {
-                        // Make sure to notify Keyguard as well if we are not running an app
-                        // transition later.
-                        notifyActivityDrawnForKeyguard();
-                    }
-                } else {
-                    ActivityOptions.abort(options);
-                }
-                updateUserStackLocked(r.userId, targetStack);
-                return ActivityManager.START_TASK_TO_FRONT;
-            }
-        }
-
-        //String uri = r.intent.toURI();
-        //Intent intent2 = new Intent(uri);
-        //Slog.i(TAG, "Given intent: " + r.intent);
-        //Slog.i(TAG, "URI is: " + uri);
-        //Slog.i(TAG, "To intent: " + intent2);
-
-        if (r.packageName != null) {
-            // If the activity being launched is the same as the one currently
-            // at the top, then we need to check if it should only be launched
-            // once.
-            ActivityStack topStack = mFocusedStack;
-            ActivityRecord top = topStack.topRunningNonDelayedActivityLocked(notTop);
-            final boolean dontStart = top != null && r.resultTo == null
-                    && top.realActivity.equals(r.realActivity) && top.userId == r.userId
-                    && top.app != null && top.app.thread != null
-                    && ((launchFlags & Intent.FLAG_ACTIVITY_SINGLE_TOP) != 0
-                            || launchSingleTop || launchSingleTask);
-            if (dontStart) {
-                ActivityStack.logStartActivity(EventLogTags.AM_NEW_INTENT, top, top.task);
-                // For paranoia, make sure we have correctly resumed the top activity.
-                topStack.mLastPausedActivity = null;
-                if (doResume) {
-                    resumeTopActivitiesLocked();
-                }
-                ActivityOptions.abort(options);
-                if ((startFlags & ActivityManager.START_FLAG_ONLY_IF_NEEDED) != 0) {
-                    // We don't need to start a new activity, and the client said not to do
-                    // anything if that is the case, so this is it!
-                    return ActivityManager.START_RETURN_INTENT_TO_CALLER;
-                }
-                top.deliverNewIntentLocked(callingUid, r.intent, r.launchedFromPackage);
-                return ActivityManager.START_DELIVERED_TO_TOP;
-            }
-        } else {
-            if (r.resultTo != null && r.resultTo.task.stack != null) {
-                r.resultTo.task.stack.sendActivityResultLocked(-1, r.resultTo, r.resultWho,
-                        r.requestCode, Activity.RESULT_CANCELED, null);
-            }
-            ActivityOptions.abort(options);
-            return ActivityManager.START_CLASS_NOT_FOUND;
-        }
-
-        boolean newTask = false;
-        boolean keepCurTransition = false;
-
-        TaskRecord taskToAffiliate = launchTaskBehind && sourceRecord != null ?
-                sourceRecord.task : null;
-
-        // Should this be considered a new task?
-        if (r.resultTo == null && inTask == null && !addingToTask
-                && (launchFlags & FLAG_ACTIVITY_NEW_TASK) != 0) {
-            newTask = true;
-            targetStack = computeStackFocus(r, newTask, newBounds, launchFlags);
-            if (doResume) {
-                targetStack.moveToFront("startingNewTask");
-            }
-
-            if (reuseTask == null) {
-                r.setTask(targetStack.createTaskRecord(getNextTaskId(),
-                        newTaskInfo != null ? newTaskInfo : r.info,
-                        newTaskIntent != null ? newTaskIntent : intent,
-                        voiceSession, voiceInteractor, !launchTaskBehind /* toTop */),
-                        taskToAffiliate);
-                if (overrideBounds) {
-                    r.task.updateOverrideConfiguration(newBounds);
-                }
-                if (DEBUG_TASKS) Slog.v(TAG_TASKS,
-                        "Starting new activity " + r + " in new task " + r.task);
-            } else {
-                r.setTask(reuseTask, taskToAffiliate);
-            }
-            if (isLockTaskModeViolation(r.task)) {
-                Slog.e(TAG, "Attempted Lock Task Mode violation r=" + r);
-                return ActivityManager.START_RETURN_LOCK_TASK_MODE_VIOLATION;
-            }
-            if (!movedHome) {
-                if ((launchFlags &
-                        (FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_TASK_ON_HOME))
-                        == (FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_TASK_ON_HOME)) {
-                    // Caller wants to appear on home activity, so before starting
-                    // their own activity we will bring home to the front.
-                    r.task.setTaskToReturnTo(HOME_ACTIVITY_TYPE);
-                }
-            }
-        } else if (sourceRecord != null) {
-            final TaskRecord sourceTask = sourceRecord.task;
-            if (isLockTaskModeViolation(sourceTask)) {
-                Slog.e(TAG, "Attempted Lock Task Mode violation r=" + r);
-                return ActivityManager.START_RETURN_LOCK_TASK_MODE_VIOLATION;
-            }
-            targetStack = null;
-            if (sourceTask.stack.topTask() != sourceTask) {
-                // We only want to allow changing stack if the target task is not the top one,
-                // otherwise we would move the launching task to the other side, rather than show
-                // two side by side.
-                targetStack = getLaunchToSideStack(r, launchFlags, r.task);
-            }
-            if (targetStack == null) {
-                targetStack = sourceTask.stack;
-            } else if (targetStack != sourceTask.stack) {
-                moveTaskToStackLocked(sourceTask.taskId, targetStack.mStackId, ON_TOP,
-                        FORCE_FOCUS, "launchToSide", !ANIMATE);
-            }
-            if (doResume) {
-                targetStack.moveToFront("sourceStackToFront");
-            }
-            final TaskRecord topTask = targetStack.topTask();
-            if (topTask != sourceTask) {
-                targetStack.moveTaskToFrontLocked(sourceTask, noAnimation, options,
-                        r.appTimeTracker, "sourceTaskToFront");
-            }
-            if (!addingToTask && (launchFlags&Intent.FLAG_ACTIVITY_CLEAR_TOP) != 0) {
-                // In this case, we are adding the activity to an existing
-                // task, but the caller has asked to clear that task if the
-                // activity is already running.
-                ActivityRecord top = sourceTask.performClearTaskLocked(r, launchFlags);
-                keepCurTransition = true;
-                if (top != null) {
-                    ActivityStack.logStartActivity(EventLogTags.AM_NEW_INTENT, r, top.task);
-                    top.deliverNewIntentLocked(callingUid, r.intent, r.launchedFromPackage);
-                    // For paranoia, make sure we have correctly
-                    // resumed the top activity.
-                    targetStack.mLastPausedActivity = null;
-                    if (doResume) {
-                        targetStack.resumeTopActivityLocked(null);
-                    }
-                    ActivityOptions.abort(options);
-                    return ActivityManager.START_DELIVERED_TO_TOP;
-                }
-            } else if (!addingToTask &&
-                    (launchFlags&Intent.FLAG_ACTIVITY_REORDER_TO_FRONT) != 0) {
-                // In this case, we are launching an activity in our own task
-                // that may already be running somewhere in the history, and
-                // we want to shuffle it to the front of the stack if so.
-                final ActivityRecord top = sourceTask.findActivityInHistoryLocked(r);
-                if (top != null) {
-                    final TaskRecord task = top.task;
-                    task.moveActivityToFrontLocked(top);
-                    ActivityStack.logStartActivity(EventLogTags.AM_NEW_INTENT, r, task);
-                    top.updateOptionsLocked(options);
-                    top.deliverNewIntentLocked(callingUid, r.intent, r.launchedFromPackage);
-                    targetStack.mLastPausedActivity = null;
-                    if (doResume) {
-                        targetStack.resumeTopActivityLocked(null);
-                    }
-                    return ActivityManager.START_DELIVERED_TO_TOP;
-                }
-            }
-            // An existing activity is starting this new activity, so we want
-            // to keep the new one in the same task as the one that is starting
-            // it.
-            r.setTask(sourceTask, null);
-            if (DEBUG_TASKS) Slog.v(TAG_TASKS, "Starting new activity " + r
-                    + " in existing task " + r.task + " from source " + sourceRecord);
-
-        } else if (inTask != null) {
-            // The caller is asking that the new activity be started in an explicit
-            // task it has provided to us.
-            if (isLockTaskModeViolation(inTask)) {
-                Slog.e(TAG, "Attempted Lock Task Mode violation r=" + r);
-                return ActivityManager.START_RETURN_LOCK_TASK_MODE_VIOLATION;
-            }
-            if (overrideBounds) {
-                inTask.updateOverrideConfiguration(newBounds);
-                int stackId = inTask.getLaunchStackId();
-                if (stackId != inTask.stack.mStackId) {
-                    moveTaskToStackUncheckedLocked(
-                            inTask, stackId, ON_TOP, !FORCE_FOCUS, "inTaskToFront");
-                }
-            }
-            targetStack = inTask.stack;
-            targetStack.moveTaskToFrontLocked(inTask, noAnimation, options, r.appTimeTracker,
-                    "inTaskToFront");
-
-            // Check whether we should actually launch the new activity in to the task,
-            // or just reuse the current activity on top.
-            ActivityRecord top = inTask.getTopActivity();
-            if (top != null && top.realActivity.equals(r.realActivity) && top.userId == r.userId) {
-                if ((launchFlags & Intent.FLAG_ACTIVITY_SINGLE_TOP) != 0
-                        || launchSingleTop || launchSingleTask) {
-                    ActivityStack.logStartActivity(EventLogTags.AM_NEW_INTENT, top, top.task);
-                    if ((startFlags&ActivityManager.START_FLAG_ONLY_IF_NEEDED) != 0) {
-                        // We don't need to start a new activity, and
-                        // the client said not to do anything if that
-                        // is the case, so this is it!
-                        return ActivityManager.START_RETURN_INTENT_TO_CALLER;
-                    }
-                    top.deliverNewIntentLocked(callingUid, r.intent, r.launchedFromPackage);
-                    return ActivityManager.START_DELIVERED_TO_TOP;
-                }
-            }
-
-            if (!addingToTask) {
-                // We don't actually want to have this activity added to the task, so just
-                // stop here but still tell the caller that we consumed the intent.
-                ActivityOptions.abort(options);
-                return ActivityManager.START_TASK_TO_FRONT;
-            }
-
-            r.setTask(inTask, null);
-            if (DEBUG_TASKS) Slog.v(TAG_TASKS, "Starting new activity " + r
-                    + " in explicit task " + r.task);
-
-        } else {
-            // This not being started from an existing activity, and not part
-            // of a new task...  just put it in the top task, though these days
-            // this case should never happen.
-            targetStack = computeStackFocus(r, newTask, null /* bounds */, launchFlags);
-            if (doResume) {
-                targetStack.moveToFront("addingToTopTask");
-            }
-            ActivityRecord prev = targetStack.topActivity();
-            r.setTask(prev != null ? prev.task : targetStack.createTaskRecord(getNextTaskId(),
-                            r.info, intent, null, null, true), null);
-            mWindowManager.moveTaskToTop(r.task.taskId);
-            if (DEBUG_TASKS) Slog.v(TAG_TASKS, "Starting new activity " + r
-                    + " in new guessed " + r.task);
-        }
-
-        mService.grantUriPermissionFromIntentLocked(callingUid, r.packageName,
-                intent, r.getUriPermissionsLocked(), r.userId);
-
-        if (sourceRecord != null && sourceRecord.isRecentsActivity()) {
-            r.task.setTaskToReturnTo(RECENTS_ACTIVITY_TYPE);
-        }
-        if (newTask) {
-            EventLog.writeEvent(EventLogTags.AM_CREATE_TASK, r.userId, r.task.taskId);
-        }
-        ActivityStack.logStartActivity(EventLogTags.AM_CREATE_ACTIVITY, r, r.task);
-        targetStack.mLastPausedActivity = null;
-        targetStack.startActivityLocked(r, newTask, keepCurTransition, options);
-        if (doResume) {
-            if (!launchTaskBehind) {
-                mService.setFocusedActivityLocked(r, "startedActivity");
-            }
-            resumeTopActivitiesLocked(targetStack, r, options);
-        } else {
-            targetStack.addRecentActivityLocked(r);
-        }
-        updateUserStackLocked(r.userId, targetStack);
-
-        if (!r.task.mResizeable && isStackDockedInEffect(targetStack.mStackId)) {
-            showNonResizeableDockToast(r.task.taskId);
-        }
-
-        return ActivityManager.START_SUCCESS;
-    }
-
-    /**
-     * Decide whether the new activity should be inserted into an existing task. Returns null if not
-     * or an ActivityRecord with the task into which the new activity should be added.
-     */
-    private ActivityRecord getReusableIntentActivity(ActivityRecord r, TaskRecord inTask, Intent intent,
-            boolean launchSingleInstance, boolean launchSingleTask, int launchFlags) {
-        // We may want to try to place the new activity in to an existing task.  We always
-        // do this if the target activity is singleTask or singleInstance; we will also do
-        // this if NEW_TASK has been requested, and there is not an additional qualifier telling
-        // us to still place it in a new task: multi task, always doc mode, or being asked to
-        // launch this as a new task behind the current one.
-        boolean putIntoExistingTask = ((launchFlags & FLAG_ACTIVITY_NEW_TASK) != 0 &&
-                (launchFlags & FLAG_ACTIVITY_MULTIPLE_TASK) == 0)
-                || launchSingleInstance || launchSingleTask;
-        // If bring to front is requested, and no result is requested and we have not
-        // been given an explicit task to launch in to, and
-        // we can find a task that was started with this same
-        // component, then instead of launching bring that one to the front.
-        putIntoExistingTask &= inTask == null && r.resultTo == null;
-        ActivityRecord intentActivity = null;
-        if (putIntoExistingTask) {
-            // See if there is a task to bring to the front.  If this is
-            // a SINGLE_INSTANCE activity, there can be one and only one
-            // instance of it in the history, and it is always in its own
-            // unique task, so we do a special search.
-            intentActivity = launchSingleInstance ?
-                    findActivityLocked(intent, r.info) : findTaskLocked(r);
-        }
-        return intentActivity;
-    }
-
-    final void doPendingActivityLaunchesLocked(boolean doResume) {
-        while (!mPendingActivityLaunches.isEmpty()) {
-            PendingActivityLaunch pal = mPendingActivityLaunches.remove(0);
-
-            try {
-                startActivityUncheckedLocked(pal.r, pal.sourceRecord, null, null, pal.startFlags,
-                        doResume && mPendingActivityLaunches.isEmpty(), null, null);
-            } catch (Exception e) {
-                Slog.e(TAG, "Exception during pending activity launch pal=" + pal, e);
-                pal.sendErrorResult(e.getMessage());
-            }
-        }
-    }
-
-    void removePendingActivityLaunchesLocked(ActivityStack stack) {
-        for (int palNdx = mPendingActivityLaunches.size() - 1; palNdx >= 0; --palNdx) {
-            PendingActivityLaunch pal = mPendingActivityLaunches.get(palNdx);
-            if (pal.stack == stack) {
-                mPendingActivityLaunches.remove(palNdx);
-            }
-        }
+        return newBounds;
     }
 
     void setLaunchSource(int uid) {
@@ -3640,7 +2194,7 @@
         }
 
         if (bounds != null) {
-            resizeStackLocked(PINNED_STACK_ID, bounds, !PRESERVE_WINDOWS, true);
+            resizeStackLocked(stackId, bounds, !PRESERVE_WINDOWS, true);
         }
 
         // The task might have already been running and its visibility needs to be synchronized with
@@ -3669,6 +2223,8 @@
     }
 
     ActivityRecord findTaskLocked(ActivityRecord r) {
+        mTmpFindTaskResult.r = null;
+        mTmpFindTaskResult.matchedByRootAffinity = false;
         if (DEBUG_TASKS) Slog.d(TAG_TASKS, "Looking for task of " + r);
         for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
             final ArrayList<ActivityStack> stacks = mActivityDisplays.valueAt(displayNdx).mStacks;
@@ -3683,14 +2239,18 @@
                             "Skipping stack: (new task not allowed) " + stack);
                     continue;
                 }
-                final ActivityRecord ar = stack.findTaskLocked(r);
-                if (ar != null) {
-                    return ar;
+                stack.findTaskLocked(r, mTmpFindTaskResult);
+                // It is possible to have task in multiple stacks with the same root affinity.
+                // If the match we found was based on root affinity we keep on looking to see if
+                // there is a better match in another stack. We eventually return the match based
+                // on root affinity if we don't find a better match.
+                if (mTmpFindTaskResult.r != null && !mTmpFindTaskResult.matchedByRootAffinity) {
+                    return mTmpFindTaskResult.r;
                 }
             }
         }
-        if (DEBUG_TASKS) Slog.d(TAG_TASKS, "No task found");
-        return null;
+        if (DEBUG_TASKS && mTmpFindTaskResult.r == null) Slog.d(TAG_TASKS, "No task found");
+        return mTmpFindTaskResult.r;
     }
 
     ActivityRecord findActivityLocked(Intent intent, ActivityInfo info) {
@@ -4538,7 +3098,7 @@
         }
     }
 
-    private void showNonResizeableDockToast(int taskId) {
+    void showNonResizeableDockToast(int taskId) {
         mWindowManager.scheduleShowNonResizeableDockToast(taskId);
     }
 
@@ -4955,7 +3515,7 @@
                 long origId = Binder.clearCallingIdentity();
                 try {
                     mStack.finishAllActivitiesLocked(false);
-                    removePendingActivityLaunchesLocked(mStack);
+                    mService.mActivityStarter.removePendingActivityLaunchesLocked(mStack);
                 } finally {
                     Binder.restoreCallingIdentity(origId);
                 }
@@ -4974,22 +3534,7 @@
 
         @Override
         public final int startActivity(Intent intent) {
-            mService.enforceNotIsolatedCaller("ActivityContainer.startActivity");
-            final int userId = mService.mUserController.handleIncomingUser(Binder.getCallingPid(),
-                    Binder.getCallingUid(), mCurrentUser, false,
-                    ActivityManagerService.ALLOW_FULL_ONLY, "ActivityContainer", null);
-
-            // TODO: Switch to user app stacks here.
-            String mimeType = intent.getType();
-            final Uri data = intent.getData();
-            if (mimeType == null && data != null && "content".equals(data.getScheme())) {
-                mimeType = mService.getProviderMimeType(data, userId);
-            }
-            checkEmbeddedAllowedInner(userId, intent, mimeType);
-
-            intent.addFlags(FORCE_NEW_TASK_FLAGS);
-            return startActivityMayWait(null, -1, null, intent, mimeType, null, null, null, null,
-                    0, 0, null, null, null, null, false, userId, this, null);
+            return mService.startActivity(intent, this);
         }
 
         @Override
@@ -5013,7 +3558,7 @@
                     FORCE_NEW_TASK_FLAGS, FORCE_NEW_TASK_FLAGS, null, this);
         }
 
-        private void checkEmbeddedAllowedInner(int userId, Intent intent, String resolvedType) {
+        void checkEmbeddedAllowedInner(int userId, Intent intent, String resolvedType) {
             ActivityInfo aInfo = resolveActivity(intent, resolvedType, 0, null, userId);
             if (aInfo != null && (aInfo.flags & ActivityInfo.FLAG_ALLOW_EMBEDDED) == 0) {
                 throw new SecurityException(
@@ -5170,7 +3715,7 @@
 
         /** All of the stacks on this display. Order matters, topmost stack is in front of all other
          * stacks, bottommost behind. Accessed directly by ActivityManager package classes */
-        final ArrayList<ActivityStack> mStacks = new ArrayList<ActivityStack>();
+        final ArrayList<ActivityStack> mStacks = new ArrayList<>();
 
         ActivityRecord mVisibleBehindActivity;
 
diff --git a/services/core/java/com/android/server/am/ActivityStarter.java b/services/core/java/com/android/server/am/ActivityStarter.java
new file mode 100644
index 0000000..b16e160
--- /dev/null
+++ b/services/core/java/com/android/server/am/ActivityStarter.java
@@ -0,0 +1,1545 @@
+package com.android.server.am;
+
+import static android.app.ActivityManager.StackId.DOCKED_STACK_ID;
+import static android.app.ActivityManager.StackId.FREEFORM_WORKSPACE_STACK_ID;
+import static android.app.ActivityManager.StackId.FULLSCREEN_WORKSPACE_STACK_ID;
+import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TASK;
+import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TOP;
+import static android.content.Intent.FLAG_ACTIVITY_LAUNCH_TO_SIDE;
+import static android.content.Intent.FLAG_ACTIVITY_MULTIPLE_TASK;
+import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
+import static android.content.Intent.FLAG_ACTIVITY_TASK_ON_HOME;
+import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_CONFIGURATION;
+import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_FOCUS;
+import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_PERMISSIONS_REVIEW;
+import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_RESULTS;
+import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_STACK;
+import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_TASKS;
+import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_USER_LEAVING;
+import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_CONFIGURATION;
+import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_FOCUS;
+import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_RESULTS;
+import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_USER_LEAVING;
+import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
+import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
+import static com.android.server.am.ActivityManagerService.ANIMATE;
+import static com.android.server.am.ActivityRecord.HOME_ACTIVITY_TYPE;
+import static com.android.server.am.ActivityRecord.RECENTS_ACTIVITY_TYPE;
+import static com.android.server.am.ActivityStack.ActivityState.RESUMED;
+import static com.android.server.am.ActivityStackSupervisor.CREATE_IF_NEEDED;
+import static com.android.server.am.ActivityStackSupervisor.FORCE_FOCUS;
+import static com.android.server.am.ActivityStackSupervisor.ON_TOP;
+import static com.android.server.am.ActivityStackSupervisor.TAG_TASKS;
+
+import android.app.Activity;
+import android.app.ActivityManager;
+import android.app.ActivityOptions;
+import android.app.AppGlobals;
+import android.app.IActivityContainer;
+import android.app.IActivityManager;
+import android.app.IApplicationThread;
+import android.app.KeyguardManager;
+import android.app.PendingIntent;
+import android.app.ProfilerInfo;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.IIntentSender;
+import android.content.Intent;
+import android.content.IntentSender;
+import android.content.pm.ActivityInfo;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.ResolveInfo;
+import android.content.pm.UserInfo;
+import android.content.res.Configuration;
+import android.graphics.Rect;
+import android.os.Binder;
+import android.os.Build;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.os.SystemClock;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.service.voice.IVoiceInteractionSession;
+import android.util.EventLog;
+import android.util.Slog;
+import android.view.Display;
+
+import com.android.internal.app.HeavyWeightSwitcherActivity;
+import com.android.internal.app.IVoiceInteractor;
+import com.android.internal.widget.LockPatternUtils;
+import com.android.server.am.ActivityStackSupervisor.PendingActivityLaunch;
+import com.android.server.wm.WindowManagerService;
+
+import java.util.ArrayList;
+
+/**
+ * Controller for interpreting how and then launching activities.
+ *
+ * This class collects all the logic for determining how an intent and flags should be turned into
+ * an activity and associated task and stack.
+ */
+public class ActivityStarter {
+    private static final String TAG = TAG_WITH_CLASS_NAME ? "ActivityStarter" : TAG_AM;
+    private static final String TAG_RESULTS = TAG + POSTFIX_RESULTS;
+    private static final String TAG_FOCUS = TAG + POSTFIX_FOCUS;
+    private static final String TAG_CONFIGURATION = TAG + POSTFIX_CONFIGURATION;
+    private static final String TAG_USER_LEAVING = TAG + POSTFIX_USER_LEAVING;
+
+    private final ActivityManagerService mService;
+    private final ActivityStackSupervisor mSupervisor;
+    private WindowManagerService mWindowManager;
+
+    final ArrayList<PendingActivityLaunch> mPendingActivityLaunches = new ArrayList<>();
+
+    ActivityStarter(ActivityManagerService service, ActivityStackSupervisor supervisor) {
+        mService = service;
+        mSupervisor = supervisor;
+    }
+
+    final int startActivityLocked(IApplicationThread caller, Intent intent, Intent ephemeralIntent,
+            String resolvedType, ActivityInfo aInfo, ResolveInfo rInfo,
+            IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
+            IBinder resultTo, String resultWho, int requestCode, int callingPid, int callingUid,
+            String callingPackage, int realCallingPid, int realCallingUid, int startFlags,
+            ActivityOptions options, boolean ignoreTargetSecurity, boolean componentSpecified,
+            ActivityRecord[] outActivity, ActivityStackSupervisor.ActivityContainer container,
+            TaskRecord inTask) {
+        int err = ActivityManager.START_SUCCESS;
+
+        ProcessRecord callerApp = null;
+        if (caller != null) {
+            callerApp = mService.getRecordForAppLocked(caller);
+            if (callerApp != null) {
+                callingPid = callerApp.pid;
+                callingUid = callerApp.info.uid;
+            } else {
+                Slog.w(TAG, "Unable to find app for caller " + caller
+                        + " (pid=" + callingPid + ") when starting: "
+                        + intent.toString());
+                err = ActivityManager.START_PERMISSION_DENIED;
+            }
+        }
+
+        final int userId = aInfo != null ? UserHandle.getUserId(aInfo.applicationInfo.uid) : 0;
+
+        if (err == ActivityManager.START_SUCCESS) {
+            Slog.i(TAG, "START u" + userId + " {" + intent.toShortString(true, true, true, false)
+                    + "} from uid " + callingUid
+                    + " on display " + (container == null ? (mSupervisor.mFocusedStack == null ?
+                    Display.DEFAULT_DISPLAY : mSupervisor.mFocusedStack.mDisplayId) :
+                    (container.mActivityDisplay == null ? Display.DEFAULT_DISPLAY :
+                            container.mActivityDisplay.mDisplayId)));
+        }
+
+        ActivityRecord sourceRecord = null;
+        ActivityRecord resultRecord = null;
+        if (resultTo != null) {
+            sourceRecord = mSupervisor.isInAnyStackLocked(resultTo);
+            if (DEBUG_RESULTS) Slog.v(TAG_RESULTS,
+                    "Will send result to " + resultTo + " " + sourceRecord);
+            if (sourceRecord != null) {
+                if (requestCode >= 0 && !sourceRecord.finishing) {
+                    resultRecord = sourceRecord;
+                }
+            }
+        }
+
+        final int launchFlags = intent.getFlags();
+
+        if ((launchFlags & Intent.FLAG_ACTIVITY_FORWARD_RESULT) != 0 && sourceRecord != null) {
+            // Transfer the result target from the source activity to the new
+            // one being started, including any failures.
+            if (requestCode >= 0) {
+                ActivityOptions.abort(options);
+                return ActivityManager.START_FORWARD_AND_REQUEST_CONFLICT;
+            }
+            resultRecord = sourceRecord.resultTo;
+            if (resultRecord != null && !resultRecord.isInStackLocked()) {
+                resultRecord = null;
+            }
+            resultWho = sourceRecord.resultWho;
+            requestCode = sourceRecord.requestCode;
+            sourceRecord.resultTo = null;
+            if (resultRecord != null) {
+                resultRecord.removeResultsLocked(sourceRecord, resultWho, requestCode);
+            }
+            if (sourceRecord.launchedFromUid == callingUid) {
+                // The new activity is being launched from the same uid as the previous
+                // activity in the flow, and asking to forward its result back to the
+                // previous.  In this case the activity is serving as a trampoline between
+                // the two, so we also want to update its launchedFromPackage to be the
+                // same as the previous activity.  Note that this is safe, since we know
+                // these two packages come from the same uid; the caller could just as
+                // well have supplied that same package name itself.  This specifially
+                // deals with the case of an intent picker/chooser being launched in the app
+                // flow to redirect to an activity picked by the user, where we want the final
+                // activity to consider it to have been launched by the previous app activity.
+                callingPackage = sourceRecord.launchedFromPackage;
+            }
+        }
+
+        if (err == ActivityManager.START_SUCCESS && intent.getComponent() == null) {
+            // We couldn't find a class that can handle the given Intent.
+            // That's the end of that!
+            err = ActivityManager.START_INTENT_NOT_RESOLVED;
+        }
+
+        if (err == ActivityManager.START_SUCCESS && aInfo == null) {
+            // We couldn't find the specific class specified in the Intent.
+            // Also the end of the line.
+            err = ActivityManager.START_CLASS_NOT_FOUND;
+        }
+
+        if (err == ActivityManager.START_SUCCESS && sourceRecord != null
+                && sourceRecord.task.voiceSession != null) {
+            // If this activity is being launched as part of a voice session, we need
+            // to ensure that it is safe to do so.  If the upcoming activity will also
+            // be part of the voice session, we can only launch it if it has explicitly
+            // said it supports the VOICE category, or it is a part of the calling app.
+            if ((launchFlags & FLAG_ACTIVITY_NEW_TASK) == 0
+                    && sourceRecord.info.applicationInfo.uid != aInfo.applicationInfo.uid) {
+                try {
+                    intent.addCategory(Intent.CATEGORY_VOICE);
+                    if (!AppGlobals.getPackageManager().activitySupportsIntent(
+                            intent.getComponent(), intent, resolvedType)) {
+                        Slog.w(TAG,
+                                "Activity being started in current voice task does not support voice: "
+                                        + intent);
+                        err = ActivityManager.START_NOT_VOICE_COMPATIBLE;
+                    }
+                } catch (RemoteException e) {
+                    Slog.w(TAG, "Failure checking voice capabilities", e);
+                    err = ActivityManager.START_NOT_VOICE_COMPATIBLE;
+                }
+            }
+        }
+
+        if (err == ActivityManager.START_SUCCESS && voiceSession != null) {
+            // If the caller is starting a new voice session, just make sure the target
+            // is actually allowing it to run this way.
+            try {
+                if (!AppGlobals.getPackageManager().activitySupportsIntent(intent.getComponent(),
+                        intent, resolvedType)) {
+                    Slog.w(TAG,
+                            "Activity being started in new voice task does not support: "
+                                    + intent);
+                    err = ActivityManager.START_NOT_VOICE_COMPATIBLE;
+                }
+            } catch (RemoteException e) {
+                Slog.w(TAG, "Failure checking voice capabilities", e);
+                err = ActivityManager.START_NOT_VOICE_COMPATIBLE;
+            }
+        }
+
+        final ActivityStack resultStack = resultRecord == null ? null : resultRecord.task.stack;
+
+        if (err != ActivityManager.START_SUCCESS) {
+            if (resultRecord != null) {
+                resultStack.sendActivityResultLocked(-1,
+                        resultRecord, resultWho, requestCode,
+                        Activity.RESULT_CANCELED, null);
+            }
+            ActivityOptions.abort(options);
+            return err;
+        }
+
+        boolean abort = !mSupervisor.checkStartAnyActivityPermission(intent, aInfo, resultWho,
+                requestCode, callingPid, callingUid, callingPackage, ignoreTargetSecurity, callerApp,
+                resultRecord, resultStack);
+        abort |= !mService.mIntentFirewall.checkStartActivity(intent, callingUid,
+                callingPid, resolvedType, aInfo.applicationInfo);
+
+        if (mService.mController != null) {
+            try {
+                // The Intent we give to the watcher has the extra data
+                // stripped off, since it can contain private information.
+                Intent watchIntent = intent.cloneFilter();
+                abort |= !mService.mController.activityStarting(watchIntent,
+                        aInfo.applicationInfo.packageName);
+            } catch (RemoteException e) {
+                mService.mController = null;
+            }
+        }
+
+        UserInfo user = mSupervisor.getUserInfo(userId);
+        KeyguardManager km = (KeyguardManager) mService.mContext
+                .getSystemService(Context.KEYGUARD_SERVICE);
+        if (user.isManagedProfile()
+                && LockPatternUtils.isSeparateWorkChallengeEnabled()
+                && km.isDeviceLocked(userId)) {
+            IIntentSender target = mService.getIntentSenderLocked(
+                    ActivityManager.INTENT_SENDER_ACTIVITY, callingPackage,
+                    Binder.getCallingUid(), userId, null, null, 0, new Intent[]{ intent },
+                    new String[]{ resolvedType },
+                    PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_ONE_SHOT
+                            | PendingIntent.FLAG_IMMUTABLE, null);
+            final int flags = intent.getFlags();
+            final Intent newIntent = km.createConfirmDeviceCredentialIntent(null, null, user.id);
+            if (newIntent != null) {
+                intent = newIntent;
+                intent.setFlags(flags
+                        | Intent.FLAG_ACTIVITY_NEW_TASK
+                        | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
+                intent.putExtra(Intent.EXTRA_PACKAGE_NAME, aInfo.packageName);
+                intent.putExtra(Intent.EXTRA_INTENT, new IntentSender(target));
+
+                resolvedType = null;
+                callingUid = realCallingUid;
+                callingPid = realCallingPid;
+
+                UserInfo parent = UserManager.get(mService.mContext).getProfileParent(userId);
+                rInfo = mSupervisor.resolveIntent(intent, resolvedType, parent.id);
+                aInfo = mSupervisor.resolveActivity(intent, rInfo, startFlags,
+                        null /*profilerInfo*/);
+            }
+        }
+
+        if (abort) {
+            if (resultRecord != null) {
+                resultStack.sendActivityResultLocked(-1, resultRecord, resultWho, requestCode,
+                        Activity.RESULT_CANCELED, null);
+            }
+            // We pretend to the caller that it was really started, but
+            // they will just get a cancel result.
+            ActivityOptions.abort(options);
+            return ActivityManager.START_SUCCESS;
+        }
+
+        // If permissions need a review before any of the app components can run, we
+        // launch the review activity and pass a pending intent to start the activity
+        // we are to launching now after the review is completed.
+        if (Build.PERMISSIONS_REVIEW_REQUIRED && aInfo != null) {
+            if (mService.getPackageManagerInternalLocked().isPermissionsReviewRequired(
+                    aInfo.packageName, userId)) {
+                IIntentSender target = mService.getIntentSenderLocked(
+                        ActivityManager.INTENT_SENDER_ACTIVITY, callingPackage,
+                        callingUid, userId, null, null, 0, new Intent[]{intent},
+                        new String[]{resolvedType}, PendingIntent.FLAG_CANCEL_CURRENT
+                                | PendingIntent.FLAG_ONE_SHOT, null);
+
+                final int flags = intent.getFlags();
+                Intent newIntent = new Intent(Intent.ACTION_REVIEW_PERMISSIONS);
+                newIntent.setFlags(flags
+                        | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
+                newIntent.putExtra(Intent.EXTRA_PACKAGE_NAME, aInfo.packageName);
+                newIntent.putExtra(Intent.EXTRA_INTENT, new IntentSender(target));
+                if (resultRecord != null) {
+                    newIntent.putExtra(Intent.EXTRA_RESULT_NEEDED, true);
+                }
+                intent = newIntent;
+
+                resolvedType = null;
+                callingUid = realCallingUid;
+                callingPid = realCallingPid;
+
+                rInfo = mSupervisor.resolveIntent(intent, resolvedType, userId);
+                aInfo = mSupervisor.resolveActivity(intent, rInfo, startFlags,
+                        null /*profilerInfo*/);
+
+                if (DEBUG_PERMISSIONS_REVIEW) {
+                    Slog.i(TAG, "START u" + userId + " {" + intent.toShortString(true, true,
+                            true, false) + "} from uid " + callingUid + " on display "
+                            + (container == null ? (mSupervisor.mFocusedStack == null ?
+                            Display.DEFAULT_DISPLAY : mSupervisor.mFocusedStack.mDisplayId) :
+                            (container.mActivityDisplay == null ? Display.DEFAULT_DISPLAY :
+                                    container.mActivityDisplay.mDisplayId)));
+                }
+            }
+        }
+
+        // If we have an ephemeral app, abort the process of launching the resolved intent.
+        // Instead, launch the ephemeral installer. Once the installer is finished, it
+        // starts either the intent we resolved here [on install error] or the ephemeral
+        // app [on install success].
+        if (rInfo != null && rInfo.ephemeralResolveInfo != null) {
+            // Create a pending intent to start the intent resolved here.
+            final IIntentSender failureTarget = mService.getIntentSenderLocked(
+                    ActivityManager.INTENT_SENDER_ACTIVITY, callingPackage,
+                    Binder.getCallingUid(), userId, null, null, 0, new Intent[]{ intent },
+                    new String[]{ resolvedType },
+                    PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_ONE_SHOT
+                            | PendingIntent.FLAG_IMMUTABLE, null);
+
+            // Create a pending intent to start the ephemeral application; force it to be
+            // directed to the ephemeral package.
+            ephemeralIntent.setPackage(rInfo.ephemeralResolveInfo.getPackageName());
+            final IIntentSender ephemeralTarget = mService.getIntentSenderLocked(
+                    ActivityManager.INTENT_SENDER_ACTIVITY, callingPackage,
+                    Binder.getCallingUid(), userId, null, null, 0, new Intent[]{ ephemeralIntent },
+                    new String[]{ resolvedType },
+                    PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_ONE_SHOT
+                            | PendingIntent.FLAG_IMMUTABLE, null);
+
+            int flags = intent.getFlags();
+            intent = new Intent();
+            intent.setFlags(flags
+                    | Intent.FLAG_ACTIVITY_NEW_TASK
+                    | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
+            intent.putExtra(Intent.EXTRA_PACKAGE_NAME,
+                    rInfo.ephemeralResolveInfo.getPackageName());
+            intent.putExtra(Intent.EXTRA_EPHEMERAL_FAILURE, new IntentSender(failureTarget));
+            intent.putExtra(Intent.EXTRA_EPHEMERAL_SUCCESS, new IntentSender(ephemeralTarget));
+
+            resolvedType = null;
+            callingUid = realCallingUid;
+            callingPid = realCallingPid;
+
+            rInfo = rInfo.ephemeralInstaller;
+            aInfo = mSupervisor.resolveActivity(intent, rInfo, startFlags, null /*profilerInfo*/);
+        }
+
+        ActivityRecord r = new ActivityRecord(mService, callerApp, callingUid, callingPackage,
+                intent, resolvedType, aInfo, mService.mConfiguration, resultRecord, resultWho,
+                requestCode, componentSpecified, voiceSession != null, mSupervisor, container,
+                options);
+        if (outActivity != null) {
+            outActivity[0] = r;
+        }
+
+        if (r.appTimeTracker == null && sourceRecord != null) {
+            // If the caller didn't specify an explicit time tracker, we want to continue
+            // tracking under any it has.
+            r.appTimeTracker = sourceRecord.appTimeTracker;
+        }
+
+        final ActivityStack stack = mSupervisor.mFocusedStack;
+        if (voiceSession == null && (stack.mResumedActivity == null
+                || stack.mResumedActivity.info.applicationInfo.uid != callingUid)) {
+            if (!mService.checkAppSwitchAllowedLocked(callingPid, callingUid,
+                    realCallingPid, realCallingUid, "Activity start")) {
+                PendingActivityLaunch pal =  new PendingActivityLaunch(r,
+                        sourceRecord, startFlags, stack, callerApp);
+                mPendingActivityLaunches.add(pal);
+                ActivityOptions.abort(options);
+                return ActivityManager.START_SWITCHES_CANCELED;
+            }
+        }
+
+        if (mService.mDidAppSwitch) {
+            // This is the second allowed switch since we stopped switches,
+            // so now just generally allow switches.  Use case: user presses
+            // home (switches disabled, switch to home, mDidAppSwitch now true);
+            // user taps a home icon (coming from home so allowed, we hit here
+            // and now allow anyone to switch again).
+            mService.mAppSwitchesAllowedTime = 0;
+        } else {
+            mService.mDidAppSwitch = true;
+        }
+
+        doPendingActivityLaunchesLocked(false);
+
+        err = startActivityUncheckedLocked(r, sourceRecord, voiceSession,
+                voiceInteractor, startFlags, true, options, inTask);
+
+        if (err < 0) {
+            // If someone asked to have the keyguard dismissed on the next
+            // activity start, but we are not actually doing an activity
+            // switch...  just dismiss the keyguard now, because we
+            // probably want to see whatever is behind it.
+            mSupervisor.notifyActivityDrawnForKeyguard();
+        }
+        return err;
+    }
+
+    void startHomeActivityLocked(Intent intent, ActivityInfo aInfo, String reason) {
+        mSupervisor.moveHomeStackTaskToTop(HOME_ACTIVITY_TYPE, reason);
+        startActivityLocked(null /*caller*/, intent, null /*ephemeralIntent*/,
+                null /*resolvedType*/, aInfo, null /*rInfo*/, null /*voiceSession*/,
+                null /*voiceInteractor*/, null /*resultTo*/, null /*resultWho*/,
+                0 /*requestCode*/, 0 /*callingPid*/, 0 /*callingUid*/, null /*callingPackage*/,
+                0 /*realCallingPid*/, 0 /*realCallingUid*/, 0 /*startFlags*/, null /*options*/,
+                false /*ignoreTargetSecurity*/, false /*componentSpecified*/,  null /*outActivity*/,
+                null /*container*/, null /*inTask*/);
+        if (mSupervisor.inResumeTopActivity) {
+            // If we are in resume section already, home activity will be initialized, but not
+            // resumed (to avoid recursive resume) and will stay that way until something pokes it
+            // again. We need to schedule another resume.
+            mSupervisor.scheduleResumeTopActivities();
+        }
+    }
+
+    final int startActivityMayWait(IApplicationThread caller, int callingUid,
+            String callingPackage, Intent intent, String resolvedType,
+            IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
+            IBinder resultTo, String resultWho, int requestCode, int startFlags,
+            ProfilerInfo profilerInfo, IActivityManager.WaitResult outResult, Configuration config,
+            Bundle bOptions, boolean ignoreTargetSecurity, int userId,
+            IActivityContainer iContainer, TaskRecord inTask) {
+        // Refuse possible leaked file descriptors
+        if (intent != null && intent.hasFileDescriptors()) {
+            throw new IllegalArgumentException("File descriptors passed in Intent");
+        }
+        boolean componentSpecified = intent.getComponent() != null;
+
+        // Save a copy in case ephemeral needs it
+        final Intent ephemeralIntent = new Intent(intent);
+        // Don't modify the client's object!
+        intent = new Intent(intent);
+
+        ResolveInfo rInfo = mSupervisor.resolveIntent(intent, resolvedType, userId);
+        // Collect information about the target of the Intent.
+        ActivityInfo aInfo = mSupervisor.resolveActivity(intent, rInfo, startFlags, profilerInfo);
+
+        ActivityOptions options = ActivityOptions.fromBundle(bOptions);
+        ActivityStackSupervisor.ActivityContainer container =
+                (ActivityStackSupervisor.ActivityContainer)iContainer;
+        synchronized (mService) {
+            if (container != null && container.mParentActivity != null &&
+                    container.mParentActivity.state != RESUMED) {
+                // Cannot start a child activity if the parent is not resumed.
+                return ActivityManager.START_CANCELED;
+            }
+            final int realCallingPid = Binder.getCallingPid();
+            final int realCallingUid = Binder.getCallingUid();
+            int callingPid;
+            if (callingUid >= 0) {
+                callingPid = -1;
+            } else if (caller == null) {
+                callingPid = realCallingPid;
+                callingUid = realCallingUid;
+            } else {
+                callingPid = callingUid = -1;
+            }
+
+            final ActivityStack stack;
+            if (container == null || container.mStack.isOnHomeDisplay()) {
+                stack = mSupervisor.mFocusedStack;
+            } else {
+                stack = container.mStack;
+            }
+            stack.mConfigWillChange = config != null && mService.mConfiguration.diff(config) != 0;
+            if (DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION,
+                    "Starting activity when config will change = " + stack.mConfigWillChange);
+
+            final long origId = Binder.clearCallingIdentity();
+
+            if (aInfo != null &&
+                    (aInfo.applicationInfo.privateFlags
+                            & ApplicationInfo.PRIVATE_FLAG_CANT_SAVE_STATE) != 0) {
+                // This may be a heavy-weight process!  Check to see if we already
+                // have another, different heavy-weight process running.
+                if (aInfo.processName.equals(aInfo.applicationInfo.packageName)) {
+                    final ProcessRecord heavy = mService.mHeavyWeightProcess;
+                    if (heavy != null && (heavy.info.uid != aInfo.applicationInfo.uid
+                            || !heavy.processName.equals(aInfo.processName))) {
+                        int appCallingUid = callingUid;
+                        if (caller != null) {
+                            ProcessRecord callerApp = mService.getRecordForAppLocked(caller);
+                            if (callerApp != null) {
+                                appCallingUid = callerApp.info.uid;
+                            } else {
+                                Slog.w(TAG, "Unable to find app for caller " + caller
+                                        + " (pid=" + callingPid + ") when starting: "
+                                        + intent.toString());
+                                ActivityOptions.abort(options);
+                                return ActivityManager.START_PERMISSION_DENIED;
+                            }
+                        }
+
+                        IIntentSender target = mService.getIntentSenderLocked(
+                                ActivityManager.INTENT_SENDER_ACTIVITY, "android",
+                                appCallingUid, userId, null, null, 0, new Intent[] { intent },
+                                new String[] { resolvedType }, PendingIntent.FLAG_CANCEL_CURRENT
+                                        | PendingIntent.FLAG_ONE_SHOT, null);
+
+                        Intent newIntent = new Intent();
+                        if (requestCode >= 0) {
+                            // Caller is requesting a result.
+                            newIntent.putExtra(HeavyWeightSwitcherActivity.KEY_HAS_RESULT, true);
+                        }
+                        newIntent.putExtra(HeavyWeightSwitcherActivity.KEY_INTENT,
+                                new IntentSender(target));
+                        if (heavy.activities.size() > 0) {
+                            ActivityRecord hist = heavy.activities.get(0);
+                            newIntent.putExtra(HeavyWeightSwitcherActivity.KEY_CUR_APP,
+                                    hist.packageName);
+                            newIntent.putExtra(HeavyWeightSwitcherActivity.KEY_CUR_TASK,
+                                    hist.task.taskId);
+                        }
+                        newIntent.putExtra(HeavyWeightSwitcherActivity.KEY_NEW_APP,
+                                aInfo.packageName);
+                        newIntent.setFlags(intent.getFlags());
+                        newIntent.setClassName("android",
+                                HeavyWeightSwitcherActivity.class.getName());
+                        intent = newIntent;
+                        resolvedType = null;
+                        caller = null;
+                        callingUid = Binder.getCallingUid();
+                        callingPid = Binder.getCallingPid();
+                        componentSpecified = true;
+                        rInfo = mSupervisor.resolveIntent(intent, null /*resolvedType*/, userId);
+                        aInfo = rInfo != null ? rInfo.activityInfo : null;
+                        if (aInfo != null) {
+                            aInfo = mService.getActivityInfoForUser(aInfo, userId);
+                        }
+                    }
+                }
+            }
+
+            int res = startActivityLocked(caller, intent, ephemeralIntent, resolvedType,
+                    aInfo, rInfo, voiceSession, voiceInteractor,
+                    resultTo, resultWho, requestCode, callingPid,
+                    callingUid, callingPackage, realCallingPid, realCallingUid, startFlags,
+                    options, ignoreTargetSecurity, componentSpecified, null, container, inTask);
+
+            Binder.restoreCallingIdentity(origId);
+
+            if (stack.mConfigWillChange) {
+                // If the caller also wants to switch to a new configuration,
+                // do so now.  This allows a clean switch, as we are waiting
+                // for the current activity to pause (so we will not destroy
+                // it), and have not yet started the next activity.
+                mService.enforceCallingPermission(android.Manifest.permission.CHANGE_CONFIGURATION,
+                        "updateConfiguration()");
+                stack.mConfigWillChange = false;
+                if (DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION,
+                        "Updating to new configuration after starting activity.");
+                mService.updateConfigurationLocked(config, null, false);
+            }
+
+            if (outResult != null) {
+                outResult.result = res;
+                if (res == ActivityManager.START_SUCCESS) {
+                    mSupervisor.mWaitingActivityLaunched.add(outResult);
+                    do {
+                        try {
+                            mService.wait();
+                        } catch (InterruptedException e) {
+                        }
+                    } while (!outResult.timeout && outResult.who == null);
+                } else if (res == ActivityManager.START_TASK_TO_FRONT) {
+                    ActivityRecord r = stack.topRunningActivityLocked();
+                    if (r.nowVisible && r.state == RESUMED) {
+                        outResult.timeout = false;
+                        outResult.who = new ComponentName(r.info.packageName, r.info.name);
+                        outResult.totalTime = 0;
+                        outResult.thisTime = 0;
+                    } else {
+                        outResult.thisTime = SystemClock.uptimeMillis();
+                        mSupervisor.mWaitingActivityVisible.add(outResult);
+                        do {
+                            try {
+                                mService.wait();
+                            } catch (InterruptedException e) {
+                            }
+                        } while (!outResult.timeout && outResult.who == null);
+                    }
+                }
+            }
+
+            return res;
+        }
+    }
+
+    final int startActivityUncheckedLocked(final ActivityRecord r, ActivityRecord sourceRecord,
+            IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor, int startFlags,
+            boolean doResume, ActivityOptions options, TaskRecord inTask) {
+        final Intent intent = r.intent;
+        final int callingUid = r.launchedFromUid;
+
+        final Rect newBounds = mSupervisor.getOverrideBounds(r, options, inTask);
+        final boolean overrideBounds = newBounds != null;
+
+        // In some flows in to this function, we retrieve the task record and hold on to it
+        // without a lock before calling back in to here...  so the task at this point may
+        // not actually be in recents.  Check for that, and if it isn't in recents just
+        // consider it invalid.
+        if (inTask != null && !inTask.inRecents) {
+            Slog.w(TAG, "Starting activity in task not in recents: " + inTask);
+            inTask = null;
+        }
+
+        final boolean launchSingleTop = r.launchMode == ActivityInfo.LAUNCH_SINGLE_TOP;
+        final boolean launchSingleInstance = r.launchMode == ActivityInfo.LAUNCH_SINGLE_INSTANCE;
+        final boolean launchSingleTask = r.launchMode == ActivityInfo.LAUNCH_SINGLE_TASK;
+        int launchFlags = adjustLaunchFlagsToDocumentMode(r, launchSingleInstance, launchSingleTask,
+                intent.getFlags());
+        final boolean launchTaskBehind = r.mLaunchTaskBehind
+                && !launchSingleTask && !launchSingleInstance
+                && (launchFlags & Intent.FLAG_ACTIVITY_NEW_DOCUMENT) != 0;
+
+        if (r.resultTo != null && (launchFlags & FLAG_ACTIVITY_NEW_TASK) != 0
+                && r.resultTo.task.stack != null) {
+            // For whatever reason this activity is being launched into a new
+            // task...  yet the caller has requested a result back.  Well, that
+            // is pretty messed up, so instead immediately send back a cancel
+            // and let the new task continue launched as normal without a
+            // dependency on its originator.
+            Slog.w(TAG, "Activity is launching as a new task, so cancelling activity result.");
+            r.resultTo.task.stack.sendActivityResultLocked(-1,
+                    r.resultTo, r.resultWho, r.requestCode,
+                    Activity.RESULT_CANCELED, null);
+            r.resultTo = null;
+        }
+
+        if ((launchFlags & Intent.FLAG_ACTIVITY_NEW_DOCUMENT) != 0 && r.resultTo == null) {
+            launchFlags |= FLAG_ACTIVITY_NEW_TASK;
+        }
+
+        // If we are actually going to launch in to a new task, there are some cases where
+        // we further want to do multiple task.
+        if ((launchFlags & FLAG_ACTIVITY_NEW_TASK) != 0) {
+            if (launchTaskBehind
+                    || r.info.documentLaunchMode == ActivityInfo.DOCUMENT_LAUNCH_ALWAYS) {
+                launchFlags |= FLAG_ACTIVITY_MULTIPLE_TASK;
+            }
+        }
+
+        // We'll invoke onUserLeaving before onPause only if the launching
+        // activity did not explicitly state that this is an automated launch.
+        mSupervisor.mUserLeaving = (launchFlags & Intent.FLAG_ACTIVITY_NO_USER_ACTION) == 0;
+        if (DEBUG_USER_LEAVING) Slog.v(TAG_USER_LEAVING,
+                "startActivity() => mUserLeaving=" + mSupervisor.mUserLeaving);
+
+        // If the caller has asked not to resume at this point, we make note
+        // of this in the record so that we can skip it when trying to find
+        // the top running activity.
+        if (!doResume || !mSupervisor.okToShowLocked(r)) {
+            r.delayedResume = true;
+            doResume = false;
+        }
+
+        final ActivityRecord notTop =
+                (launchFlags & Intent.FLAG_ACTIVITY_PREVIOUS_IS_TOP) != 0 ? r : null;
+
+        // If the onlyIfNeeded flag is set, then we can do this if the activity
+        // being launched is the same as the one making the call...  or, as
+        // a special case, if we do not know the caller then we count the
+        // current top activity as the caller.
+        if ((startFlags&ActivityManager.START_FLAG_ONLY_IF_NEEDED) != 0) {
+            ActivityRecord checkedCaller = sourceRecord;
+            if (checkedCaller == null) {
+                checkedCaller = mSupervisor.mFocusedStack.topRunningNonDelayedActivityLocked(
+                        notTop);
+            }
+            if (!checkedCaller.realActivity.equals(r.realActivity)) {
+                // Caller is not the same as launcher, so always needed.
+                startFlags &= ~ActivityManager.START_FLAG_ONLY_IF_NEEDED;
+            }
+        }
+
+        boolean addingToTask = false;
+        TaskRecord reuseTask = null;
+
+        // If the caller is not coming from another activity, but has given us an
+        // explicit task into which they would like us to launch the new activity,
+        // then let's see about doing that.
+        if (sourceRecord == null && inTask != null && inTask.stack != null) {
+            final Intent baseIntent = inTask.getBaseIntent();
+            final ActivityRecord root = inTask.getRootActivity();
+            if (baseIntent == null) {
+                ActivityOptions.abort(options);
+                throw new IllegalArgumentException("Launching into task without base intent: "
+                        + inTask);
+            }
+
+            // If this task is empty, then we are adding the first activity -- it
+            // determines the root, and must be launching as a NEW_TASK.
+            if (launchSingleInstance || launchSingleTask) {
+                if (!baseIntent.getComponent().equals(r.intent.getComponent())) {
+                    ActivityOptions.abort(options);
+                    throw new IllegalArgumentException("Trying to launch singleInstance/Task "
+                            + r + " into different task " + inTask);
+                }
+                if (root != null) {
+                    ActivityOptions.abort(options);
+                    throw new IllegalArgumentException("Caller with inTask " + inTask
+                            + " has root " + root + " but target is singleInstance/Task");
+                }
+            }
+
+            // If task is empty, then adopt the interesting intent launch flags in to the
+            // activity being started.
+            if (root == null) {
+                final int flagsOfInterest = FLAG_ACTIVITY_NEW_TASK
+                        | FLAG_ACTIVITY_MULTIPLE_TASK | Intent.FLAG_ACTIVITY_NEW_DOCUMENT
+                        | Intent.FLAG_ACTIVITY_RETAIN_IN_RECENTS;
+                launchFlags = (launchFlags&~flagsOfInterest)
+                        | (baseIntent.getFlags()&flagsOfInterest);
+                intent.setFlags(launchFlags);
+                inTask.setIntent(r);
+                addingToTask = true;
+
+                // If the task is not empty and the caller is asking to start it as the root
+                // of a new task, then we don't actually want to start this on the task.  We
+                // will bring the task to the front, and possibly give it a new intent.
+            } else if ((launchFlags & FLAG_ACTIVITY_NEW_TASK) != 0) {
+                addingToTask = false;
+
+            } else {
+                addingToTask = true;
+            }
+
+            reuseTask = inTask;
+        } else {
+            inTask = null;
+            // Launch ResolverActivity in the source task, so that it stays in the task
+            // bounds when in freeform workspace.
+            // Also put noDisplay activities in the source task. These by itself can
+            // be placed in any task/stack, however it could launch other activities
+            // like ResolverActivity, and we want those to stay in the original task.
+            if ((r.isResolverActivity() || r.noDisplay) && sourceRecord != null
+                    && sourceRecord.isFreeform())  {
+                addingToTask = true;
+            }
+        }
+
+        if (inTask == null) {
+            if (sourceRecord == null) {
+                // This activity is not being started from another...  in this
+                // case we -always- start a new task.
+                if ((launchFlags & FLAG_ACTIVITY_NEW_TASK) == 0 && inTask == null) {
+                    Slog.w(TAG, "startActivity called from non-Activity context; forcing " +
+                            "Intent.FLAG_ACTIVITY_NEW_TASK for: " + intent);
+                    launchFlags |= FLAG_ACTIVITY_NEW_TASK;
+                }
+            } else if (sourceRecord.launchMode == ActivityInfo.LAUNCH_SINGLE_INSTANCE) {
+                // The original activity who is starting us is running as a single
+                // instance...  this new activity it is starting must go on its
+                // own task.
+                launchFlags |= FLAG_ACTIVITY_NEW_TASK;
+            } else if (launchSingleInstance || launchSingleTask) {
+                // The activity being started is a single instance...  it always
+                // gets launched into its own task.
+                launchFlags |= FLAG_ACTIVITY_NEW_TASK;
+            }
+        }
+
+        ActivityInfo newTaskInfo = null;
+        Intent newTaskIntent = null;
+        final ActivityStack sourceStack;
+        if (sourceRecord != null) {
+            if (sourceRecord.finishing) {
+                // If the source is finishing, we can't further count it as our source.  This
+                // is because the task it is associated with may now be empty and on its way out,
+                // so we don't want to blindly throw it in to that task.  Instead we will take
+                // the NEW_TASK flow and try to find a task for it. But save the task information
+                // so it can be used when creating the new task.
+                if ((launchFlags & FLAG_ACTIVITY_NEW_TASK) == 0) {
+                    Slog.w(TAG, "startActivity called from finishing " + sourceRecord
+                            + "; forcing " + "Intent.FLAG_ACTIVITY_NEW_TASK for: " + intent);
+                    launchFlags |= FLAG_ACTIVITY_NEW_TASK;
+                    newTaskInfo = sourceRecord.info;
+                    newTaskIntent = sourceRecord.task.intent;
+                }
+                sourceRecord = null;
+                sourceStack = null;
+            } else {
+                sourceStack = sourceRecord.task.stack;
+            }
+        } else {
+            sourceStack = null;
+        }
+
+        boolean movedHome = false;
+        ActivityStack targetStack;
+
+        intent.setFlags(launchFlags);
+        final boolean noAnimation = (launchFlags & Intent.FLAG_ACTIVITY_NO_ANIMATION) != 0;
+
+        ActivityRecord intentActivity = getReusableIntentActivity(r, inTask, intent,
+                launchSingleInstance, launchSingleTask, launchFlags);
+        if (intentActivity != null) {
+            // When the flags NEW_TASK and CLEAR_TASK are set, then the task gets reused
+            // but still needs to be a lock task mode violation since the task gets
+            // cleared out and the device would otherwise leave the locked task.
+            if (mSupervisor.isLockTaskModeViolation(intentActivity.task,
+                    (launchFlags & (FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK))
+                            == (FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK))) {
+                mSupervisor.showLockTaskToast();
+                Slog.e(TAG, "startActivityUnchecked: Attempt to violate Lock Task Mode");
+                return ActivityManager.START_RETURN_LOCK_TASK_MODE_VIOLATION;
+            }
+            if (r.task == null) {
+                r.task = intentActivity.task;
+            }
+            if (intentActivity.task.intent == null) {
+                // This task was started because of movement of the activity based on affinity...
+                // Now that we are actually launching it, we can assign the base intent.
+                intentActivity.task.setIntent(r);
+            }
+
+            targetStack = intentActivity.task.stack;
+            targetStack.mLastPausedActivity = null;
+            // If the target task is not in the front, then we need
+            // to bring it to the front...  except...  well, with
+            // SINGLE_TASK_LAUNCH it's not entirely clear.  We'd like
+            // to have the same behavior as if a new instance was
+            // being started, which means not bringing it to the front
+            // if the caller is not itself in the front.
+            final ActivityStack focusStack = mSupervisor.getFocusedStack();
+            ActivityRecord curTop = (focusStack == null)
+                    ? null : focusStack.topRunningNonDelayedActivityLocked(notTop);
+            boolean movedToFront = false;
+            if (curTop != null && (curTop.task != intentActivity.task ||
+                    curTop.task != focusStack.topTask())) {
+                r.intent.addFlags(Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT);
+                if (sourceRecord == null || (sourceStack.topActivity() != null &&
+                        sourceStack.topActivity().task == sourceRecord.task)) {
+                    // We really do want to push this one into the user's face, right now.
+                    if (launchTaskBehind && sourceRecord != null) {
+                        intentActivity.setTaskToAffiliateWith(sourceRecord.task);
+                    }
+                    movedHome = true;
+                    final ActivityStack sideStack = getLaunchToSideStack(r, launchFlags, r.task);
+                    if (sideStack == null || sideStack == targetStack) {
+                        // We only want to move to the front, if we aren't going to launch on a
+                        // different stack. If we launch on a different stack, we will put the
+                        // task on top there.
+                        targetStack.moveTaskToFrontLocked(intentActivity.task, noAnimation,
+                                options, r.appTimeTracker, "bringingFoundTaskToFront");
+                        movedToFront = true;
+                    }
+                    if ((launchFlags & (FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_TASK_ON_HOME))
+                            == (FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_TASK_ON_HOME)) {
+                        // Caller wants to appear on home activity.
+                        intentActivity.task.setTaskToReturnTo(HOME_ACTIVITY_TYPE);
+                    }
+                    options = null;
+                }
+            }
+            if (!movedToFront && doResume) {
+                if (DEBUG_TASKS) Slog.d(TAG_TASKS, "Bring to front target: " + targetStack
+                        + " from " + intentActivity);
+                targetStack.moveToFront("intentActivityFound");
+            }
+
+            // If the caller has requested that the target task be
+            // reset, then do so.
+            if ((launchFlags & Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED) != 0) {
+                intentActivity = targetStack.resetTaskIfNeededLocked(intentActivity, r);
+            }
+            if ((startFlags & ActivityManager.START_FLAG_ONLY_IF_NEEDED) != 0) {
+                // We don't need to start a new activity, and
+                // the client said not to do anything if that
+                // is the case, so this is it!  And for paranoia, make
+                // sure we have correctly resumed the top activity.
+                if (doResume) {
+                    mSupervisor.resumeTopActivitiesLocked(targetStack, null, options);
+
+                    // Make sure to notify Keyguard as well if we are not running an app
+                    // transition later.
+                    if (!movedToFront) {
+                        mSupervisor.notifyActivityDrawnForKeyguard();
+                    }
+                } else {
+                    ActivityOptions.abort(options);
+                }
+                mSupervisor.updateUserStackLocked(r.userId, targetStack);
+                return ActivityManager.START_RETURN_INTENT_TO_CALLER;
+            }
+            if ((launchFlags & (FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK))
+                    == (FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK)) {
+                // The caller has requested to completely replace any
+                // existing task with its new activity.  Well that should
+                // not be too hard...
+                reuseTask = intentActivity.task;
+                reuseTask.performClearTaskLocked();
+                reuseTask.setIntent(r);
+            } else if ((launchFlags & FLAG_ACTIVITY_CLEAR_TOP) != 0
+                    || launchSingleInstance || launchSingleTask) {
+                // In this situation we want to remove all activities
+                // from the task up to the one being started.  In most
+                // cases this means we are resetting the task to its
+                // initial state.
+                ActivityRecord top = intentActivity.task.performClearTaskLocked(r, launchFlags);
+                if (top != null) {
+                    if (top.frontOfTask) {
+                        // Activity aliases may mean we use different
+                        // intents for the top activity, so make sure
+                        // the task now has the identity of the new
+                        // intent.
+                        top.task.setIntent(r);
+                    }
+                    ActivityStack.logStartActivity(EventLogTags.AM_NEW_INTENT, r, top.task);
+                    top.deliverNewIntentLocked(callingUid, r.intent, r.launchedFromPackage);
+                } else {
+                    // A special case: we need to start the activity because it is not
+                    // currently running, and the caller has asked to clear the current
+                    // task to have this activity at the top.
+                    addingToTask = true;
+                    // Now pretend like this activity is being started by the top of its
+                    // task, so it is put in the right place.
+                    sourceRecord = intentActivity;
+                    TaskRecord task = sourceRecord.task;
+                    if (task != null && task.stack == null) {
+                        // Target stack got cleared when we all activities were removed
+                        // above. Go ahead and reset it.
+                        targetStack = computeStackFocus(
+                                sourceRecord, false /* newTask */, null /* bounds */, launchFlags);
+                        targetStack.addTask(task,
+                                !launchTaskBehind /* toTop */, "startActivityUnchecked");
+                    }
+
+                }
+            } else if (r.realActivity.equals(intentActivity.task.realActivity)) {
+                // In this case the top activity on the task is the
+                // same as the one being launched, so we take that
+                // as a request to bring the task to the foreground.
+                // If the top activity in the task is the root
+                // activity, deliver this new intent to it if it
+                // desires.
+                if (((launchFlags&Intent.FLAG_ACTIVITY_SINGLE_TOP) != 0 || launchSingleTop)
+                        && intentActivity.realActivity.equals(r.realActivity)) {
+                    ActivityStack.logStartActivity(EventLogTags.AM_NEW_INTENT, r,
+                            intentActivity.task);
+                    if (intentActivity.frontOfTask) {
+                        intentActivity.task.setIntent(r);
+                    }
+                    intentActivity.deliverNewIntentLocked(callingUid, r.intent,
+                            r.launchedFromPackage);
+                } else if (!r.intent.filterEquals(intentActivity.task.intent)) {
+                    // In this case we are launching the root activity
+                    // of the task, but with a different intent.  We
+                    // should start a new instance on top.
+                    addingToTask = true;
+                    sourceRecord = intentActivity;
+                }
+            } else if ((launchFlags&Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED) == 0) {
+                // In this case an activity is being launched in to an
+                // existing task, without resetting that task.  This
+                // is typically the situation of launching an activity
+                // from a notification or shortcut.  We want to place
+                // the new activity on top of the current task.
+                addingToTask = true;
+                sourceRecord = intentActivity;
+            } else if (!intentActivity.task.rootWasReset) {
+                // In this case we are launching in to an existing task
+                // that has not yet been started from its front door.
+                // The current task has been brought to the front.
+                // Ideally, we'd probably like to place this new task
+                // at the bottom of its stack, but that's a little hard
+                // to do with the current organization of the code so
+                // for now we'll just drop it.
+                intentActivity.task.setIntent(r);
+            }
+            if (!addingToTask && reuseTask == null) {
+                // We didn't do anything...  but it was needed (a.k.a., client
+                // don't use that intent!)  And for paranoia, make
+                // sure we have correctly resumed the top activity.
+                if (doResume) {
+                    targetStack.resumeTopActivityLocked(null, options);
+                    if (!movedToFront) {
+                        // Make sure to notify Keyguard as well if we are not running an app
+                        // transition later.
+                        mSupervisor.notifyActivityDrawnForKeyguard();
+                    }
+                } else {
+                    ActivityOptions.abort(options);
+                }
+                mSupervisor.updateUserStackLocked(r.userId, targetStack);
+                return ActivityManager.START_TASK_TO_FRONT;
+            }
+        }
+
+        //String uri = r.intent.toURI();
+        //Intent intent2 = new Intent(uri);
+        //Slog.i(TAG, "Given intent: " + r.intent);
+        //Slog.i(TAG, "URI is: " + uri);
+        //Slog.i(TAG, "To intent: " + intent2);
+
+        if (r.packageName != null) {
+            // If the activity being launched is the same as the one currently
+            // at the top, then we need to check if it should only be launched
+            // once.
+            ActivityStack topStack = mSupervisor.mFocusedStack;
+            ActivityRecord top = topStack.topRunningNonDelayedActivityLocked(notTop);
+            final boolean dontStart = top != null && r.resultTo == null
+                    && top.realActivity.equals(r.realActivity) && top.userId == r.userId
+                    && top.app != null && top.app.thread != null
+                    && ((launchFlags & Intent.FLAG_ACTIVITY_SINGLE_TOP) != 0
+                    || launchSingleTop || launchSingleTask);
+            if (dontStart) {
+                ActivityStack.logStartActivity(EventLogTags.AM_NEW_INTENT, top, top.task);
+                // For paranoia, make sure we have correctly resumed the top activity.
+                topStack.mLastPausedActivity = null;
+                if (doResume) {
+                    mSupervisor.resumeTopActivitiesLocked();
+                }
+                ActivityOptions.abort(options);
+                if ((startFlags & ActivityManager.START_FLAG_ONLY_IF_NEEDED) != 0) {
+                    // We don't need to start a new activity, and the client said not to do
+                    // anything if that is the case, so this is it!
+                    return ActivityManager.START_RETURN_INTENT_TO_CALLER;
+                }
+                top.deliverNewIntentLocked(callingUid, r.intent, r.launchedFromPackage);
+                return ActivityManager.START_DELIVERED_TO_TOP;
+            }
+        } else {
+            if (r.resultTo != null && r.resultTo.task.stack != null) {
+                r.resultTo.task.stack.sendActivityResultLocked(-1, r.resultTo, r.resultWho,
+                        r.requestCode, Activity.RESULT_CANCELED, null);
+            }
+            ActivityOptions.abort(options);
+            return ActivityManager.START_CLASS_NOT_FOUND;
+        }
+
+        boolean newTask = false;
+        boolean keepCurTransition = false;
+
+        final TaskRecord taskToAffiliate = launchTaskBehind && sourceRecord != null ?
+                sourceRecord.task : null;
+
+        // Should this be considered a new task?
+        if (r.resultTo == null && inTask == null && !addingToTask
+                && (launchFlags & FLAG_ACTIVITY_NEW_TASK) != 0) {
+            newTask = true;
+            targetStack = computeStackFocus(r, newTask, newBounds, launchFlags);
+            if (doResume) {
+                targetStack.moveToFront("startingNewTask");
+            }
+
+            if (reuseTask == null) {
+                r.setTask(targetStack.createTaskRecord(mSupervisor.getNextTaskId(),
+                        newTaskInfo != null ? newTaskInfo : r.info,
+                        newTaskIntent != null ? newTaskIntent : intent,
+                        voiceSession, voiceInteractor, !launchTaskBehind /* toTop */),
+                        taskToAffiliate);
+                if (overrideBounds) {
+                    r.task.updateOverrideConfiguration(newBounds);
+                }
+                if (DEBUG_TASKS) Slog.v(TAG_TASKS,
+                        "Starting new activity " + r + " in new task " + r.task);
+            } else {
+                r.setTask(reuseTask, taskToAffiliate);
+            }
+            if (mSupervisor.isLockTaskModeViolation(r.task)) {
+                Slog.e(TAG, "Attempted Lock Task Mode violation r=" + r);
+                return ActivityManager.START_RETURN_LOCK_TASK_MODE_VIOLATION;
+            }
+            if (!movedHome) {
+                if ((launchFlags &
+                        (FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_TASK_ON_HOME))
+                        == (FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_TASK_ON_HOME)) {
+                    // Caller wants to appear on home activity, so before starting
+                    // their own activity we will bring home to the front.
+                    r.task.setTaskToReturnTo(HOME_ACTIVITY_TYPE);
+                }
+            }
+        } else if (sourceRecord != null) {
+            final TaskRecord sourceTask = sourceRecord.task;
+            if (mSupervisor.isLockTaskModeViolation(sourceTask)) {
+                Slog.e(TAG, "Attempted Lock Task Mode violation r=" + r);
+                return ActivityManager.START_RETURN_LOCK_TASK_MODE_VIOLATION;
+            }
+            targetStack = null;
+            if (sourceTask.stack.topTask() != sourceTask) {
+                // We only want to allow changing stack if the target task is not the top one,
+                // otherwise we would move the launching task to the other side, rather than show
+                // two side by side.
+                targetStack = getLaunchToSideStack(r, launchFlags, r.task);
+            }
+            if (targetStack == null) {
+                targetStack = sourceTask.stack;
+            } else if (targetStack != sourceTask.stack) {
+                mSupervisor.moveTaskToStackLocked(sourceTask.taskId, targetStack.mStackId,
+                        ON_TOP, FORCE_FOCUS, "launchToSide", !ANIMATE);
+            }
+            if (doResume) {
+                targetStack.moveToFront("sourceStackToFront");
+            }
+            final TaskRecord topTask = targetStack.topTask();
+            if (topTask != sourceTask) {
+                targetStack.moveTaskToFrontLocked(sourceTask, noAnimation, options,
+                        r.appTimeTracker, "sourceTaskToFront");
+            }
+            if (!addingToTask && (launchFlags&Intent.FLAG_ACTIVITY_CLEAR_TOP) != 0) {
+                // In this case, we are adding the activity to an existing
+                // task, but the caller has asked to clear that task if the
+                // activity is already running.
+                ActivityRecord top = sourceTask.performClearTaskLocked(r, launchFlags);
+                keepCurTransition = true;
+                if (top != null) {
+                    ActivityStack.logStartActivity(EventLogTags.AM_NEW_INTENT, r, top.task);
+                    top.deliverNewIntentLocked(callingUid, r.intent, r.launchedFromPackage);
+                    // For paranoia, make sure we have correctly
+                    // resumed the top activity.
+                    targetStack.mLastPausedActivity = null;
+                    if (doResume) {
+                        targetStack.resumeTopActivityLocked(null);
+                    }
+                    ActivityOptions.abort(options);
+                    return ActivityManager.START_DELIVERED_TO_TOP;
+                }
+            } else if (!addingToTask &&
+                    (launchFlags&Intent.FLAG_ACTIVITY_REORDER_TO_FRONT) != 0) {
+                // In this case, we are launching an activity in our own task
+                // that may already be running somewhere in the history, and
+                // we want to shuffle it to the front of the stack if so.
+                final ActivityRecord top = sourceTask.findActivityInHistoryLocked(r);
+                if (top != null) {
+                    final TaskRecord task = top.task;
+                    task.moveActivityToFrontLocked(top);
+                    ActivityStack.logStartActivity(EventLogTags.AM_NEW_INTENT, r, task);
+                    top.updateOptionsLocked(options);
+                    top.deliverNewIntentLocked(callingUid, r.intent, r.launchedFromPackage);
+                    targetStack.mLastPausedActivity = null;
+                    if (doResume) {
+                        targetStack.resumeTopActivityLocked(null);
+                    }
+                    return ActivityManager.START_DELIVERED_TO_TOP;
+                }
+            }
+            // An existing activity is starting this new activity, so we want
+            // to keep the new one in the same task as the one that is starting
+            // it.
+            r.setTask(sourceTask, null);
+            if (DEBUG_TASKS) Slog.v(TAG_TASKS, "Starting new activity " + r
+                    + " in existing task " + r.task + " from source " + sourceRecord);
+
+        } else if (inTask != null) {
+            // The caller is asking that the new activity be started in an explicit
+            // task it has provided to us.
+            if (mSupervisor.isLockTaskModeViolation(inTask)) {
+                Slog.e(TAG, "Attempted Lock Task Mode violation r=" + r);
+                return ActivityManager.START_RETURN_LOCK_TASK_MODE_VIOLATION;
+            }
+            if (overrideBounds) {
+                inTask.updateOverrideConfiguration(newBounds);
+                int stackId = inTask.getLaunchStackId();
+                if (stackId != inTask.stack.mStackId) {
+                    mSupervisor.moveTaskToStackUncheckedLocked(
+                            inTask, stackId, ON_TOP, !FORCE_FOCUS, "inTaskToFront");
+                }
+            }
+            targetStack = inTask.stack;
+            targetStack.moveTaskToFrontLocked(inTask, noAnimation, options, r.appTimeTracker,
+                    "inTaskToFront");
+
+            // Check whether we should actually launch the new activity in to the task,
+            // or just reuse the current activity on top.
+            ActivityRecord top = inTask.getTopActivity();
+            if (top != null && top.realActivity.equals(r.realActivity) && top.userId == r.userId) {
+                if ((launchFlags & Intent.FLAG_ACTIVITY_SINGLE_TOP) != 0
+                        || launchSingleTop || launchSingleTask) {
+                    ActivityStack.logStartActivity(EventLogTags.AM_NEW_INTENT, top, top.task);
+                    if ((startFlags&ActivityManager.START_FLAG_ONLY_IF_NEEDED) != 0) {
+                        // We don't need to start a new activity, and
+                        // the client said not to do anything if that
+                        // is the case, so this is it!
+                        return ActivityManager.START_RETURN_INTENT_TO_CALLER;
+                    }
+                    top.deliverNewIntentLocked(callingUid, r.intent, r.launchedFromPackage);
+                    return ActivityManager.START_DELIVERED_TO_TOP;
+                }
+            }
+
+            if (!addingToTask) {
+                // We don't actually want to have this activity added to the task, so just
+                // stop here but still tell the caller that we consumed the intent.
+                ActivityOptions.abort(options);
+                return ActivityManager.START_TASK_TO_FRONT;
+            }
+
+            r.setTask(inTask, null);
+            if (DEBUG_TASKS) Slog.v(TAG_TASKS, "Starting new activity " + r
+                    + " in explicit task " + r.task);
+
+        } else {
+            // This not being started from an existing activity, and not part
+            // of a new task...  just put it in the top task, though these days
+            // this case should never happen.
+            targetStack = computeStackFocus(r, newTask, null /* bounds */, launchFlags);
+            if (doResume) {
+                targetStack.moveToFront("addingToTopTask");
+            }
+            ActivityRecord prev = targetStack.topActivity();
+            r.setTask(prev != null ? prev.task : targetStack.createTaskRecord(
+                    mSupervisor.getNextTaskId(), r.info, intent, null, null, true), null);
+            mWindowManager.moveTaskToTop(r.task.taskId);
+            if (DEBUG_TASKS) Slog.v(TAG_TASKS, "Starting new activity " + r
+                    + " in new guessed " + r.task);
+        }
+
+        mService.grantUriPermissionFromIntentLocked(callingUid, r.packageName,
+                intent, r.getUriPermissionsLocked(), r.userId);
+
+        if (sourceRecord != null && sourceRecord.isRecentsActivity()) {
+            r.task.setTaskToReturnTo(RECENTS_ACTIVITY_TYPE);
+        }
+        if (newTask) {
+            EventLog.writeEvent(EventLogTags.AM_CREATE_TASK, r.userId, r.task.taskId);
+        }
+        ActivityStack.logStartActivity(EventLogTags.AM_CREATE_ACTIVITY, r, r.task);
+        targetStack.mLastPausedActivity = null;
+        targetStack.startActivityLocked(r, newTask, keepCurTransition, options);
+        if (doResume) {
+            if (!launchTaskBehind) {
+                mService.setFocusedActivityLocked(r, "startedActivity");
+            }
+            mSupervisor.resumeTopActivitiesLocked(targetStack, r, options);
+        } else {
+            targetStack.addRecentActivityLocked(r);
+        }
+        mSupervisor.updateUserStackLocked(r.userId, targetStack);
+
+        if (!r.task.mResizeable && mSupervisor.isStackDockedInEffect(targetStack.mStackId)) {
+            mSupervisor.showNonResizeableDockToast(r.task.taskId);
+        }
+
+        return ActivityManager.START_SUCCESS;
+    }
+
+    private int adjustLaunchFlagsToDocumentMode(ActivityRecord r, boolean launchSingleInstance,
+            boolean launchSingleTask, int launchFlags) {
+        if ((launchFlags & Intent.FLAG_ACTIVITY_NEW_DOCUMENT) != 0 &&
+                (launchSingleInstance || launchSingleTask)) {
+            // We have a conflict between the Intent and the Activity manifest, manifest wins.
+            Slog.i(TAG, "Ignoring FLAG_ACTIVITY_NEW_DOCUMENT, launchMode is " +
+                    "\"singleInstance\" or \"singleTask\"");
+            launchFlags &=
+                    ~(Intent.FLAG_ACTIVITY_NEW_DOCUMENT | FLAG_ACTIVITY_MULTIPLE_TASK);
+        } else {
+            switch (r.info.documentLaunchMode) {
+                case ActivityInfo.DOCUMENT_LAUNCH_NONE:
+                    break;
+                case ActivityInfo.DOCUMENT_LAUNCH_INTO_EXISTING:
+                    launchFlags |= Intent.FLAG_ACTIVITY_NEW_DOCUMENT;
+                    break;
+                case ActivityInfo.DOCUMENT_LAUNCH_ALWAYS:
+                    launchFlags |= Intent.FLAG_ACTIVITY_NEW_DOCUMENT;
+                    break;
+                case ActivityInfo.DOCUMENT_LAUNCH_NEVER:
+                    launchFlags &= ~FLAG_ACTIVITY_MULTIPLE_TASK;
+                    break;
+            }
+        }
+        return launchFlags;
+    }
+
+    final int startActivities(IApplicationThread caller, int callingUid, String callingPackage,
+            Intent[] intents, String[] resolvedTypes, IBinder resultTo,
+            Bundle bOptions, int userId) {
+        if (intents == null) {
+            throw new NullPointerException("intents is null");
+        }
+        if (resolvedTypes == null) {
+            throw new NullPointerException("resolvedTypes is null");
+        }
+        if (intents.length != resolvedTypes.length) {
+            throw new IllegalArgumentException("intents are length different than resolvedTypes");
+        }
+
+
+        int callingPid;
+        if (callingUid >= 0) {
+            callingPid = -1;
+        } else if (caller == null) {
+            callingPid = Binder.getCallingPid();
+            callingUid = Binder.getCallingUid();
+        } else {
+            callingPid = callingUid = -1;
+        }
+        final long origId = Binder.clearCallingIdentity();
+        try {
+            synchronized (mService) {
+                ActivityRecord[] outActivity = new ActivityRecord[1];
+                for (int i=0; i<intents.length; i++) {
+                    Intent intent = intents[i];
+                    if (intent == null) {
+                        continue;
+                    }
+
+                    // Refuse possible leaked file descriptors
+                    if (intent != null && intent.hasFileDescriptors()) {
+                        throw new IllegalArgumentException("File descriptors passed in Intent");
+                    }
+
+                    boolean componentSpecified = intent.getComponent() != null;
+
+                    // Don't modify the client's object!
+                    intent = new Intent(intent);
+
+                    // Collect information about the target of the Intent.
+                    ActivityInfo aInfo = mSupervisor.resolveActivity(intent, resolvedTypes[i], 0,
+                            null, userId);
+                    // TODO: New, check if this is correct
+                    aInfo = mService.getActivityInfoForUser(aInfo, userId);
+
+                    if (aInfo != null &&
+                            (aInfo.applicationInfo.privateFlags
+                                    & ApplicationInfo.PRIVATE_FLAG_CANT_SAVE_STATE)  != 0) {
+                        throw new IllegalArgumentException(
+                                "FLAG_CANT_SAVE_STATE not supported here");
+                    }
+
+                    ActivityOptions options = ActivityOptions.fromBundle(
+                            i == intents.length - 1 ? bOptions : null);
+                    int res = startActivityLocked(caller, intent, null /*ephemeralIntent*/,
+                            resolvedTypes[i], aInfo, null /*rInfo*/, null, null, resultTo, null, -1,
+                            callingPid, callingUid, callingPackage, callingPid, callingUid, 0,
+                            options, false, componentSpecified, outActivity, null, null);
+                    if (res < 0) {
+                        return res;
+                    }
+
+                    resultTo = outActivity[0] != null ? outActivity[0].appToken : null;
+                }
+            }
+        } finally {
+            Binder.restoreCallingIdentity(origId);
+        }
+
+        return ActivityManager.START_SUCCESS;
+    }
+
+    final void doPendingActivityLaunchesLocked(boolean doResume) {
+        while (!mPendingActivityLaunches.isEmpty()) {
+            PendingActivityLaunch pal = mPendingActivityLaunches.remove(0);
+
+            try {
+                startActivityUncheckedLocked(pal.r, pal.sourceRecord, null, null,
+                        pal.startFlags, doResume && mPendingActivityLaunches.isEmpty(),
+                        null, null);
+            } catch (Exception e) {
+                Slog.e(TAG, "Exception during pending activity launch pal=" + pal, e);
+                pal.sendErrorResult(e.getMessage());
+            }
+        }
+    }
+
+    private ActivityStack computeStackFocus(ActivityRecord r, boolean newTask, Rect bounds,
+            int launchFlags) {
+        final TaskRecord task = r.task;
+        if (!(r.isApplicationActivity() || (task != null && task.isApplicationTask()))) {
+            return mSupervisor.mHomeStack;
+        }
+
+        ActivityStack stack = getLaunchToSideStack(r, launchFlags, task);
+        if (stack != null) {
+            return stack;
+        }
+
+        if (task != null && task.stack != null) {
+            stack = task.stack;
+            if (stack.isOnHomeDisplay()) {
+                if (mSupervisor.mFocusedStack != stack) {
+                    if (DEBUG_FOCUS || DEBUG_STACK) Slog.d(TAG_FOCUS,
+                            "computeStackFocus: Setting " + "focused stack to r=" + r
+                                    + " task=" + task);
+                } else {
+                    if (DEBUG_FOCUS || DEBUG_STACK) Slog.d(TAG_FOCUS,
+                            "computeStackFocus: Focused stack already="
+                                    + mSupervisor.mFocusedStack);
+                }
+            }
+            return stack;
+        }
+
+        final ActivityStackSupervisor.ActivityContainer container = r.mInitialActivityContainer;
+        if (container != null) {
+            // The first time put it on the desired stack, after this put on task stack.
+            r.mInitialActivityContainer = null;
+            return container.mStack;
+        }
+
+        // The fullscreen stack can contain any task regardless of if the task is resizeable
+        // or not. So, we let the task go in the fullscreen task if it is the focus stack.
+        // If the freeform or docked stack has focus, and the activity to be launched is resizeable,
+        // we can also put it in the focused stack.
+        final int focusedStackId = mSupervisor.mFocusedStack.mStackId;
+        final boolean canUseFocusedStack =
+                focusedStackId == FULLSCREEN_WORKSPACE_STACK_ID
+                        || focusedStackId == DOCKED_STACK_ID
+                        || (focusedStackId == FREEFORM_WORKSPACE_STACK_ID && r.info.resizeable);
+        if (canUseFocusedStack && (!newTask
+                || mSupervisor.mFocusedStack.mActivityContainer.isEligibleForNewTasks())) {
+            if (DEBUG_FOCUS || DEBUG_STACK) Slog.d(TAG_FOCUS,
+                    "computeStackFocus: Have a focused stack=" + mSupervisor.mFocusedStack);
+            return mSupervisor.mFocusedStack;
+        }
+
+        // We first try to put the task in the first dynamic stack.
+        final ArrayList<ActivityStack> homeDisplayStacks = mSupervisor.mHomeStack.mStacks;
+        for (int stackNdx = homeDisplayStacks.size() - 1; stackNdx >= 0; --stackNdx) {
+            stack = homeDisplayStacks.get(stackNdx);
+            if (!ActivityManager.StackId.isStaticStack(stack.mStackId)) {
+                if (DEBUG_FOCUS || DEBUG_STACK) Slog.d(TAG_FOCUS,
+                        "computeStackFocus: Setting focused stack=" + stack);
+                return stack;
+            }
+        }
+
+        // If there is no suitable dynamic stack then we figure out which static stack to use.
+        final int stackId = task != null ? task.getLaunchStackId() :
+                bounds != null ? FREEFORM_WORKSPACE_STACK_ID :
+                        FULLSCREEN_WORKSPACE_STACK_ID;
+        stack = mSupervisor.getStack(stackId, CREATE_IF_NEEDED, ON_TOP);
+        if (DEBUG_FOCUS || DEBUG_STACK) Slog.d(TAG_FOCUS, "computeStackFocus: New stack r="
+                + r + " stackId=" + stack.mStackId);
+        return stack;
+    }
+
+    private ActivityStack getLaunchToSideStack(ActivityRecord r, int launchFlags, TaskRecord task) {
+        if ((launchFlags & FLAG_ACTIVITY_LAUNCH_TO_SIDE) == 0) {
+            return null;
+        }
+        // The parent activity doesn't want to launch the activity on top of itself, but
+        // instead tries to put it onto other side in side-by-side mode.
+        final ActivityStack parentStack = task != null ? task.stack
+                : r.mInitialActivityContainer != null ? r.mInitialActivityContainer.mStack
+                : mSupervisor.mFocusedStack;
+        if (parentStack != null && parentStack.mStackId == DOCKED_STACK_ID) {
+            // If parent was in docked stack, the natural place to launch another activity
+            // will be fullscreen, so it can appear alongside the docked window.
+            return mSupervisor.getStack(FULLSCREEN_WORKSPACE_STACK_ID, CREATE_IF_NEEDED, ON_TOP);
+        } else {
+            // If the parent is not in the docked stack, we check if there is docked window
+            // and if yes, we will launch into that stack. If not, we just put the new
+            // activity into parent's stack, because we can't find a better place.
+            final ActivityStack stack = mSupervisor.getStack(DOCKED_STACK_ID);
+            if (stack != null && !stack.isStackVisibleLocked()) {
+                // There is a docked stack, but it isn't visible, so we can't launch into that.
+                return null;
+            } else {
+                return stack;
+            }
+        }
+    }
+
+    /**
+     * Decide whether the new activity should be inserted into an existing task. Returns null if not
+     * or an ActivityRecord with the task into which the new activity should be added.
+     */
+    private ActivityRecord getReusableIntentActivity(ActivityRecord r, TaskRecord inTask,
+            Intent intent, boolean launchSingleInstance, boolean launchSingleTask,
+            int launchFlags) {
+        // We may want to try to place the new activity in to an existing task.  We always
+        // do this if the target activity is singleTask or singleInstance; we will also do
+        // this if NEW_TASK has been requested, and there is not an additional qualifier telling
+        // us to still place it in a new task: multi task, always doc mode, or being asked to
+        // launch this as a new task behind the current one.
+        boolean putIntoExistingTask = ((launchFlags & FLAG_ACTIVITY_NEW_TASK) != 0 &&
+                (launchFlags & FLAG_ACTIVITY_MULTIPLE_TASK) == 0)
+                || launchSingleInstance || launchSingleTask;
+        // If bring to front is requested, and no result is requested and we have not
+        // been given an explicit task to launch in to, and
+        // we can find a task that was started with this same
+        // component, then instead of launching bring that one to the front.
+        putIntoExistingTask &= inTask == null && r.resultTo == null;
+        ActivityRecord intentActivity = null;
+        if (putIntoExistingTask) {
+            // See if there is a task to bring to the front.  If this is
+            // a SINGLE_INSTANCE activity, there can be one and only one
+            // instance of it in the history, and it is always in its own
+            // unique task, so we do a special search.
+            intentActivity = launchSingleInstance ? mSupervisor.findActivityLocked(intent, r.info)
+                    : mSupervisor.findTaskLocked(r);
+        }
+        return intentActivity;
+    }
+
+    void setWindowManager(WindowManagerService wm) {
+        mWindowManager = wm;
+    }
+
+    void removePendingActivityLaunchesLocked(ActivityStack stack) {
+        for (int palNdx = mPendingActivityLaunches.size() - 1; palNdx >= 0; --palNdx) {
+            PendingActivityLaunch pal = mPendingActivityLaunches.get(palNdx);
+            if (pal.stack == stack) {
+                mPendingActivityLaunches.remove(palNdx);
+            }
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/am/TaskRecord.java b/services/core/java/com/android/server/am/TaskRecord.java
index 5ee9eea..4647d77 100644
--- a/services/core/java/com/android/server/am/TaskRecord.java
+++ b/services/core/java/com/android/server/am/TaskRecord.java
@@ -1394,6 +1394,12 @@
         return mLastNonFullscreenBounds;
     }
 
+    boolean canMatchRootAffinity() {
+        // We don't allow root affinity matching on the pinned stack as no other task should
+        // be launching in it based on affinity.
+        return rootAffinity != null && (stack == null || stack.mStackId != PINNED_STACK_ID);
+    }
+
     void dump(PrintWriter pw, String prefix) {
         pw.print(prefix); pw.print("userId="); pw.print(userId);
                 pw.print(" effectiveUid="); UserHandle.formatUid(pw, effectiveUid);
diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java
index 62e78a4..5426b72 100644
--- a/services/core/java/com/android/server/am/UserController.java
+++ b/services/core/java/com/android/server/am/UserController.java
@@ -215,7 +215,8 @@
 
                 Intent intent = new Intent(Intent.ACTION_LOCKED_BOOT_COMPLETED, null);
                 intent.putExtra(Intent.EXTRA_USER_HANDLE, userId);
-                intent.addFlags(Intent.FLAG_RECEIVER_NO_ABORT);
+                intent.addFlags(Intent.FLAG_RECEIVER_NO_ABORT
+                        | Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
                 mService.broadcastIntentLocked(null, null, intent, null, resultTo, 0, null, null,
                         new String[] { android.Manifest.permission.RECEIVE_BOOT_COMPLETED },
                         AppOpsManager.OP_NONE, null, true, false, MY_PID, SYSTEM_UID, userId);
@@ -245,6 +246,7 @@
                 mHandler.sendMessage(mHandler.obtainMessage(SYSTEM_USER_UNLOCK_MSG, userId, 0));
 
                 final Intent unlockedIntent = new Intent(Intent.ACTION_USER_UNLOCKED);
+                unlockedIntent.putExtra(Intent.EXTRA_USER_HANDLE, userId);
                 unlockedIntent.addFlags(
                         Intent.FLAG_RECEIVER_REGISTERED_ONLY | Intent.FLAG_RECEIVER_FOREGROUND);
                 mService.broadcastIntentLocked(null, null, unlockedIntent, null, null, 0, null,
diff --git a/services/core/java/com/android/server/job/JobStore.java b/services/core/java/com/android/server/job/JobStore.java
index d9f94d0..472e8f6 100644
--- a/services/core/java/com/android/server/job/JobStore.java
+++ b/services/core/java/com/android/server/job/JobStore.java
@@ -46,6 +46,7 @@
 import java.util.ArrayList;
 import java.util.Iterator;
 import java.util.List;
+import java.util.Set;
 
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
@@ -348,9 +349,27 @@
         private void writeBundleToXml(PersistableBundle extras, XmlSerializer out)
                 throws IOException, XmlPullParserException {
             out.startTag(null, XML_TAG_EXTRAS);
-            extras.saveToXml(out);
+            PersistableBundle extrasCopy = deepCopyBundle(extras, 10);
+            extrasCopy.saveToXml(out);
             out.endTag(null, XML_TAG_EXTRAS);
         }
+
+        private PersistableBundle deepCopyBundle(PersistableBundle bundle, int maxDepth) {
+            if (maxDepth <= 0) {
+                return null;
+            }
+            PersistableBundle copy = (PersistableBundle) bundle.clone();
+            Set<String> keySet = bundle.keySet();
+            for (String key: keySet) {
+                PersistableBundle b = copy.getPersistableBundle(key);
+                if (b != null) {
+                    PersistableBundle bCopy = deepCopyBundle(b, maxDepth-1);
+                    copy.putPersistableBundle(key, bCopy);
+                }
+            }
+            return copy;
+        }
+
         /**
          * Write out a tag with data identifying this job's constraints. If the constraint isn't here
          * it doesn't apply.
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 3a8f041..80fb15d 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -1263,6 +1263,13 @@
             return mRankingHelper.getTopicImportance(pkg, uid, topic);
         }
 
+        @Override
+        public void setAppImportance(String pkg, int uid, int importance) {
+            enforceSystemOrSystemUI("Caller not system or systemui");
+            mRankingHelper.setAppImportance(pkg, uid, importance);
+            savePolicyFile();
+        }
+
         /**
          * System-only API for getting a list of current (i.e. not cleared) notifications.
          *
diff --git a/services/core/java/com/android/server/notification/RankingConfig.java b/services/core/java/com/android/server/notification/RankingConfig.java
index acdd90a..a6c9b0d 100644
--- a/services/core/java/com/android/server/notification/RankingConfig.java
+++ b/services/core/java/com/android/server/notification/RankingConfig.java
@@ -35,4 +35,6 @@
     void setTopicImportance(String packageName, int uid, Notification.Topic topic, int importance);
 
     int getTopicImportance(String packageName, int uid, Notification.Topic topic);
+
+    void setAppImportance(String packageName, int uid, int importance);
 }
diff --git a/services/core/java/com/android/server/notification/RankingHelper.java b/services/core/java/com/android/server/notification/RankingHelper.java
index 5a31c6a..32c0ce2 100644
--- a/services/core/java/com/android/server/notification/RankingHelper.java
+++ b/services/core/java/com/android/server/notification/RankingHelper.java
@@ -251,6 +251,7 @@
             }
             out.startTag(null, TAG_PACKAGE);
             out.attribute(null, ATT_NAME, r.pkg);
+            out.attribute(null, ATT_IMPORTANCE, Integer.toString(r.importance));
 
             if (!forBackup) {
                 out.attribute(null, ATT_UID, Integer.toString(r.uid));
@@ -426,6 +427,20 @@
         updateConfig();
     }
 
+    /**
+     * Sets the default importance for all new topics that appear in the future, and resets
+     * the importance of all current topics.
+     */
+    @Override
+    public void setAppImportance(String pkgName, int uid, int importance) {
+        final Record r = getOrCreateRecord(pkgName, uid);
+        r.importance = importance;
+        for (Topic t :  r.topics.values()) {
+            t.importance = importance;
+        }
+        updateConfig();
+    }
+
     private Topic getOrCreateTopic(Record r, Notification.Topic topic) {
         if (topic == null) {
             topic = createDefaultTopic();
@@ -435,6 +450,7 @@
             return t;
         } else {
             t = new Topic(topic);
+            t.importance = r.importance;
             r.topics.put(topic.getId(), t);
             return t;
         }
@@ -477,6 +493,8 @@
                 pw.print(" (");
                 pw.print(r.uid == Record.UNKNOWN_UID ? "UNKNOWN_UID" : Integer.toString(r.uid));
                 pw.print(')');
+                pw.print(" importance=");
+                pw.print(Ranking.importanceToString(r.importance));
                 pw.println();
                 for (Topic t : r.topics.values()) {
                     pw.print(prefix);
@@ -532,6 +550,7 @@
 
         String pkg;
         int uid = UNKNOWN_UID;
+        int importance = DEFAULT_IMPORTANCE;
         Map<String, Topic> topics = new ArrayMap<>();
    }
 
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index fa0aa37..66d10b5 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -482,7 +482,6 @@
             throw new PackageManagerException(INSTALL_FAILED_CONTAINER_ERROR,
                     "Failed to resolve stage location", e);
         }
-        final boolean quickInstall = (params.installFlags & PackageManager.INSTALL_QUICK) != 0;
 
         // Verify that stage looks sane with respect to existing application.
         // This currently only ensures packageName, versionCode, and certificate
@@ -490,10 +489,7 @@
         validateInstallLocked();
 
         Preconditions.checkNotNull(mPackageName);
-        // TODO: fix b/25118622; don't bypass signature check
-        if (!quickInstall) {
-            Preconditions.checkNotNull(mSignatures);
-        }
+        Preconditions.checkNotNull(mSignatures);
         Preconditions.checkNotNull(mResolvedBaseFile);
 
         if (!mPermissionsAccepted) {
@@ -603,7 +599,6 @@
      * {@link PackageManagerService}.
      */
     private void validateInstallLocked() throws PackageManagerException {
-        final boolean quickInstall = (params.installFlags & PackageManager.INSTALL_QUICK) != 0;
         mPackageName = null;
         mVersionCode = -1;
         mSignatures = null;
@@ -627,9 +622,7 @@
 
             final ApkLite apk;
             try {
-                // TODO: fix b/25118622; always use PARSE_COLLECT_CERTIFICATES
-                final int parseFlags = quickInstall ? 0 : PackageParser.PARSE_COLLECT_CERTIFICATES;
-                apk = PackageParser.parseApkLite(file, parseFlags);
+                apk = PackageParser.parseApkLite(file, PackageParser.PARSE_COLLECT_CERTIFICATES);
             } catch (PackageParserException e) {
                 throw PackageManagerException.from(e);
             }
@@ -750,7 +743,6 @@
     }
 
     private void assertApkConsistent(String tag, ApkLite apk) throws PackageManagerException {
-        final boolean quickInstall = (params.installFlags & PackageManager.INSTALL_QUICK) != 0;
         if (!mPackageName.equals(apk.packageName)) {
             throw new PackageManagerException(INSTALL_FAILED_INVALID_APK, tag + " package "
                     + apk.packageName + " inconsistent with " + mPackageName);
@@ -760,8 +752,7 @@
                     + " version code " + apk.versionCode + " inconsistent with "
                     + mVersionCode);
         }
-        // TODO: fix b/25118622; don't bypass signature check
-        if (!quickInstall && !Signature.areExactMatch(mSignatures, apk.signatures)) {
+        if (!Signature.areExactMatch(mSignatures, apk.signatures)) {
             throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
                     tag + " signatures are inconsistent");
         }
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 42e8b01..dfb01eb 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -10426,10 +10426,6 @@
         if (!DEFAULT_VERIFY_ENABLE) {
             return false;
         }
-        // TODO: fix b/25118622; don't bypass verification
-        if (Build.IS_DEBUGGABLE && (installFlags & PackageManager.INSTALL_QUICK) != 0) {
-            return false;
-        }
         // Ephemeral apps don't get the full verification treatment
         if ((installFlags & PackageManager.INSTALL_EPHEMERAL) != 0) {
             if (DEBUG_EPHEMERAL) {
@@ -12792,7 +12788,6 @@
         final boolean forwardLocked = ((installFlags & PackageManager.INSTALL_FORWARD_LOCK) != 0);
         final boolean onExternal = (((installFlags & PackageManager.INSTALL_EXTERNAL) != 0)
                 || (args.volumeUuid != null));
-        final boolean quickInstall = ((installFlags & PackageManager.INSTALL_QUICK) != 0);
         final boolean ephemeral = ((installFlags & PackageManager.INSTALL_EPHEMERAL) != 0);
         boolean replace = false;
         int scanFlags = SCAN_NEW_INSTALL | SCAN_UPDATE_SIGNATURE;
@@ -12818,7 +12813,6 @@
                 | PackageParser.PARSE_ENFORCE_CODE
                 | (forwardLocked ? PackageParser.PARSE_FORWARD_LOCK : 0)
                 | (onExternal ? PackageParser.PARSE_EXTERNAL_STORAGE : 0)
-                | (quickInstall ? PackageParser.PARSE_SKIP_VERIFICATION : 0)
                 | (ephemeral ? PackageParser.PARSE_IS_EPHEMERAL : 0);
         PackageParser pp = new PackageParser();
         pp.setSeparateProcesses(mSeparateProcesses);
diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
index 7c42ae1..901749e 100644
--- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
+++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
@@ -141,6 +141,7 @@
                 return 1;
             }
             abandonSession = false;
+            pw.println("Success");
             return 0;
         } finally {
             if (abandonSession) {
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index ef2f29b..da1e546 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -65,7 +65,9 @@
 import com.android.server.pm.PackageManagerService.DumpState;
 import com.android.server.pm.PermissionsState.PermissionState;
 
+import java.io.BufferedInputStream;
 import java.io.FileNotFoundException;
+import java.io.InputStream;
 import java.util.Collection;
 
 import org.xmlpull.v1.XmlPullParser;
@@ -2799,9 +2801,9 @@
             }
 
             if (PackageManagerService.DEBUG_PREFERRED) Log.d(TAG, "Reading default preferred " + f);
-            FileInputStream str = null;
+            InputStream str = null;
             try {
-                str = new FileInputStream(f);
+                str = new BufferedInputStream(new FileInputStream(f));
                 XmlPullParser parser = Xml.newPullParser();
                 parser.setInput(str, null);
 
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index f5da103..13f4826 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -526,6 +526,7 @@
         if (parentHandle != null) {
             intent = new Intent(Intent.ACTION_MANAGED_PROFILE_AVAILABILITY_CHANGED);
             intent.putExtra(Intent.EXTRA_USER, profileHandle);
+            intent.putExtra(Intent.EXTRA_USER_HANDLE, profileHandle.getIdentifier());
             intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
             intent.putExtras(extras);
             mContext.sendBroadcastAsUser(intent, parentHandle);
@@ -2073,6 +2074,7 @@
         managedProfileIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY |
                 Intent.FLAG_RECEIVER_FOREGROUND);
         managedProfileIntent.putExtra(Intent.EXTRA_USER, new UserHandle(removedUserId));
+        managedProfileIntent.putExtra(Intent.EXTRA_USER_HANDLE, removedUserId);
         mContext.sendBroadcastAsUser(managedProfileIntent, new UserHandle(parentUserId), null);
     }
 
diff --git a/services/core/java/com/android/server/tv/TvInputHardwareManager.java b/services/core/java/com/android/server/tv/TvInputHardwareManager.java
index 7f4c42b..9bf7ae4 100644
--- a/services/core/java/com/android/server/tv/TvInputHardwareManager.java
+++ b/services/core/java/com/android/server/tv/TvInputHardwareManager.java
@@ -17,6 +17,7 @@
 package com.android.server.tv;
 
 import static android.media.tv.TvInputManager.INPUT_STATE_CONNECTED;
+import static android.media.tv.TvInputManager.INPUT_STATE_CONNECTED_STANDBY;
 import static android.media.tv.TvInputManager.INPUT_STATE_DISCONNECTED;
 
 import android.content.BroadcastReceiver;
@@ -102,7 +103,6 @@
     private int mCurrentIndex = 0;
     private int mCurrentMaxIndex = 0;
 
-    // TODO: Should handle STANDBY case.
     private final SparseBooleanArray mHdmiStateMap = new SparseBooleanArray();
     private final List<Message> mPendingHdmiDeviceEvents = new LinkedList<>();
 
@@ -206,7 +206,7 @@
             String inputId = mHardwareInputIdMap.get(deviceId);
             if (inputId != null) {
                 mHandler.obtainMessage(ListenerHandler.STATE_CHANGED,
-                        convertConnectedToState(configs.length > 0), 0, inputId).sendToTarget();
+                        obtainStateFromConfigs(configs), 0, inputId).sendToTarget();
             }
             ITvInputHardwareCallback callback = connection.getCallbackLocked();
             if (callback != null) {
@@ -256,12 +256,13 @@
                 || connectionCallingUid != callingUid || connectionResolvedUserId != resolvedUserId;
     }
 
-    private int convertConnectedToState(boolean connected) {
-        if (connected) {
-            return INPUT_STATE_CONNECTED;
-        } else {
-            return INPUT_STATE_DISCONNECTED;
+    private int obtainStateFromConfigs(TvStreamConfig[] configs) {
+        for (TvStreamConfig config : configs) {
+            if ((config.getFlags() & TvStreamConfig.FLAG_MASK_SIGNAL_DETECTION) != 0) {
+                return INPUT_STATE_CONNECTED;
+            }
         }
+        return (configs.length > 0) ? INPUT_STATE_CONNECTED_STANDBY : INPUT_STATE_DISCONNECTED;
     }
 
     public void addHardwareTvInput(int deviceId, TvInputInfo info) {
@@ -286,9 +287,14 @@
                 }
                 String inputId = mHardwareInputIdMap.get(hardwareInfo.getDeviceId());
                 if (inputId != null && inputId.equals(info.getId())) {
-                    mHandler.obtainMessage(ListenerHandler.STATE_CHANGED,
-                            convertConnectedToState(mHdmiStateMap.valueAt(i)), 0,
-                            inputId).sendToTarget();
+                    // No HDMI hotplug does not necessarily mean disconnected, as old devices may
+                    // not report hotplug state correctly. Using INPUT_STATE_CONNECTED_STANDBY to
+                    // denote unknown state.
+                    int state = mHdmiStateMap.valueAt(i)
+                            ? INPUT_STATE_CONNECTED
+                            : INPUT_STATE_CONNECTED_STANDBY;
+                    mHandler.obtainMessage(
+                            ListenerHandler.STATE_CHANGED, state, 0, inputId).sendToTarget();
                     return;
                 }
             }
@@ -296,7 +302,7 @@
             Connection connection = mConnections.get(deviceId);
             if (connection != null) {
                 mHandler.obtainMessage(ListenerHandler.STATE_CHANGED,
-                        convertConnectedToState(connection.getConfigsLocked().length > 0), 0,
+                        obtainStateFromConfigs(connection.getConfigsLocked()), 0,
                         info.getId()).sendToTarget();
             }
         }
@@ -1110,8 +1116,14 @@
                 if (inputId == null) {
                     return;
                 }
-                mHandler.obtainMessage(ListenerHandler.STATE_CHANGED,
-                        convertConnectedToState(event.isConnected()), 0, inputId).sendToTarget();
+                // No HDMI hotplug does not necessarily mean disconnected, as old devices may
+                // not report hotplug state correctly. Using INPUT_STATE_CONNECTED_STANDBY to
+                // denote unknown state.
+                int state = event.isConnected()
+                        ? INPUT_STATE_CONNECTED
+                        : INPUT_STATE_CONNECTED_STANDBY;
+                mHandler.obtainMessage(
+                        ListenerHandler.STATE_CHANGED, state, 0, inputId).sendToTarget();
             }
         }
     }
diff --git a/services/core/java/com/android/server/wm/AppWindowAnimator.java b/services/core/java/com/android/server/wm/AppWindowAnimator.java
index ab47f07..8292997 100644
--- a/services/core/java/com/android/server/wm/AppWindowAnimator.java
+++ b/services/core/java/com/android/server/wm/AppWindowAnimator.java
@@ -223,7 +223,7 @@
             }
             if (DEBUG_LAYERS) Slog.v(TAG, "Updating layer " + w + ": " + winAnimator.mAnimLayer);
             if (w == mService.mInputMethodTarget && !mService.mInputMethodTargetWaitingAnim) {
-                mService.setInputMethodAnimLayerAdjustment(adj);
+                mService.mLayersController.setInputMethodAnimLayerAdjustment(adj);
             }
             wallpaperController.setAnimLayerAdjustment(w, adj);
         }
diff --git a/services/core/java/com/android/server/wm/DragState.java b/services/core/java/com/android/server/wm/DragState.java
index da89481..7b0a8d7 100644
--- a/services/core/java/com/android/server/wm/DragState.java
+++ b/services/core/java/com/android/server/wm/DragState.java
@@ -35,7 +35,6 @@
 import android.util.Slog;
 import android.view.Display;
 import android.view.DragEvent;
-import android.view.DropPermissionHolder;
 import android.view.InputChannel;
 import android.view.SurfaceControl;
 import android.view.View;
@@ -54,6 +53,8 @@
 import com.android.server.wm.WindowManagerService.DragInputEventReceiver;
 import com.android.server.wm.WindowManagerService.H;
 
+import com.android.internal.view.IDropPermissions;
+
 import java.util.ArrayList;
 
 /**
@@ -428,7 +429,7 @@
     // Tell the drop target about the data.  Returns 'true' if we can immediately
     // dispatch the global drag-ended message, 'false' if we need to wait for a
     // result from the recipient.
-    boolean notifyDropLw(WindowState touchedWin, DropPermissionHolder dropPermissionHolder,
+    boolean notifyDropLw(WindowState touchedWin, IDropPermissions dropPermissions,
             float x, float y) {
         if (mAnimation != null) {
             return false;
@@ -449,7 +450,7 @@
         final int myPid = Process.myPid();
         final IBinder token = touchedWin.mClient.asBinder();
         DragEvent evt = obtainDragEvent(touchedWin, DragEvent.ACTION_DROP, x, y,
-                null, null, mData, dropPermissionHolder, false);
+                null, null, mData, dropPermissions, false);
         try {
             touchedWin.mClient.dispatchDragEvent(evt);
 
@@ -516,7 +517,7 @@
     private static DragEvent obtainDragEvent(WindowState win, int action,
             float x, float y, Object localState,
             ClipDescription description, ClipData data,
-            DropPermissionHolder dropPermissionHolder,
+            IDropPermissions dropPermissions,
             boolean result) {
         float winX = x - win.mFrame.left;
         float winY = y - win.mFrame.top;
@@ -525,7 +526,7 @@
             winY *= win.mGlobalScale;
         }
         return DragEvent.obtain(action, winX, winY, localState, description, data,
-                dropPermissionHolder, result);
+                dropPermissions, result);
     }
 
     boolean stepAnimationLocked(long currentTimeMs) {
diff --git a/services/core/java/com/android/server/wm/DropPermissionsHandler.java b/services/core/java/com/android/server/wm/DropPermissionsHandler.java
new file mode 100644
index 0000000..2ac1ef4
--- /dev/null
+++ b/services/core/java/com/android/server/wm/DropPermissionsHandler.java
@@ -0,0 +1,86 @@
+/*
+** Copyright 2015, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+package com.android.server.wm;
+
+import android.app.ActivityManagerNative;
+import android.content.ClipData;
+import android.net.Uri;
+import android.os.Binder;
+import android.os.IBinder;
+import android.os.RemoteException;
+
+import com.android.internal.view.IDropPermissions;
+
+import java.util.ArrayList;
+
+class DropPermissionsHandler extends IDropPermissions.Stub {
+
+    private final int mSourceUid;
+    private final String mTargetPackage;
+    private final int mMode;
+    private final int mSourceUserId;
+    private final int mTargetUserId;
+
+    private final ArrayList<Uri> mUris = new ArrayList<Uri>();
+
+    private IBinder mPermissionOwner = null;
+
+    DropPermissionsHandler(ClipData clipData, int sourceUid, String targetPackage, int mode,
+            int sourceUserId, int targetUserId) {
+        mSourceUid = sourceUid;
+        mTargetPackage = targetPackage;
+        mMode = mode;
+        mSourceUserId = sourceUserId;
+        mTargetUserId = targetUserId;
+
+        clipData.collectUris(mUris);
+    }
+
+    @Override
+    public void take() throws RemoteException {
+        if (mPermissionOwner != null) {
+            return;
+        }
+
+        mPermissionOwner = ActivityManagerNative.getDefault().newUriPermissionOwner("drop");
+
+        long origId = Binder.clearCallingIdentity();
+        try {
+            for (int i = 0; i < mUris.size(); i++) {
+                ActivityManagerNative.getDefault().grantUriPermissionFromOwner(
+                        mPermissionOwner, mSourceUid, mTargetPackage, mUris.get(i), mMode,
+                        mSourceUserId, mTargetUserId);
+            }
+        } finally {
+            Binder.restoreCallingIdentity(origId);
+        }
+    }
+
+    @Override
+    public void release() throws RemoteException {
+        if (mPermissionOwner == null) {
+            return;
+        }
+
+        for (int i = 0; i < mUris.size(); ++i) {
+            ActivityManagerNative.getDefault().revokeUriPermissionFromOwner(
+                    mPermissionOwner, mUris.get(i), mMode, mSourceUserId);
+        }
+
+        mPermissionOwner = null;
+    }
+}
diff --git a/services/core/java/com/android/server/wm/WindowAnimator.java b/services/core/java/com/android/server/wm/WindowAnimator.java
index 6a5183f..62d4f36 100644
--- a/services/core/java/com/android/server/wm/WindowAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowAnimator.java
@@ -782,7 +782,7 @@
         SurfaceControl.openTransaction();
         try {
             for (int i = mService.mDisplayContents.size() - 1; i >= 0; i--) {
-                DisplayContent display = mService.mDisplayContents.get(i);
+                DisplayContent display = mService.mDisplayContents.valueAt(i);
                 final WindowList windows = mService.getWindowListLocked(display.getDisplayId());
                 for (int j = windows.size() - 1; j >= 0; j--) {
                     windows.get(j).maybeRemoveReplacedWindow();
diff --git a/services/core/java/com/android/server/wm/WindowLayersController.java b/services/core/java/com/android/server/wm/WindowLayersController.java
new file mode 100644
index 0000000..4a77b22
--- /dev/null
+++ b/services/core/java/com/android/server/wm/WindowLayersController.java
@@ -0,0 +1,202 @@
+package com.android.server.wm;
+
+import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER;
+import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_LAYERS;
+import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
+import static com.android.server.wm.WindowManagerService.WINDOW_LAYER_MULTIPLIER;
+
+import android.app.ActivityManager.StackId;
+import android.util.Slog;
+import android.view.Display;
+
+import java.io.PrintWriter;
+
+/**
+ * Controller for assigning layers to windows on the display.
+ *
+ * This class encapsulates general algorithm for assigning layers and special rules that we need to
+ * apply on top. The general algorithm goes through windows from bottom to the top and the higher
+ * the window is, the higher layer is assigned. The final layer is equal to base layer +
+ * adjustment from the order. This means that the window list is assumed to be ordered roughly by
+ * the base layer (there are exceptions, e.g. due to keyguard and wallpaper and they need to be
+ * handled with care, because they break the algorithm).
+ *
+ * On top of the general algorithm we add special rules, that govern such amazing things as:
+ * <li>IME (which has higher base layer, but will be positioned above application windows)</li>
+ * <li>docked/pinned windows (that need to be lifted above other application windows, including
+ * animations)
+ * <li>dock divider (which needs to live above applications, but below IME)</li>
+ * <li>replaced windows, which need to live above their normal level, because they anticipate
+ * an animation</li>.
+ */
+public class WindowLayersController {
+    private final WindowManagerService mService;
+
+    private int mInputMethodAnimLayerAdjustment;
+
+    public WindowLayersController(WindowManagerService service) {
+        mService = service;
+    }
+
+    private int mHighestApplicationLayer = 0;
+    private WindowState mPinnedWindow = null;
+    private WindowState mDockedWindow = null;
+    private WindowState mDockDivider = null;
+    private WindowState mImeWindow = null;
+    private WindowState mReplacingWindow = null;
+
+    final void assignLayersLocked(WindowList windows) {
+        if (DEBUG_LAYERS) Slog.v(TAG_WM, "Assigning layers based on windows=" + windows,
+                new RuntimeException("here").fillInStackTrace());
+
+        clear();
+        int curBaseLayer = 0;
+        int curLayer = 0;
+        boolean anyLayerChanged = false;
+        for (int i = 0, windowCount = windows.size(); i < windowCount; i++) {
+            final WindowState w = windows.get(i);
+            boolean layerChanged = false;
+
+            int oldLayer = w.mLayer;
+            if (w.mBaseLayer == curBaseLayer || w.mIsImWindow || (i > 0 && w.mIsWallpaper)) {
+                curLayer += WINDOW_LAYER_MULTIPLIER;
+                w.mLayer = curLayer;
+            } else {
+                curBaseLayer = curLayer = w.mBaseLayer;
+                w.mLayer = curLayer;
+            }
+            if (w.mLayer != oldLayer) {
+                layerChanged = true;
+                anyLayerChanged = true;
+            }
+
+            final WindowStateAnimator winAnimator = w.mWinAnimator;
+            oldLayer = winAnimator.mAnimLayer;
+            winAnimator.mAnimLayer = w.mLayer + w.getAnimLayerAdjustment() +
+                    getSpecialWindowAnimLayerAdjustment(w);
+            if (winAnimator.mAnimLayer != oldLayer) {
+                layerChanged = true;
+                anyLayerChanged = true;
+            }
+
+            if (w.mAppToken != null) {
+                mHighestApplicationLayer = Math.max(mHighestApplicationLayer,
+                        winAnimator.mAnimLayer);
+            }
+            collectSpecialWindows(w);
+
+            if (layerChanged) {
+                w.scheduleAnimationIfDimming();
+            }
+        }
+
+        adjustSpecialWindows();
+
+        //TODO (multidisplay): Magnification is supported only for the default display.
+        if (mService.mAccessibilityController != null && anyLayerChanged
+                && windows.get(windows.size() - 1).getDisplayId() == Display.DEFAULT_DISPLAY) {
+            mService.mAccessibilityController.onWindowLayersChangedLocked();
+        }
+
+        if (DEBUG_LAYERS) logDebugLayers(windows);
+    }
+
+    void setInputMethodAnimLayerAdjustment(int adj) {
+        if (DEBUG_LAYERS) Slog.v(TAG_WM, "Setting im layer adj to " + adj);
+        mInputMethodAnimLayerAdjustment = adj;
+        final WindowState imw = mService.mInputMethodWindow;
+        if (imw != null) {
+            imw.mWinAnimator.mAnimLayer = imw.mLayer + adj;
+            if (DEBUG_LAYERS) Slog.v(TAG_WM, "IM win " + imw
+                    + " anim layer: " + imw.mWinAnimator.mAnimLayer);
+            for (int i = imw.mChildWindows.size() - 1; i >= 0; i--) {
+                final WindowState childWindow = imw.mChildWindows.get(i);
+                childWindow.mWinAnimator.mAnimLayer = childWindow.mLayer + adj;
+                if (DEBUG_LAYERS) Slog.v(TAG_WM, "IM win " + childWindow
+                        + " anim layer: " + childWindow.mWinAnimator.mAnimLayer);
+            }
+        }
+        for (int i = mService.mInputMethodDialogs.size() - 1; i >= 0; i--) {
+            final WindowState dialog = mService.mInputMethodDialogs.get(i);
+            dialog.mWinAnimator.mAnimLayer = dialog.mLayer + adj;
+            if (DEBUG_LAYERS) Slog.v(TAG_WM, "IM win " + imw
+                    + " anim layer: " + dialog.mWinAnimator.mAnimLayer);
+        }
+    }
+
+    int getSpecialWindowAnimLayerAdjustment(WindowState win) {
+        if (win.mIsImWindow) {
+            return mInputMethodAnimLayerAdjustment;
+        } else if (win.mIsWallpaper) {
+            return mService.mWallpaperControllerLocked.getAnimLayerAdjustment();
+        }
+        return 0;
+    }
+
+    private void logDebugLayers(WindowList windows) {
+        for (int i = 0, n = windows.size(); i < n; i++) {
+            final WindowState w = windows.get(i);
+            final WindowStateAnimator winAnimator = w.mWinAnimator;
+            Slog.v(TAG_WM, "Assign layer " + w + ": " + "mBase=" + w.mBaseLayer
+                    + " mLayer=" + w.mLayer + (w.mAppToken == null
+                    ? "" : " mAppLayer=" + w.mAppToken.mAppAnimator.animLayerAdjustment)
+                    + " =mAnimLayer=" + winAnimator.mAnimLayer);
+        }
+    }
+
+    private void clear() {
+        mHighestApplicationLayer = 0;
+        mImeWindow = null;
+        mPinnedWindow = null;
+        mDockedWindow = null;
+        mDockDivider = null;
+    }
+
+    private void collectSpecialWindows(WindowState w) {
+        if (w.mIsImWindow) {
+            mImeWindow = w;
+        } else if (w.mAttrs.type == TYPE_DOCK_DIVIDER) {
+            mDockDivider = w;
+        } else {
+            final TaskStack stack = w.getStack();
+            if (stack.mStackId == StackId.PINNED_STACK_ID) {
+                mPinnedWindow = w;
+            } else if (stack.mStackId == StackId.DOCKED_STACK_ID) {
+                mDockedWindow = w;
+            }
+        }
+    }
+
+    private void adjustSpecialWindows() {
+        int layer = mHighestApplicationLayer + 1;
+        // For pinned and docked stack window, we want to make them above other windows
+        // also when these windows are animating.
+        layer = assignAndIncreaseLayerIfNeeded(mDockedWindow, layer);
+        layer = assignAndIncreaseLayerIfNeeded(mDockDivider, layer);
+        // We know that we will be animating a relaunching window in the near future,
+        // which will receive a z-order increase. We want the replaced window to
+        // immediately receive the same treatment, e.g. to be above the dock divider.
+        layer = assignAndIncreaseLayerIfNeeded(mReplacingWindow, layer);
+        layer = assignAndIncreaseLayerIfNeeded(mPinnedWindow, layer);
+        layer = assignAndIncreaseLayerIfNeeded(mImeWindow, layer);
+    }
+
+    private int assignAndIncreaseLayerIfNeeded(WindowState win, int layer) {
+        if (win != null) {
+            win.mLayer = layer;
+            win.mWinAnimator.mAnimLayer = layer;
+            layer++;
+        }
+        return layer;
+    }
+
+    void dump(PrintWriter pw, String s) {
+        if (mInputMethodAnimLayerAdjustment != 0 ||
+                mService.mWallpaperControllerLocked.getAnimLayerAdjustment() != 0) {
+            pw.print("  mInputMethodAnimLayerAdjustment=");
+            pw.print(mInputMethodAnimLayerAdjustment);
+            pw.print("  mWallpaperAnimLayerAdjustment=");
+            pw.println(mService.mWallpaperControllerLocked.getAnimLayerAdjustment());
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 456c416..6385caa 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -66,7 +66,6 @@
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_FOCUS_LIGHT;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_INPUT_METHOD;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_KEYGUARD;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_LAYERS;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_LAYOUT;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ORIENTATION;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_RESIZE;
@@ -92,7 +91,6 @@
 import android.animation.ValueAnimator;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.app.ActivityManager.StackId;
 import android.app.ActivityManagerNative;
 import android.app.AppOpsManager;
 import android.app.IActivityManager;
@@ -153,7 +151,6 @@
 import android.view.Choreographer;
 import android.view.Display;
 import android.view.DisplayInfo;
-import android.view.DropPermissionHolder;
 import android.view.Gravity;
 import android.view.IAppTransitionAnimationSpecsFuture;
 import android.view.IApplicationToken;
@@ -567,7 +564,6 @@
 
     /** If true hold off on modifying the animation layer of mInputMethodTarget */
     boolean mInputMethodTargetWaitingAnim;
-    int mInputMethodAnimLayerAdjustment;
 
     WindowState mInputMethodWindow = null;
     final ArrayList<WindowState> mInputMethodDialogs = new ArrayList<>();
@@ -609,6 +605,8 @@
 
     WallpaperController mWallpaperControllerLocked;
 
+    final WindowLayersController mLayersController;
+
     boolean mAnimateWallpaperWithTarget;
 
     AppWindowToken mFocusedApp = null;
@@ -758,13 +756,12 @@
     private boolean completeDropLw(float x, float y) {
         WindowState dropTargetWin = mDragState.getDropTargetWinLw(x, y);
 
-        DropPermissionHolder dropPermissionHolder = null;
+        DropPermissionsHandler dropPermissions = null;
         if (dropTargetWin != null &&
                 (mDragState.mFlags & View.DRAG_FLAG_GLOBAL) != 0 &&
                 (mDragState.mFlags & DRAG_FLAGS_URI_ACCESS) != 0) {
-            dropPermissionHolder = new DropPermissionHolder(
+            dropPermissions = new DropPermissionsHandler(
                     mDragState.mData,
-                    mActivityManager,
                     mDragState.mUid,
                     dropTargetWin.getOwningPackage(),
                     mDragState.mFlags & DRAG_FLAGS_URI_PERMISSIONS,
@@ -772,7 +769,7 @@
                     UserHandle.getUserId(dropTargetWin.getOwningUid()));
         }
 
-        return mDragState.notifyDropLw(dropTargetWin, dropPermissionHolder, x, y);
+        return mDragState.notifyDropLw(dropTargetWin, dropPermissions, x, y);
     }
 
     /**
@@ -882,6 +879,7 @@
 
         mWallpaperControllerLocked = new WallpaperController(this);
         mWindowPlacerLocked = new WindowSurfacePlacer(this);
+        mLayersController = new WindowLayersController(this);
 
         LocalServices.addService(WindowManagerPolicy.class, mPolicy);
 
@@ -1511,9 +1509,10 @@
                 mInputMethodTarget = w;
                 mInputMethodTargetWaitingAnim = false;
                 if (w.mAppToken != null) {
-                    setInputMethodAnimLayerAdjustment(w.mAppToken.mAppAnimator.animLayerAdjustment);
+                    mLayersController.setInputMethodAnimLayerAdjustment(
+                            w.mAppToken.mAppAnimator.animLayerAdjustment);
                 } else {
-                    setInputMethodAnimLayerAdjustment(0);
+                    mLayersController.setInputMethodAnimLayerAdjustment(0);
                 }
             }
             return i+1;
@@ -1522,7 +1521,7 @@
             if (DEBUG_INPUT_METHOD) Slog.w(TAG_WM, "Moving IM target from " + curTarget + " to null."
                     + (HIDE_STACK_CRAWLS ? "" : " Callers=" + Debug.getCallers(4)));
             mInputMethodTarget = null;
-            setInputMethodAnimLayerAdjustment(0);
+            mLayersController.setInputMethodAnimLayerAdjustment(0);
         }
         return -1;
     }
@@ -1544,33 +1543,6 @@
         moveInputMethodDialogsLocked(pos);
     }
 
-    void setInputMethodAnimLayerAdjustment(int adj) {
-        if (DEBUG_LAYERS) Slog.v(TAG_WM, "Setting im layer adj to " + adj);
-        mInputMethodAnimLayerAdjustment = adj;
-        WindowState imw = mInputMethodWindow;
-        if (imw != null) {
-            imw.mWinAnimator.mAnimLayer = imw.mLayer + adj;
-            if (DEBUG_LAYERS) Slog.v(TAG_WM, "IM win " + imw
-                    + " anim layer: " + imw.mWinAnimator.mAnimLayer);
-            int wi = imw.mChildWindows.size();
-            while (wi > 0) {
-                wi--;
-                WindowState cw = imw.mChildWindows.get(wi);
-                cw.mWinAnimator.mAnimLayer = cw.mLayer + adj;
-                if (DEBUG_LAYERS) Slog.v(TAG_WM, "IM win " + cw
-                        + " anim layer: " + cw.mWinAnimator.mAnimLayer);
-            }
-        }
-        int di = mInputMethodDialogs.size();
-        while (di > 0) {
-            di --;
-            imw = mInputMethodDialogs.get(di);
-            imw.mWinAnimator.mAnimLayer = imw.mLayer + adj;
-            if (DEBUG_LAYERS) Slog.v(TAG_WM, "IM win " + imw
-                    + " anim layer: " + imw.mWinAnimator.mAnimLayer);
-        }
-    }
-
     private int tmpRemoveWindowLocked(int interestingPos, WindowState win) {
         WindowList windows = win.getWindowList();
         int wpos = windows.indexOf(win);
@@ -1769,7 +1741,7 @@
         }
 
         if (needAssignLayers) {
-            assignLayersLocked(windows);
+            mLayersController.assignLayersLocked(windows);
         }
 
         return true;
@@ -2061,7 +2033,7 @@
                 moveInputMethodWindowsIfNeededLocked(false);
             }
 
-            assignLayersLocked(displayContent.getWindowList());
+            mLayersController.assignLayersLocked(displayContent.getWindowList());
             // Don't do layout here, the window must call
             // relayout to be displayed, so we'll do it there.
 
@@ -2388,7 +2360,7 @@
         if (windows != null) {
             windows.remove(win);
             if (!mWindowPlacerLocked.isInLayout()) {
-                assignLayersLocked(windows);
+                mLayersController.assignLayersLocked(windows);
                 win.setDisplayLayoutNeeded();
                 mWindowPlacerLocked.performSurfacePlacement();
                 if (win.mAppToken != null) {
@@ -2743,7 +2715,7 @@
                 // its layer recomputed.  However, if the IME was hidden
                 // and isn't actually moved in the list, its layer may be
                 // out of data so we make sure to recompute it.
-                assignLayersLocked(win.getWindowList());
+                mLayersController.assignLayersLocked(win.getWindowList());
             }
 
             if (wallpaperMayMove) {
@@ -4586,7 +4558,7 @@
 
         if (!updateFocusedWindowLocked(UPDATE_FOCUS_WILL_PLACE_SURFACES,
                 false /*updateInputWindows*/)) {
-            assignLayersLocked(displayContent.getWindowList());
+            mLayersController.assignLayersLocked(displayContent.getWindowList());
         }
 
         mInputMonitor.setUpdateInputWindowsNeededLw();
@@ -8662,102 +8634,6 @@
         Arrays.fill(mRebuildTmp, null);
     }
 
-    final void assignLayersLocked(WindowList windows) {
-        int N = windows.size();
-        int curBaseLayer = 0;
-        int curLayer = 0;
-        int i;
-
-        if (DEBUG_LAYERS) Slog.v(TAG_WM, "Assigning layers based on windows=" + windows,
-                new RuntimeException("here").fillInStackTrace());
-
-        boolean anyLayerChanged = false;
-
-        for (i=0; i<N; i++) {
-            final WindowState w = windows.get(i);
-            final WindowStateAnimator winAnimator = w.mWinAnimator;
-            boolean layerChanged = false;
-            int oldLayer = w.mLayer;
-            if (w.mBaseLayer == curBaseLayer || w.mIsImWindow || (i > 0 && w.mIsWallpaper)) {
-                curLayer += WINDOW_LAYER_MULTIPLIER;
-                w.mLayer = curLayer;
-            } else {
-                curBaseLayer = curLayer = w.mBaseLayer;
-                w.mLayer = curLayer;
-            }
-            if (w.mLayer != oldLayer) {
-                layerChanged = true;
-                anyLayerChanged = true;
-            }
-            final AppWindowToken wtoken = w.mAppToken;
-            oldLayer = winAnimator.mAnimLayer;
-            if (w.mTargetAppToken != null) {
-                winAnimator.mAnimLayer =
-                        w.mLayer + w.mTargetAppToken.mAppAnimator.animLayerAdjustment;
-            } else if (wtoken != null) {
-                winAnimator.mAnimLayer =
-                        w.mLayer + wtoken.mAppAnimator.animLayerAdjustment;
-                forceHigherLayerIfNeeded(w, winAnimator, wtoken);
-            } else {
-                winAnimator.mAnimLayer = w.mLayer;
-            }
-            if (w.mIsImWindow) {
-                winAnimator.mAnimLayer += mInputMethodAnimLayerAdjustment;
-            } else if (w.mIsWallpaper) {
-                winAnimator.mAnimLayer += mWallpaperControllerLocked.getAnimLayerAdjustment();
-            }
-            if (winAnimator.mAnimLayer != oldLayer) {
-                layerChanged = true;
-                anyLayerChanged = true;
-            }
-            final DimLayer.DimLayerUser dimLayerUser = w.getDimLayerUser();
-            final DisplayContent displayContent = w.getDisplayContent();
-            if (layerChanged && dimLayerUser != null && displayContent != null &&
-                    displayContent.mDimLayerController.isDimming(dimLayerUser, winAnimator)) {
-                // Force an animation pass just to update the mDimLayer layer.
-                scheduleAnimationLocked();
-            }
-            if (DEBUG_LAYERS) Slog.v(TAG_WM, "Assign layer " + w + ": "
-                    + "mBase=" + w.mBaseLayer
-                    + " mLayer=" + w.mLayer
-                    + (wtoken == null ?
-                            "" : " mAppLayer=" + wtoken.mAppAnimator.animLayerAdjustment)
-                    + " =mAnimLayer=" + winAnimator.mAnimLayer);
-            //System.out.println(
-            //    "Assigned layer " + curLayer + " to " + w.mClient.asBinder());
-        }
-
-        //TODO (multidisplay): Magnification is supported only for the default display.
-        if (mAccessibilityController != null && anyLayerChanged
-                && windows.get(windows.size() - 1).getDisplayId() == Display.DEFAULT_DISPLAY) {
-            mAccessibilityController.onWindowLayersChangedLocked();
-        }
-    }
-
-    private void forceHigherLayerIfNeeded(WindowState w, WindowStateAnimator winAnimator,
-            AppWindowToken wtoken) {
-        boolean force = false;
-
-        if (w.mWillReplaceWindow) {
-            // We know that we will be animating a relaunching window in the near future,
-            // which will receive a z-order increase. We want the replaced window to
-            // immediately receive the same treatment, e.g. to be above the dock divider.
-            force = true;
-        }
-        if (!force) {
-            final TaskStack stack = w.getStack();
-            if (stack != null && (StackId.shouldIncreaseApplicationWindowLayer(stack.mStackId))) {
-                // For pinned and docked stack window, we want to make them above other windows
-                // also when these windows are animating.
-                force = true;
-            }
-        }
-        if (force) {
-            w.mLayer += TYPE_LAYER_OFFSET;
-            winAnimator.mAnimLayer += TYPE_LAYER_OFFSET;
-        }
-    }
-
     void makeWindowFreezingScreenIfNeededLocked(WindowState w) {
         // If the screen is currently frozen or off, then keep
         // it frozen/off until this window draws at its new
@@ -9128,7 +9004,7 @@
                 } else if (mode == UPDATE_FOCUS_WILL_PLACE_SURFACES) {
                     // Client will do the layout, but we need to assign layers
                     // for handleNewWindowLocked() below.
-                    assignLayersLocked(displayContent.getWindowList());
+                    mLayersController.assignLayersLocked(displayContent.getWindowList());
                 }
             }
 
@@ -9816,13 +9692,7 @@
             }
             mWindowPlacerLocked.dump(pw, "  ");
             mWallpaperControllerLocked.dump(pw, "  ");
-            if (mInputMethodAnimLayerAdjustment != 0 ||
-                    mWallpaperControllerLocked.getAnimLayerAdjustment() != 0) {
-                pw.print("  mInputMethodAnimLayerAdjustment=");
-                        pw.print(mInputMethodAnimLayerAdjustment);
-                        pw.print("  mWallpaperAnimLayerAdjustment=");
-                        pw.println(mWallpaperControllerLocked.getAnimLayerAdjustment());
-            }
+            mLayersController.dump(pw, "  ");
             pw.print("  mSystemBooted="); pw.print(mSystemBooted);
                     pw.print(" mDisplayEnabled="); pw.println(mDisplayEnabled);
             if (needsLayout()) {
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index e4a6806..c636185 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -74,6 +74,7 @@
 import static android.view.WindowManager.LayoutParams.LAST_SUB_WINDOW;
 import static android.view.WindowManager.LayoutParams.MATCH_PARENT;
 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_COMPATIBLE_WINDOW;
+import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_LAYOUT_CHILD_WINDOW_IN_PARENT_FRAME;
 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_KEYGUARD;
 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_WILL_NOT_REPLACE_ON_RELAUNCH;
 import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE;
@@ -603,9 +604,15 @@
         mHaveFrame = true;
 
         final Task task = getTask();
-        final boolean nonFullscreenTask = task != null && !task.isFullscreen();
+        final boolean fullscreenTask = task == null || task.isFullscreen();
         final boolean freeformWorkspace = task != null && task.inFreeformWorkspace();
-        if (nonFullscreenTask) {
+
+        if (fullscreenTask || (isChildWindow()
+                && (mAttrs.privateFlags & PRIVATE_FLAG_LAYOUT_CHILD_WINDOW_IN_PARENT_FRAME) != 0)) {
+            // We use the parent frame as the containing frame for fullscreen and child windows
+            mContainingFrame.set(pf);
+            mDisplayFrame.set(df);
+        } else {
             task.getBounds(mContainingFrame);
             final WindowState imeWin = mService.mInputMethodWindow;
             if (imeWin != null && imeWin.isVisibleNow() && mService.mInputMethodTarget == this
@@ -623,9 +630,6 @@
                 }
             }
             mDisplayFrame.set(mContainingFrame);
-        } else {
-            mContainingFrame.set(pf);
-            mDisplayFrame.set(df);
         }
 
         final int pw = mContainingFrame.width();
@@ -1273,6 +1277,29 @@
         mHasSurface = hasSurface;
     }
 
+    int getAnimLayerAdjustment() {
+        if (mTargetAppToken != null) {
+            return mTargetAppToken.mAppAnimator.animLayerAdjustment;
+        } else if (mAppToken != null) {
+            return mAppToken.mAppAnimator.animLayerAdjustment;
+        } else {
+            // Nothing is animating, so there is no animation adjustment.
+            return 0;
+        }
+    }
+
+    void scheduleAnimationIfDimming() {
+        if (mDisplayContent == null) {
+            return;
+        }
+        final DimLayer.DimLayerUser dimLayerUser = getDimLayerUser();
+        if (dimLayerUser != null && mDisplayContent.mDimLayerController.isDimming(
+                dimLayerUser, mWinAnimator)) {
+            // Force an animation pass just to update the mDimLayer layer.
+            mService.scheduleAnimationLocked();
+        }
+    }
+
     private final class DeadWindowEventReceiver extends InputEventReceiver {
         DeadWindowEventReceiver(InputChannel inputChannel) {
             super(inputChannel, mService.mH.getLooper());
@@ -2253,7 +2280,7 @@
         }
 
         if (nonFullscreenTask) {
-            // Make sure window fits in containing frame since it is in a non-fullscreen stack as
+            // Make sure window fits in containing frame since it is in a non-fullscreen task as
             // required by {@link Gravity#apply} call.
             w = Math.min(w, pw);
             h = Math.min(h, ph);
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index d4001cd..7605af0 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -384,12 +384,8 @@
         if (mAnimator.mWindowDetachedWallpaper == mWin) {
             mAnimator.mWindowDetachedWallpaper = null;
         }
-        mAnimLayer = mWin.mLayer;
-        if (mWin.mIsImWindow) {
-            mAnimLayer += mService.mInputMethodAnimLayerAdjustment;
-        } else if (mIsWallpaper) {
-            mAnimLayer += mWallpaperControllerLocked.getAnimLayerAdjustment();
-        }
+        mAnimLayer = mWin.mLayer
+                + mService.mLayersController.getSpecialWindowAnimLayerAdjustment(mWin);
         if (DEBUG_LAYERS) Slog.v(TAG, "Stepping win " + this + " anim layer: " + mAnimLayer);
         mHasTransformation = false;
         mHasLocalTransformation = false;
diff --git a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
index 160c97f..cbfb201 100644
--- a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
+++ b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
@@ -524,7 +524,7 @@
             }
 
             for (DisplayContent displayContent : displayList) {
-                mService.assignLayersLocked(displayContent.getWindowList());
+                mService.mLayersController.assignLayersLocked(displayContent.getWindowList());
                 displayContent.layoutNeeded = true;
             }
         }
@@ -599,7 +599,7 @@
 
                 if ((displayContent.pendingLayoutChanges & FINISH_LAYOUT_REDO_WALLPAPER) != 0 &&
                         mWallpaperControllerLocked.adjustWallpaperWindows()) {
-                    mService.assignLayersLocked(windows);
+                    mService.mLayersController.assignLayersLocked(windows);
                     displayContent.layoutNeeded = true;
                 }
 
@@ -1134,7 +1134,7 @@
         // TODO(multidisplay): IMEs are only supported on the default display.
         if (windows == mService.getDefaultWindowListLocked()
                 && !mService.moveInputMethodWindowsIfNeededLocked(true)) {
-            mService.assignLayersLocked(windows);
+            mService.mLayersController.assignLayersLocked(windows);
         }
         mService.updateFocusedWindowLocked(UPDATE_FOCUS_PLACING_SURFACES,
                 true /*updateInputWindows*/);
diff --git a/services/core/jni/com_android_server_tv_TvInputHal.cpp b/services/core/jni/com_android_server_tv_TvInputHal.cpp
index 01acdef..6c640ba 100644
--- a/services/core/jni/com_android_server_tv_TvInputHal.cpp
+++ b/services/core/jni/com_android_server_tv_TvInputHal.cpp
@@ -54,6 +54,7 @@
     jmethodID maxWidth;
     jmethodID maxHeight;
     jmethodID generation;
+    jmethodID flags;
     jmethodID build;
 } gTvStreamConfigBuilderClassInfo;
 
@@ -239,7 +240,7 @@
 
     int addOrUpdateStream(int deviceId, int streamId, const sp<Surface>& surface);
     int removeStream(int deviceId, int streamId);
-    const tv_stream_config_t* getStreamConfigs(int deviceId, int* numConfigs);
+    const tv_stream_config_ext_t* getStreamConfigs(int deviceId, int* numConfigs);
 
     void onDeviceAvailable(const tv_input_device_info_t& info);
     void onDeviceUnavailable(int deviceId);
@@ -288,10 +289,15 @@
     sp<Looper> mLooper;
 
     KeyedVector<int, KeyedVector<int, Connection> > mConnections;
+
+    tv_stream_config_ext_t* mConfigBuffer;
+    int mConfigBufferSize;
 };
 
 JTvInputHal::JTvInputHal(JNIEnv* env, jobject thiz, tv_input_device_t* device,
-        const sp<Looper>& looper) {
+        const sp<Looper>& looper)
+    : mConfigBuffer(NULL),
+      mConfigBufferSize(0) {
     mThiz = env->NewWeakGlobalRef(thiz);
     mDevice = device;
     mCallback.notify = &JTvInputHal::notify;
@@ -306,6 +312,10 @@
     JNIEnv* env = AndroidRuntime::getJNIEnv();
     env->DeleteWeakGlobalRef(mThiz);
     mThiz = NULL;
+
+    if (mConfigBuffer != NULL) {
+        delete[] mConfigBuffer;
+    }
 }
 
 JTvInputHal* JTvInputHal::createInstance(JNIEnv* env, jobject thiz, const sp<Looper>& looper) {
@@ -354,15 +364,14 @@
     if (connection.mSourceHandle == NULL && connection.mThread == NULL) {
         // Need to configure stream
         int numConfigs = 0;
-        const tv_stream_config_t* configs = NULL;
-        if (mDevice->get_stream_configurations(
-                mDevice, deviceId, &numConfigs, &configs) != 0) {
+        const tv_stream_config_ext_t* configs = getStreamConfigs(deviceId, &numConfigs);
+        if (configs == NULL) {
             ALOGE("Couldn't get stream configs");
             return UNKNOWN_ERROR;
         }
         int configIndex = -1;
         for (int i = 0; i < numConfigs; ++i) {
-            if (configs[i].stream_id == streamId) {
+            if (configs[i].config.stream_id == streamId) {
                 configIndex = i;
                 break;
             }
@@ -371,13 +380,13 @@
             ALOGE("Cannot find a config with given stream ID: %d", streamId);
             return BAD_VALUE;
         }
-        connection.mStreamType = configs[configIndex].type;
+        connection.mStreamType = configs[configIndex].config.type;
 
         tv_stream_t stream;
-        stream.stream_id = configs[configIndex].stream_id;
+        stream.stream_id = configs[configIndex].config.stream_id;
         if (connection.mStreamType == TV_STREAM_TYPE_BUFFER_PRODUCER) {
-            stream.buffer_producer.width = configs[configIndex].max_video_width;
-            stream.buffer_producer.height = configs[configIndex].max_video_height;
+            stream.buffer_producer.width = configs[configIndex].config.max_video_width;
+            stream.buffer_producer.height = configs[configIndex].config.max_video_height;
         }
         if (mDevice->open_stream(mDevice, deviceId, &stream) != 0) {
             ALOGE("Couldn't add stream");
@@ -431,12 +440,33 @@
     return NO_ERROR;
 }
 
-const tv_stream_config_t* JTvInputHal::getStreamConfigs(int deviceId, int* numConfigs) {
-    const tv_stream_config_t* configs = NULL;
-    if (mDevice->get_stream_configurations(
-            mDevice, deviceId, numConfigs, &configs) != 0) {
-        ALOGE("Couldn't get stream configs");
-        return NULL;
+const tv_stream_config_ext_t* JTvInputHal::getStreamConfigs(int deviceId, int* numConfigs) {
+    const tv_stream_config_ext_t* configs = NULL;
+    if (mDevice->common.version >= TV_INPUT_DEVICE_API_VERSION_0_2) {
+        if (mDevice->get_stream_configurations_ext(
+                mDevice, deviceId, numConfigs, &configs) != 0) {
+            ALOGE("Couldn't get stream configs");
+            return NULL;
+        }
+    } else {
+        const tv_stream_config_t* oldConfigs;
+        if (mDevice->get_stream_configurations(
+                mDevice, deviceId, numConfigs, &oldConfigs) != 0) {
+            ALOGE("Couldn't get stream configs");
+            return NULL;
+        }
+        if (mConfigBufferSize < *numConfigs) {
+            mConfigBufferSize = (*numConfigs / 16 + 1) * 16;
+            if (mConfigBuffer != NULL) {
+                delete[] mConfigBuffer;
+            }
+            mConfigBuffer = new tv_stream_config_ext_t[mConfigBufferSize];
+        }
+        for (int i = 0; i < *numConfigs; ++i) {
+            mConfigBuffer[i].config = oldConfigs[i];
+            mConfigBuffer[i].flags = 0;
+        }
+        configs = mConfigBuffer;
     }
     return configs;
 }
@@ -629,7 +659,7 @@
         jlong ptr, jint deviceId, jint generation) {
     JTvInputHal* tvInputHal = (JTvInputHal*)ptr;
     int numConfigs = 0;
-    const tv_stream_config_t* configs = tvInputHal->getStreamConfigs(deviceId, &numConfigs);
+    const tv_stream_config_ext_t* configs = tvInputHal->getStreamConfigs(deviceId, &numConfigs);
 
     jobjectArray result = env->NewObjectArray(numConfigs, gTvStreamConfigClassInfo.clazz, NULL);
     for (int i = 0; i < numConfigs; ++i) {
@@ -637,15 +667,20 @@
                 gTvStreamConfigBuilderClassInfo.clazz,
                 gTvStreamConfigBuilderClassInfo.constructor);
         env->CallObjectMethod(
-                builder, gTvStreamConfigBuilderClassInfo.streamId, configs[i].stream_id);
+                builder, gTvStreamConfigBuilderClassInfo.streamId, configs[i].config.stream_id);
         env->CallObjectMethod(
-                builder, gTvStreamConfigBuilderClassInfo.type, configs[i].type);
+                builder, gTvStreamConfigBuilderClassInfo.type, configs[i].config.type);
         env->CallObjectMethod(
-                builder, gTvStreamConfigBuilderClassInfo.maxWidth, configs[i].max_video_width);
+                builder, gTvStreamConfigBuilderClassInfo.maxWidth,
+                configs[i].config.max_video_width);
         env->CallObjectMethod(
-                builder, gTvStreamConfigBuilderClassInfo.maxHeight, configs[i].max_video_height);
+                builder, gTvStreamConfigBuilderClassInfo.maxHeight,
+                configs[i].config.max_video_height);
         env->CallObjectMethod(
                 builder, gTvStreamConfigBuilderClassInfo.generation, generation);
+        env->CallObjectMethod(
+                builder, gTvStreamConfigBuilderClassInfo.flags,
+                configs[i].flags);
 
         jobject config = env->CallObjectMethod(builder, gTvStreamConfigBuilderClassInfo.build);
 
@@ -737,6 +772,10 @@
             gTvStreamConfigBuilderClassInfo.clazz,
             "generation", "(I)Landroid/media/tv/TvStreamConfig$Builder;");
     GET_METHOD_ID(
+            gTvStreamConfigBuilderClassInfo.flags,
+            gTvStreamConfigBuilderClassInfo.clazz,
+            "flags", "(I)Landroid/media/tv/TvStreamConfig$Builder;");
+    GET_METHOD_ID(
             gTvStreamConfigBuilderClassInfo.build,
             gTvStreamConfigBuilderClassInfo.clazz,
             "build", "()Landroid/media/tv/TvStreamConfig;");
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 810ee6b..2082911 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -16,8 +16,6 @@
 
 package com.android.server.devicepolicy;
 
-import com.google.android.collect.Sets;
-
 import static android.Manifest.permission.MANAGE_CA_CERTIFICATES;
 import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_COMPLEX;
 import static android.app.admin.DevicePolicyManager.WIPE_EXTERNAL_STORAGE;
@@ -28,6 +26,8 @@
 import static org.xmlpull.v1.XmlPullParser.END_TAG;
 import static org.xmlpull.v1.XmlPullParser.TEXT;
 
+import com.google.android.collect.Sets;
+
 import android.Manifest.permission;
 import android.accessibilityservice.AccessibilityServiceInfo;
 import android.accounts.AccountManager;
@@ -193,6 +193,8 @@
     private static final String ATTR_PERMISSION_POLICY = "permission-policy";
 
     private static final String ATTR_DELEGATED_CERT_INSTALLER = "delegated-cert-installer";
+    private static final String ATTR_APPLICATION_RESTRICTIONS_MANAGER
+            = "application-restrictions-manager";
 
     private static final int STATUS_BAR_DISABLE_MASK =
             StatusBarManager.DISABLE_EXPAND |
@@ -322,6 +324,8 @@
 
         boolean doNotAskCredentialsOnBoot = false;
 
+        String mApplicationRestrictionsManagingPackage;
+
         public DevicePolicyData(int userHandle) {
             mUserHandle = userHandle;
         }
@@ -1035,19 +1039,15 @@
                 saveSettingsLocked(policy.mUserHandle);
             }
 
-            if (policy.mDelegatedCertInstallerPackage != null &&
-                    (packageName == null
-                    || packageName.equals(policy.mDelegatedCertInstallerPackage))) {
-                try {
-                    // Check if delegated cert installer package is removed.
-                    if (mIPackageManager.getPackageInfo(
-                            policy.mDelegatedCertInstallerPackage, 0, userHandle) == null) {
-                        policy.mDelegatedCertInstallerPackage = null;
-                        saveSettingsLocked(policy.mUserHandle);
-                    }
-                } catch (RemoteException e) {
-                    // Shouldn't happen
-                }
+            // Check if delegated cert installer or app restrictions managing packages are removed.
+            if (isRemovedPackage(packageName, policy.mDelegatedCertInstallerPackage, userHandle)) {
+                policy.mDelegatedCertInstallerPackage = null;
+                saveSettingsLocked(policy.mUserHandle);
+            }
+            if (isRemovedPackage(
+                    packageName, policy.mApplicationRestrictionsManagingPackage, userHandle)) {
+                policy.mApplicationRestrictionsManagingPackage = null;
+                saveSettingsLocked(policy.mUserHandle);
             }
         }
         if (removed) {
@@ -1056,6 +1056,18 @@
         }
     }
 
+    private boolean isRemovedPackage(String changedPackage, String targetPackage, int userHandle) {
+        try {
+            return targetPackage != null
+                    && (changedPackage == null || changedPackage.equals(targetPackage))
+                    && mIPackageManager.getPackageInfo(targetPackage, 0, userHandle) == null;
+        } catch (RemoteException e) {
+            // Shouldn't happen
+        }
+
+        return false;
+    }
+
     /**
      * Unit test will subclass it to inject mocks.
      */
@@ -1162,6 +1174,10 @@
             mContext.getSystemService(PowerManager.class).goToSleep(time, reason, flags);
         }
 
+        void powerManagerReboot(String reason) {
+            mContext.getSystemService(PowerManager.class).reboot(reason);
+        }
+
         boolean systemPropertiesGetBoolean(String key, boolean def) {
             return SystemProperties.getBoolean(key, def);
         }
@@ -1795,6 +1811,10 @@
                 out.attribute(null, ATTR_DELEGATED_CERT_INSTALLER,
                         policy.mDelegatedCertInstallerPackage);
             }
+            if (policy.mApplicationRestrictionsManagingPackage != null) {
+                out.attribute(null, ATTR_APPLICATION_RESTRICTIONS_MANAGER,
+                        policy.mApplicationRestrictionsManagingPackage);
+            }
 
             final int N = policy.mAdminList.size();
             for (int i=0; i<N; i++) {
@@ -1920,6 +1940,8 @@
             }
             policy.mDelegatedCertInstallerPackage = parser.getAttributeValue(null,
                     ATTR_DELEGATED_CERT_INSTALLER);
+            policy.mApplicationRestrictionsManagingPackage = parser.getAttributeValue(null,
+                    ATTR_APPLICATION_RESTRICTIONS_MANAGER);
 
             type = parser.next();
             int outerDepth = parser.getDepth();
@@ -4815,6 +4837,7 @@
         DevicePolicyData policy = getUserData(userId);
         policy.mPermissionPolicy = DevicePolicyManager.PERMISSION_POLICY_PROMPT;
         policy.mDelegatedCertInstallerPackage = null;
+        policy.mApplicationRestrictionsManagingPackage = null;
         policy.mStatusBarDisabled = false;
         saveSettingsLocked(userId);
 
@@ -5187,18 +5210,68 @@
     }
 
     @Override
-    public void setApplicationRestrictions(ComponentName who, String packageName, Bundle settings) {
-        Preconditions.checkNotNull(who, "ComponentName is null");
-        final UserHandle userHandle = new UserHandle(UserHandle.getCallingUserId());
+    public void setApplicationRestrictionsManagingPackage(ComponentName admin, String packageName) {
+        final int userHandle = mInjector.userHandleGetCallingUserId();
         synchronized (this) {
-            getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
+            getActiveAdminForCallerLocked(admin, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
+            DevicePolicyData policy = getUserData(userHandle);
+            policy.mApplicationRestrictionsManagingPackage = packageName;
+            saveSettingsLocked(userHandle);
+        }
+    }
 
-            long id = mInjector.binderClearCallingIdentity();
-            try {
-                mUserManager.setApplicationRestrictions(packageName, settings, userHandle);
-            } finally {
-                mInjector.binderRestoreCallingIdentity(id);
+    @Override
+    public String getApplicationRestrictionsManagingPackage(ComponentName admin) {
+        final int userHandle = mInjector.userHandleGetCallingUserId();
+        synchronized (this) {
+            getActiveAdminForCallerLocked(admin, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
+            DevicePolicyData policy = getUserData(userHandle);
+            return policy.mApplicationRestrictionsManagingPackage;
+        }
+    }
+
+    @Override
+    public boolean isCallerApplicationRestrictionsManagingPackage() {
+        final int callingUid = mInjector.binderGetCallingUid();
+        final int userHandle = UserHandle.getUserId(callingUid);
+        synchronized (this) {
+            final DevicePolicyData policy = getUserData(userHandle);
+            if (policy.mApplicationRestrictionsManagingPackage == null) {
+                return false;
             }
+
+            try {
+                int uid = mContext.getPackageManager().getPackageUid(
+                        policy.mApplicationRestrictionsManagingPackage, userHandle);
+                return uid == callingUid;
+            } catch (NameNotFoundException e) {
+                return false;
+            }
+        }
+    }
+
+    private void enforceCanManageApplicationRestrictions(ComponentName who) {
+        if (who != null) {
+            synchronized (this) {
+                getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
+            }
+        } else if (!isCallerApplicationRestrictionsManagingPackage()) {
+            throw new SecurityException(
+                    "No admin component given, and caller cannot manage application restrictions "
+                    + "for other apps.");
+        }
+    }
+
+    @Override
+    public void setApplicationRestrictions(ComponentName who, String packageName, Bundle settings) {
+        enforceCanManageApplicationRestrictions(who);
+
+        final UserHandle userHandle = mInjector.binderGetCallingUserHandle();
+        final long id = mInjector.binderClearCallingIdentity();
+        try {
+            mUserManager.setApplicationRestrictions(packageName, settings, userHandle);
+        } finally {
+            mInjector.binderRestoreCallingIdentity(id);
         }
     }
 
@@ -5764,21 +5837,17 @@
 
     @Override
     public Bundle getApplicationRestrictions(ComponentName who, String packageName) {
-        Preconditions.checkNotNull(who, "ComponentName is null");
-        final UserHandle userHandle = new UserHandle(UserHandle.getCallingUserId());
+        enforceCanManageApplicationRestrictions(who);
 
-        synchronized (this) {
-            getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
-
-            long id = mInjector.binderClearCallingIdentity();
-            try {
-                Bundle bundle = mUserManager.getApplicationRestrictions(packageName, userHandle);
-                // if no restrictions were saved, mUserManager.getApplicationRestrictions
-                // returns null, but DPM method should return an empty Bundle as per JavaDoc
-                return bundle != null ? bundle : Bundle.EMPTY;
-            } finally {
-                mInjector.binderRestoreCallingIdentity(id);
-            }
+        final UserHandle userHandle = mInjector.binderGetCallingUserHandle();
+        final long id = mInjector.binderClearCallingIdentity();
+        try {
+           Bundle bundle = mUserManager.getApplicationRestrictions(packageName, userHandle);
+           // if no restrictions were saved, mUserManager.getApplicationRestrictions
+           // returns null, but DPM method should return an empty Bundle as per JavaDoc
+           return bundle != null ? bundle : Bundle.EMPTY;
+        } finally {
+            mInjector.binderRestoreCallingIdentity(id);
         }
     }
 
@@ -7018,4 +7087,19 @@
         return UserManager.isSplitSystemUser() && callingUserId == UserHandle.USER_SYSTEM;
     }
 
+    @Override
+    public void reboot(ComponentName admin) {
+        Preconditions.checkNotNull(admin);
+        // Make sure caller has DO.
+        synchronized (this) {
+            getActiveAdminForCallerLocked(admin, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER);
+        }
+        long ident = mInjector.binderClearCallingIdentity();
+        try {
+            mInjector.powerManagerReboot(PowerManager.REBOOT_REQUESTED_BY_DEVICE_OWNER);
+        } finally {
+            mInjector.binderRestoreCallingIdentity(ident);
+        }
+    }
+
 }
diff --git a/services/print/java/com/android/server/print/PrintManagerService.java b/services/print/java/com/android/server/print/PrintManagerService.java
index 0a8c014..f18617e 100644
--- a/services/print/java/com/android/server/print/PrintManagerService.java
+++ b/services/print/java/com/android/server/print/PrintManagerService.java
@@ -77,8 +77,8 @@
     }
 
     @Override
-    public void onStartUser(int userHandle) {
-        mPrintManagerImpl.handleUserStarted(userHandle);
+    public void onUnlockUser(int userHandle) {
+        mPrintManagerImpl.handleUserUnlocked(userHandle);
     }
 
     @Override
@@ -473,14 +473,11 @@
                 public void onChange(boolean selfChange, Uri uri, int userId) {
                     if (enabledPrintServicesUri.equals(uri)) {
                         synchronized (mLock) {
-                            if (userId != UserHandle.USER_ALL) {
-                                UserState userState = getOrCreateUserStateLocked(userId);
-                                userState.updateIfNeededLocked();
-                            } else {
-                                final int userCount = mUserStates.size();
-                                for (int i = 0; i < userCount; i++) {
-                                    UserState userState = mUserStates.valueAt(i);
-                                    userState.updateIfNeededLocked();
+                            final int userCount = mUserStates.size();
+                            for (int i = 0; i < userCount; i++) {
+                                if (userId == UserHandle.USER_ALL
+                                        || userId == mUserStates.keyAt(i)) {
+                                    mUserStates.valueAt(i).updateIfNeededLocked();
                                 }
                             }
                         }
@@ -496,6 +493,7 @@
             PackageMonitor monitor = new PackageMonitor() {
                 @Override
                 public void onPackageModified(String packageName) {
+                    if (!mUserManager.isUserUnlocked(getChangingUserId())) return;
                     synchronized (mLock) {
                         // A background user/profile's print jobs are running but there is
                         // no UI shown. Hence, if the packages of such a user change we need
@@ -517,6 +515,7 @@
 
                 @Override
                 public void onPackageRemoved(String packageName, int uid) {
+                    if (!mUserManager.isUserUnlocked(getChangingUserId())) return;
                     synchronized (mLock) {
                         // A background user/profile's print jobs are running but there is
                         // no UI shown. Hence, if the packages of such a user change we need
@@ -544,6 +543,7 @@
                 @Override
                 public boolean onHandleForceStop(Intent intent, String[] stoppedPackages,
                         int uid, boolean doit) {
+                    if (!mUserManager.isUserUnlocked(getChangingUserId())) return false;
                     synchronized (mLock) {
                         // A background user/profile's print jobs are running but there is
                         // no UI shown. Hence, if the packages of such a user change we need
@@ -574,6 +574,8 @@
 
                 @Override
                 public void onPackageAdded(String packageName, int uid) {
+                    if (!mUserManager.isUserUnlocked(getChangingUserId())) return;
+
                     // A background user/profile's print jobs are running but there is
                     // no UI shown. Hence, if the packages of such a user change we need
                     // to handle it as the change may affect ongoing print jobs.
@@ -634,6 +636,11 @@
         }
 
         private UserState getOrCreateUserStateLocked(int userId) {
+            if (!mUserManager.isUserUnlocked(userId)) {
+                throw new IllegalStateException(
+                        "User " + userId + " must be unlocked for printing to be available");
+            }
+
             UserState userState = mUserStates.get(userId);
             if (userState == null) {
                 userState = new UserState(mContext, userId, mLock);
@@ -642,7 +649,7 @@
             return userState;
         }
 
-        private void handleUserStarted(final int userId) {
+        private void handleUserUnlocked(final int userId) {
             // This code will touch the remote print spooler which
             // must be called off the main thread, so post the work.
             BackgroundThread.getHandler().post(new Runnable() {
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
index 565ef4b..7747fd9 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -944,6 +944,88 @@
         assertEquals(0, dpm.getApplicationRestrictions(admin1, "pkg2").size());
     }
 
+    public void testApplicationRestrictionsManagingApp() throws Exception {
+        setAsProfileOwner(admin1);
+
+        final String appRestrictionsManagerPackage = "com.google.app.restrictions.manager";
+        final int appRestrictionsManagerAppId = 20987;
+        final int appRestrictionsManagerUid = UserHandle.getUid(
+                DpmMockContext.CALLER_USER_HANDLE, appRestrictionsManagerAppId);
+        doReturn(appRestrictionsManagerUid).when(mContext.packageManager).getPackageUid(
+                eq(appRestrictionsManagerPackage),
+                eq(DpmMockContext.CALLER_USER_HANDLE));
+        mContext.binder.callingUid = appRestrictionsManagerUid;
+
+        // appRestrictionsManager package shouldn't be able to manage restrictions as the PO hasn't
+        // delegated that permission yet.
+        assertFalse(dpm.isCallerApplicationRestrictionsManagingPackage());
+        Bundle rest = new Bundle();
+        rest.putString("KEY_STRING", "Foo1");
+        try {
+            dpm.setApplicationRestrictions(null, "pkg1", rest);
+            fail("Didn't throw expected SecurityException");
+        } catch (SecurityException expected) {
+            MoreAsserts.assertContainsRegex(
+                    "caller cannot manage application restrictions", expected.getMessage());
+        }
+        try {
+            dpm.getApplicationRestrictions(null, "pkg1");
+            fail("Didn't throw expected SecurityException");
+        } catch (SecurityException expected) {
+            MoreAsserts.assertContainsRegex(
+                    "caller cannot manage application restrictions", expected.getMessage());
+        }
+
+        // Check via the profile owner that no restrictions were set.
+        mContext.binder.callingUid = DpmMockContext.CALLER_UID;
+        assertEquals(0, dpm.getApplicationRestrictions(admin1, "pkg1").size());
+
+        // Let appRestrictionsManagerPackage manage app restrictions
+        dpm.setApplicationRestrictionsManagingPackage(admin1, appRestrictionsManagerPackage);
+        assertEquals(appRestrictionsManagerPackage,
+                dpm.getApplicationRestrictionsManagingPackage(admin1));
+
+        // Now that package should be able to set and retrieve app restrictions.
+        mContext.binder.callingUid = appRestrictionsManagerUid;
+        assertTrue(dpm.isCallerApplicationRestrictionsManagingPackage());
+        dpm.setApplicationRestrictions(null, "pkg1", rest);
+        Bundle returned = dpm.getApplicationRestrictions(null, "pkg1");
+        assertEquals(1, returned.size(), 1);
+        assertEquals("Foo1", returned.get("KEY_STRING"));
+
+        // The same app running on a separate user shouldn't be able to manage app restrictions.
+        mContext.binder.callingUid = UserHandle.getUid(
+                UserHandle.USER_SYSTEM, appRestrictionsManagerAppId);
+        assertFalse(dpm.isCallerApplicationRestrictionsManagingPackage());
+        try {
+            dpm.setApplicationRestrictions(null, "pkg1", rest);
+            fail("Didn't throw expected SecurityException");
+        } catch (SecurityException expected) {
+            MoreAsserts.assertContainsRegex(
+                    "caller cannot manage application restrictions", expected.getMessage());
+        }
+
+        // The DPM is still able to manage app restrictions, even if it allowed another app to do it
+        // too.
+        mContext.binder.callingUid = DpmMockContext.CALLER_UID;
+        assertEquals(returned, dpm.getApplicationRestrictions(admin1, "pkg1"));
+        dpm.setApplicationRestrictions(admin1, "pkg1", null);
+        assertEquals(0, dpm.getApplicationRestrictions(admin1, "pkg1").size());
+
+        // Removing the ability for the package to manage app restrictions.
+        dpm.setApplicationRestrictionsManagingPackage(admin1, null);
+        assertNull(dpm.getApplicationRestrictionsManagingPackage(admin1));
+        mContext.binder.callingUid = appRestrictionsManagerUid;
+        assertFalse(dpm.isCallerApplicationRestrictionsManagingPackage());
+        try {
+            dpm.setApplicationRestrictions(null, "pkg1", null);
+            fail("Didn't throw expected SecurityException");
+        } catch (SecurityException expected) {
+            MoreAsserts.assertContainsRegex(
+                    "caller cannot manage application restrictions", expected.getMessage());
+        }
+    }
+
     public void testSetUserRestriction_asDo() throws Exception {
         mContext.callerPermissions.add(permission.MANAGE_DEVICE_ADMINS);
         mContext.callerPermissions.add(permission.MANAGE_USERS);
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 3b281e2..0b5aaa3 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -766,7 +766,7 @@
             IPhoneSubInfo info = getSubscriberInfo();
             if (info == null)
                 return null;
-            return info.getDeviceIdForPhone(slotId);
+            return info.getDeviceIdForPhone(slotId, mContext.getOpPackageName());
         } catch (RemoteException ex) {
             return null;
         } catch (NullPointerException ex) {
@@ -4865,4 +4865,4 @@
         }
         return null;
     }
-}
\ No newline at end of file
+}
diff --git a/telephony/java/com/android/internal/telephony/IPhoneSubInfo.aidl b/telephony/java/com/android/internal/telephony/IPhoneSubInfo.aidl
index ed85392..dc2b297 100644
--- a/telephony/java/com/android/internal/telephony/IPhoneSubInfo.aidl
+++ b/telephony/java/com/android/internal/telephony/IPhoneSubInfo.aidl
@@ -36,7 +36,7 @@
      * Retrieves the unique device ID of a phone for the device, e.g., IMEI
      * for GSM phones.
      */
-    String getDeviceIdForPhone(int phoneId);
+    String getDeviceIdForPhone(int phoneId, String callingPackage);
 
     /**
      * Retrieves the IMEI.
diff --git a/tests/StatusBar/src/com/android/statusbartest/NotificationTestList.java b/tests/StatusBar/src/com/android/statusbartest/NotificationTestList.java
index cf1a4aa..fecfdf9 100644
--- a/tests/StatusBar/src/com/android/statusbartest/NotificationTestList.java
+++ b/tests/StatusBar/src/com/android/statusbartest/NotificationTestList.java
@@ -23,6 +23,7 @@
 import android.content.ContentResolver;
 import android.content.Intent;
 import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
 import android.graphics.drawable.BitmapDrawable;
 import android.os.Bundle;
 import android.os.Vibrator;
@@ -180,6 +181,9 @@
 
         new Test("with topic GoodBye") {
             public void run() {
+                Notification.BigPictureStyle picture = new Notification.BigPictureStyle();
+                picture.bigPicture(BitmapFactory.decodeResource(getResources(),
+                        R.id.large_icon_pineapple2));
                 Notification n = new Notification.Builder(NotificationTestList.this)
                         .setSmallIcon(R.drawable.icon1)
                         .setWhen(mActivityCreateTime)
@@ -187,11 +191,29 @@
                         .setContentText("This is a notification!!!")
                         .setContentIntent(makeIntent2())
                         .setTopic(new Notification.Topic("bye", "Goodbye"))
+                        .setStyle(picture)
                         .build();
 
                 mNM.notify(9999, n);
             }
         },
+        new Test("with topic Bananas") {
+            public void run() {
+                Notification.BigTextStyle bigText = new Notification.BigTextStyle();
+                bigText.bigText("bananas are great\nso tasty\nyum\nyum\nyum\n");
+                Notification n = new Notification.Builder(NotificationTestList.this)
+                        .setSmallIcon(R.drawable.icon1)
+                        .setStyle(bigText)
+                        .setWhen(mActivityCreateTime)
+                        .setContentTitle("bananananana")
+                        .setContentText("This is a banana!!!")
+                        .setContentIntent(makeIntent2())
+                        .setTopic(new Notification.Topic("bananas", "Bananas"))
+                        .build();
+
+                mNM.notify(999, n);
+            }
+        },
 
         new Test("Whens") {
             public void run()