Merge "Check wallpaper visibility to determine if wallpaper is obscuring apps."
diff --git a/Android.mk b/Android.mk
index 87c5610..6ec434c 100644
--- a/Android.mk
+++ b/Android.mk
@@ -1193,6 +1193,11 @@
 
 LOCAL_DX_FLAGS := --core-library
 
+ifneq ($(INCREMENTAL_BUILDS),)
+    LOCAL_PROGUARD_ENABLED := disabled
+    LOCAL_JACK_ENABLED := incremental
+endif
+
 include $(BUILD_JAVA_LIBRARY)
 
 
diff --git a/api/current.txt b/api/current.txt
index 8a39a40..5c0628c 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -28,7 +28,6 @@
     field public static final java.lang.String BIND_INPUT_METHOD = "android.permission.BIND_INPUT_METHOD";
     field public static final java.lang.String BIND_MIDI_DEVICE_SERVICE = "android.permission.BIND_MIDI_DEVICE_SERVICE";
     field public static final java.lang.String BIND_NFC_SERVICE = "android.permission.BIND_NFC_SERVICE";
-    field public static final java.lang.String BIND_NOTIFICATION_ASSISTANT_SERVICE = "android.permission.BIND_NOTIFICATION_ASSISTANT_SERVICE";
     field public static final java.lang.String BIND_NOTIFICATION_LISTENER_SERVICE = "android.permission.BIND_NOTIFICATION_LISTENER_SERVICE";
     field public static final java.lang.String BIND_PRINT_SERVICE = "android.permission.BIND_PRINT_SERVICE";
     field public static final java.lang.String BIND_QUICK_SETTINGS_TILE = "android.permission.BIND_QUICK_SETTINGS_TILE";
@@ -8088,6 +8087,7 @@
     field public static final int BIND_ALLOW_OOM_MANAGEMENT = 16; // 0x10
     field public static final int BIND_AUTO_CREATE = 1; // 0x1
     field public static final int BIND_DEBUG_UNBIND = 2; // 0x2
+    field public static final int BIND_EXTERNAL_SERVICE = -2147483648; // 0x80000000
     field public static final int BIND_IMPORTANT = 64; // 0x40
     field public static final int BIND_NOT_FOREGROUND = 4; // 0x4
     field public static final int BIND_WAIVE_PRIORITY = 32; // 0x20
@@ -9943,6 +9943,7 @@
     method public int describeContents();
     method public void dump(android.util.Printer, java.lang.String);
     field public static final android.os.Parcelable.Creator<android.content.pm.ServiceInfo> CREATOR;
+    field public static final int FLAG_EXTERNAL_SERVICE = 4; // 0x4
     field public static final int FLAG_ISOLATED_PROCESS = 2; // 0x2
     field public static final int FLAG_SINGLE_USER = 1073741824; // 0x40000000
     field public static final int FLAG_STOP_WITH_TASK = 1; // 0x1
@@ -32110,6 +32111,7 @@
     field public static final java.lang.String ACTION_DISPLAY_SETTINGS = "android.settings.DISPLAY_SETTINGS";
     field public static final java.lang.String ACTION_DREAM_SETTINGS = "android.settings.DREAM_SETTINGS";
     field public static final java.lang.String ACTION_HOME_SETTINGS = "android.settings.HOME_SETTINGS";
+    field public static final java.lang.String ACTION_IGNORE_BACKGROUND_DATA_RESTRICTIONS_SETTINGS = "android.settings.IGNORE_BACKGROUND_DATA_RESTRICTIONS_SETTINGS";
     field public static final java.lang.String ACTION_IGNORE_BATTERY_OPTIMIZATION_SETTINGS = "android.settings.IGNORE_BATTERY_OPTIMIZATION_SETTINGS";
     field public static final java.lang.String ACTION_INPUT_METHOD_SETTINGS = "android.settings.INPUT_METHOD_SETTINGS";
     field public static final java.lang.String ACTION_INPUT_METHOD_SUBTYPE_SETTINGS = "android.settings.INPUT_METHOD_SUBTYPE_SETTINGS";
@@ -34366,6 +34368,9 @@
     ctor public MediaBrowserService.BrowserRoot(java.lang.String, android.os.Bundle);
     method public android.os.Bundle getExtras();
     method public java.lang.String getRootId();
+    field public static final java.lang.String EXTRA_OFFLINE = "android.service.media.extra.OFFLINE";
+    field public static final java.lang.String EXTRA_RECENT = "android.service.media.extra.RECENT";
+    field public static final java.lang.String EXTRA_SUGGESTED = "android.service.media.extra.SUGGESTED";
   }
 
   public class MediaBrowserService.Result {
@@ -34419,39 +34424,6 @@
     field public static final java.lang.String SERVICE_INTERFACE = "android.service.notification.ConditionProviderService";
   }
 
-  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 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 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);
-    field public static final int REASON_APP_CANCEL = 8; // 0x8
-    field public static final int REASON_APP_CANCEL_ALL = 9; // 0x9
-    field public static final int REASON_DELEGATE_CANCEL = 2; // 0x2
-    field public static final int REASON_DELEGATE_CANCEL_ALL = 3; // 0x3
-    field public static final int REASON_DELEGATE_CLICK = 1; // 0x1
-    field public static final int REASON_DELEGATE_ERROR = 4; // 0x4
-    field public static final int REASON_GROUP_OPTIMIZATION = 13; // 0xd
-    field public static final int REASON_GROUP_SUMMARY_CANCELED = 12; // 0xc
-    field public static final int REASON_LISTENER_CANCEL = 10; // 0xa
-    field public static final int REASON_LISTENER_CANCEL_ALL = 11; // 0xb
-    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_PACKAGE_SUSPENDED = 15; // 0xf
-    field public static final int REASON_PROFILE_TURNED_OFF = 16; // 0x10
-    field public static final int REASON_TOPIC_BANNED = 14; // 0xe
-    field public static final int REASON_USER_STOPPED = 6; // 0x6
-    field public static final java.lang.String SERVICE_INTERFACE = "android.service.notification.NotificationAssistantService";
-  }
-
-  public class NotificationAssistantService.Adjustment {
-    ctor public NotificationAssistantService.Adjustment(int, java.lang.CharSequence, android.net.Uri);
-  }
-
   public abstract class NotificationListenerService extends android.app.Service {
     ctor public NotificationListenerService();
     method public final void cancelAllNotifications();
@@ -36351,6 +36323,7 @@
     method public boolean handleMmi(java.lang.String, android.telecom.PhoneAccountHandle);
     method public boolean isInCall();
     method public boolean isVoiceMailNumber(android.telecom.PhoneAccountHandle, java.lang.String);
+    method public void launchManageBlockedNumbersActivity();
     method public void placeCall(android.net.Uri, android.os.Bundle);
     method public void registerPhoneAccount(android.telecom.PhoneAccount);
     method public void showInCallScreen(boolean);
@@ -36471,6 +36444,7 @@
     field public static final java.lang.String KEY_DEFAULT_SIM_CALL_MANAGER_STRING = "default_sim_call_manager_string";
     field public static final java.lang.String KEY_DISABLE_CDMA_ACTIVATION_CODE_BOOL = "disable_cdma_activation_code_bool";
     field public static final java.lang.String KEY_DTMF_TYPE_ENABLED_BOOL = "dtmf_type_enabled_bool";
+    field public static final java.lang.String KEY_DURATION_BLOCKING_DISABLED_AFTER_EMERGENCY_INT = "duration_blocking_disabled_after_emergency_int";
     field public static final java.lang.String KEY_EDITABLE_ENHANCED_4G_LTE_BOOL = "editable_enhanced_4g_lte_bool";
     field public static final java.lang.String KEY_ENABLE_DIALER_KEY_VIBRATION_BOOL = "enable_dialer_key_vibration_bool";
     field public static final java.lang.String KEY_FORCE_HOME_NETWORK_BOOL = "force_home_network_bool";
@@ -40066,7 +40040,6 @@
     method public static android.util.LocaleList getDefault();
     method public static android.util.LocaleList getEmptyLocaleList();
     method public java.util.Locale getFirstMatch(java.lang.String[]);
-    method public java.util.Locale getPrimary();
     method public int indexOf(java.util.Locale);
     method public boolean isEmpty();
     method public static void setDefault(android.util.LocaleList);
@@ -50204,13 +50177,16 @@
     method public static java.lang.Class<?> forName(java.lang.String, boolean, java.lang.ClassLoader) throws java.lang.ClassNotFoundException;
     method public A getAnnotation(java.lang.Class<A>);
     method public java.lang.annotation.Annotation[] getAnnotations();
+    method public T[] getAnnotationsByType(java.lang.Class<T>);
     method public java.lang.String getCanonicalName();
     method public java.lang.ClassLoader getClassLoader();
     method public java.lang.Class<?>[] getClasses();
     method public java.lang.Class<?> getComponentType();
     method public java.lang.reflect.Constructor<T> getConstructor(java.lang.Class<?>...) throws java.lang.NoSuchMethodException, java.lang.SecurityException;
     method public java.lang.reflect.Constructor<?>[] getConstructors() throws java.lang.SecurityException;
+    method public T getDeclaredAnnotation(java.lang.Class<T>);
     method public java.lang.annotation.Annotation[] getDeclaredAnnotations();
+    method public T[] getDeclaredAnnotationsByType(java.lang.Class<T>);
     method public java.lang.Class<?>[] getDeclaredClasses();
     method public java.lang.reflect.Constructor<T> getDeclaredConstructor(java.lang.Class<?>...) throws java.lang.NoSuchMethodException, java.lang.SecurityException;
     method public java.lang.reflect.Constructor<?>[] getDeclaredConstructors() throws java.lang.SecurityException;
@@ -50741,7 +50717,10 @@
   public class Package implements java.lang.reflect.AnnotatedElement {
     method public A getAnnotation(java.lang.Class<A>);
     method public java.lang.annotation.Annotation[] getAnnotations();
+    method public T[] getAnnotationsByType(java.lang.Class<T>);
+    method public java.lang.annotation.Annotation getDeclaredAnnotation(java.lang.Class<T>);
     method public java.lang.annotation.Annotation[] getDeclaredAnnotations();
+    method public T[] getDeclaredAnnotationsByType(java.lang.Class<T>);
     method public java.lang.String getImplementationTitle();
     method public java.lang.String getImplementationVendor();
     method public java.lang.String getImplementationVersion();
@@ -51435,7 +51414,10 @@
     ctor protected AccessibleObject();
     method public T getAnnotation(java.lang.Class<T>);
     method public java.lang.annotation.Annotation[] getAnnotations();
+    method public T[] getAnnotationsByType(java.lang.Class<T>);
+    method public java.lang.annotation.Annotation getDeclaredAnnotation(java.lang.Class<T>);
     method public java.lang.annotation.Annotation[] getDeclaredAnnotations();
+    method public T[] getDeclaredAnnotationsByType(java.lang.Class<T>);
     method public boolean isAccessible();
     method public boolean isAnnotationPresent(java.lang.Class<? extends java.lang.annotation.Annotation>);
     method public static void setAccessible(java.lang.reflect.AccessibleObject[], boolean) throws java.lang.SecurityException;
@@ -51445,7 +51427,10 @@
   public abstract interface AnnotatedElement {
     method public abstract T getAnnotation(java.lang.Class<T>);
     method public abstract java.lang.annotation.Annotation[] getAnnotations();
+    method public abstract T[] getAnnotationsByType(java.lang.Class<T>);
+    method public abstract java.lang.annotation.Annotation getDeclaredAnnotation(java.lang.Class<T>);
     method public abstract java.lang.annotation.Annotation[] getDeclaredAnnotations();
+    method public abstract T[] getDeclaredAnnotationsByType(java.lang.Class<T>);
     method public abstract boolean isAnnotationPresent(java.lang.Class<? extends java.lang.annotation.Annotation>);
   }
 
diff --git a/api/system-current.txt b/api/system-current.txt
index fd7528e..2adb1e6 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -41,7 +41,6 @@
     field public static final java.lang.String BIND_KEYGUARD_APPWIDGET = "android.permission.BIND_KEYGUARD_APPWIDGET";
     field public static final java.lang.String BIND_MIDI_DEVICE_SERVICE = "android.permission.BIND_MIDI_DEVICE_SERVICE";
     field public static final java.lang.String BIND_NFC_SERVICE = "android.permission.BIND_NFC_SERVICE";
-    field public static final java.lang.String BIND_NOTIFICATION_ASSISTANT_SERVICE = "android.permission.BIND_NOTIFICATION_ASSISTANT_SERVICE";
     field public static final java.lang.String BIND_NOTIFICATION_LISTENER_SERVICE = "android.permission.BIND_NOTIFICATION_LISTENER_SERVICE";
     field public static final java.lang.String BIND_PRINT_SERVICE = "android.permission.BIND_PRINT_SERVICE";
     field public static final java.lang.String BIND_QUICK_SETTINGS_TILE = "android.permission.BIND_QUICK_SETTINGS_TILE";
@@ -91,6 +90,7 @@
     field public static final java.lang.String DEVICE_POWER = "android.permission.DEVICE_POWER";
     field public static final java.lang.String DIAGNOSTIC = "android.permission.DIAGNOSTIC";
     field public static final java.lang.String DISABLE_KEYGUARD = "android.permission.DISABLE_KEYGUARD";
+    field public static final java.lang.String DISPATCH_PROVISIONING_MESSAGE = "android.permission.DISPATCH_PROVISIONING_MESSAGE";
     field public static final java.lang.String DUMP = "android.permission.DUMP";
     field public static final java.lang.String EXPAND_STATUS_BAR = "android.permission.EXPAND_STATUS_BAR";
     field public static final java.lang.String FACTORY_TEST = "android.permission.FACTORY_TEST";
@@ -6103,6 +6103,7 @@
     field public static final java.lang.String ACTION_DEVICE_OWNER_CHANGED = "android.app.action.DEVICE_OWNER_CHANGED";
     field public static final java.lang.String ACTION_MANAGED_PROFILE_PROVISIONED = "android.app.action.MANAGED_PROFILE_PROVISIONED";
     field public static final java.lang.String ACTION_PROVISION_MANAGED_DEVICE = "android.app.action.PROVISION_MANAGED_DEVICE";
+    field public static final java.lang.String ACTION_PROVISION_MANAGED_DEVICE_FROM_TRUSTED_SOURCE = "android.app.action.PROVISION_MANAGED_DEVICE_FROM_TRUSTED_SOURCE";
     field public static final java.lang.String ACTION_PROVISION_MANAGED_PROFILE = "android.app.action.PROVISION_MANAGED_PROFILE";
     field public static final java.lang.String ACTION_SET_NEW_PARENT_PROFILE_PASSWORD = "android.app.action.SET_NEW_PARENT_PROFILE_PASSWORD";
     field public static final java.lang.String ACTION_SET_NEW_PASSWORD = "android.app.action.SET_NEW_PASSWORD";
@@ -10344,6 +10345,7 @@
     method public int describeContents();
     method public void dump(android.util.Printer, java.lang.String);
     field public static final android.os.Parcelable.Creator<android.content.pm.ServiceInfo> CREATOR;
+    field public static final int FLAG_EXTERNAL_SERVICE = 4; // 0x4
     field public static final int FLAG_ISOLATED_PROCESS = 2; // 0x2
     field public static final int FLAG_SINGLE_USER = 1073741824; // 0x40000000
     field public static final int FLAG_STOP_WITH_TASK = 1; // 0x1
@@ -34518,6 +34520,7 @@
     field public static final java.lang.String ACTION_DISPLAY_SETTINGS = "android.settings.DISPLAY_SETTINGS";
     field public static final java.lang.String ACTION_DREAM_SETTINGS = "android.settings.DREAM_SETTINGS";
     field public static final java.lang.String ACTION_HOME_SETTINGS = "android.settings.HOME_SETTINGS";
+    field public static final java.lang.String ACTION_IGNORE_BACKGROUND_DATA_RESTRICTIONS_SETTINGS = "android.settings.IGNORE_BACKGROUND_DATA_RESTRICTIONS_SETTINGS";
     field public static final java.lang.String ACTION_IGNORE_BATTERY_OPTIMIZATION_SETTINGS = "android.settings.IGNORE_BATTERY_OPTIMIZATION_SETTINGS";
     field public static final java.lang.String ACTION_INPUT_METHOD_SETTINGS = "android.settings.INPUT_METHOD_SETTINGS";
     field public static final java.lang.String ACTION_INPUT_METHOD_SUBTYPE_SETTINGS = "android.settings.INPUT_METHOD_SUBTYPE_SETTINGS";
@@ -36775,6 +36778,9 @@
     ctor public MediaBrowserService.BrowserRoot(java.lang.String, android.os.Bundle);
     method public android.os.Bundle getExtras();
     method public java.lang.String getRootId();
+    field public static final java.lang.String EXTRA_OFFLINE = "android.service.media.extra.OFFLINE";
+    field public static final java.lang.String EXTRA_RECENT = "android.service.media.extra.RECENT";
+    field public static final java.lang.String EXTRA_SUGGESTED = "android.service.media.extra.SUGGESTED";
   }
 
   public class MediaBrowserService.Result {
@@ -36832,13 +36838,11 @@
   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 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 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);
     field public static final int REASON_APP_CANCEL = 8; // 0x8
     field public static final int REASON_APP_CANCEL_ALL = 9; // 0x9
     field public static final int REASON_DELEGATE_CANCEL = 2; // 0x2
@@ -38927,6 +38931,7 @@
     method public boolean isRinging();
     method public boolean isTtySupported();
     method public boolean isVoiceMailNumber(android.telecom.PhoneAccountHandle, java.lang.String);
+    method public void launchManageBlockedNumbersActivity();
     method public void placeCall(android.net.Uri, android.os.Bundle);
     method public void registerPhoneAccount(android.telecom.PhoneAccount);
     method public void showInCallScreen(boolean);
@@ -39054,6 +39059,7 @@
     field public static final java.lang.String KEY_DEFAULT_SIM_CALL_MANAGER_STRING = "default_sim_call_manager_string";
     field public static final java.lang.String KEY_DISABLE_CDMA_ACTIVATION_CODE_BOOL = "disable_cdma_activation_code_bool";
     field public static final java.lang.String KEY_DTMF_TYPE_ENABLED_BOOL = "dtmf_type_enabled_bool";
+    field public static final java.lang.String KEY_DURATION_BLOCKING_DISABLED_AFTER_EMERGENCY_INT = "duration_blocking_disabled_after_emergency_int";
     field public static final java.lang.String KEY_EDITABLE_ENHANCED_4G_LTE_BOOL = "editable_enhanced_4g_lte_bool";
     field public static final java.lang.String KEY_ENABLE_DIALER_KEY_VIBRATION_BOOL = "enable_dialer_key_vibration_bool";
     field public static final java.lang.String KEY_FORCE_HOME_NETWORK_BOOL = "force_home_network_bool";
@@ -42713,7 +42719,6 @@
     method public static android.util.LocaleList getDefault();
     method public static android.util.LocaleList getEmptyLocaleList();
     method public java.util.Locale getFirstMatch(java.lang.String[]);
-    method public java.util.Locale getPrimary();
     method public int indexOf(java.util.Locale);
     method public boolean isEmpty();
     method public static void setDefault(android.util.LocaleList);
@@ -53187,13 +53192,16 @@
     method public static java.lang.Class<?> forName(java.lang.String, boolean, java.lang.ClassLoader) throws java.lang.ClassNotFoundException;
     method public A getAnnotation(java.lang.Class<A>);
     method public java.lang.annotation.Annotation[] getAnnotations();
+    method public T[] getAnnotationsByType(java.lang.Class<T>);
     method public java.lang.String getCanonicalName();
     method public java.lang.ClassLoader getClassLoader();
     method public java.lang.Class<?>[] getClasses();
     method public java.lang.Class<?> getComponentType();
     method public java.lang.reflect.Constructor<T> getConstructor(java.lang.Class<?>...) throws java.lang.NoSuchMethodException, java.lang.SecurityException;
     method public java.lang.reflect.Constructor<?>[] getConstructors() throws java.lang.SecurityException;
+    method public T getDeclaredAnnotation(java.lang.Class<T>);
     method public java.lang.annotation.Annotation[] getDeclaredAnnotations();
+    method public T[] getDeclaredAnnotationsByType(java.lang.Class<T>);
     method public java.lang.Class<?>[] getDeclaredClasses();
     method public java.lang.reflect.Constructor<T> getDeclaredConstructor(java.lang.Class<?>...) throws java.lang.NoSuchMethodException, java.lang.SecurityException;
     method public java.lang.reflect.Constructor<?>[] getDeclaredConstructors() throws java.lang.SecurityException;
@@ -53724,7 +53732,10 @@
   public class Package implements java.lang.reflect.AnnotatedElement {
     method public A getAnnotation(java.lang.Class<A>);
     method public java.lang.annotation.Annotation[] getAnnotations();
+    method public T[] getAnnotationsByType(java.lang.Class<T>);
+    method public java.lang.annotation.Annotation getDeclaredAnnotation(java.lang.Class<T>);
     method public java.lang.annotation.Annotation[] getDeclaredAnnotations();
+    method public T[] getDeclaredAnnotationsByType(java.lang.Class<T>);
     method public java.lang.String getImplementationTitle();
     method public java.lang.String getImplementationVendor();
     method public java.lang.String getImplementationVersion();
@@ -54418,7 +54429,10 @@
     ctor protected AccessibleObject();
     method public T getAnnotation(java.lang.Class<T>);
     method public java.lang.annotation.Annotation[] getAnnotations();
+    method public T[] getAnnotationsByType(java.lang.Class<T>);
+    method public java.lang.annotation.Annotation getDeclaredAnnotation(java.lang.Class<T>);
     method public java.lang.annotation.Annotation[] getDeclaredAnnotations();
+    method public T[] getDeclaredAnnotationsByType(java.lang.Class<T>);
     method public boolean isAccessible();
     method public boolean isAnnotationPresent(java.lang.Class<? extends java.lang.annotation.Annotation>);
     method public static void setAccessible(java.lang.reflect.AccessibleObject[], boolean) throws java.lang.SecurityException;
@@ -54428,7 +54442,10 @@
   public abstract interface AnnotatedElement {
     method public abstract T getAnnotation(java.lang.Class<T>);
     method public abstract java.lang.annotation.Annotation[] getAnnotations();
+    method public abstract T[] getAnnotationsByType(java.lang.Class<T>);
+    method public abstract java.lang.annotation.Annotation getDeclaredAnnotation(java.lang.Class<T>);
     method public abstract java.lang.annotation.Annotation[] getDeclaredAnnotations();
+    method public abstract T[] getDeclaredAnnotationsByType(java.lang.Class<T>);
     method public abstract boolean isAnnotationPresent(java.lang.Class<? extends java.lang.annotation.Annotation>);
   }
 
diff --git a/api/test-current.txt b/api/test-current.txt
index b2a6750..7abc6d4 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -28,7 +28,6 @@
     field public static final java.lang.String BIND_INPUT_METHOD = "android.permission.BIND_INPUT_METHOD";
     field public static final java.lang.String BIND_MIDI_DEVICE_SERVICE = "android.permission.BIND_MIDI_DEVICE_SERVICE";
     field public static final java.lang.String BIND_NFC_SERVICE = "android.permission.BIND_NFC_SERVICE";
-    field public static final java.lang.String BIND_NOTIFICATION_ASSISTANT_SERVICE = "android.permission.BIND_NOTIFICATION_ASSISTANT_SERVICE";
     field public static final java.lang.String BIND_NOTIFICATION_LISTENER_SERVICE = "android.permission.BIND_NOTIFICATION_LISTENER_SERVICE";
     field public static final java.lang.String BIND_PRINT_SERVICE = "android.permission.BIND_PRINT_SERVICE";
     field public static final java.lang.String BIND_QUICK_SETTINGS_TILE = "android.permission.BIND_QUICK_SETTINGS_TILE";
@@ -8092,6 +8091,7 @@
     field public static final int BIND_ALLOW_OOM_MANAGEMENT = 16; // 0x10
     field public static final int BIND_AUTO_CREATE = 1; // 0x1
     field public static final int BIND_DEBUG_UNBIND = 2; // 0x2
+    field public static final int BIND_EXTERNAL_SERVICE = -2147483648; // 0x80000000
     field public static final int BIND_IMPORTANT = 64; // 0x40
     field public static final int BIND_NOT_FOREGROUND = 4; // 0x4
     field public static final int BIND_WAIVE_PRIORITY = 32; // 0x20
@@ -9951,6 +9951,7 @@
     method public int describeContents();
     method public void dump(android.util.Printer, java.lang.String);
     field public static final android.os.Parcelable.Creator<android.content.pm.ServiceInfo> CREATOR;
+    field public static final int FLAG_EXTERNAL_SERVICE = 4; // 0x4
     field public static final int FLAG_ISOLATED_PROCESS = 2; // 0x2
     field public static final int FLAG_SINGLE_USER = 1073741824; // 0x40000000
     field public static final int FLAG_STOP_WITH_TASK = 1; // 0x1
@@ -32123,6 +32124,7 @@
     field public static final java.lang.String ACTION_DISPLAY_SETTINGS = "android.settings.DISPLAY_SETTINGS";
     field public static final java.lang.String ACTION_DREAM_SETTINGS = "android.settings.DREAM_SETTINGS";
     field public static final java.lang.String ACTION_HOME_SETTINGS = "android.settings.HOME_SETTINGS";
+    field public static final java.lang.String ACTION_IGNORE_BACKGROUND_DATA_RESTRICTIONS_SETTINGS = "android.settings.IGNORE_BACKGROUND_DATA_RESTRICTIONS_SETTINGS";
     field public static final java.lang.String ACTION_IGNORE_BATTERY_OPTIMIZATION_SETTINGS = "android.settings.IGNORE_BATTERY_OPTIMIZATION_SETTINGS";
     field public static final java.lang.String ACTION_INPUT_METHOD_SETTINGS = "android.settings.INPUT_METHOD_SETTINGS";
     field public static final java.lang.String ACTION_INPUT_METHOD_SUBTYPE_SETTINGS = "android.settings.INPUT_METHOD_SUBTYPE_SETTINGS";
@@ -34381,6 +34383,9 @@
     ctor public MediaBrowserService.BrowserRoot(java.lang.String, android.os.Bundle);
     method public android.os.Bundle getExtras();
     method public java.lang.String getRootId();
+    field public static final java.lang.String EXTRA_OFFLINE = "android.service.media.extra.OFFLINE";
+    field public static final java.lang.String EXTRA_RECENT = "android.service.media.extra.RECENT";
+    field public static final java.lang.String EXTRA_SUGGESTED = "android.service.media.extra.SUGGESTED";
   }
 
   public class MediaBrowserService.Result {
@@ -34434,39 +34439,6 @@
     field public static final java.lang.String SERVICE_INTERFACE = "android.service.notification.ConditionProviderService";
   }
 
-  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 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 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);
-    field public static final int REASON_APP_CANCEL = 8; // 0x8
-    field public static final int REASON_APP_CANCEL_ALL = 9; // 0x9
-    field public static final int REASON_DELEGATE_CANCEL = 2; // 0x2
-    field public static final int REASON_DELEGATE_CANCEL_ALL = 3; // 0x3
-    field public static final int REASON_DELEGATE_CLICK = 1; // 0x1
-    field public static final int REASON_DELEGATE_ERROR = 4; // 0x4
-    field public static final int REASON_GROUP_OPTIMIZATION = 13; // 0xd
-    field public static final int REASON_GROUP_SUMMARY_CANCELED = 12; // 0xc
-    field public static final int REASON_LISTENER_CANCEL = 10; // 0xa
-    field public static final int REASON_LISTENER_CANCEL_ALL = 11; // 0xb
-    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_PACKAGE_SUSPENDED = 15; // 0xf
-    field public static final int REASON_PROFILE_TURNED_OFF = 16; // 0x10
-    field public static final int REASON_TOPIC_BANNED = 14; // 0xe
-    field public static final int REASON_USER_STOPPED = 6; // 0x6
-    field public static final java.lang.String SERVICE_INTERFACE = "android.service.notification.NotificationAssistantService";
-  }
-
-  public class NotificationAssistantService.Adjustment {
-    ctor public NotificationAssistantService.Adjustment(int, java.lang.CharSequence, android.net.Uri);
-  }
-
   public abstract class NotificationListenerService extends android.app.Service {
     ctor public NotificationListenerService();
     method public final void cancelAllNotifications();
@@ -36366,6 +36338,7 @@
     method public boolean handleMmi(java.lang.String, android.telecom.PhoneAccountHandle);
     method public boolean isInCall();
     method public boolean isVoiceMailNumber(android.telecom.PhoneAccountHandle, java.lang.String);
+    method public void launchManageBlockedNumbersActivity();
     method public void placeCall(android.net.Uri, android.os.Bundle);
     method public void registerPhoneAccount(android.telecom.PhoneAccount);
     method public void showInCallScreen(boolean);
@@ -36486,6 +36459,7 @@
     field public static final java.lang.String KEY_DEFAULT_SIM_CALL_MANAGER_STRING = "default_sim_call_manager_string";
     field public static final java.lang.String KEY_DISABLE_CDMA_ACTIVATION_CODE_BOOL = "disable_cdma_activation_code_bool";
     field public static final java.lang.String KEY_DTMF_TYPE_ENABLED_BOOL = "dtmf_type_enabled_bool";
+    field public static final java.lang.String KEY_DURATION_BLOCKING_DISABLED_AFTER_EMERGENCY_INT = "duration_blocking_disabled_after_emergency_int";
     field public static final java.lang.String KEY_EDITABLE_ENHANCED_4G_LTE_BOOL = "editable_enhanced_4g_lte_bool";
     field public static final java.lang.String KEY_ENABLE_DIALER_KEY_VIBRATION_BOOL = "enable_dialer_key_vibration_bool";
     field public static final java.lang.String KEY_FORCE_HOME_NETWORK_BOOL = "force_home_network_bool";
@@ -40083,7 +40057,6 @@
     method public static android.util.LocaleList getDefault();
     method public static android.util.LocaleList getEmptyLocaleList();
     method public java.util.Locale getFirstMatch(java.lang.String[]);
-    method public java.util.Locale getPrimary();
     method public int indexOf(java.util.Locale);
     method public boolean isEmpty();
     method public static void setDefault(android.util.LocaleList);
@@ -50221,13 +50194,16 @@
     method public static java.lang.Class<?> forName(java.lang.String, boolean, java.lang.ClassLoader) throws java.lang.ClassNotFoundException;
     method public A getAnnotation(java.lang.Class<A>);
     method public java.lang.annotation.Annotation[] getAnnotations();
+    method public T[] getAnnotationsByType(java.lang.Class<T>);
     method public java.lang.String getCanonicalName();
     method public java.lang.ClassLoader getClassLoader();
     method public java.lang.Class<?>[] getClasses();
     method public java.lang.Class<?> getComponentType();
     method public java.lang.reflect.Constructor<T> getConstructor(java.lang.Class<?>...) throws java.lang.NoSuchMethodException, java.lang.SecurityException;
     method public java.lang.reflect.Constructor<?>[] getConstructors() throws java.lang.SecurityException;
+    method public T getDeclaredAnnotation(java.lang.Class<T>);
     method public java.lang.annotation.Annotation[] getDeclaredAnnotations();
+    method public T[] getDeclaredAnnotationsByType(java.lang.Class<T>);
     method public java.lang.Class<?>[] getDeclaredClasses();
     method public java.lang.reflect.Constructor<T> getDeclaredConstructor(java.lang.Class<?>...) throws java.lang.NoSuchMethodException, java.lang.SecurityException;
     method public java.lang.reflect.Constructor<?>[] getDeclaredConstructors() throws java.lang.SecurityException;
@@ -50758,7 +50734,10 @@
   public class Package implements java.lang.reflect.AnnotatedElement {
     method public A getAnnotation(java.lang.Class<A>);
     method public java.lang.annotation.Annotation[] getAnnotations();
+    method public T[] getAnnotationsByType(java.lang.Class<T>);
+    method public java.lang.annotation.Annotation getDeclaredAnnotation(java.lang.Class<T>);
     method public java.lang.annotation.Annotation[] getDeclaredAnnotations();
+    method public T[] getDeclaredAnnotationsByType(java.lang.Class<T>);
     method public java.lang.String getImplementationTitle();
     method public java.lang.String getImplementationVendor();
     method public java.lang.String getImplementationVersion();
@@ -51452,7 +51431,10 @@
     ctor protected AccessibleObject();
     method public T getAnnotation(java.lang.Class<T>);
     method public java.lang.annotation.Annotation[] getAnnotations();
+    method public T[] getAnnotationsByType(java.lang.Class<T>);
+    method public java.lang.annotation.Annotation getDeclaredAnnotation(java.lang.Class<T>);
     method public java.lang.annotation.Annotation[] getDeclaredAnnotations();
+    method public T[] getDeclaredAnnotationsByType(java.lang.Class<T>);
     method public boolean isAccessible();
     method public boolean isAnnotationPresent(java.lang.Class<? extends java.lang.annotation.Annotation>);
     method public static void setAccessible(java.lang.reflect.AccessibleObject[], boolean) throws java.lang.SecurityException;
@@ -51462,7 +51444,10 @@
   public abstract interface AnnotatedElement {
     method public abstract T getAnnotation(java.lang.Class<T>);
     method public abstract java.lang.annotation.Annotation[] getAnnotations();
+    method public abstract T[] getAnnotationsByType(java.lang.Class<T>);
+    method public abstract java.lang.annotation.Annotation getDeclaredAnnotation(java.lang.Class<T>);
     method public abstract java.lang.annotation.Annotation[] getDeclaredAnnotations();
+    method public abstract T[] getDeclaredAnnotationsByType(java.lang.Class<T>);
     method public abstract boolean isAnnotationPresent(java.lang.Class<? extends java.lang.annotation.Annotation>);
   }
 
diff --git a/cmds/am/src/com/android/commands/am/Am.java b/cmds/am/src/com/android/commands/am/Am.java
index 9d81c43..a74a1ca 100644
--- a/cmds/am/src/com/android/commands/am/Am.java
+++ b/cmds/am/src/com/android/commands/am/Am.java
@@ -1774,18 +1774,33 @@
             System.err.println("Error: invalid input bounds");
             return;
         }
-        resizeStack(stackId, bounds, 0, false);
+        resizeStack(stackId, bounds, 0);
     }
 
     private void runStackResizeAnimated() throws Exception {
         String stackIdStr = nextArgRequired();
         int stackId = Integer.valueOf(stackIdStr);
-        final Rect bounds = getBounds();
-        if (bounds == null) {
-            System.err.println("Error: invalid input bounds");
-            return;
+        final Rect bounds;
+        if ("null".equals(mArgs.peekNextArg())) {
+            bounds = null;
+        } else {
+            bounds = getBounds();
+            if (bounds == null) {
+                System.err.println("Error: invalid input bounds");
+                return;
+            }
         }
-        resizeStack(stackId, bounds, 0, true);
+        resizeStackUnchecked(stackId, bounds, 0, true);
+    }
+
+    private void resizeStackUnchecked(int stackId, Rect bounds, int delayMs, boolean animate) {
+        try {
+            mAm.resizeStack(stackId, bounds, false, false, animate);
+            Thread.sleep(delayMs);
+        } catch (RemoteException e) {
+            showError("Error: resizing stack " + e);
+        } catch (InterruptedException e) {
+        }
     }
 
     private void runStackResizeDocked() throws Exception {
@@ -1802,20 +1817,13 @@
         }
     }
 
-    private void resizeStack(int stackId, Rect bounds, int delayMs, boolean animate)
+    private void resizeStack(int stackId, Rect bounds, int delayMs)
             throws Exception {
         if (bounds == null) {
             showError("Error: invalid input bounds");
             return;
         }
-
-        try {
-            mAm.resizeStack(stackId, bounds, false, false, animate);
-            Thread.sleep(delayMs);
-        } catch (RemoteException e) {
-            showError("Error: resizing stack " + e);
-        } catch (InterruptedException e) {
-        }
+        resizeStackUnchecked(stackId, bounds, delayMs, false);
     }
 
     private void runStackPositionTask() throws Exception {
@@ -1924,7 +1932,7 @@
             maxChange = Math.min(stepSize, currentPoint - minPoint);
             currentPoint -= maxChange;
             setBoundsSide(bounds, side, currentPoint);
-            resizeStack(DOCKED_STACK_ID, bounds, delayMs, false);
+            resizeStack(DOCKED_STACK_ID, bounds, delayMs);
         }
 
         System.out.println("Growing docked stack side=" + side);
@@ -1932,7 +1940,7 @@
             maxChange = Math.min(stepSize, maxPoint - currentPoint);
             currentPoint += maxChange;
             setBoundsSide(bounds, side, currentPoint);
-            resizeStack(DOCKED_STACK_ID, bounds, delayMs, false);
+            resizeStack(DOCKED_STACK_ID, bounds, delayMs);
         }
 
         System.out.println("Back to Original size side=" + side);
@@ -1940,7 +1948,7 @@
             maxChange = Math.min(stepSize, currentPoint - startPoint);
             currentPoint -= maxChange;
             setBoundsSide(bounds, side, currentPoint);
-            resizeStack(DOCKED_STACK_ID, bounds, delayMs, false);
+            resizeStack(DOCKED_STACK_ID, bounds, delayMs);
         }
     }
 
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 805b203..6424520 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -4822,14 +4822,12 @@
     }
 
     private void updateDefaultDensity() {
-        if (mCurDefaultDisplayDpi != Configuration.DENSITY_DPI_UNDEFINED
-                && mCurDefaultDisplayDpi != DisplayMetrics.DENSITY_DEVICE
-                && !mDensityCompatMode) {
-            Slog.i(TAG, "Switching default density from "
-                    + DisplayMetrics.DENSITY_DEVICE + " to "
-                    + mCurDefaultDisplayDpi);
-            DisplayMetrics.DENSITY_DEVICE = mCurDefaultDisplayDpi;
-            Bitmap.setDefaultDensity(DisplayMetrics.DENSITY_DEFAULT);
+        final int densityDpi = mCurDefaultDisplayDpi;
+        if (!mDensityCompatMode
+                && densityDpi != Configuration.DENSITY_DPI_UNDEFINED
+                && densityDpi != DisplayMetrics.DENSITY_DEVICE) {
+            DisplayMetrics.DENSITY_DEVICE = densityDpi;
+            Bitmap.setDefaultDensity(densityDpi);
         }
     }
 
diff --git a/core/java/android/app/LoadedApk.java b/core/java/android/app/LoadedApk.java
index 1e22bef..da52c1e 100644
--- a/core/java/android/app/LoadedApk.java
+++ b/core/java/android/app/LoadedApk.java
@@ -382,6 +382,13 @@
                     libraryPermittedPath += File.pathSeparator +
                                             System.getProperty("java.library.path");
                 }
+                // DO NOT SHIP: this is a workaround for apps loading native libraries
+                // provided by 3rd party apps using absolute path instead of corresponding
+                // classloader; see http://b/26954419 for example.
+                if (mApplicationInfo.targetSdkVersion <= 23) {
+                    libraryPermittedPath += File.pathSeparator + "/data/app";
+                }
+                // -----------------------------------------------------------------------------
 
                 final String librarySearchPath = TextUtils.join(File.pathSeparator, libPaths);
 
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 34c90c1..a78076b 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -3236,6 +3236,7 @@
                 return;
             }
             contentView.setTextViewText(R.id.app_name_text, appName);
+            contentView.setTextColor(R.id.app_name_text, resolveColor());
         }
 
         private void bindSmallIcon(RemoteViews contentView) {
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 9bad2f9..02eb115 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -241,6 +241,48 @@
 
     /**
      * Activity action: Starts the provisioning flow which sets up a managed device.
+     *
+     * <p>During device owner provisioning, a device admin app is downloaded and set as the owner of
+     * the device. A device owner has full control over the device. The device owner can not be
+     * modified by the user and the only way of resetting the device is via factory reset.
+     *
+     * <p>A typical use case would be a device that is owned by a company, but used by either an
+     * employee or client.
+     *
+     * <p>The provisioning message should be sent to an unprovisioned device.
+     *
+     * <p>Unlike {@link #ACTION_PROVISION_MANAGED_DEVICE}, the provisioning message can only be sent
+     * by a privileged app with the permission
+     * {@link android.Manifest.permission#DISPATCH_PROVISIONING_MESSAGE}.
+     *
+     * <p>The provisioning intent contains the following properties:
+     * <ul>
+     * <li>{@link #EXTRA_PROVISIONING_DEVICE_ADMIN_COMPONENT_NAME}</li>
+     * <li>{@link #EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_DOWNLOAD_LOCATION}, optional</li>
+     * <li>{@link #EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_DOWNLOAD_COOKIE_HEADER}, optional</li>
+     * <li>{@link #EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_CHECKSUM}, optional</li>
+     * <li>{@link #EXTRA_PROVISIONING_LOCAL_TIME} (convert to String), optional</li>
+     * <li>{@link #EXTRA_PROVISIONING_TIME_ZONE}, optional</li>
+     * <li>{@link #EXTRA_PROVISIONING_LOCALE}, optional</li>
+     * <li>{@link #EXTRA_PROVISIONING_WIFI_SSID}, optional</li>
+     * <li>{@link #EXTRA_PROVISIONING_WIFI_HIDDEN} (convert to String), optional</li>
+     * <li>{@link #EXTRA_PROVISIONING_WIFI_SECURITY_TYPE}, optional</li>
+     * <li>{@link #EXTRA_PROVISIONING_WIFI_PASSWORD}, optional</li>
+     * <li>{@link #EXTRA_PROVISIONING_WIFI_PROXY_HOST}, optional</li>
+     * <li>{@link #EXTRA_PROVISIONING_WIFI_PROXY_PORT} (convert to String), optional</li>
+     * <li>{@link #EXTRA_PROVISIONING_WIFI_PROXY_BYPASS}, optional</li>
+     * <li>{@link #EXTRA_PROVISIONING_WIFI_PAC_URL}, optional</li>
+     * <li>{@link #EXTRA_PROVISIONING_ADMIN_EXTRAS_BUNDLE}, optional</li></ul>
+     *
+     * @hide
+     */
+    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+    @SystemApi
+    public static final String ACTION_PROVISION_MANAGED_DEVICE_FROM_TRUSTED_SOURCE =
+            "android.app.action.PROVISION_MANAGED_DEVICE_FROM_TRUSTED_SOURCE";
+
+    /**
+     * Activity action: Starts the provisioning flow which sets up a managed device.
      * Must be started with {@link android.app.Activity#startActivityForResult(Intent, int)}.
      *
      * <p>NOTE: This is only supported on split system user devices, and puts the device into a
@@ -4067,6 +4109,29 @@
     }
 
     /**
+     * Called by the system to check if a specific accessibility service is disabled by admin.
+     *
+     * @param admin Which {@link DeviceAdminReceiver} this request is associated with.
+     * @param packageName Accessibility service package name that needs to be checked.
+     * @param userHandle user id the admin is running as.
+     * @return true if the accessibility service is permitted, otherwise false.
+     *
+     * @hide
+     */
+    public boolean isAccessibilityServicePermittedByAdmin(@NonNull ComponentName admin,
+            @NonNull String packageName, int userHandle) {
+        if (mService != null) {
+            try {
+                return mService.isAccessibilityServicePermittedByAdmin(admin, packageName,
+                        userHandle);
+            } catch (RemoteException e) {
+                Log.w(TAG, REMOTE_EXCEPTION_MESSAGE, e);
+            }
+        }
+        return false;
+    }
+
+    /**
      * Returns the list of accessibility services permitted by the device or profiles
      * owners of this user.
      *
@@ -4146,6 +4211,28 @@
     }
 
     /**
+     * Called by the system to check if a specific input method is disabled by admin.
+     *
+     * @param admin Which {@link DeviceAdminReceiver} this request is associated with.
+     * @param packageName Input method package name that needs to be checked.
+     * @param userHandle user id the admin is running as.
+     * @return true if the input method is permitted, otherwise false.
+     *
+     * @hide
+     */
+    public boolean isInputMethodPermittedByAdmin(@NonNull ComponentName admin,
+            @NonNull String packageName, int userHandle) {
+        if (mService != null) {
+            try {
+                return mService.isInputMethodPermittedByAdmin(admin, packageName, userHandle);
+            } catch (RemoteException e) {
+                Log.w(TAG, REMOTE_EXCEPTION_MESSAGE, e);
+            }
+        }
+        return false;
+    }
+
+    /**
      * Returns the list of input methods permitted by the device or profiles
      * owners of the current user.  (*Not* calling user, due to a limitation in InputMethodManager.)
      *
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index b57e1b7..c6a5344 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -174,10 +174,12 @@
     boolean setPermittedAccessibilityServices(in ComponentName admin,in List packageList);
     List getPermittedAccessibilityServices(in ComponentName admin);
     List getPermittedAccessibilityServicesForUser(int userId);
+    boolean isAccessibilityServicePermittedByAdmin(in ComponentName admin, String packageName, int userId);
 
     boolean setPermittedInputMethods(in ComponentName admin,in List packageList);
     List getPermittedInputMethods(in ComponentName admin);
     List getPermittedInputMethodsForCurrentUser();
+    boolean isInputMethodPermittedByAdmin(in ComponentName admin, String packageName, int userId);
 
     boolean setApplicationHidden(in ComponentName admin, in String packageName, boolean hidden);
     boolean isApplicationHidden(in ComponentName admin, in String packageName);
diff --git a/core/java/android/app/job/JobInfo.java b/core/java/android/app/job/JobInfo.java
index b2ca023..5398e7f 100644
--- a/core/java/android/app/job/JobInfo.java
+++ b/core/java/android/app/job/JobInfo.java
@@ -24,6 +24,7 @@
 import android.os.Parcelable;
 import android.os.PersistableBundle;
 import android.util.Log;
+import static android.util.TimeUtils.formatForLogging;
 
 import java.util.ArrayList;
 
@@ -640,12 +641,14 @@
             }
             JobInfo job = new JobInfo(this);
             if (job.intervalMillis != job.getIntervalMillis()) {
-                Log.w(TAG, "Specified interval is less than minimum interval. Clamped to "
-                        + job.getIntervalMillis());
+                Log.w(TAG, "Specified interval for " + mJobService.getPackageName() + " is "
+                        + formatForLogging(mIntervalMillis) + ". Clamped to " +
+                        formatForLogging(job.getIntervalMillis()));
             }
             if (job.flexMillis != job.getFlexMillis()) {
-                Log.w(TAG, "Specified flex is less than minimum flex. Clamped to "
-                        + job.getFlexMillis());
+                Log.w(TAG, "Specified interval for " + mJobService.getPackageName() + " is "
+                        + formatForLogging(mFlexMillis) + ". Clamped to " +
+                        formatForLogging(job.getFlexMillis()));
             }
             return job;
         }
diff --git a/core/java/android/app/usage/UsageStats.java b/core/java/android/app/usage/UsageStats.java
index a88aa3125..2937ccc 100644
--- a/core/java/android/app/usage/UsageStats.java
+++ b/core/java/android/app/usage/UsageStats.java
@@ -47,20 +47,6 @@
     public long mLastTimeUsed;
 
     /**
-     * The last time the package was used via implicit, non-user initiated actions (service
-     * was bound, etc).
-     * {@hide}
-     */
-    public long mLastTimeSystemUsed;
-
-    /**
-     * Last time the package was used and the beginning of the idle countdown.
-     * This uses a different timebase that is about how much the device has been in use in general.
-     * {@hide}
-     */
-    public long mBeginIdleTime;
-
-    /**
      * {@hide}
      */
     public long mTotalTimeInForeground;
@@ -89,8 +75,6 @@
         mTotalTimeInForeground = stats.mTotalTimeInForeground;
         mLaunchCount = stats.mLaunchCount;
         mLastEvent = stats.mLastEvent;
-        mBeginIdleTime = stats.mBeginIdleTime;
-        mLastTimeSystemUsed = stats.mLastTimeSystemUsed;
     }
 
     public String getPackageName() {
@@ -127,25 +111,6 @@
     }
 
     /**
-     * @hide
-     * Get the last time this package was used by the system (not the user). This can be different
-     * from {@link #getLastTimeUsed()} when the system binds to one of this package's services.
-     * See {@link System#currentTimeMillis()}.
-     */
-    public long getLastTimeSystemUsed() {
-        return mLastTimeSystemUsed;
-    }
-
-    /**
-     * @hide
-     * Get the last time this package was active, measured in milliseconds. This timestamp
-     * uses a timebase that represents how much the device was used and not wallclock time.
-     */
-    public long getBeginIdleTime() {
-        return mBeginIdleTime;
-    }
-
-    /**
      * Get the total time this package spent in the foreground, measured in milliseconds.
      */
     public long getTotalTimeInForeground() {
@@ -172,8 +137,6 @@
             // regards to their mEndTimeStamp.
             mLastEvent = right.mLastEvent;
             mLastTimeUsed = right.mLastTimeUsed;
-            mBeginIdleTime = right.mBeginIdleTime;
-            mLastTimeSystemUsed = right.mLastTimeSystemUsed;
         }
         mBeginTimeStamp = Math.min(mBeginTimeStamp, right.mBeginTimeStamp);
         mEndTimeStamp = Math.max(mEndTimeStamp, right.mEndTimeStamp);
@@ -195,8 +158,6 @@
         dest.writeLong(mTotalTimeInForeground);
         dest.writeInt(mLaunchCount);
         dest.writeInt(mLastEvent);
-        dest.writeLong(mBeginIdleTime);
-        dest.writeLong(mLastTimeSystemUsed);
     }
 
     public static final Creator<UsageStats> CREATOR = new Creator<UsageStats>() {
@@ -210,8 +171,6 @@
             stats.mTotalTimeInForeground = in.readLong();
             stats.mLaunchCount = in.readInt();
             stats.mLastEvent = in.readInt();
-            stats.mBeginIdleTime = in.readLong();
-            stats.mLastTimeSystemUsed = in.readLong();
             return stats;
         }
 
diff --git a/core/java/android/content/ContentProviderClient.java b/core/java/android/content/ContentProviderClient.java
index 4135487..9221fbb 100644
--- a/core/java/android/content/ContentProviderClient.java
+++ b/core/java/android/content/ContentProviderClient.java
@@ -148,8 +148,7 @@
                 return null;
             }
 
-            if ("com.google.android.gms".equals(mPackageName)
-                    || "com.google.android.syncadapters.contacts".equals(mPackageName)) {
+            if ("com.google.android.gms".equals(mPackageName)) {
                 // They're casting to a concrete subclass, sigh
                 return cursor;
             } else {
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 622aad9..4918914 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -342,9 +342,7 @@
      * {@link android.R.attr#isolatedProcess isolated},
      * {@link android.R.attr#externalService external} service.  This binds the service into the
      * calling application's package, rather than the package in which the service is declared.
-     * @hide
      */
-    @SystemApi
     public static final int BIND_EXTERNAL_SERVICE = 0x80000000;
 
     /**
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 83943ad..7dc7401 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -8939,6 +8939,8 @@
                 case ACTION_MEDIA_SCANNER_STARTED:
                 case ACTION_MEDIA_SCANNER_FINISHED:
                 case ACTION_MEDIA_SCANNER_SCAN_FILE:
+                case ACTION_PACKAGE_NEEDS_VERIFICATION:
+                case ACTION_PACKAGE_VERIFIED:
                     // Ignore legacy actions
                     break;
                 default:
diff --git a/core/java/android/content/pm/ServiceInfo.java b/core/java/android/content/pm/ServiceInfo.java
index eecf0de..6bd285a 100644
--- a/core/java/android/content/pm/ServiceInfo.java
+++ b/core/java/android/content/pm/ServiceInfo.java
@@ -52,7 +52,6 @@
      * Bit in {@link #flags}: If set, the service can be bound and run in the
      * calling application's package, rather than the package in which it is
      * declared.  Set from {@link android.R.attr#externalService} attribute.
-     * @hide
      */
     public static final int FLAG_EXTERNAL_SERVICE = 0x0004;
 
diff --git a/core/java/android/content/res/Configuration.java b/core/java/android/content/res/Configuration.java
index 7db5a08..be4f895 100644
--- a/core/java/android/content/res/Configuration.java
+++ b/core/java/android/content/res/Configuration.java
@@ -715,7 +715,7 @@
      * about setLocales() has changed locale directly. */
     private void fixUpLocaleList() {
         if ((locale == null && !mLocaleList.isEmpty()) ||
-                (locale != null && !locale.equals(mLocaleList.getPrimary()))) {
+                (locale != null && !locale.equals(mLocaleList.get(0)))) {
             mLocaleList = new LocaleList(locale);
         }
     }
@@ -1269,7 +1269,7 @@
             localeArray[i] = Locale.forLanguageTag(source.readString());
         }
         mLocaleList = new LocaleList(localeArray);
-        locale = mLocaleList.getPrimary();
+        locale = mLocaleList.get(0);
 
         userSetLocale = (source.readInt()==1);
         touchscreen = source.readInt();
@@ -1435,7 +1435,7 @@
      */
     public void setLocales(@Nullable LocaleList locales) {
         mLocaleList = locales == null ? LocaleList.getEmptyLocaleList() : locales;
-        locale = mLocaleList.getPrimary();
+        locale = mLocaleList.get(0);
         setLayoutDirection(locale);
     }
 
@@ -1900,7 +1900,7 @@
 
         final String localesStr = XmlUtils.readStringAttribute(parser, XML_ATTR_LOCALES);
         configOut.mLocaleList = LocaleList.forLanguageTags(localesStr);
-        configOut.locale = configOut.mLocaleList.getPrimary();
+        configOut.locale = configOut.mLocaleList.get(0);
 
         configOut.touchscreen = XmlUtils.readIntAttribute(parser, XML_ATTR_TOUCHSCREEN,
                 TOUCHSCREEN_UNDEFINED);
diff --git a/core/java/android/content/res/Resources.java b/core/java/android/content/res/Resources.java
index 4967d05..915fae0 100644
--- a/core/java/android/content/res/Resources.java
+++ b/core/java/android/content/res/Resources.java
@@ -381,7 +381,7 @@
     private PluralRules getPluralRule() {
         synchronized (sSync) {
             if (mPluralRule == null) {
-                mPluralRule = PluralRules.forLocale(mConfiguration.getLocales().getPrimary());
+                mPluralRule = PluralRules.forLocale(mConfiguration.getLocales().get(0));
             }
             return mPluralRule;
         }
@@ -444,7 +444,7 @@
     @NonNull
     public String getString(@StringRes int id, Object... formatArgs) throws NotFoundException {
         final String raw = getString(id);
-        return String.format(mConfiguration.getLocales().getPrimary(), raw, formatArgs);
+        return String.format(mConfiguration.getLocales().get(0), raw, formatArgs);
     }
 
     /**
@@ -475,7 +475,7 @@
     public String getQuantityString(@PluralsRes int id, int quantity, Object... formatArgs)
             throws NotFoundException {
         String raw = getQuantityText(id, quantity).toString();
-        return String.format(mConfiguration.getLocales().getPrimary(), raw, formatArgs);
+        return String.format(mConfiguration.getLocales().get(0), raw, formatArgs);
     }
 
     /**
@@ -1971,7 +1971,7 @@
             }
 
             mAssets.setConfiguration(mConfiguration.mcc, mConfiguration.mnc,
-                    adjustLanguageTag(locales.getPrimary().toLanguageTag()),
+                    adjustLanguageTag(locales.get(0).toLanguageTag()),
                     mConfiguration.orientation,
                     mConfiguration.touchscreen,
                     mConfiguration.densityDpi, mConfiguration.keyboard,
@@ -1996,7 +1996,7 @@
         }
         synchronized (sSync) {
             if (mPluralRule != null) {
-                mPluralRule = PluralRules.forLocale(mConfiguration.getLocales().getPrimary());
+                mPluralRule = PluralRules.forLocale(mConfiguration.getLocales().get(0));
             }
         }
     }
diff --git a/core/java/android/nfc/tech/NfcF.java b/core/java/android/nfc/tech/NfcF.java
index b3e3ab6..4487121 100644
--- a/core/java/android/nfc/tech/NfcF.java
+++ b/core/java/android/nfc/tech/NfcF.java
@@ -98,8 +98,13 @@
     /**
      * Send raw NFC-F commands to the tag and receive the response.
      *
-     * <p>Applications must not append the SoD (length) or EoD (CRC) to the payload,
-     * it will be automatically calculated.
+     * <p>Applications must not prefix the SoD (preamble and sync code)
+     * and/or append the EoD (CRC) to the payload, it will be automatically calculated.
+     *
+     * <p>A typical NFC-F frame for this method looks like:
+     * <pre>
+     * LENGTH (1 byte) --- CMD (1 byte) -- IDm (8 bytes) -- PARAMS (LENGTH - 10 bytes)
+     * </pre>
      *
      * <p>Use {@link #getMaxTransceiveLength} to retrieve the maximum amount of bytes
      * that can be sent with {@link #transceive}.
diff --git a/core/java/android/os/ShellCommand.java b/core/java/android/os/ShellCommand.java
index 54d1090..fc804e5 100644
--- a/core/java/android/os/ShellCommand.java
+++ b/core/java/android/os/ShellCommand.java
@@ -234,6 +234,16 @@
         }
     }
 
+    public String peekNextArg() {
+        if (mCurArgData != null) {
+            return mCurArgData;
+        } else if (mArgPos < mArgs.length) {
+            return mArgs[mArgPos];
+        } else {
+            return null;
+        }
+    }
+
     /**
      * Return the next argument on the command line, whatever it is; if there are
      * no arguments left, throws an IllegalArgumentException to report this to the user.
diff --git a/core/java/android/os/UserHandle.java b/core/java/android/os/UserHandle.java
index 344d06e..24666fe 100644
--- a/core/java/android/os/UserHandle.java
+++ b/core/java/android/os/UserHandle.java
@@ -74,6 +74,9 @@
     /** @hide A user id constant to indicate the "system" user of the device */
     public static final @UserIdInt int USER_SYSTEM = 0;
 
+    /** @hide A user serial constant to indicate the "system" user of the device */
+    public static final int USER_SERIAL_SYSTEM = 0;
+
     /** @hide A user handle to indicate the "system" user of the device */
     public static final UserHandle SYSTEM = new UserHandle(USER_SYSTEM);
 
diff --git a/core/java/android/os/storage/IMountService.java b/core/java/android/os/storage/IMountService.java
index 9b3f02d..dd8eb5f 100644
--- a/core/java/android/os/storage/IMountService.java
+++ b/core/java/android/os/storage/IMountService.java
@@ -1284,7 +1284,7 @@
 
             @Override
             public void prepareUserStorage(
-                    String volumeUuid, int userId, int serialNumber, boolean ephemeral)
+                    String volumeUuid, int userId, int serialNumber, int flags)
                     throws RemoteException {
                 Parcel _data = Parcel.obtain();
                 Parcel _reply = Parcel.obtain();
@@ -1293,7 +1293,7 @@
                     _data.writeString(volumeUuid);
                     _data.writeInt(userId);
                     _data.writeInt(serialNumber);
-                    _data.writeInt(ephemeral ? 1 : 0);
+                    _data.writeInt(flags);
                     mRemote.transact(Stub.TRANSACTION_prepareUserStorage, _data, _reply, 0);
                     _reply.readException();
                 } finally {
@@ -2055,8 +2055,8 @@
                     String volumeUuid = data.readString();
                     int userId = data.readInt();
                     int serialNumber = data.readInt();
-                    boolean ephemeral = data.readInt() != 0;
-                    prepareUserStorage(volumeUuid, userId, serialNumber, ephemeral);
+                    int _flags = data.readInt();
+                    prepareUserStorage(volumeUuid, userId, serialNumber, _flags);
                     reply.writeNoException();
                     return true;
                 }
@@ -2389,7 +2389,7 @@
     public boolean isUserKeyUnlocked(int userId) throws RemoteException;
 
     public void prepareUserStorage(String volumeUuid, int userId, int serialNumber,
-            boolean ephemeral) throws RemoteException;
+            int flags) throws RemoteException;
 
     public ParcelFileDescriptor mountAppFuse(String name) throws RemoteException;
 }
diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java
index c8b942b..b82638a 100644
--- a/core/java/android/os/storage/StorageManager.java
+++ b/core/java/android/os/storage/StorageManager.java
@@ -92,8 +92,14 @@
     /** {@hide} */
     public static final int DEBUG_EMULATE_FBE = 1 << 1;
 
+    // NOTE: keep in sync with installd
     /** {@hide} */
-    public static final int FLAG_FOR_WRITE = 1 << 0;
+    public static final int FLAG_STORAGE_DE = 1 << 0;
+    /** {@hide} */
+    public static final int FLAG_STORAGE_CE = 1 << 1;
+
+    /** {@hide} */
+    public static final int FLAG_FOR_WRITE = 1 << 8;
 
     private final Context mContext;
     private final ContentResolver mResolver;
@@ -1003,10 +1009,9 @@
     }
 
     /** {@hide} */
-    public void prepareUserStorage(
-            String volumeUuid, int userId, int serialNumber, boolean ephemeral) {
+    public void prepareUserStorage(String volumeUuid, int userId, int serialNumber, int flags) {
         try {
-            mMountService.prepareUserStorage(volumeUuid, userId, serialNumber, ephemeral);
+            mMountService.prepareUserStorage(volumeUuid, userId, serialNumber, flags);
         } catch (RemoteException e) {
             throw e.rethrowAsRuntimeException();
         }
diff --git a/core/java/android/provider/BlockedNumberContract.java b/core/java/android/provider/BlockedNumberContract.java
index 7a9d062..ea54f92 100644
--- a/core/java/android/provider/BlockedNumberContract.java
+++ b/core/java/android/provider/BlockedNumberContract.java
@@ -20,35 +20,126 @@
 import android.os.Bundle;
 
 /**
- * Constants and methods to access blocked phone numbers for incoming calls and texts.
+ * <p>
+ * The contract between the blockednumber provider and applications. Contains definitions for
+ * the supported URIs and columns.
+ * </p>
  *
- * TODO javadoc
- * - Proper javadoc tagging.
- * - Code sample?
- * - Describe who can access it.
+ * <h3> Overview </h3>
+ * <p>
+ * The content provider exposes a table containing blocked numbers. The columns and URIs for
+ * accessing this table are defined by the {@link BlockedNumbers} class. Messages, and calls from
+ * blocked numbers are discarded by the platform. Notifications upon provider changes can be
+ * received using a {@link android.database.ContentObserver}.
+ * </p>
+ *
+ * <h3> Permissions </h3>
+ * <p>
+ * Only the system, the default SMS application, and the default phone app
+ * (See {@link android.telecom.TelecomManager#getDefaultDialerPackage()}), and carrier apps
+ * (See {@link android.service.carrier.CarrierService}) can read, and write to the blockednumber
+ * provider.
+ * </p>
+ *
+ * <h3> Data </h3>
+ * <p>
+ * Other than regular phone numbers, the blocked number provider can also store addresses (such
+ * as email) from which a user can receive messages, and calls. The blocked numbers are stored
+ * in the {@link BlockedNumbers#COLUMN_ORIGINAL_NUMBER} column. A normalized version of phone
+ * numbers (if normalization is possible) is stored in {@link BlockedNumbers#COLUMN_E164_NUMBER}
+ * column. The platform blocks calls, and messages from an address if it is present in in the
+ * {@link BlockedNumbers#COLUMN_ORIGINAL_NUMBER} column or if the E164 version of the address
+ * matches the {@link BlockedNumbers#COLUMN_E164_NUMBER} column.
+ * </p>
+ *
+ * <h3> Operations </h3>
+ * <dl>
+ * <dt><b>Insert</b></dt>
+ * <dd>
+ * <p>
+ * {@link BlockedNumbers#COLUMN_ORIGINAL_NUMBER} is a required column that needs to be populated.
+ * Apps can optionally provide the {@link BlockedNumbers#COLUMN_E164_NUMBER} which is the phone
+ * number's E164 representation. The provider automatically populates this column if the app does
+ * not provide it. Note that this column is not populated if normalization fails or if the address
+ * is not a phone number (eg: email). The provider enforces uniqueness constraint on this column.
+ * Examples:
+ * <pre>
+ * ContentValues values = new ContentValues();
+ * values.put(BlockedNumbers.COLUMN_ORIGINAL_NUMBER, "1234567890");
+ * Uri uri = getContentResolver().insert(BlockedNumbers.CONTENT_URI, values);
+ * </pre>
+ * <pre>
+ * ContentValues values = new ContentValues();
+ * values.put(BlockedNumbers.COLUMN_ORIGINAL_NUMBER, "1234567890");
+ * values.put(BlockedNumbers.COLUMN_E164_NUMBER, "+11234567890");
+ * Uri uri = getContentResolver().insert(BlockedNumbers.CONTENT_URI, values);
+ * </pre>
+ * <pre>
+ * ContentValues values = new ContentValues();
+ * values.put(BlockedNumbers.COLUMN_ORIGINAL_NUMBER, "12345@abdcde.com");
+ * Uri uri = getContentResolver().insert(BlockedNumbers.CONTENT_URI, values);
+ * </pre>
+ * </p>
+ * </dd>
+ * <dt><b>Update</b></dt>
+ * <dd>
+ * <p>
+ * Updates are not supported. Use Delete, and Insert instead.
+ * </p>
+ * </dd>
+ * <dt><b>Delete</b></dt>
+ * <dd>
+ * <p>
+ * Deletions can be performed as follows:
+ * <pre>
+ * ContentValues values = new ContentValues();
+ * values.put(BlockedNumbers.COLUMN_ORIGINAL_NUMBER, "1234567890");
+ * Uri uri = getContentResolver().insert(BlockedNumbers.CONTENT_URI, values);
+ * getContentResolver().delete(uri, null, null);
+ * </pre>
+ * </p>
+ * </dd>
+ * <dt><b>Query</b></dt>
+ * <dd>
+ * <p>
+ * All blocked numbers can be enumerated as follows:
+ * <pre>
+ * Cursor c = getContentResolver().query(BlockedNumbers.CONTENT_URI,
+ *          new String[]{BlockedNumbers.COLUMN_ID, BlockedNumbers.COLUMN_ORIGINAL_NUMBER,
+ *          BlockedNumbers.COLUMN_E164_NUMBER}, null, null, null);
+ * </pre>
+ * To check if a particular number is blocked, use the method
+ * {@link #isBlocked(Context, String)}.
+ * </p>
+ * </dd>
+ *
+ * <h3> Multi-user </h3>
+ * <p>
+ * Apps must use the method {@link #canCurrentUserBlockNumbers(Context)} before performing any
+ * operation on the blocked number provider. If {@link #canCurrentUserBlockNumbers(Context)} returns
+ * {@code false}, all operations on the provider will fail with an
+ * {@link UnsupportedOperationException}. The platform will block calls, and messages from numbers
+ * in the provider independent of the current user.
+ * </p>
  */
 public class BlockedNumberContract {
     private BlockedNumberContract() {
     }
 
-    /** The authority for the contacts provider */
+    /** The authority for the blocked number provider */
     public static final String AUTHORITY = "com.android.blockednumber";
 
-    /** A content:// style uri to the authority for the contacts provider */
+    /** A content:// style uri to the authority for the blocked number provider */
     public static final Uri AUTHORITY_URI = Uri.parse("content://" + AUTHORITY);
 
     /**
-     * TODO javadoc
-     *
-     * Constants to interact with the blocked phone number list.
+     * Constants to interact with the blocked numbers list.
      */
     public static class BlockedNumbers {
         private BlockedNumbers() {
         }
 
         /**
-         * TODO javadoc
-         *
          * Content URI for the blocked numbers.
          *
          * Supported operations
@@ -117,8 +208,8 @@
      * context {@code context}, this method will throw an {@link UnsupportedOperationException}.
      */
     public static boolean isBlocked(Context context, String phoneNumber) {
-        final Bundle res = context.getContentResolver().call(AUTHORITY_URI,
-                METHOD_IS_BLOCKED, phoneNumber, null);
+        final Bundle res = context.getContentResolver().call(
+                AUTHORITY_URI, METHOD_IS_BLOCKED, phoneNumber, null);
         return res != null && res.getBoolean(RES_NUMBER_IS_BLOCKED, false);
     }
 
diff --git a/core/java/android/provider/ContactsContract.java b/core/java/android/provider/ContactsContract.java
index 330fcf6..dfdd36d 100644
--- a/core/java/android/provider/ContactsContract.java
+++ b/core/java/android/provider/ContactsContract.java
@@ -2079,10 +2079,11 @@
             if (preferHighres) {
                 final Uri displayPhotoUri = Uri.withAppendedPath(contactUri,
                         Contacts.Photo.DISPLAY_PHOTO);
-                InputStream inputStream;
                 try {
                     AssetFileDescriptor fd = cr.openAssetFileDescriptor(displayPhotoUri, "r");
-                    return fd.createInputStream();
+                    if (fd != null) {
+                        return fd.createInputStream();
+                    }
                 } catch (IOException e) {
                     // fallback to the thumbnail code
                 }
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 5535eaa..7830142 100755
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -670,14 +670,14 @@
             "android.settings.IGNORE_BATTERY_OPTIMIZATION_SETTINGS";
 
     /**
-     * Activity Action: Ask the user to allow an to ignore battery optimizations (that is,
+     * Activity Action: Ask the user to allow an app to ignore battery optimizations (that is,
      * put them on the whitelist of apps shown by
      * {@link #ACTION_IGNORE_BATTERY_OPTIMIZATION_SETTINGS}).  For an app to use this, it also
      * must hold the {@link android.Manifest.permission#REQUEST_IGNORE_BATTERY_OPTIMIZATIONS}
      * permission.
      * <p><b>Note:</b> most applications should <em>not</em> use this; there are many facilities
      * provided by the platform for applications to operate correctly in the various power
-     * saving mode.  This is only for unusual applications that need to deeply control their own
+     * saving modes.  This is only for unusual applications that need to deeply control their own
      * execution, at the potential expense of the user's battery life.  Note that these applications
      * greatly run the risk of showing to the user as high power consumers on their device.</p>
      * <p>
@@ -695,6 +695,24 @@
             "android.settings.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS";
 
     /**
+     * Activity Action: Show screen for controlling which apps can ignore background data
+     * restrictions.
+     * <p>
+     * Input: if the Intent's data URI is set with an application name (using the "package" schema,
+     * like "package:com.my.app"), then when the screen is displayed it will focus on such app. If
+     * the data is not set, it will just open the screen.
+     * <p>
+     * Output: Nothing.
+     * <p>
+     * Applications can also use {@link android.net.ConnectivityManager#getRestrictBackgroundStatus
+     * ConnectivityManager#getRestrictBackgroundStatus()} to determine the status of the background
+     * data restrictions for them.
+     */
+    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+    public static final String ACTION_IGNORE_BACKGROUND_DATA_RESTRICTIONS_SETTINGS =
+            "android.settings.IGNORE_BACKGROUND_DATA_RESTRICTIONS_SETTINGS";
+
+    /**
      * @hide
      * Activity Action: Show the "app ops" settings screen.
      * <p>
@@ -3851,7 +3869,6 @@
             MOVED_TO_GLOBAL.add(Settings.Global.DATA_ROAMING);
             MOVED_TO_GLOBAL.add(Settings.Global.DEVELOPMENT_SETTINGS_ENABLED);
             MOVED_TO_GLOBAL.add(Settings.Global.DEVICE_PROVISIONED);
-            MOVED_TO_GLOBAL.add(Settings.Global.DISPLAY_DENSITY_FORCED);
             MOVED_TO_GLOBAL.add(Settings.Global.DISPLAY_SIZE_FORCED);
             MOVED_TO_GLOBAL.add(Settings.Global.DOWNLOAD_MAX_BYTES_OVER_MOBILE);
             MOVED_TO_GLOBAL.add(Settings.Global.DOWNLOAD_RECOMMENDED_MAX_BYTES_OVER_MOBILE);
@@ -5086,6 +5103,15 @@
             "disabled_print_services";
 
         /**
+         * The saved value for WindowManagerService.setForcedDisplayDensity()
+         * formatted as a single integer representing DPI. If unset, then use
+         * the real display density.
+         *
+         * @hide
+         */
+        public static final String DISPLAY_DENSITY_FORCED = "display_density_forced";
+
+        /**
          * Setting to always use the default text-to-speech settings regardless
          * of the application settings.
          * 1 = override application settings,
@@ -6517,13 +6543,6 @@
        public static final String DEVICE_PROVISIONED = "device_provisioned";
 
        /**
-        * The saved value for WindowManagerService.setForcedDisplayDensity().
-        * One integer in dpi.  If unset, then use the real display density.
-        * @hide
-        */
-       public static final String DISPLAY_DENSITY_FORCED = "display_density_forced";
-
-       /**
         * The saved value for WindowManagerService.setForcedDisplaySize().
         * Two integers separated by a comma.  If unset, then use the real display size.
         * @hide
diff --git a/core/java/android/service/notification/NotificationAssistantService.java b/core/java/android/service/notification/NotificationAssistantService.java
index a56e030..fb58f4e 100644
--- a/core/java/android/service/notification/NotificationAssistantService.java
+++ b/core/java/android/service/notification/NotificationAssistantService.java
@@ -45,7 +45,9 @@
  *         &lt;action android:name="android.service.notification.NotificationAssistantService" />
  *     &lt;/intent-filter>
  * &lt;/service></pre>
+ * @hide
  */
+@SystemApi
 public abstract class NotificationAssistantService extends NotificationListenerService {
     private static final String TAG = "NotificationAssistant";
 
@@ -210,29 +212,6 @@
         }
     }
 
-    /**
-     * Add an annotation to a an existing notification. The delete intent will
-     * be fired when the host notification is deleted, or when this annotation
-     * is removed or replaced.
-     *
-     * @param key the key of the notification to be annotated
-     * @param annotation the new annotation object
-     */
-    public final void setAnnotation(String key, Notification annotation)
-    {
-        // TODO: pack up the annotation and send it to the NotificationManager.
-    }
-
-    /**
-     * Remove the annotation from a notification.
-     *
-     * @param key the key of the notification to be cleansed of annotatons
-     */
-    public final void clearAnnotation(String key)
-    {
-        // TODO: ask the NotificationManager to clear the annotation.
-    }
-
     private class NotificationAssistantWrapper extends NotificationListenerWrapper {
         @Override
         public void onNotificationEnqueued(IStatusBarNotificationHolder sbnHolder,
diff --git a/core/java/android/service/notification/NotificationListenerService.java b/core/java/android/service/notification/NotificationListenerService.java
index ed90e79..7ff883e 100644
--- a/core/java/android/service/notification/NotificationListenerService.java
+++ b/core/java/android/service/notification/NotificationListenerService.java
@@ -695,7 +695,7 @@
     /**
      * Request that the listener be rebound, after a previous call to (@link requestUnbind).
      *
-     * <P>This method will fail for assistants that have
+     * <P>This method will fail for listeners that have
      * not been granted the permission by the user.
      *
      * <P>The service should wait for the {@link #onListenerConnected()} event
@@ -1022,8 +1022,7 @@
         }
 
         /**
-         * If the importance has been overriden by user preference, or by a
-         * {@link NotificationAssistantService}, then this will be non-null,
+         * If the importance has been overriden by user preference, then this will be non-null,
          * and should be displayed to the user.
          *
          * @return the explanation for the importance, or null if it is the natural importance
diff --git a/core/java/android/text/BidiFormatter.java b/core/java/android/text/BidiFormatter.java
index a535480..675803c 100644
--- a/core/java/android/text/BidiFormatter.java
+++ b/core/java/android/text/BidiFormatter.java
@@ -293,7 +293,7 @@
      * directionality is determined by scanning the end of the string, the overall directionality is
      * given explicitly by a heuristic to estimate the {@code str}'s directionality.
      *
-     * @param str String after which the mark may need to appear.
+     * @param str CharSequence after which the mark may need to appear.
      * @param heuristic The text direction heuristic that will be used to estimate the {@code str}'s
      *                  directionality.
      * @return LRM for RTL text in LTR context; RLM for LTR text in RTL context;
@@ -301,7 +301,7 @@
      *
      * @hide
      */
-    public String markAfter(String str, TextDirectionHeuristic heuristic) {
+    public String markAfter(CharSequence str, TextDirectionHeuristic heuristic) {
         final boolean isRtl = heuristic.isRtl(str, 0, str.length());
         // getExitDir() is called only if needed (short-circuit).
         if (!mIsRtlContext && (isRtl || getExitDir(str) == DIR_RTL)) {
@@ -322,7 +322,7 @@
      * entry directionality is determined by scanning the beginning of the string, the overall
      * directionality is given explicitly by a heuristic to estimate the {@code str}'s directionality.
      *
-     * @param str String before which the mark may need to appear.
+     * @param str CharSequence before which the mark may need to appear.
      * @param heuristic The text direction heuristic that will be used to estimate the {@code str}'s
      *                  directionality.
      * @return LRM for RTL text in LTR context; RLM for LTR text in RTL context;
@@ -330,7 +330,7 @@
      *
      * @hide
      */
-    public String markBefore(String str, TextDirectionHeuristic heuristic) {
+    public String markBefore(CharSequence str, TextDirectionHeuristic heuristic) {
         final boolean isRtl = heuristic.isRtl(str, 0, str.length());
         // getEntryDir() is called only if needed (short-circuit).
         if (!mIsRtlContext && (isRtl || getEntryDir(str) == DIR_RTL)) {
@@ -350,6 +350,13 @@
      *          false.
      */
     public boolean isRtl(String str) {
+        return isRtl((CharSequence) str);
+    }
+
+    /**
+     * @hide
+     */
+    public boolean isRtl(CharSequence str) {
         return mDefaultTextDirectionHeuristic.isRtl(str, 0, str.length());
     }
 
@@ -384,9 +391,16 @@
      *     {@code null}.
      */
     public String unicodeWrap(String str, TextDirectionHeuristic heuristic, boolean isolate) {
+        return unicodeWrap((CharSequence) str, heuristic, isolate).toString();
+    }
+
+    /**
+     * @hide
+     */
+    public CharSequence unicodeWrap(CharSequence str, TextDirectionHeuristic heuristic, boolean isolate) {
         if (str == null) return null;
         final boolean isRtl = heuristic.isRtl(str, 0, str.length());
-        StringBuilder result = new StringBuilder();
+        SpannableStringBuilder result = new SpannableStringBuilder();
         if (getStereoReset() && isolate) {
             result.append(markBefore(str,
                     isRtl ? TextDirectionHeuristics.RTL : TextDirectionHeuristics.LTR));
@@ -402,7 +416,7 @@
             result.append(markAfter(str,
                     isRtl ? TextDirectionHeuristics.RTL : TextDirectionHeuristics.LTR));
         }
-        return result.toString();
+        return result;
     }
 
     /**
@@ -419,6 +433,14 @@
     }
 
     /**
+     * @hide
+     */
+    public CharSequence unicodeWrap(CharSequence str, TextDirectionHeuristic heuristic) {
+        return unicodeWrap(str, heuristic, true /* isolate */);
+    }
+
+
+    /**
      * Operates like {@link #unicodeWrap(String, TextDirectionHeuristic, boolean)}, but uses the
      * formatter's default direction estimation algorithm.
      *
@@ -432,6 +454,13 @@
     }
 
     /**
+     * @hide
+     */
+    public CharSequence unicodeWrap(CharSequence str, boolean isolate) {
+        return unicodeWrap(str, mDefaultTextDirectionHeuristic, isolate);
+    }
+
+    /**
      * Operates like {@link #unicodeWrap(String, TextDirectionHeuristic, boolean)}, but uses the
      * formatter's default direction estimation algorithm and assumes {@code isolate} is true.
      *
@@ -442,6 +471,13 @@
         return unicodeWrap(str, mDefaultTextDirectionHeuristic, true /* isolate */);
     }
 
+    /**
+     * @hide
+     */
+    public CharSequence unicodeWrap(CharSequence str) {
+        return unicodeWrap(str, mDefaultTextDirectionHeuristic, true /* isolate */);
+    }
+
     private static BidiFormatter getDefaultInstanceFromContext(boolean isRtlContext) {
         return isRtlContext ? DEFAULT_RTL_INSTANCE : DEFAULT_LTR_INSTANCE;
     }
@@ -477,7 +513,7 @@
      *
      * @param str the string to check.
      */
-    private static int getExitDir(String str) {
+    private static int getExitDir(CharSequence str) {
         return new DirectionalityEstimator(str, false /* isHtml */).getExitDir();
     }
 
@@ -494,7 +530,7 @@
      *
      * @param str the string to check.
      */
-    private static int getEntryDir(String str) {
+    private static int getEntryDir(CharSequence str) {
         return new DirectionalityEstimator(str, false /* isHtml */).getEntryDir();
     }
 
@@ -532,7 +568,7 @@
         /**
          * The text to be scanned.
          */
-        private final String text;
+        private final CharSequence text;
 
         /**
          * Whether the text to be scanned is to be treated as HTML, i.e. skipping over tags and
@@ -565,7 +601,7 @@
          * @param isHtml Whether the text to be scanned is to be treated as HTML, i.e. skipping over
          *     tags and entities.
          */
-        DirectionalityEstimator(String text, boolean isHtml) {
+        DirectionalityEstimator(CharSequence text, boolean isHtml) {
             this.text = text;
             this.isHtml = isHtml;
             length = text.length();
@@ -896,4 +932,4 @@
             return Character.DIRECTIONALITY_OTHER_NEUTRALS;
         }
     }
-}
\ No newline at end of file
+}
diff --git a/core/java/android/text/style/LocaleSpan.java b/core/java/android/text/style/LocaleSpan.java
index 117de774..4f687c8 100644
--- a/core/java/android/text/style/LocaleSpan.java
+++ b/core/java/android/text/style/LocaleSpan.java
@@ -97,12 +97,12 @@
      * @return The {@link Locale} for this span.  If multiple locales are associated with this
      * span, only the first locale is returned.  {@code null} if no {@link Locale} is specified.
      *
-     * @see LocaleList#getPrimary()
+     * @see LocaleList#get()
      * @see #getLocales()
      */
     @Nullable
     public Locale getLocale() {
-        return mLocales.getPrimary();
+        return mLocales.get(0);
     }
 
     /**
diff --git a/core/java/android/util/LocaleList.java b/core/java/android/util/LocaleList.java
index 90a20bc..fc39004 100644
--- a/core/java/android/util/LocaleList.java
+++ b/core/java/android/util/LocaleList.java
@@ -47,12 +47,7 @@
     private static final LocaleList sEmptyLocaleList = new LocaleList();
 
     public Locale get(int location) {
-        return location < mList.length ? mList[location] : null;
-    }
-
-    @Nullable
-    public Locale getPrimary() {
-        return mList.length == 0 ? null : get(0);
+        return (0 <= location && location < mList.length) ? mList[location] : null;
     }
 
     public boolean isEmpty() {
@@ -464,7 +459,7 @@
                 // someone has called Locale.setDefault() since we last set or adjusted the default
                 // locale list. So let's recalculate the locale list.
                 if (sDefaultLocaleList != null
-                        && defaultLocale.equals(sDefaultLocaleList.getPrimary())) {
+                        && defaultLocale.equals(sDefaultLocaleList.get(0))) {
                     // The default Locale has changed, but it happens to be the first locale in the
                     // default locale list, so we don't need to construct a new locale list.
                     return sDefaultLocaleList;
diff --git a/core/java/android/view/NotificationHeaderView.java b/core/java/android/view/NotificationHeaderView.java
index 501c6e9..b9a7421 100644
--- a/core/java/android/view/NotificationHeaderView.java
+++ b/core/java/android/view/NotificationHeaderView.java
@@ -21,7 +21,6 @@
 import android.graphics.Rect;
 import android.util.AttributeSet;
 import android.widget.ImageView;
-import android.widget.LinearLayout;
 import android.widget.RemoteViews;
 import android.widget.TextView;
 
@@ -35,7 +34,7 @@
 @RemoteViews.RemoteView
 public class NotificationHeaderView extends ViewGroup {
     public static final int NO_COLOR = -1;
-    private final int mHeaderMinWidth;
+    private final int mChildMinWidth;
     private final int mExpandTopPadding;
     private final int mContentEndMargin;
     private View mAppName;
@@ -46,6 +45,7 @@
     private View mIcon;
     private TextView mChildCount;
     private View mProfileBadge;
+    private View mInfo;
     private int mIconColor;
     private int mOriginalNotificationColor;
     private boolean mGroupHeader;
@@ -66,7 +66,7 @@
 
     public NotificationHeaderView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
         super(context, attrs, defStyleAttr, defStyleRes);
-        mHeaderMinWidth = getResources().getDimensionPixelSize(
+        mChildMinWidth = getResources().getDimensionPixelSize(
                 com.android.internal.R.dimen.notification_header_shrink_min_width);
         mContentEndMargin = getResources().getDimensionPixelSize(
                 com.android.internal.R.dimen.notification_content_margin_end);
@@ -82,6 +82,7 @@
         mIcon = findViewById(com.android.internal.R.id.icon);
         mChildCount = (TextView) findViewById(com.android.internal.R.id.number_of_children);
         mProfileBadge = findViewById(com.android.internal.R.id.profile_badge);
+        mInfo = findViewById(com.android.internal.R.id.header_content_info);
     }
 
     @Override
@@ -109,14 +110,23 @@
         }
         if (totalWidth > givenWidth) {
             int overFlow = totalWidth - givenWidth;
-            // We are overflowing, lets shrink
+            // We are overflowing, lets shrink the info first
+            final int infoWidth = mInfo.getMeasuredWidth();
+            if (mInfo.getVisibility() != GONE && infoWidth > mChildMinWidth) {
+                int newSize = infoWidth - Math.min(infoWidth - mChildMinWidth, overFlow);
+                int childWidthSpec = MeasureSpec.makeMeasureSpec(newSize, MeasureSpec.AT_MOST);
+                mInfo.measure(childWidthSpec, wrapContentHeightSpec);
+                overFlow -= infoWidth - newSize;
+            }
+            // still overflowing, lets shrink the app name now
             final int appWidth = mAppName.getMeasuredWidth();
-            if (mAppName.getVisibility() != GONE && appWidth > mHeaderMinWidth) {
-                int newSize = appWidth - Math.min(appWidth - mHeaderMinWidth, overFlow);
+            if (overFlow > 0 && mAppName.getVisibility() != GONE && appWidth > mChildMinWidth) {
+                int newSize = appWidth - Math.min(appWidth - mChildMinWidth, overFlow);
                 int childWidthSpec = MeasureSpec.makeMeasureSpec(newSize, MeasureSpec.AT_MOST);
                 mAppName.measure(childWidthSpec, wrapContentHeightSpec);
                 overFlow -= appWidth - newSize;
             }
+            // still overflowing, finaly we shrink the subtext
             if (overFlow > 0 && mSubTextView.getVisibility() != GONE) {
                 // we're still too big
                 final int subTextWidth = mSubTextView.getMeasuredWidth();
@@ -124,12 +134,8 @@
                 int childWidthSpec = MeasureSpec.makeMeasureSpec(newSize, MeasureSpec.AT_MOST);
                 mSubTextView.measure(childWidthSpec, wrapContentHeightSpec);
             }
-            totalWidth = givenWidth;
         }
-        if (mProfileBadge.getVisibility() != View.GONE) {
-            totalWidth = givenWidth;
-        }
-        setMeasuredDimension(totalWidth, givenHeight);
+        setMeasuredDimension(givenWidth, givenHeight);
     }
 
     @Override
@@ -275,18 +281,16 @@
             mTouchRects.clear();
             addRectAroundViewView(mIcon);
             addRectAroundViewView(mExpandButton);
-            addInBetweenRect();
+            addWidthRect();
             mTouchSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop();
         }
 
-        private void addInBetweenRect() {
-            final Rect r = new Rect();
+        private void addWidthRect() {
+            Rect r = new Rect();
             r.top = 0;
             r.bottom = (int) (32 * getResources().getDisplayMetrics().density);
-            Rect leftRect = mTouchRects.get(0);
-            r.left = leftRect.right;
-            Rect rightRect = mTouchRects.get(1);
-            r.right = rightRect.left;
+            r.left = 0;
+            r.right = getWidth();
             mTouchRects.add(r);
         }
 
diff --git a/core/java/android/view/RenderNode.java b/core/java/android/view/RenderNode.java
index 2aace0f..7017ff5 100644
--- a/core/java/android/view/RenderNode.java
+++ b/core/java/android/view/RenderNode.java
@@ -136,6 +136,9 @@
     private RenderNode(String name, View owningView) {
         mNativeRenderNode = nCreate(name);
         mOwningView = owningView;
+        if (mOwningView instanceof SurfaceView) {
+            nRequestPositionUpdates(mNativeRenderNode, (SurfaceView) mOwningView);
+        }
     }
 
     /**
@@ -863,6 +866,8 @@
     private static native void nOutput(long renderNode);
     private static native int nGetDebugSize(long renderNode);
 
+    private static native void nRequestPositionUpdates(long renderNode, SurfaceView callback);
+
     ///////////////////////////////////////////////////////////////////////////
     // Animations
     ///////////////////////////////////////////////////////////////////////////
diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java
index 5b48e28..a296051 100644
--- a/core/java/android/view/SurfaceView.java
+++ b/core/java/android/view/SurfaceView.java
@@ -135,7 +135,7 @@
         }
     };
 
-    final ViewTreeObserver.OnScrollChangedListener mScrollChangedListener
+    private final ViewTreeObserver.OnScrollChangedListener mScrollChangedListener
             = new ViewTreeObserver.OnScrollChangedListener() {
                     @Override
                     public void onScrollChanged() {
@@ -143,6 +143,17 @@
                     }
             };
 
+    private final ViewTreeObserver.OnPreDrawListener mDrawListener =
+            new ViewTreeObserver.OnPreDrawListener() {
+                @Override
+                public boolean onPreDraw() {
+                    // reposition ourselves where the surface is
+                    mHaveFrame = getWidth() > 0 && getHeight() > 0;
+                    updateWindow(false, false);
+                    return true;
+                }
+            };
+
     boolean mRequestedVisible = false;
     boolean mWindowVisibility = false;
     boolean mViewVisibility = false;
@@ -168,17 +179,9 @@
     boolean mUpdateWindowNeeded;
     boolean mReportDrawNeeded;
     private Translator mTranslator;
+    private int mWindowInsetLeft;
+    private int mWindowInsetTop;
 
-    private final ViewTreeObserver.OnPreDrawListener mDrawListener =
-            new ViewTreeObserver.OnPreDrawListener() {
-                @Override
-                public boolean onPreDraw() {
-                    // reposition ourselves where the surface is
-                    mHaveFrame = getWidth() > 0 && getHeight() > 0;
-                    updateWindow(false, false);
-                    return true;
-                }
-            };
     private boolean mGlobalListenersAdded;
 
     public SurfaceView(Context context) {
@@ -443,17 +446,17 @@
         int myHeight = mRequestedHeight;
         if (myHeight <= 0) myHeight = getHeight();
 
-        getLocationInWindow(mLocation);
         final boolean creating = mWindow == null;
         final boolean formatChanged = mFormat != mRequestedFormat;
         final boolean sizeChanged = mWindowSpaceWidth != myWidth || mWindowSpaceHeight != myHeight;
         final boolean visibleChanged = mVisible != mRequestedVisible;
         final boolean layoutSizeChanged = getWidth() != mLayout.width
                 || getHeight() != mLayout.height;
-        final boolean positionChanged = mWindowSpaceLeft != mLocation[0] || mWindowSpaceTop != mLocation[1];
 
         if (force || creating || formatChanged || sizeChanged || visibleChanged
             || mUpdateWindowNeeded || mReportDrawNeeded || redrawNeeded) {
+            getLocationInWindow(mLocation);
+
             if (DEBUG) Log.i(TAG, "Changes: creating=" + creating
                     + " format=" + formatChanged + " size=" + sizeChanged
                     + " visible=" + visibleChanged
@@ -643,27 +646,69 @@
                 TAG, "Layout: x=" + mLayout.x + " y=" + mLayout.y +
                 " w=" + mLayout.width + " h=" + mLayout.height +
                 ", frame=" + mSurfaceFrame);
-        } else if (positionChanged || layoutSizeChanged) { // Only the position has changed
-            mWindowSpaceLeft = mLocation[0];
-            mWindowSpaceTop = mLocation[1];
-            // For our size changed check, we keep mLayout.width and mLayout.height
-            // in view local space.
-            mLocation[0] = mLayout.width = getWidth();
-            mLocation[1] = mLayout.height = getHeight();
+        } else if (!isHardwareAccelerated()) {
+            getLocationInWindow(mLocation);
+            final boolean positionChanged = mWindowSpaceLeft != mLocation[0]
+                    || mWindowSpaceTop != mLocation[1];
+            if (positionChanged || layoutSizeChanged) { // Only the position has changed
+                mWindowSpaceLeft = mLocation[0];
+                mWindowSpaceTop = mLocation[1];
+                // For our size changed check, we keep mLayout.width and mLayout.height
+                // in view local space.
+                mLocation[0] = mLayout.width = getWidth();
+                mLocation[1] = mLayout.height = getHeight();
 
-            transformFromViewToWindowSpace(mLocation);
+                transformFromViewToWindowSpace(mLocation);
 
-            try {
-                mSession.repositionChild(mWindow, mWindowSpaceLeft, mWindowSpaceTop,
-                        mLocation[0], mLocation[1],
-                        viewRoot != null ? viewRoot.getNextFrameNumber() : -1,
-                        mWinFrame);
-            } catch (RemoteException ex) {
-                Log.e(TAG, "Exception from relayout", ex);
+                try {
+                    Log.d(TAG, String.format("updateWindowPosition UI, " +
+                            "postion = [%d, %d, %d, %d]", mWindowSpaceLeft, mWindowSpaceTop,
+                            mLocation[0], mLocation[1]));
+                    mSession.repositionChild(mWindow, mWindowSpaceLeft, mWindowSpaceTop,
+                            mLocation[0], mLocation[1], -1, mWinFrame);
+                } catch (RemoteException ex) {
+                    Log.e(TAG, "Exception from relayout", ex);
+                }
             }
         }
     }
 
+    private Rect mRTLastReportedPosition = new Rect();
+
+    /**
+     * Called by native on RenderThread to update the window position
+     * @hide
+     */
+    public final void updateWindowPositionRT(long frameNumber,
+            int left, int top, int right, int bottom) {
+        IWindowSession session = mSession;
+        MyWindow window = mWindow;
+        if (session == null || window == null) {
+            // Guess we got detached, that sucks
+            return;
+        }
+        if (mRTLastReportedPosition.left == left
+                && mRTLastReportedPosition.top == top
+                && mRTLastReportedPosition.right == right
+                && mRTLastReportedPosition.bottom == bottom) {
+            return;
+        }
+        try {
+            if (DEBUG) {
+                Log.d(TAG, String.format("updateWindowPosition RT, frameNr = %d, " +
+                        "postion = [%d, %d, %d, %d]", frameNumber, left, top,
+                        right, bottom));
+            }
+            // Just using mRTLastReportedPosition as a dummy rect here
+            session.repositionChild(window, left, top, right, bottom, frameNumber,
+                    mRTLastReportedPosition);
+            // Now overwrite mRTLastReportedPosition with our values
+            mRTLastReportedPosition.set(left, top, right, bottom);
+        } catch (RemoteException ex) {
+            Log.e(TAG, "Exception from repositionChild", ex);
+        }
+    }
+
     private SurfaceHolder.Callback[] getSurfaceCallbacks() {
         SurfaceHolder.Callback callbacks[];
         synchronized (mCallbacks) {
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 4a0a0b0..127157b 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -4506,9 +4506,15 @@
                     }
                     break;
                 case R.styleable.View_pointerShape:
-                    final int pointerShape = a.getInt(attr, PointerIcon.STYLE_NOT_SPECIFIED);
-                    if (pointerShape != PointerIcon.STYLE_NOT_SPECIFIED) {
-                        setPointerIcon(PointerIcon.getSystemIcon(context, pointerShape));
+                    final int resourceId = a.getResourceId(attr, 0);
+                    if (resourceId != 0) {
+                        setPointerIcon(PointerIcon.loadCustomIcon(
+                                context.getResources(), resourceId));
+                    } else {
+                        final int pointerShape = a.getInt(attr, PointerIcon.STYLE_NOT_SPECIFIED);
+                        if (pointerShape != PointerIcon.STYLE_NOT_SPECIFIED) {
+                            setPointerIcon(PointerIcon.getSystemIcon(context, pointerShape));
+                        }
                     }
                     break;
             }
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 98e3289..96853e0 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -1811,7 +1811,9 @@
                 final boolean dragResizing = freeformResizing || dockedResizing;
                 if (mDragResizing != dragResizing) {
                     if (dragResizing) {
-                        startDragResizing(mPendingBackDropFrame);
+                        startDragResizing(mPendingBackDropFrame,
+                                mWinFrame.equals(mPendingBackDropFrame), mPendingVisibleInsets,
+                                mPendingStableInsets);
                         mResizeMode = freeformResizing
                                 ? RESIZE_MODE_FREEFORM
                                 : RESIZE_MODE_DOCKED_DIVIDER;
@@ -5845,9 +5847,11 @@
         // Tell all listeners that we are resizing the window so that the chrome can get
         // updated as fast as possible on a separate thread,
         if (mDragResizing) {
+            boolean fullscreen = frame.equals(backDropFrame);
             synchronized (mWindowCallbacks) {
                 for (int i = mWindowCallbacks.size() - 1; i >= 0; i--) {
-                    mWindowCallbacks.get(i).onWindowSizeIsChanging(backDropFrame);
+                    mWindowCallbacks.get(i).onWindowSizeIsChanging(backDropFrame, fullscreen,
+                            visibleInsets, stableInsets);
                 }
             }
         }
@@ -6815,20 +6819,6 @@
         }
     }
 
-    long getNextFrameNumber() {
-        long frameNumber = -1;
-        if (mSurfaceHolder != null) {
-            mSurfaceHolder.mSurfaceLock.lock();
-        }
-        if (mSurface.isValid()) {
-            frameNumber =  mSurface.getNextFrameNumber();
-        }
-        if (mSurfaceHolder != null) {
-            mSurfaceHolder.mSurfaceLock.unlock();
-        }
-        return frameNumber;
-    }
-
     class TakenSurfaceHolder extends BaseSurfaceHolder {
         @Override
         public boolean onAllowLockCanvas() {
@@ -7060,12 +7050,14 @@
     /**
      * Start a drag resizing which will inform all listeners that a window resize is taking place.
      */
-    private void startDragResizing(Rect initialBounds) {
+    private void startDragResizing(Rect initialBounds, boolean fullscreen, Rect systemInsets,
+            Rect stableInsets) {
         if (!mDragResizing) {
             mDragResizing = true;
             synchronized (mWindowCallbacks) {
                 for (int i = mWindowCallbacks.size() - 1; i >= 0; i--) {
-                    mWindowCallbacks.get(i).onWindowDragResizeStart(initialBounds);
+                    mWindowCallbacks.get(i).onWindowDragResizeStart(initialBounds, fullscreen,
+                            systemInsets, stableInsets);
                 }
             }
             mFullRedrawNeeded = true;
diff --git a/core/java/android/view/WindowCallbacks.java b/core/java/android/view/WindowCallbacks.java
index def02365..d2bfca7 100644
--- a/core/java/android/view/WindowCallbacks.java
+++ b/core/java/android/view/WindowCallbacks.java
@@ -28,20 +28,30 @@
 public interface WindowCallbacks {
     /**
      * Called by the system when the window got changed by the user, before the layouter got called.
-     * It can be used to perform a "quick and dirty" resize which should never take more then 4ms to
-     * complete.
+     * It also gets called when the insets changed, or when the window switched between a fullscreen
+     * layout or a non-fullscreen layout. It can be used to perform a "quick and dirty" resize which
+     * should never take more then 4ms to complete.
      *
      * <p>At the time the layouting has not happened yet.
      *
      * @param newBounds The new window frame bounds.
+     * @param fullscreen Whether the window is currently drawing in fullscreen.
+     * @param systemInsets The current visible system insets for the window.
+     * @param stableInsets The stable insets for the window.
      */
-    void onWindowSizeIsChanging(Rect newBounds);
+    void onWindowSizeIsChanging(Rect newBounds, boolean fullscreen, Rect systemInsets,
+            Rect stableInsets);
 
     /**
      * Called when a drag resize starts.
+     *
      * @param initialBounds The initial bounds where the window will be.
+     * @param fullscreen Whether the window is currently drawing in fullscreen.
+     * @param systemInsets The current visible system insets for the window.
+     * @param stableInsets The stable insets for the window.
      */
-    void onWindowDragResizeStart(Rect initialBounds);
+    void onWindowDragResizeStart(Rect initialBounds, boolean fullscreen, Rect systemInsets,
+            Rect stableInsets);
 
     /**
      * Called when a drag resize ends.
diff --git a/core/java/android/view/WindowManagerInternal.java b/core/java/android/view/WindowManagerInternal.java
index 89b1eb9..c22d60d 100644
--- a/core/java/android/view/WindowManagerInternal.java
+++ b/core/java/android/view/WindowManagerInternal.java
@@ -268,4 +268,9 @@
 
     /** Returns true if the stack with the input Id is currently visible. */
     public abstract boolean isStackVisible(int stackId);
+
+    /**
+     * @return True if and only if the docked divider is currently in resize mode.
+     */
+    public abstract boolean isDockedDividerResizing();
 }
diff --git a/core/java/android/widget/AbsSeekBar.java b/core/java/android/widget/AbsSeekBar.java
index 4ca8971..9a68593 100644
--- a/core/java/android/widget/AbsSeekBar.java
+++ b/core/java/android/widget/AbsSeekBar.java
@@ -955,7 +955,7 @@
                 if (!canUserSetProgress()) {
                     return false;
                 }
-                int increment = Math.max(1, Math.round((float) getMax() / 5));
+                int increment = Math.max(1, Math.round((float) getMax() / 20));
                 if (action == AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD) {
                     increment = -increment;
                 }
diff --git a/core/java/android/widget/GridView.java b/core/java/android/widget/GridView.java
index ee73092..1321221 100644
--- a/core/java/android/widget/GridView.java
+++ b/core/java/android/widget/GridView.java
@@ -2404,7 +2404,7 @@
         }
 
         final LayoutParams lp = (LayoutParams) view.getLayoutParams();
-        final boolean isHeading = lp != null && lp.viewType != ITEM_VIEW_TYPE_HEADER_OR_FOOTER;
+        final boolean isHeading = lp != null && lp.viewType == ITEM_VIEW_TYPE_HEADER_OR_FOOTER;
         final boolean isSelected = isItemChecked(position);
         final CollectionItemInfo itemInfo = CollectionItemInfo.obtain(
                 row, 1, column, 1, isHeading, isSelected);
diff --git a/core/java/android/widget/PopupWindow.java b/core/java/android/widget/PopupWindow.java
index 584df08..8fa71a2 100644
--- a/core/java/android/widget/PopupWindow.java
+++ b/core/java/android/widget/PopupWindow.java
@@ -43,6 +43,7 @@
 import android.view.KeyEvent;
 import android.view.MotionEvent;
 import android.view.View;
+import android.view.View.OnAttachStateChangeListener;
 import android.view.View.OnTouchListener;
 import android.view.ViewGroup;
 import android.view.ViewParent;
@@ -164,7 +165,20 @@
         com.android.internal.R.attr.state_above_anchor
     };
 
+    private final OnAttachStateChangeListener mOnAnchorRootDetachedListener =
+            new OnAttachStateChangeListener() {
+                @Override
+                public void onViewAttachedToWindow(View v) {}
+
+                @Override
+                public void onViewDetachedFromWindow(View v) {
+                    mIsAnchorRootAttached = false;
+                }
+            };
+
     private WeakReference<View> mAnchor;
+    private WeakReference<View> mAnchorRoot;
+    private boolean mIsAnchorRootAttached;
 
     private final OnScrollChangedListener mOnScrollChangedListener = new OnScrollChangedListener() {
         @Override
@@ -1037,7 +1051,7 @@
 
         TransitionManager.endTransitions(mDecorView);
 
-        unregisterForScrollChanged();
+        unregisterForViewTreeChanges();
 
         mIsShowing = true;
         mIsDropdown = false;
@@ -1120,7 +1134,7 @@
 
         TransitionManager.endTransitions(mDecorView);
 
-        registerForScrollChanged(anchor, xoff, yoff, gravity);
+        registerForViewTreeChanges(anchor, xoff, yoff, gravity);
 
         mIsShowing = true;
         mIsDropdown = true;
@@ -1633,14 +1647,23 @@
         mIsShowing = false;
         mIsTransitioningToDismiss = true;
 
+        // This method may be called as part of window detachment, in which
+        // case the anchor view (and its root) will still return true from
+        // isAttachedToWindow() during execution of this method; however, we
+        // can expect the OnAttachStateChangeListener to have been called prior
+        // to executing this method, so we can rely on that instead.
         final Transition exitTransition = mExitTransition;
-        if (exitTransition != null && decorView.isLaidOut()) {
+        if (!mIsAnchorRootAttached && exitTransition != null && decorView.isLaidOut()) {
             // The decor view is non-interactive during exit transitions.
             final LayoutParams p = (LayoutParams) decorView.getLayoutParams();
             p.flags |= LayoutParams.FLAG_NOT_TOUCHABLE;
             p.flags |= LayoutParams.FLAG_NOT_FOCUSABLE;
             mWindowManager.updateViewLayout(decorView, p);
 
+            // Once we start dismissing the decor view, all state (including
+            // the anchor root) needs to be moved to the decor view since we
+            // may open another popup while it's busy exiting.
+            final View anchorRoot = mAnchorRoot != null ? mAnchorRoot.get() : null;
             final Rect epicenter = getTransitionEpicenter();
             exitTransition.setEpicenterCallback(new EpicenterCallback() {
                 @Override
@@ -1648,18 +1671,19 @@
                     return epicenter;
                 }
             });
-            decorView.startExitTransition(exitTransition, new TransitionListenerAdapter() {
-                @Override
-                public void onTransitionEnd(Transition transition) {
-                    dismissImmediate(decorView, contentHolder, contentView);
-                }
-            });
+            decorView.startExitTransition(exitTransition, anchorRoot,
+                    new TransitionListenerAdapter() {
+                        @Override
+                        public void onTransitionEnd(Transition transition) {
+                            dismissImmediate(decorView, contentHolder, contentView);
+                        }
+                    });
         } else {
             dismissImmediate(decorView, contentHolder, contentView);
         }
 
         // Clears the anchor view.
-        unregisterForScrollChanged();
+        unregisterForViewTreeChanges();
 
         if (mOnDismissListener != null) {
             mOnDismissListener.onDismiss();
@@ -1925,7 +1949,7 @@
         final WeakReference<View> oldAnchor = mAnchor;
         final boolean needsUpdate = updateLocation && (mAnchorXoff != xoff || mAnchorYoff != yoff);
         if (oldAnchor == null || oldAnchor.get() != anchor || (needsUpdate && !mIsDropdown)) {
-            registerForScrollChanged(anchor, xoff, yoff, mAnchoredGravity);
+            registerForViewTreeChanges(anchor, xoff, yoff, mAnchoredGravity);
         } else if (needsUpdate) {
             // No need to register again if this is a DropDown, showAsDropDown already did.
             mAnchorXoff = xoff;
@@ -1969,27 +1993,38 @@
         public void onDismiss();
     }
 
-    private void unregisterForScrollChanged() {
-        final WeakReference<View> anchorRef = mAnchor;
-        final View anchor = anchorRef == null ? null : anchorRef.get();
+    private void unregisterForViewTreeChanges() {
+        final View anchor = mAnchor != null ? mAnchor.get() : null;
         if (anchor != null) {
             final ViewTreeObserver vto = anchor.getViewTreeObserver();
             vto.removeOnScrollChangedListener(mOnScrollChangedListener);
         }
 
+        final View anchorRoot = mAnchorRoot != null ? mAnchorRoot.get() : null;
+        if (anchorRoot != null) {
+            anchorRoot.removeOnAttachStateChangeListener(mOnAnchorRootDetachedListener);
+        }
+
         mAnchor = null;
+        mAnchorRoot = null;
+        mIsAnchorRootAttached = false;
     }
 
-    private void registerForScrollChanged(View anchor, int xoff, int yoff, int gravity) {
-        unregisterForScrollChanged();
-
-        mAnchor = new WeakReference<>(anchor);
+    private void registerForViewTreeChanges(View anchor, int xoff, int yoff, int gravity) {
+        unregisterForViewTreeChanges();
 
         final ViewTreeObserver vto = anchor.getViewTreeObserver();
         if (vto != null) {
             vto.addOnScrollChangedListener(mOnScrollChangedListener);
         }
 
+        final View anchorRoot = anchor.getRootView();
+        anchorRoot.addOnAttachStateChangeListener(mOnAnchorRootDetachedListener);
+
+        mAnchor = new WeakReference<>(anchor);
+        mAnchorRoot = new WeakReference<>(anchorRoot);
+        mIsAnchorRootAttached = anchorRoot.isAttachedToWindow();
+
         mAnchorXoff = xoff;
         mAnchorYoff = yoff;
         mAnchoredGravity = gravity;
@@ -2109,16 +2144,23 @@
          * its {@code onTransitionEnd} method called even if the transition
          * never starts; however, it may be called with a {@code null} argument.
          */
-        public void startExitTransition(Transition transition, final TransitionListener listener) {
+        public void startExitTransition(Transition transition, final View anchorRoot,
+                final TransitionListener listener) {
             if (transition == null) {
                 return;
             }
 
+            // The anchor view's window may go away while we're executing our
+            // transition, in which case we need to end the transition
+            // immediately and execute the listener to remove the popup.
+            anchorRoot.addOnAttachStateChangeListener(mOnAnchorRootDetachedListener);
+
             // The exit listener MUST be called for cleanup, even if the
             // transition never starts or ends. Stash it for later.
             mPendingExitListener = new TransitionListenerAdapter() {
                 @Override
                 public void onTransitionEnd(Transition transition) {
+                    anchorRoot.removeOnAttachStateChangeListener(mOnAnchorRootDetachedListener);
                     listener.onTransitionEnd(transition);
 
                     // The listener was called. Our job here is done.
@@ -2153,6 +2195,19 @@
                 mPendingExitListener.onTransitionEnd(null);
             }
         }
+
+        private final OnAttachStateChangeListener mOnAnchorRootDetachedListener =
+                new OnAttachStateChangeListener() {
+                    @Override
+                    public void onViewAttachedToWindow(View v) {}
+
+                    @Override
+                    public void onViewDetachedFromWindow(View v) {
+                        v.removeOnAttachStateChangeListener(this);
+
+                        TransitionManager.endTransitions(PopupDecorView.this);
+                    }
+                };
     }
 
     private class PopupBackgroundView extends FrameLayout {
diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java
index cf13a13..b0fb93b 100644
--- a/core/java/com/android/internal/app/ChooserActivity.java
+++ b/core/java/com/android/internal/app/ChooserActivity.java
@@ -761,6 +761,7 @@
         public static final int TARGET_STANDARD = 2;
 
         private static final int MAX_SERVICE_TARGETS = 8;
+        private static final int MAX_TARGETS_PER_SERVICE = 4;
 
         private final List<ChooserTargetInfo> mServiceTargets = new ArrayList<>();
         private final List<TargetInfo> mCallerTargets = new ArrayList<>();
@@ -925,7 +926,7 @@
             final float parentScore = getScore(origTarget);
             Collections.sort(targets, mBaseTargetComparator);
             float lastScore = 0;
-            for (int i = 0, N = targets.size(); i < N; i++) {
+            for (int i = 0, N = Math.min(targets.size(), MAX_TARGETS_PER_SERVICE); i < N; i++) {
                 final ChooserTarget target = targets.get(i);
                 float targetScore = target.getScore();
                 targetScore *= parentScore;
diff --git a/core/java/com/android/internal/app/IntentForwarderActivity.java b/core/java/com/android/internal/app/IntentForwarderActivity.java
index dbec740..015e60d 100644
--- a/core/java/com/android/internal/app/IntentForwarderActivity.java
+++ b/core/java/com/android/internal/app/IntentForwarderActivity.java
@@ -85,8 +85,10 @@
         int callingUserId = getUserId();
 
         if (canForward(newIntent, targetUserId)) {
-            if (newIntent.getAction().equals(Intent.ACTION_CHOOSER)) {
+            if (Intent.ACTION_CHOOSER.equals(newIntent.getAction())) {
                 Intent innerIntent = (Intent) newIntent.getParcelableExtra(Intent.EXTRA_INTENT);
+                // At this point, innerIntent is not null. Otherwise, canForward would have returned
+                // false.
                 innerIntent.prepareToLeaveUser(callingUserId);
             } else {
                 newIntent.prepareToLeaveUser(callingUserId);
@@ -124,7 +126,7 @@
                 Toast.makeText(this, getString(userMessageId), Toast.LENGTH_LONG).show();
             }
         } else {
-            Slog.wtf(TAG, "the intent: " + newIntent + "cannot be forwarded from user "
+            Slog.wtf(TAG, "the intent: " + newIntent + " cannot be forwarded from user "
                     + callingUserId + " to user " + targetUserId);
         }
         finish();
@@ -132,7 +134,7 @@
 
     boolean canForward(Intent intent, int targetUserId)  {
         IPackageManager ipm = AppGlobals.getPackageManager();
-        if (intent.getAction().equals(Intent.ACTION_CHOOSER)) {
+        if (Intent.ACTION_CHOOSER.equals(intent.getAction())) {
             // The EXTRA_INITIAL_INTENTS may not be allowed to be forwarded.
             if (intent.hasExtra(Intent.EXTRA_INITIAL_INTENTS)) {
                 Slog.wtf(TAG, "An chooser intent with extra initial intents cannot be forwarded to"
@@ -145,6 +147,11 @@
                 return false;
             }
             intent = (Intent) intent.getParcelableExtra(Intent.EXTRA_INTENT);
+            if (intent == null) {
+                Slog.wtf(TAG, "Cannot forward a chooser intent with no extra "
+                        + Intent.EXTRA_INTENT);
+                return false;
+            }
         }
         String resolvedType = intent.resolveTypeIfNeeded(getContentResolver());
         if (intent.getSelector() != null) {
diff --git a/core/java/com/android/internal/app/LocalePickerWithRegion.java b/core/java/com/android/internal/app/LocalePickerWithRegion.java
index 9a178837..956ee8c 100644
--- a/core/java/com/android/internal/app/LocalePickerWithRegion.java
+++ b/core/java/com/android/internal/app/LocalePickerWithRegion.java
@@ -138,22 +138,12 @@
         super.onCreate(savedInstanceState);
         setHasOptionsMenu(true);
 
-        Locale sortingLocale;
-        if (mCountryMode) {
-            if (mParentLocale == null) {
-                sortingLocale = Locale.getDefault();
-                this.getActivity().setTitle(R.string.country_selection_title);
-            } else {
-                sortingLocale = mParentLocale.getLocale();
-                this.getActivity().setTitle(mParentLocale.getFullNameNative());
-            }
-        } else {
-            sortingLocale = Locale.getDefault();
-            this.getActivity().setTitle(R.string.language_selection_title);
-        }
+        final Locale sortingLocale = (mCountryMode && mParentLocale != null)
+                ? mParentLocale.getLocale()
+                : Locale.getDefault();
 
         mAdapter = new SuggestedLocaleAdapter(mLocaleList, mCountryMode);
-        LocaleHelper.LocaleInfoComparator comp =
+        final LocaleHelper.LocaleInfoComparator comp =
                 new LocaleHelper.LocaleInfoComparator(sortingLocale);
         mAdapter.sort(comp);
         setListAdapter(mAdapter);
@@ -173,6 +163,17 @@
     @Override
     public void onResume() {
         super.onResume();
+
+        if (mCountryMode) {
+            if (mParentLocale == null) {
+                this.getActivity().setTitle(R.string.country_selection_title);
+            } else {
+                this.getActivity().setTitle(mParentLocale.getFullNameNative());
+            }
+        } else {
+            this.getActivity().setTitle(R.string.language_selection_title);
+        }
+
         getListView().requestFocus();
     }
 
diff --git a/core/java/com/android/internal/app/ResolverActivity.java b/core/java/com/android/internal/app/ResolverActivity.java
index 53e329d..3fb768f 100644
--- a/core/java/com/android/internal/app/ResolverActivity.java
+++ b/core/java/com/android/internal/app/ResolverActivity.java
@@ -52,6 +52,7 @@
 import android.os.Bundle;
 import android.os.PatternMatcher;
 import android.os.RemoteException;
+import android.os.StrictMode;
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.util.Log;
@@ -173,6 +174,10 @@
 
     @Override
     protected void onCreate(Bundle savedInstanceState) {
+        // We're dispatching intents that might be coming from legacy apps, so
+        // don't kill ourselves.
+        StrictMode.disableDeathOnFileUriExposure();
+
         // Use a specialized prompt when we're handling the 'Home' app startActivity()
         final Intent intent = makeMyIntent();
         final Set<String> categories = intent.getCategories();
diff --git a/core/java/com/android/internal/app/ResolverComparator.java b/core/java/com/android/internal/app/ResolverComparator.java
index 0ae21c6..03a3a38 100644
--- a/core/java/com/android/internal/app/ResolverComparator.java
+++ b/core/java/com/android/internal/app/ResolverComparator.java
@@ -52,7 +52,7 @@
 
     private static final long RECENCY_TIME_PERIOD = 1000 * 60 * 60 * 12;
 
-    private static final float RECENCY_MULTIPLIER = 3.f;
+    private static final float RECENCY_MULTIPLIER = 2.f;
 
     private final Collator mCollator;
     private final boolean mHttp;
diff --git a/core/java/com/android/internal/policy/BackdropFrameRenderer.java b/core/java/com/android/internal/policy/BackdropFrameRenderer.java
index 5047c4c..6931193 100644
--- a/core/java/com/android/internal/policy/BackdropFrameRenderer.java
+++ b/core/java/com/android/internal/policy/BackdropFrameRenderer.java
@@ -24,7 +24,6 @@
 import android.view.DisplayListCanvas;
 import android.view.RenderNode;
 import android.view.ThreadedRenderer;
-import android.view.View;
 
 /**
  * The thread which draws a fill in background while the app is resizing in areas where the app
@@ -49,6 +48,7 @@
 
     private final Rect mOldTargetRect = new Rect();
     private final Rect mNewTargetRect = new Rect();
+
     private Choreographer mChoreographer;
 
     // Cached size values from the last render for the case that the view hierarchy is gone
@@ -66,15 +66,23 @@
     private Drawable mUserCaptionBackgroundDrawable;
     private Drawable mResizingBackgroundDrawable;
     private ColorDrawable mStatusBarColor;
+    private ColorDrawable mNavigationBarColor;
+    private boolean mOldFullscreen;
+    private boolean mFullscreen;
+    private final Rect mOldSystemInsets = new Rect();
+    private final Rect mOldStableInsets = new Rect();
+    private final Rect mSystemInsets = new Rect();
+    private final Rect mStableInsets = new Rect();
 
     public BackdropFrameRenderer(DecorView decorView, ThreadedRenderer renderer, Rect initialBounds,
             Drawable resizingBackgroundDrawable, Drawable captionBackgroundDrawable,
-            Drawable userCaptionBackgroundDrawable, int statusBarColor) {
+            Drawable userCaptionBackgroundDrawable, int statusBarColor, int navigationBarColor,
+            boolean fullscreen, Rect systemInsets, Rect stableInsets) {
         setName("ResizeFrame");
 
         mRenderer = renderer;
         onResourcesLoaded(decorView, resizingBackgroundDrawable, captionBackgroundDrawable,
-                userCaptionBackgroundDrawable, statusBarColor);
+                userCaptionBackgroundDrawable, statusBarColor, navigationBarColor);
 
         // Create a render node for the content and frame backdrop
         // which can be resized independently from the content.
@@ -84,8 +92,14 @@
 
         // Set the initial bounds and draw once so that we do not get a broken frame.
         mTargetRect.set(initialBounds);
+        mFullscreen = fullscreen;
+        mOldFullscreen = fullscreen;
+        mSystemInsets.set(systemInsets);
+        mStableInsets.set(stableInsets);
+        mOldSystemInsets.set(systemInsets);
+        mOldStableInsets.set(stableInsets);
         synchronized (this) {
-            changeWindowSizeLocked(initialBounds);
+            redrawLocked(initialBounds, fullscreen, mSystemInsets, mStableInsets);
         }
 
         // Kick off our draw thread.
@@ -94,7 +108,7 @@
 
     void onResourcesLoaded(DecorView decorView, Drawable resizingBackgroundDrawable,
             Drawable captionBackgroundDrawableDrawable, Drawable userCaptionBackgroundDrawable,
-            int statusBarColor) {
+            int statusBarColor, int navigationBarColor) {
         mDecorView = decorView;
         mResizingBackgroundDrawable = resizingBackgroundDrawable;
         mCaptionBackgroundDrawable = captionBackgroundDrawableDrawable;
@@ -108,6 +122,12 @@
         } else {
             mStatusBarColor = null;
         }
+        if (navigationBarColor != 0) {
+            mNavigationBarColor = new ColorDrawable(navigationBarColor);
+            addSystemBarNodeIfNeeded();
+        } else {
+            mNavigationBarColor = null;
+        }
     }
 
     private void addSystemBarNodeIfNeeded() {
@@ -119,13 +139,22 @@
     }
 
     /**
-     * Call this function asynchronously when the window size has been changed. The change will
-     * be picked up once per frame and the frame will be re-rendered accordingly.
+     * Call this function asynchronously when the window size has been changed or when the insets
+     * have changed or whether window switched between a fullscreen or non-fullscreen layout.
+     * The change will be picked up once per frame and the frame will be re-rendered accordingly.
+     *
      * @param newTargetBounds The new target bounds.
+     * @param fullscreen Whether the window is currently drawing in fullscreen.
+     * @param systemInsets The current visible system insets for the window.
+     * @param stableInsets The stable insets for the window.
      */
-    public void setTargetRect(Rect newTargetBounds) {
+    public void setTargetRect(Rect newTargetBounds, boolean fullscreen, Rect systemInsets,
+            Rect stableInsets) {
         synchronized (this) {
+            mFullscreen = fullscreen;
             mTargetRect.set(newTargetBounds);
+            mSystemInsets.set(systemInsets);
+            mStableInsets.set(stableInsets);
             // Notify of a bounds change.
             pingRenderLocked();
         }
@@ -204,16 +233,23 @@
                 return;
             }
             mNewTargetRect.set(mTargetRect);
-            if (!mNewTargetRect.equals(mOldTargetRect) || mReportNextDraw) {
+            if (!mNewTargetRect.equals(mOldTargetRect)
+                    || mOldFullscreen != mFullscreen
+                    || !mStableInsets.equals(mOldStableInsets)
+                    || !mSystemInsets.equals(mOldSystemInsets)
+                    || mReportNextDraw) {
+                mOldFullscreen = mFullscreen;
                 mOldTargetRect.set(mNewTargetRect);
-                changeWindowSizeLocked(mNewTargetRect);
+                mOldSystemInsets.set(mSystemInsets);
+                mOldStableInsets.set(mStableInsets);
+                redrawLocked(mNewTargetRect, mFullscreen, mSystemInsets, mStableInsets);
             }
         }
     }
 
     /**
      * The content is about to be drawn and we got the location of where it will be shown.
-     * If a "changeWindowSizeLocked" call has already been processed, we will re-issue the call
+     * If a "redrawLocked" call has already been processed, we will re-issue the call
      * if the previous call was ignored since the size was unknown.
      * @param xOffset The x offset where the content is drawn to.
      * @param yOffset The y offset where the content is drawn to.
@@ -235,8 +271,8 @@
                     mLastYOffset,
                     mLastXOffset + mLastContentWidth,
                     mLastYOffset + mLastCaptionHeight + mLastContentHeight);
-            // If this was the first call and changeWindowSizeLocked got already called prior
-            // to us, we should re-issue a changeWindowSizeLocked now.
+            // If this was the first call and redrawLocked got already called prior
+            // to us, we should re-issue a redrawLocked now.
             return firstCall
                     && (mLastCaptionHeight != 0 || !mDecorView.isShowingCaption());
         }
@@ -251,16 +287,20 @@
     }
 
     /**
-     * Resizing the frame to fit the new window size.
+     * Redraws the background, the caption and the system inset backgrounds if something changed.
+     *
      * @param newBounds The window bounds which needs to be drawn.
+     * @param fullscreen Whether the window is currently drawing in fullscreen.
+     * @param systemInsets The current visible system insets for the window.
+     * @param stableInsets The stable insets for the window.
      */
-    private void changeWindowSizeLocked(Rect newBounds) {
+    private void redrawLocked(Rect newBounds, boolean fullscreen, Rect systemInsets,
+            Rect stableInsets) {
 
         // While a configuration change is taking place the view hierarchy might become
         // inaccessible. For that case we remember the previous metrics to avoid flashes.
         // Note that even when there is no visible caption, the caption child will exist.
         final int captionHeight = mDecorView.getCaptionHeight();
-        final int statusBarHeight = mDecorView.getStatusBarHeight();
 
         // The caption height will probably never dynamically change while we are resizing.
         // Once set to something other then 0 it should be kept that way.
@@ -302,14 +342,7 @@
         }
         mFrameAndBackdropNode.end(canvas);
 
-        if (mSystemBarBackgroundNode != null && mStatusBarColor != null) {
-            canvas = mSystemBarBackgroundNode.start(width, height);
-            mSystemBarBackgroundNode.setLeftTopRightBottom(left, top, left + width, top + height);
-            mStatusBarColor.setBounds(0, 0, left + width, statusBarHeight);
-            mStatusBarColor.draw(canvas);
-            mSystemBarBackgroundNode.end(canvas);
-            mRenderer.drawRenderNode(mSystemBarBackgroundNode);
-        }
+        drawColorViews(left, top, width, height, fullscreen, systemInsets, stableInsets);
 
         // We need to render the node explicitly
         mRenderer.drawRenderNode(mFrameAndBackdropNode);
@@ -317,6 +350,39 @@
         reportDrawIfNeeded();
     }
 
+    private void drawColorViews(int left, int top, int width, int height,
+            boolean fullscreen, Rect systemInsets, Rect stableInsets) {
+        if (mSystemBarBackgroundNode == null) {
+            return;
+        }
+        DisplayListCanvas canvas = mSystemBarBackgroundNode.start(width, height);
+        mSystemBarBackgroundNode.setLeftTopRightBottom(left, top, left + width, top + height);
+        final int topInset = DecorView.getColorViewTopInset(mStableInsets.top, mSystemInsets.top);
+        final int bottomInset = DecorView.getColorViewBottomInset(stableInsets.bottom,
+                systemInsets.bottom);
+        final int rightInset = DecorView.getColorViewRightInset(stableInsets.right,
+                systemInsets.right);
+        if (mStatusBarColor != null) {
+            mStatusBarColor.setBounds(0, 0, left + width, topInset);
+            mStatusBarColor.draw(canvas);
+        }
+
+        // We only want to draw the navigation bar if our window is currently fullscreen because we
+        // don't want the navigation bar background be moving around when resizing in docked mode.
+        // However, we need it for the transitions into/out of docked mode.
+        if (mNavigationBarColor != null && fullscreen) {
+            final int size = DecorView.getNavBarSize(bottomInset, rightInset);
+            if (DecorView.isNavBarToRightEdge(bottomInset, rightInset)) {
+                mNavigationBarColor.setBounds(width - size, 0, width, height);
+            } else {
+                mNavigationBarColor.setBounds(0, height - size, width, height);
+            }
+            mNavigationBarColor.draw(canvas);
+        }
+        mSystemBarBackgroundNode.end(canvas);
+        mRenderer.drawRenderNode(mSystemBarBackgroundNode);
+    }
+
     /** Notify view root that a frame has been drawn by us, if it has requested so. */
     private void reportDrawIfNeeded() {
         if (mReportNextDraw) {
diff --git a/core/java/com/android/internal/policy/DecorView.java b/core/java/com/android/internal/policy/DecorView.java
index 1a20e5c..d4ada95 100644
--- a/core/java/com/android/internal/policy/DecorView.java
+++ b/core/java/com/android/internal/policy/DecorView.java
@@ -922,6 +922,26 @@
         return false;
     }
 
+    static int getColorViewTopInset(int stableTop, int systemTop) {
+        return Math.min(stableTop, systemTop);
+    }
+
+    static int getColorViewBottomInset(int stableBottom, int systemBottom) {
+        return Math.min(stableBottom, systemBottom);
+    }
+
+    static int getColorViewRightInset(int stableRight, int systemRight) {
+        return Math.min(stableRight, systemRight);
+    }
+
+    static boolean isNavBarToRightEdge(int bottomInset, int rightInset) {
+        return bottomInset == 0 && rightInset > 0;
+    }
+
+    static int getNavBarSize(int bottomInset, int rightInset) {
+        return isNavBarToRightEdge(bottomInset, rightInset) ? rightInset : bottomInset;
+    }
+
     WindowInsets updateColorViews(WindowInsets insets, boolean animate) {
         WindowManager.LayoutParams attrs = mWindow.getAttributes();
         int sysUiVisibility = attrs.systemUiVisibility | getWindowSystemUiVisibility();
@@ -933,11 +953,11 @@
             mLastWindowFlags = attrs.flags;
 
             if (insets != null) {
-                mLastTopInset = Math.min(insets.getStableInsetTop(),
+                mLastTopInset = getColorViewTopInset(insets.getStableInsetTop(),
                         insets.getSystemWindowInsetTop());
-                mLastBottomInset = Math.min(insets.getStableInsetBottom(),
+                mLastBottomInset = getColorViewBottomInset(insets.getStableInsetBottom(),
                         insets.getSystemWindowInsetBottom());
-                mLastRightInset = Math.min(insets.getStableInsetRight(),
+                mLastRightInset = getColorViewRightInset(insets.getStableInsetRight(),
                         insets.getSystemWindowInsetRight());
 
                 // Don't animate if the presence of stable insets has changed, because that
@@ -956,8 +976,8 @@
                 mLastHasRightStableInset = hasRightStableInset;
             }
 
-            boolean navBarToRightEdge = mLastBottomInset == 0 && mLastRightInset > 0;
-            int navBarSize = navBarToRightEdge ? mLastRightInset : mLastBottomInset;
+            boolean navBarToRightEdge = isNavBarToRightEdge(mLastBottomInset, mLastRightInset);
+            int navBarSize = getNavBarSize(mLastBottomInset, mLastRightInset);
             updateColorViewInt(mNavigationColorViewState, sysUiVisibility,
                     mWindow.mNavigationBarColor, navBarSize, navBarToRightEdge,
                     0 /* rightInset */, animate && !disallowAnimate, false /* force */);
@@ -1041,14 +1061,14 @@
      */
     private void updateColorViewInt(final ColorViewState state, int sysUiVis, int color,
             int size, boolean verticalBar, int rightMargin, boolean animate, boolean force) {
-        state.present = size > 0 && (sysUiVis & state.systemUiHideFlag) == 0
+        state.present = (sysUiVis & state.systemUiHideFlag) == 0
                 && (mWindow.getAttributes().flags & state.hideWindowFlag) == 0
                 && ((mWindow.getAttributes().flags & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) != 0
                         || force);
         boolean show = state.present
                 && (color & Color.BLACK) != 0
                 && ((mWindow.getAttributes().flags & state.translucentFlag) == 0  || force);
-        boolean showView = show && !isResizing();
+        boolean showView = show && !isResizing() && size > 0;
 
         boolean visibilityChanged = false;
         View view = state.view;
@@ -1672,7 +1692,8 @@
             loadBackgroundDrawablesIfNeeded();
             mBackdropFrameRenderer.onResourcesLoaded(
                     this, mResizingBackgroundDrawable, mCaptionBackgroundDrawable,
-                    mUserCaptionBackgroundDrawable, getCurrentColor(mStatusColorViewState));
+                    mUserCaptionBackgroundDrawable, getCurrentColor(mStatusColorViewState),
+                    getCurrentColor(mNavigationColorViewState));
         }
 
         mDecorCaptionView = createDecorCaptionView(inflater);
@@ -1854,14 +1875,16 @@
     }
 
     @Override
-    public void onWindowSizeIsChanging(Rect newBounds) {
+    public void onWindowSizeIsChanging(Rect newBounds, boolean fullscreen, Rect systemInsets,
+            Rect stableInsets) {
         if (mBackdropFrameRenderer != null) {
-            mBackdropFrameRenderer.setTargetRect(newBounds);
+            mBackdropFrameRenderer.setTargetRect(newBounds, fullscreen, systemInsets, stableInsets);
         }
     }
 
     @Override
-    public void onWindowDragResizeStart(Rect initialBounds) {
+    public void onWindowDragResizeStart(Rect initialBounds, boolean fullscreen, Rect systemInsets,
+            Rect stableInsets) {
         if (mWindow.isDestroyed()) {
             // If the owner's window is gone, we should not be able to come here anymore.
             releaseThreadedRenderer();
@@ -1875,7 +1898,9 @@
             loadBackgroundDrawablesIfNeeded();
             mBackdropFrameRenderer = new BackdropFrameRenderer(this, renderer,
                     initialBounds, mResizingBackgroundDrawable, mCaptionBackgroundDrawable,
-                    mUserCaptionBackgroundDrawable, getCurrentColor(mStatusColorViewState));
+                    mUserCaptionBackgroundDrawable, getCurrentColor(mStatusColorViewState),
+                    getCurrentColor(mNavigationColorViewState), fullscreen, systemInsets,
+                    stableInsets);
 
             // Get rid of the shadow while we are resizing. Shadow drawing takes considerable time.
             // If we want to get the shadow shown while resizing, we would need to elevate a new
@@ -1971,10 +1996,6 @@
         return isShowingCaption() ? mDecorCaptionView.getCaptionHeight() : 0;
     }
 
-    int getStatusBarHeight() {
-        return mStatusColorViewState.view != null ? mStatusColorViewState.view.getHeight() : 0;
-    }
-
     /**
      * Converts a DIP measure into physical pixels.
      * @param dip The dip value.
diff --git a/core/java/com/android/internal/widget/FloatingToolbar.java b/core/java/com/android/internal/widget/FloatingToolbar.java
index 02efcc6..ce03bb8 100644
--- a/core/java/com/android/internal/widget/FloatingToolbar.java
+++ b/core/java/com/android/internal/widget/FloatingToolbar.java
@@ -322,7 +322,7 @@
         /* View components */
         private final ViewGroup mContentContainer;  // holds all contents.
         private final ViewGroup mMainPanel;  // holds menu items that are initially displayed.
-        private final ListView mOverflowPanel;  // holds menu items hidden in the overflow.
+        private final OverflowPanel mOverflowPanel;  // holds menu items hidden in the overflow.
         private final ImageButton mOverflowButton;  // opens/closes the overflow.
         /* overflow button drawables. */
         private final Drawable mArrow;
@@ -895,6 +895,7 @@
 
         private void setPanelsStatesAtRestingPosition() {
             mOverflowButton.setEnabled(true);
+            mOverflowPanel.awakenScrollBars();
 
             if (mIsOverflowOpen) {
                 // Set open state.
@@ -1333,27 +1334,8 @@
             return overflowButton;
         }
 
-        private ListView createOverflowPanel() {
-            final ListView overflowPanel = new ListView(FloatingToolbarPopup.this.mContext) {
-                @Override
-                protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
-                    // Update heightMeasureSpec to make sure that this view is not clipped
-                    // as we offset it's coordinates with respect to it's parent.
-                    heightMeasureSpec = MeasureSpec.makeMeasureSpec(
-                            mOverflowPanelSize.getHeight() - mOverflowButtonSize.getHeight(),
-                            MeasureSpec.EXACTLY);
-                    super.onMeasure(widthMeasureSpec, heightMeasureSpec);
-                }
-
-                @Override
-                public boolean dispatchTouchEvent(MotionEvent ev) {
-                    if (isOverflowAnimating()) {
-                        // Eat the touch event.
-                        return true;
-                    }
-                    return super.dispatchTouchEvent(ev);
-                }
-            };
+        private OverflowPanel createOverflowPanel() {
+            final OverflowPanel overflowPanel = new OverflowPanel(this);
             overflowPanel.setLayoutParams(new ViewGroup.LayoutParams(
                     ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
             overflowPanel.setDivider(null);
@@ -1464,6 +1446,43 @@
         }
 
         /**
+         * A custom ListView for the overflow panel.
+         */
+        private static final class OverflowPanel extends ListView {
+
+            private final FloatingToolbarPopup mPopup;
+
+            OverflowPanel(FloatingToolbarPopup popup) {
+                super(Preconditions.checkNotNull(popup).mContext);
+                this.mPopup = popup;
+            }
+
+            @Override
+            protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+                // Update heightMeasureSpec to make sure that this view is not clipped
+                // as we offset it's coordinates with respect to it's parent.
+                int height = mPopup.mOverflowPanelSize.getHeight()
+                        - mPopup.mOverflowButtonSize.getHeight();
+                heightMeasureSpec = MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY);
+                super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+            }
+
+            @Override
+            public boolean dispatchTouchEvent(MotionEvent ev) {
+                if (mPopup.isOverflowAnimating()) {
+                    // Eat the touch event.
+                    return true;
+                }
+                return super.dispatchTouchEvent(ev);
+            }
+
+            @Override
+            protected boolean awakenScrollBars() {
+                return super.awakenScrollBars();
+            }
+        }
+
+        /**
          * A custom interpolator used for various floating toolbar animations.
          */
         private static final class LogAccelerateInterpolator implements Interpolator {
diff --git a/core/jni/android/graphics/Graphics.cpp b/core/jni/android/graphics/Graphics.cpp
index 3d5091a..bd01c73 100644
--- a/core/jni/android/graphics/Graphics.cpp
+++ b/core/jni/android/graphics/Graphics.cpp
@@ -746,10 +746,12 @@
     if (mNeedsCopy) {
         SkPixelRef* recycledPixels = mRecycledBitmap->refPixelRef();
         void* dst = recycledPixels->pixels();
-        size_t dstRowBytes = mRecycledBitmap->rowBytes();
-        size_t bytesToCopy = SkTMin(mRecycledBitmap->info().minRowBytes(),
+        const size_t dstRowBytes = mRecycledBitmap->rowBytes();
+        const size_t bytesToCopy = std::min(mRecycledBitmap->info().minRowBytes(),
                 mSkiaBitmap->info().minRowBytes());
-        for (int y = 0; y < mRecycledBitmap->info().height(); y++) {
+        const int rowsToCopy = std::min(mRecycledBitmap->info().height(),
+                mSkiaBitmap->info().height());
+        for (int y = 0; y < rowsToCopy; y++) {
             memcpy(dst, mSkiaBitmap->getAddr(0, y), bytesToCopy);
             dst = SkTAddOffset<void>(dst, dstRowBytes);
         }
diff --git a/core/jni/android_graphics_Canvas.cpp b/core/jni/android_graphics_Canvas.cpp
index 35b5016..cf73316 100644
--- a/core/jni/android_graphics_Canvas.cpp
+++ b/core/jni/android_graphics_Canvas.cpp
@@ -510,7 +510,7 @@
 
         size_t glyphCount = end - start;
 
-        if (CC_UNLIKELY(canvas->isHighContrastText())) {
+        if (CC_UNLIKELY(canvas->isHighContrastText() && paint.getAlpha() != 0)) {
             // high contrast draw path
             int color = paint.getColor();
             int channelSum = SkColorGetR(color) + SkColorGetG(color) + SkColorGetB(color);
diff --git a/core/jni/android_graphics_drawable_VectorDrawable.cpp b/core/jni/android_graphics_drawable_VectorDrawable.cpp
index e882876..7314fbc 100644
--- a/core/jni/android_graphics_drawable_VectorDrawable.cpp
+++ b/core/jni/android_graphics_drawable_VectorDrawable.cpp
@@ -138,11 +138,6 @@
     return reinterpret_cast<jlong>(newGroup);
 }
 
-static void deleteNode(JNIEnv*, jobject, jlong nodePtr) {
-    VectorDrawable::Node* node = reinterpret_cast<VectorDrawable::Node*>(nodePtr);
-    delete node;
-}
-
 static void setNodeName(JNIEnv* env, jobject, jlong nodePtr, jstring nameStr) {
     VectorDrawable::Node* node = reinterpret_cast<VectorDrawable::Node*>(nodePtr);
     const char* nodeName = env->GetStringUTFChars(nameStr, NULL);
@@ -346,7 +341,6 @@
         {"nCreateClipPath", "!(J)J", (void*)createClipPath},
         {"nCreateGroup", "!()J", (void*)createEmptyGroup},
         {"nCreateGroup", "!(J)J", (void*)createGroup},
-        {"nDestroy", "!(J)V", (void*)deleteNode},
         {"nSetName", "(JLjava/lang/String;)V", (void*)setNodeName},
         {"nUpdateGroupProperties", "!(JFFFFFFF)V", (void*)updateGroupProperties},
 
diff --git a/core/jni/android_hardware_SensorManager.cpp b/core/jni/android_hardware_SensorManager.cpp
index 4842e1b..e39bb1c 100644
--- a/core/jni/android_hardware_SensorManager.cpp
+++ b/core/jni/android_hardware_SensorManager.cpp
@@ -205,8 +205,8 @@
     SensorManager* mgr = reinterpret_cast<SensorManager*>(sensorManager);
 
     Sensor const* const* sensorList;
-    size_t count = mgr->getSensorList(&sensorList);
-    if (size_t(index) >= count) {
+    ssize_t count = mgr->getSensorList(&sensorList);
+    if (ssize_t(index) >= count) {
         return false;
     }
 
diff --git a/core/jni/android_view_RenderNode.cpp b/core/jni/android_view_RenderNode.cpp
index b1d4e26..a9003c1 100644
--- a/core/jni/android_view_RenderNode.cpp
+++ b/core/jni/android_view_RenderNode.cpp
@@ -15,6 +15,7 @@
  */
 
 #define LOG_TAG "OpenGLRenderer"
+#define ATRACE_TAG ATRACE_TAG_VIEW
 
 #include <EGL/egl.h>
 
@@ -24,7 +25,10 @@
 #include <android_runtime/AndroidRuntime.h>
 
 #include <Animator.h>
+#include <DamageAccumulator.h>
+#include <Matrix.h>
 #include <RenderNode.h>
+#include <TreeInfo.h>
 #include <Paint.h>
 
 #include "core_jni_helpers.h"
@@ -462,6 +466,69 @@
 }
 
 // ----------------------------------------------------------------------------
+// SurfaceView position callback
+// ----------------------------------------------------------------------------
+
+jmethodID gSurfaceViewPositionUpdateMethod;
+
+static void android_view_RenderNode_requestPositionUpdates(JNIEnv* env, jobject,
+        jlong renderNodePtr, jobject surfaceview) {
+    class SurfaceViewPositionUpdater : public RenderNode::PositionListener {
+    public:
+        SurfaceViewPositionUpdater(JNIEnv* env, jobject surfaceview) {
+            env->GetJavaVM(&mVm);
+            mWeakRef = env->NewWeakGlobalRef(surfaceview);
+        }
+
+        virtual ~SurfaceViewPositionUpdater() {
+            jnienv()->DeleteWeakGlobalRef(mWeakRef);
+            mWeakRef = nullptr;
+        }
+
+        virtual void onPositionUpdated(RenderNode& node, const TreeInfo& info) override {
+            if (CC_UNLIKELY(!mWeakRef || !info.updateWindowPositions)) return;
+            ATRACE_NAME("Update SurfaceView position");
+
+            JNIEnv* env = jnienv();
+            jobject localref = env->NewLocalRef(mWeakRef);
+            if (CC_UNLIKELY(!localref)) {
+                jnienv()->DeleteWeakGlobalRef(mWeakRef);
+                mWeakRef = nullptr;
+                return;
+            }
+            Matrix4 transform;
+            info.damageAccumulator->computeCurrentTransform(&transform);
+            const RenderProperties& props = node.properties();
+            uirenderer::Rect bounds(props.getWidth(), props.getHeight());
+            transform.mapRect(bounds);
+            bounds.left -= info.windowInsetLeft;
+            bounds.right -= info.windowInsetLeft;
+            bounds.top -= info.windowInsetTop;
+            bounds.bottom -= info.windowInsetTop;
+            env->CallVoidMethod(localref, gSurfaceViewPositionUpdateMethod,
+                    (jlong) info.frameNumber, (jint) bounds.left, (jint) bounds.top,
+                    (jint) bounds.right, (jint) bounds.bottom);
+            env->DeleteLocalRef(localref);
+        }
+
+    private:
+        JNIEnv* jnienv() {
+            JNIEnv* env;
+            if (mVm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) {
+                LOG_ALWAYS_FATAL("Failed to get JNIEnv for JavaVM: %p", mVm);
+            }
+            return env;
+        }
+
+        JavaVM* mVm;
+        jobject mWeakRef;
+    };
+
+    RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr);
+    renderNode->setPositionListener(new SurfaceViewPositionUpdater(env, surfaceview));
+}
+
+// ----------------------------------------------------------------------------
 // JNI Glue
 // ----------------------------------------------------------------------------
 
@@ -539,9 +606,14 @@
 
     { "nAddAnimator",              "(JJ)V", (void*) android_view_RenderNode_addAnimator },
     { "nEndAllAnimators",          "(J)V", (void*) android_view_RenderNode_endAllAnimators },
+
+    { "nRequestPositionUpdates",   "(JLandroid/view/SurfaceView;)V", (void*) android_view_RenderNode_requestPositionUpdates },
 };
 
 int register_android_view_RenderNode(JNIEnv* env) {
+    jclass clazz = FindClassOrDie(env, "android/view/SurfaceView");
+    gSurfaceViewPositionUpdateMethod = GetMethodIDOrDie(env, clazz,
+            "updateWindowPositionRT", "(JIIII)V");
     return RegisterMethodsOrDie(env, kClassPathName, gMethods, NELEM(gMethods));
 }
 
diff --git a/core/jni/android_view_ThreadedRenderer.cpp b/core/jni/android_view_ThreadedRenderer.cpp
index 8c907dd..acd0501 100644
--- a/core/jni/android_view_ThreadedRenderer.cpp
+++ b/core/jni/android_view_ThreadedRenderer.cpp
@@ -134,7 +134,14 @@
 
     virtual void prepareTree(TreeInfo& info) {
         info.errorHandler = this;
+        // TODO: This is hacky
+        info.windowInsetLeft = -stagingProperties().getLeft();
+        info.windowInsetTop = -stagingProperties().getTop();
+        info.updateWindowPositions = true;
         RenderNode::prepareTree(info);
+        info.updateWindowPositions = false;
+        info.windowInsetLeft = 0;
+        info.windowInsetTop = 0;
         info.errorHandler = NULL;
     }
 
@@ -369,28 +376,28 @@
 static void android_view_ThreadedRenderer_initialize(JNIEnv* env, jobject clazz,
         jlong proxyPtr, jobject jsurface) {
     RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr);
-    sp<ANativeWindow> window = android_view_Surface_getNativeWindow(env, jsurface);
-    proxy->initialize(window);
+    sp<Surface> surface = android_view_Surface_getSurface(env, jsurface);
+    proxy->initialize(surface);
 }
 
 static void android_view_ThreadedRenderer_updateSurface(JNIEnv* env, jobject clazz,
         jlong proxyPtr, jobject jsurface) {
     RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr);
-    sp<ANativeWindow> window;
+    sp<Surface> surface;
     if (jsurface) {
-        window = android_view_Surface_getNativeWindow(env, jsurface);
+        surface = android_view_Surface_getSurface(env, jsurface);
     }
-    proxy->updateSurface(window);
+    proxy->updateSurface(surface);
 }
 
 static jboolean android_view_ThreadedRenderer_pauseSurface(JNIEnv* env, jobject clazz,
         jlong proxyPtr, jobject jsurface) {
     RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr);
-    sp<ANativeWindow> window;
+    sp<Surface> surface;
     if (jsurface) {
-        window = android_view_Surface_getNativeWindow(env, jsurface);
+        surface = android_view_Surface_getSurface(env, jsurface);
     }
-    return proxy->pauseSurface(window);
+    return proxy->pauseSurface(surface);
 }
 
 static void android_view_ThreadedRenderer_setup(JNIEnv* env, jobject clazz, jlong proxyPtr,
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 7729d48c..4cddb6c 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -292,9 +292,12 @@
     <protected-broadcast android:name="android.intent.action.REMOTE_BUGREPORT_FINISHED" />
     <protected-broadcast android:name="android.intent.action.REMOTE_BUGREPORT_DISPATCH" />
 
+    <!-- Legacy -->
     <protected-broadcast android:name="android.intent.action.ACTION_IDLE_MAINTENANCE_START" />
     <protected-broadcast android:name="android.intent.action.ACTION_IDLE_MAINTENANCE_END" />
 
+    <protected-broadcast android:name="com.android.server.task.controllers.IdleController.ACTION_TRIGGER_IDLE" />
+
     <protected-broadcast android:name="android.intent.action.HDMI_PLUGGED" />
 
     <protected-broadcast android:name="android.intent.action.PHONE_STATE" />
@@ -2736,6 +2739,7 @@
          android.service.notification.NotificationAssistantService},
          to ensure that only the system can bind to it.
          <p>Protection level: signature
+         @hide This is not a third-party API (intended for system apps). -->
     -->
     <permission android:name="android.permission.BIND_NOTIFICATION_ASSISTANT_SERVICE"
         android:protectionLevel="signature" />
@@ -2874,6 +2878,12 @@
     <permission android:name="android.permission.MANAGE_SOUND_TRIGGER"
         android:protectionLevel="signature|privileged" />
 
+    <!-- @SystemApi Allows trusted applications to dispatch managed provisioning message to Managed
+         Provisioning app. If requesting app does not have permission, it will be ignored.
+         @hide -->
+    <permission android:name="android.permission.DISPATCH_PROVISIONING_MESSAGE"
+                android:protectionLevel="signature|privileged" />
+
     <application android:process="system"
                  android:persistent="true"
                  android:hasCode="false"
diff --git a/core/res/res/drawable/ic_close.xml b/core/res/res/drawable/ic_close.xml
new file mode 100644
index 0000000..7086959
--- /dev/null
+++ b/core/res/res/drawable/ic_close.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2016 The Android Open Source Project
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24.0dp"
+        android:height="24.0dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:pathData="M19.000000,6.400000l-1.400000,-1.400000 -5.600000,5.600000 -5.600000,-5.600000 -1.400000,1.400000 5.600000,5.600000 -5.600000,5.600000 1.400000,1.400000 5.600000,-5.600000 5.600000,5.600000 1.400000,-1.400000 -5.600000,-5.600000z"
+        android:fillColor="#FF000000"/>
+</vector>
diff --git a/core/res/res/drawable/ic_feedback.xml b/core/res/res/drawable/ic_feedback.xml
new file mode 100644
index 0000000..b2d1cb8
--- /dev/null
+++ b/core/res/res/drawable/ic_feedback.xml
@@ -0,0 +1,27 @@
+<!--
+Copyright (C) 2016 The Android Open Source Project
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24.0dp"
+        android:height="24.0dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:pathData="M0 0h24v24H0z"
+        android:fillColor="#00000000"/>
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M20.0,2.0L4.0,2.0c-1.1,0.0 -1.9,0.9 -1.99,2.0L2.0,22.0l4.0,-4.0l14.0,0.0c1.1,0.0 2.0,-0.9 2.0,-2.0L22.0,4.0c0.0,-1.1 -0.9,-2.0 -2.0,-2.0zm-7.0,12.0l-2.0,0.0l0.0,-2.0l2.0,0.0l0.0,2.0zm0.0,-4.0l-2.0,0.0L11.0,6.0l2.0,0.0l0.0,4.0z"/>
+</vector>
diff --git a/core/res/res/drawable/ic_refresh.xml b/core/res/res/drawable/ic_refresh.xml
new file mode 100644
index 0000000..1f67168
--- /dev/null
+++ b/core/res/res/drawable/ic_refresh.xml
@@ -0,0 +1,27 @@
+<!--
+Copyright (C) 2016 The Android Open Source Project
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24.0dp"
+        android:height="24.0dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M17.65,6.35C16.2,4.9 14.21,4.0 12.0,4.0c-4.42,0.0 -7.99,3.58 -7.99,8.0s3.57,8.0 7.99,8.0c3.73,0.0 6.84,-2.55 7.73,-6.0l-2.08,0.0c-0.82,2.33 -3.04,4.0 -5.65,4.0 -3.31,0.0 -6.0,-2.69 -6.0,-6.0s2.69,-6.0 6.0,-6.0c1.66,0.0 3.1,0.69 4.22,1.78L13.0,11.0l7.0,0.0L20.0,4.0l-2.35,2.35z"/>
+    <path
+        android:pathData="M0 0h24v24H0z"
+        android:fillColor="#00000000"/>
+</vector>
diff --git a/core/res/res/drawable/ic_schedule.xml b/core/res/res/drawable/ic_schedule.xml
new file mode 100644
index 0000000..899dc82
--- /dev/null
+++ b/core/res/res/drawable/ic_schedule.xml
@@ -0,0 +1,30 @@
+<!--
+Copyright (C) 2016 The Android Open Source Project
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24.0dp"
+        android:height="24.0dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M11.99,2.0C6.47,2.0 2.0,6.48 2.0,12.0s4.47,10.0 9.99,10.0C17.52,22.0 22.0,17.52 22.0,12.0S17.52,2.0 11.99,2.0zM12.0,20.0c-4.42,0.0 -8.0,-3.58 -8.0,-8.0s3.58,-8.0 8.0,-8.0 8.0,3.58 8.0,8.0 -3.58,8.0 -8.0,8.0z"/>
+    <path
+        android:pathData="M0 0h24v24H0z"
+        android:fillColor="#00000000"/>
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M12.5,7.0L11.0,7.0l0.0,6.0l5.25,3.1 0.75,-1.23 -4.5,-2.67z"/>
+</vector>
diff --git a/core/res/res/layout/app_anr_dialog.xml b/core/res/res/layout/app_anr_dialog.xml
index e8169ee..8bef116 100644
--- a/core/res/res/layout/app_anr_dialog.xml
+++ b/core/res/res/layout/app_anr_dialog.xml
@@ -18,28 +18,31 @@
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
         android:orientation="vertical"
-        android:paddingTop="@dimen/dialog_list_padding_vertical_material"
+        android:paddingTop="@dimen/aerr_padding_list_top"
         android:paddingBottom="@dimen/dialog_list_padding_vertical_material">
 
-    <TextView
+    <Button
             android:id="@+id/aerr_close"
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
             android:text="@string/aerr_close_app"
+            android:drawableStart="@drawable/ic_close"
             style="@style/aerr_list_item" />
 
-    <TextView
+    <Button
             android:id="@+id/aerr_wait"
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
             android:text="@string/aerr_wait"
+            android:drawableStart="@drawable/ic_schedule"
             style="@style/aerr_list_item" />
 
-    <TextView
+    <Button
             android:id="@+id/aerr_report"
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
             android:text="@string/aerr_report"
+            android:drawableStart="@drawable/ic_feedback"
             style="@style/aerr_list_item" />
 
 </LinearLayout>
diff --git a/core/res/res/layout/app_error_dialog.xml b/core/res/res/layout/app_error_dialog.xml
index 46a2b2a..824f97f 100644
--- a/core/res/res/layout/app_error_dialog.xml
+++ b/core/res/res/layout/app_error_dialog.xml
@@ -21,9 +21,8 @@
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
         android:orientation="vertical"
-        android:paddingTop="@dimen/dialog_list_padding_vertical_material"
-        android:paddingBottom="@dimen/dialog_list_padding_vertical_material"
->
+        android:paddingTop="@dimen/aerr_padding_list_top"
+        android:paddingBottom="@dimen/dialog_list_padding_vertical_material">
 
 
     <Button
@@ -31,6 +30,7 @@
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
             android:text="@string/aerr_restart"
+            android:drawableStart="@drawable/ic_refresh"
             style="@style/aerr_list_item"
     />
 
@@ -39,6 +39,7 @@
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
             android:text="@string/aerr_reset"
+            android:drawableStart="@drawable/ic_refresh"
             style="@style/aerr_list_item"
     />
 
@@ -47,6 +48,7 @@
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
             android:text="@string/aerr_report"
+            android:drawableStart="@drawable/ic_feedback"
             style="@style/aerr_list_item"
     />
 
@@ -55,6 +57,7 @@
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
             android:text="@string/aerr_close"
+            android:drawableStart="@drawable/ic_close"
             style="@style/aerr_list_item"
     />
 
@@ -63,6 +66,7 @@
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
             android:text="@string/aerr_mute"
+            android:drawableStart="@drawable/ic_close"
             style="@style/aerr_list_item"
     />
 
diff --git a/core/res/res/layout/notification_template_header.xml b/core/res/res/layout/notification_template_header.xml
index b45f8bb..6669bae 100644
--- a/core/res/res/layout/notification_template_header.xml
+++ b/core/res/res/layout/notification_template_header.xml
@@ -87,7 +87,6 @@
         android:layout_marginStart="2dp"
         android:layout_marginEnd="2dp"
         android:visibility="gone"
-        android:maxWidth="72dp"
         android:singleLine="true"/>
     <TextView
         android:id="@+id/time_divider"
diff --git a/core/res/res/layout/resolve_grid_item.xml b/core/res/res/layout/resolve_grid_item.xml
index 0a7ac77..305c8b0 100644
--- a/core/res/res/layout/resolve_grid_item.xml
+++ b/core/res/res/layout/resolve_grid_item.xml
@@ -25,6 +25,7 @@
               android:gravity="center"
               android:paddingTop="8dp"
               android:paddingBottom="8dp"
+              android:focusable="true"
               android:background="?attr/selectableItemBackgroundBorderless">
 
     <FrameLayout android:layout_width="wrap_content"
diff --git a/core/res/res/menu/language_selection_list.xml b/core/res/res/menu/language_selection_list.xml
index 63b9627..377c4fa 100644
--- a/core/res/res/menu/language_selection_list.xml
+++ b/core/res/res/menu/language_selection_list.xml
@@ -14,12 +14,12 @@
      limitations under the License.
 -->
 
-<menu xmlns:android="http://schemas.android.com/apk/res/android"
-      xmlns:app="http://schemas.android.com/apk/res-auto">
+<menu xmlns:android="http://schemas.android.com/apk/res/android">
     <item
         android:id="@+id/locale_search_menu"
         android:title="@string/locale_search_menu"
-        app:showAsAction="always"
-        app:actionViewClass="android.widget.SearchView" />
+        android:icon="@*android:drawable/ic_search_api_material"
+        android:showAsAction="always|collapseActionView"
+        android:actionViewClass="android.widget.SearchView" />
 
 </menu>
diff --git a/core/res/res/values/colors_material.xml b/core/res/res/values/colors_material.xml
index dd18544..7399fa9 100644
--- a/core/res/res/values/colors_material.xml
+++ b/core/res/res/values/colors_material.xml
@@ -48,8 +48,6 @@
     <color name="primary_text_default_material_light">#de000000</color>
     <!-- 54% black -->
     <color name="secondary_text_default_material_light">#8a000000</color>
-    <!-- 38% black -->
-    <color name="tertiary_text_default_material_light">#61000000</color>
 
     <!-- 100% white -->
     <color name="primary_text_default_material_dark">#ffffffff</color>
diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml
index 7c6f338..8e86f78 100644
--- a/core/res/res/values/dimens.xml
+++ b/core/res/res/values/dimens.xml
@@ -449,5 +449,7 @@
     <item type="dimen" format="integer" name="time_picker_column_start_material">0</item>
     <item type="dimen" format="integer" name="time_picker_column_end_material">1</item>
 
+    <item type="dimen" name="aerr_padding_list_top">15dp</item>
+
     <item type="fraction" name="docked_stack_divider_fixed_ratio">34.15%</item>
 </resources>
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index e5a6226..4e10d39 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -4176,7 +4176,7 @@
     <string name="language_selection_title">Language preference</string>
     <!-- Title for the region selection screen [CHAR LIMIT=25] -->
     <string name="country_selection_title">Region preference</string>
-    <!-- Hint text in a search edit box (used to filter long language / country lists) [CHAR LIMIT=20] -->
+    <!-- Hint text in a search edit box (used to filter long language / country lists) [CHAR LIMIT=25] -->
     <string name="search_language_hint">Type language name</string>
 
     <!-- List section subheader for the language picker, containing a list of suggested languages determined by the default region [CHAR LIMIT=30] -->
diff --git a/core/res/res/values/styles.xml b/core/res/res/values/styles.xml
index f0960c7..86b9f1d 100644
--- a/core/res/res/values/styles.xml
+++ b/core/res/res/values/styles.xml
@@ -1403,8 +1403,12 @@
         <item name="textAppearance">?attr/textAppearanceListItemSmall</item>
         <item name="textColor">?attr/textColorAlertDialogListItem</item>
         <item name="gravity">center_vertical</item>
-        <item name="paddingStart">?attr/listPreferredItemPaddingStart</item>
-        <item name="paddingEnd">?attr/listPreferredItemPaddingEnd</item>
+        <item name="paddingStart">?attr/dialogPreferredPadding</item>
+        <item name="paddingEnd">?attr/dialogPreferredPadding</item>
+        <item name="background">?attr/selectableItemBackground</item>
+        <item name="drawablePadding">32dp</item>
+        <item name="drawableTint">@color/accent_material_light</item>
+        <item name="drawableTintMode">src_atop</item>
     </style>
 
     <!-- Wifi dialog styles -->
diff --git a/core/res/res/values/styles_material.xml b/core/res/res/values/styles_material.xml
index 0a52f41..9efcfda 100644
--- a/core/res/res/values/styles_material.xml
+++ b/core/res/res/values/styles_material.xml
@@ -444,7 +444,7 @@
     </style>
 
     <style name="TextAppearance.Material.Notification.Info">
-        <item name="textColor">@color/tertiary_text_default_material_light</item>
+        <item name="textColor">@color/secondary_text_default_material_light</item>
         <item name="textSize">@dimen/notification_subtext_size</item>
     </style>
 
diff --git a/core/tests/coretests/src/android/print/BasePrintTest.java b/core/tests/coretests/src/android/print/BasePrintTest.java
index 19ce44a..3feb0e9 100644
--- a/core/tests/coretests/src/android/print/BasePrintTest.java
+++ b/core/tests/coretests/src/android/print/BasePrintTest.java
@@ -141,7 +141,7 @@
         // Set to US locale.
         Resources resources = getInstrumentation().getTargetContext().getResources();
         Configuration oldConfiguration = resources.getConfiguration();
-        if (!oldConfiguration.getLocales().getPrimary().equals(Locale.US)) {
+        if (!oldConfiguration.getLocales().get(0).equals(Locale.US)) {
             mOldLocale = oldConfiguration.getLocales();
             DisplayMetrics displayMetrics = resources.getDisplayMetrics();
             Configuration newConfiguration = new Configuration(oldConfiguration);
diff --git a/data/fonts/Android.mk b/data/fonts/Android.mk
index d2bd106..de741b3 100644
--- a/data/fonts/Android.mk
+++ b/data/fonts/Android.mk
@@ -89,10 +89,7 @@
 endef
 
 font_src_files := \
-    Clockopia.ttf \
-    AndroidClock.ttf \
-    AndroidClock_Highlight.ttf \
-    AndroidClock_Solid.ttf
+    AndroidClock.ttf
 
 $(foreach f, $(font_src_files), $(call build-one-font-module, $(f)))
 
diff --git a/data/fonts/AndroidClock_Highlight.ttf b/data/fonts/AndroidClock_Highlight.ttf
deleted file mode 100644
index 923bb30..0000000
--- a/data/fonts/AndroidClock_Highlight.ttf
+++ /dev/null
Binary files differ
diff --git a/data/fonts/AndroidClock_Solid.ttf b/data/fonts/AndroidClock_Solid.ttf
deleted file mode 100644
index 923bb30..0000000
--- a/data/fonts/AndroidClock_Solid.ttf
+++ /dev/null
Binary files differ
diff --git a/data/fonts/Clockopia.ttf b/data/fonts/Clockopia.ttf
deleted file mode 100644
index 3f7b6aaa..0000000
--- a/data/fonts/Clockopia.ttf
+++ /dev/null
Binary files differ
diff --git a/data/fonts/MTLc3m.ttf b/data/fonts/MTLc3m.ttf
deleted file mode 100644
index e9018f6..0000000
--- a/data/fonts/MTLc3m.ttf
+++ /dev/null
Binary files differ
diff --git a/data/fonts/MTLmr3m.ttf b/data/fonts/MTLmr3m.ttf
deleted file mode 100644
index 14f27d4..0000000
--- a/data/fonts/MTLmr3m.ttf
+++ /dev/null
Binary files differ
diff --git a/data/fonts/fonts.mk b/data/fonts/fonts.mk
index 597a122..acd785e 100644
--- a/data/fonts/fonts.mk
+++ b/data/fonts/fonts.mk
@@ -20,7 +20,4 @@
 PRODUCT_PACKAGES := \
     DroidSansFallback.ttf \
     DroidSansMono.ttf \
-    Clockopia.ttf \
     AndroidClock.ttf \
-    AndroidClock_Highlight.ttf \
-    AndroidClock_Solid.ttf \
diff --git a/data/fonts/fonts.xml b/data/fonts/fonts.xml
index 961d0eb..dc302c7 100644
--- a/data/fonts/fonts.xml
+++ b/data/fonts/fonts.xml
@@ -344,6 +344,9 @@
     <family lang="und-Zsye">
         <font weight="400" style="normal">NotoColorEmoji.ttf</font>
     </family>
+    <family>
+        <font weight="400" style="normal">DroidSansFallback.ttf</font>
+    </family>
     <!--
         Tai Le and Mongolian are intentionally kept last, to make sure they don't override
         the East Asian punctuation for Chinese.
diff --git a/graphics/java/android/graphics/Paint.java b/graphics/java/android/graphics/Paint.java
index dfb8bb8..534121a 100644
--- a/graphics/java/android/graphics/Paint.java
+++ b/graphics/java/android/graphics/Paint.java
@@ -459,7 +459,7 @@
         // setHinting(DisplayMetrics.DENSITY_DEVICE >= DisplayMetrics.DENSITY_TV
         //        ? HINTING_OFF : HINTING_ON);
         mCompatScaling = mInvCompatScaling = 1;
-        setTextLocales(LocaleList.getDefault());
+        setTextLocales(LocaleList.getAdjustedDefault());
     }
 
     /**
@@ -500,7 +500,7 @@
         mInvCompatScaling = 1;
 
         mBidiFlags = BIDI_DEFAULT_LTR;
-        setTextLocales(LocaleList.getDefault());
+        setTextLocales(LocaleList.getAdjustedDefault());
         setElegantTextHeight(false);
         mFontFeatureSettings = null;
     }
@@ -1292,7 +1292,7 @@
      */
     @NonNull
     public Locale getTextLocale() {
-        return mLocales.getPrimary();
+        return mLocales.get(0);
     }
 
     /**
@@ -1317,7 +1317,7 @@
         if (locale == null) {
             throw new IllegalArgumentException("locale cannot be null");
         }
-        if (mLocales != null && mLocales.size() == 1 && locale.equals(mLocales.getPrimary())) {
+        if (mLocales != null && mLocales.size() == 1 && locale.equals(mLocales.get(0))) {
             return;
         }
         mLocales = new LocaleList(locale);
@@ -1340,8 +1340,8 @@
      * each language.
      *
      * By default, the text locale list is initialized to a one-member list just containing the
-     * system locale (as returned by {@link LocaleList#getDefault()}). This assumes that the text to
-     * be rendered will most likely be in the user's preferred language.
+     * system locales. This assumes that the text to be rendered will most likely be in the user's
+     * preferred language.
      *
      * If the actual language or languages of the text is/are known, then they can be provided to
      * the text renderer using this method. The text renderer may attempt to guess the
diff --git a/graphics/java/android/graphics/Rect.java b/graphics/java/android/graphics/Rect.java
index 40dbe27..0cde0b9 100644
--- a/graphics/java/android/graphics/Rect.java
+++ b/graphics/java/android/graphics/Rect.java
@@ -335,6 +335,21 @@
     }
 
     /**
+     * Insets the rectangle on all sides specified by the insets.
+     * @hide
+     * @param left The amount to add from the rectangle's left
+     * @param top The amount to add from the rectangle's top
+     * @param right The amount to subtract from the rectangle's right
+     * @param bottom The amount to subtract from the rectangle's bottom
+     */
+    public void inset(int left, int top, int right, int bottom) {
+        this.left += left;
+        this.top += top;
+        this.right -= right;
+        this.bottom -= bottom;
+    }
+
+    /**
      * Returns true if (x,y) is inside the rectangle. The left and top are
      * considered to be inside, while the right and bottom are not. This means
      * that for a x,y to be contained: left <= x < right and top <= y < bottom.
diff --git a/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java b/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java
index 21ed7dd..af8ccf5 100644
--- a/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java
+++ b/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java
@@ -25,6 +25,8 @@
 import android.animation.ObjectAnimator;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.app.ActivityThread;
+import android.app.Application;
 import android.content.res.ColorStateList;
 import android.content.res.Resources;
 import android.content.res.Resources.Theme;
@@ -35,6 +37,7 @@
 import android.graphics.Outline;
 import android.graphics.PorterDuff;
 import android.graphics.Rect;
+import android.os.Build;
 import android.util.ArrayMap;
 import android.util.AttributeSet;
 import android.util.Log;
@@ -200,6 +203,24 @@
         mMutated = false;
     }
 
+    /**
+     * In order to avoid breaking old apps, we only throw exception on invalid VectorDrawable
+     * animations * for apps targeting N and later. For older apps, we ignore (i.e. quietly skip)
+     * these animations.
+     *
+     * @return whether invalid animations for vector drawable should be ignored.
+     */
+    private static boolean shouldIgnoreInvalidAnimation() {
+        Application app = ActivityThread.currentApplication();
+        if (app == null || app.getApplicationInfo() == null) {
+            return true;
+        }
+        if (app.getApplicationInfo().targetSdkVersion < Build.VERSION_CODES.N) {
+            return true;
+        }
+        return false;
+    }
+
     @Override
     public ConstantState getConstantState() {
         mAnimatedVectorState.mChangingConfigurations = getChangingConfigurations();
@@ -763,6 +784,8 @@
         private boolean mInitialized = false;
         private boolean mAnimationPending = false;
         private boolean mIsReversible = false;
+        // This needs to be set before parsing starts.
+        private boolean mShouldIgnoreInvalidAnim;
         // TODO: Consider using NativeAllocationRegistery to track native allocation
         private final VirtualRefBasePtr mSetRefBasePtr;
         private WeakReference<RenderNode> mTarget = null;
@@ -782,6 +805,7 @@
                 throw new UnsupportedOperationException("VectorDrawableAnimator cannot be " +
                         "re-initialized");
             }
+            mShouldIgnoreInvalidAnim = shouldIgnoreInvalidAnimation();
             parseAnimatorSet(set, 0);
             mInitialized = true;
 
@@ -841,7 +865,7 @@
                     }  else if (target instanceof VectorDrawable.VFullPath) {
                         createRTAnimatorForFullPath(animator, (VectorDrawable.VFullPath) target,
                                 startTime);
-                    } else {
+                    } else if (!mShouldIgnoreInvalidAnim) {
                         throw new IllegalArgumentException("ClipPath only supports PathData " +
                                 "property");
                     }
@@ -850,10 +874,11 @@
             } else if (target instanceof VectorDrawable.VectorDrawableState) {
                 createRTAnimatorForRootGroup(values, animator,
                         (VectorDrawable.VectorDrawableState) target, startTime);
-            } else {
+            } else if (!mShouldIgnoreInvalidAnim) {
                 // Should never get here
-                throw new UnsupportedOperationException("Target should be group, path or vector. " +
-                        target == null ? "Null target" : target.getClass() + " is not supported");
+                throw new UnsupportedOperationException("Target should be either VGroup, VPath, " +
+                        "or ConstantState, " + target == null ? "Null target" : target.getClass() +
+                        " is not supported");
             }
         }
 
@@ -912,8 +937,12 @@
             long nativePtr = target.getNativePtr();
             if (mTmpValues.type == Float.class || mTmpValues.type == float.class) {
                 if (propertyId < 0) {
-                    throw new IllegalArgumentException("Property: " + mTmpValues
-                            .propertyName + " is not supported for FullPath");
+                    if (mShouldIgnoreInvalidAnim) {
+                        return;
+                    } else {
+                        throw new IllegalArgumentException("Property: " + mTmpValues.propertyName
+                                + " is not supported for FullPath");
+                    }
                 }
                 propertyPtr = nCreatePathPropertyHolder(nativePtr, propertyId,
                         (Float) mTmpValues.startValue, (Float) mTmpValues.endValue);
@@ -922,9 +951,13 @@
                 propertyPtr = nCreatePathColorPropertyHolder(nativePtr, propertyId,
                         (Integer) mTmpValues.startValue, (Integer) mTmpValues.endValue);
             } else {
-                throw new UnsupportedOperationException("Unsupported type: " +
-                        mTmpValues.type + ". Only float, int or PathData value is " +
-                        "supported for Paths.");
+                if (mShouldIgnoreInvalidAnim) {
+                    return;
+                } else {
+                    throw new UnsupportedOperationException("Unsupported type: " +
+                            mTmpValues.type + ". Only float, int or PathData value is " +
+                            "supported for Paths.");
+                }
             }
             if (mTmpValues.dataSource != null) {
                 float[] dataPoints = createDataPoints(mTmpValues.dataSource, animator
@@ -939,8 +972,12 @@
                 long startTime) {
                 long nativePtr = target.getNativeRenderer();
                 if (!animator.getPropertyName().equals("alpha")) {
-                    throw new UnsupportedOperationException("Only alpha is supported for root " +
-                            "group");
+                    if (mShouldIgnoreInvalidAnim) {
+                        return;
+                    } else {
+                        throw new UnsupportedOperationException("Only alpha is supported for root "
+                                + "group");
+                    }
                 }
                 Float startValue = null;
                 Float endValue = null;
@@ -953,7 +990,11 @@
                     }
                 }
                 if (startValue == null && endValue == null) {
-                    throw new UnsupportedOperationException("No alpha values are specified");
+                    if (mShouldIgnoreInvalidAnim) {
+                        return;
+                    } else {
+                        throw new UnsupportedOperationException("No alpha values are specified");
+                    }
                 }
                 long propertyPtr = nCreateRootAlphaPropertyHolder(nativePtr, startValue, endValue);
                 createNativeChildAnimator(propertyPtr, startTime, animator);
diff --git a/graphics/java/android/graphics/drawable/VectorDrawable.java b/graphics/java/android/graphics/drawable/VectorDrawable.java
index f4bbc8c..bdbf3c0 100644
--- a/graphics/java/android/graphics/drawable/VectorDrawable.java
+++ b/graphics/java/android/graphics/drawable/VectorDrawable.java
@@ -924,8 +924,11 @@
         private int mChangingConfigurations;
         private int[] mThemeAttrs;
         private String mGroupName = null;
-        private long mNativePtr = 0;
 
+        // The native object will be created in the constructor and will be destroyed in native
+        // when the neither java nor native has ref to the tree. This pointer should be valid
+        // throughout this VGroup Java object's life.
+        private final long mNativePtr;
         public VGroup(VGroup copy, ArrayMap<String, Object> targetsMap) {
 
             mIsStateful = copy.mIsStateful;
@@ -1065,16 +1068,6 @@
         }
 
         @Override
-        protected void finalize() throws Throwable {
-            if (mNativePtr != 0) {
-                nDestroy(mNativePtr);
-                mNativePtr = 0;
-            }
-            super.finalize();
-        }
-
-
-        @Override
         public void applyTheme(Theme t) {
             if (mThemeAttrs != null) {
                 final TypedArray a = t.resolveAttributes(mThemeAttrs,
@@ -1208,10 +1201,10 @@
      * Clip path, which only has name and pathData.
      */
     private static class VClipPath extends VPath {
-        long mNativePtr = 0;
+        private final long mNativePtr;
+
         public VClipPath() {
             mNativePtr = nCreateClipPath();
-            // Empty constructor.
         }
 
         public VClipPath(VClipPath copy) {
@@ -1225,14 +1218,6 @@
         }
 
         @Override
-        protected void finalize() throws Throwable {
-            if (mNativePtr != 0) {
-                nDestroy(mNativePtr);
-                mNativePtr = 0;
-            }
-            super.finalize();
-        }
-        @Override
         public void inflate(Resources r, AttributeSet attrs, Theme theme) {
             final TypedArray a = obtainAttributes(r, theme, attrs,
                     R.styleable.VectorDrawableClipPath);
@@ -1317,10 +1302,9 @@
 
         ComplexColor mStrokeColors = null;
         ComplexColor mFillColors = null;
-        private long mNativePtr = 0;
+        private final long mNativePtr;
 
         public VFullPath() {
-            // Empty constructor.
             mNativePtr = nCreateFullPath();
         }
 
@@ -1384,15 +1368,6 @@
             a.recycle();
         }
 
-        @Override
-        protected void finalize() throws Throwable {
-            if (mNativePtr != 0) {
-                nDestroy(mNativePtr);
-                mNativePtr = 0;
-            }
-            super.finalize();
-        }
-
         private void updateStateFromTypedArray(TypedArray a) {
             int byteCount = TOTAL_PROPERTY_COUNT * 4;
             if (mPropertyData == null) {
@@ -1647,7 +1622,7 @@
     private static native void nDraw(long rendererPtr, long canvasWrapperPtr,
             long colorFilterPtr, Rect bounds, boolean needsMirroring, boolean canReuseCache);
     private static native long nCreateFullPath();
-    private static native long nCreateFullPath(long mNativeFullPathPtr);
+    private static native long nCreateFullPath(long nativeFullPathPtr);
     private static native boolean nGetFullPathProperties(long pathPtr, byte[] properties,
             int length);
 
@@ -1663,7 +1638,6 @@
 
     private static native long nCreateGroup();
     private static native long nCreateGroup(long groupPtr);
-    private static native void nDestroy(long nodePtr);
     private static native void nSetName(long nodePtr, String name);
     private static native boolean nGetGroupProperties(long groupPtr, float[] properties,
             int length);
diff --git a/keystore/java/android/security/keystore/KeyInfo.java b/keystore/java/android/security/keystore/KeyInfo.java
index 7cf4b04..d726880 100644
--- a/keystore/java/android/security/keystore/KeyInfo.java
+++ b/keystore/java/android/security/keystore/KeyInfo.java
@@ -269,7 +269,7 @@
 
     /**
      * Returns {@code true} if the requirement that this key can only be used if the user has been
-     * authenticated if enforced by secure hardware (e.g., Trusted Execution Environment (TEE) or
+     * authenticated is enforced by secure hardware (e.g., Trusted Execution Environment (TEE) or
      * Secure Element (SE)).
      *
      * @see #isUserAuthenticationRequired()
diff --git a/libs/hwui/Android.mk b/libs/hwui/Android.mk
index 6988b02..7b43947 100644
--- a/libs/hwui/Android.mk
+++ b/libs/hwui/Android.mk
@@ -238,6 +238,7 @@
 
 LOCAL_SRC_FILES += \
     $(hwui_test_common_src_files) \
+    tests/unit/BufferPoolTests.cpp \
     tests/unit/CanvasStateTests.cpp \
     tests/unit/ClipAreaTests.cpp \
     tests/unit/CrashHandlerInjector.cpp \
@@ -247,11 +248,12 @@
     tests/unit/GpuMemoryTrackerTests.cpp \
     tests/unit/LayerUpdateQueueTests.cpp \
     tests/unit/LinearAllocatorTests.cpp \
-    tests/unit/VectorDrawableTests.cpp \
     tests/unit/OffscreenBufferPoolTests.cpp \
+    tests/unit/SkiaBehaviorTests.cpp \
     tests/unit/StringUtilsTests.cpp \
-    tests/unit/BufferPoolTests.cpp \
-    tests/unit/TextDropShadowCacheTests.cpp
+    tests/unit/TextDropShadowCacheTests.cpp \
+    tests/unit/VectorDrawableTests.cpp \
+    tests/unit/GradientCacheTests.cpp
 
 ifeq (true, $(HWUI_NEW_OPS))
     LOCAL_SRC_FILES += \
diff --git a/libs/hwui/BakedOpDispatcher.cpp b/libs/hwui/BakedOpDispatcher.cpp
index 2184755..e3a5f3e 100644
--- a/libs/hwui/BakedOpDispatcher.cpp
+++ b/libs/hwui/BakedOpDispatcher.cpp
@@ -663,7 +663,7 @@
 }
 
 void BakedOpDispatcher::onShadowOp(BakedOpRenderer& renderer, const ShadowOp& op, const BakedOpState& state) {
-    TessellationCache::vertexBuffer_pair_t buffers = *(op.shadowTask->getResult());
+    TessellationCache::vertexBuffer_pair_t buffers = op.shadowTask->getResult();
     renderShadow(renderer, state, op.casterAlpha, buffers.first, buffers.second);
 }
 
diff --git a/libs/hwui/ClipArea.cpp b/libs/hwui/ClipArea.cpp
index 160090d..9c08b4d 100644
--- a/libs/hwui/ClipArea.cpp
+++ b/libs/hwui/ClipArea.cpp
@@ -213,6 +213,7 @@
 
 void ClipArea::clipRectWithTransform(const Rect& r, const mat4* transform,
         SkRegion::Op op) {
+    if (!mPostViewportClipObserved && op == SkRegion::kIntersect_Op) op = SkRegion::kReplace_Op;
     onClipUpdated();
     switch (mMode) {
     case ClipMode::Rectangle:
@@ -228,6 +229,7 @@
 }
 
 void ClipArea::clipRegion(const SkRegion& region, SkRegion::Op op) {
+    if (!mPostViewportClipObserved && op == SkRegion::kIntersect_Op) op = SkRegion::kReplace_Op;
     onClipUpdated();
     enterRegionMode();
     mClipRegion.op(region, op);
@@ -236,6 +238,7 @@
 
 void ClipArea::clipPathWithTransform(const SkPath& path, const mat4* transform,
         SkRegion::Op op) {
+    if (!mPostViewportClipObserved && op == SkRegion::kIntersect_Op) op = SkRegion::kReplace_Op;
     onClipUpdated();
     SkMatrix skTransform;
     transform->copyTo(skTransform);
diff --git a/libs/hwui/DamageAccumulator.h b/libs/hwui/DamageAccumulator.h
index e44fc20..250296e 100644
--- a/libs/hwui/DamageAccumulator.h
+++ b/libs/hwui/DamageAccumulator.h
@@ -57,7 +57,7 @@
     // Returns the current dirty area, *NOT* transformed by pushed transforms
     void peekAtDirty(SkRect* dest) const;
 
-    void computeCurrentTransform(Matrix4* outMatrix) const;
+    ANDROID_API void computeCurrentTransform(Matrix4* outMatrix) const;
 
     void finish(SkRect* totalDirty);
 
diff --git a/libs/hwui/FrameBuilder.cpp b/libs/hwui/FrameBuilder.cpp
index 57e5b9d..185acce 100644
--- a/libs/hwui/FrameBuilder.cpp
+++ b/libs/hwui/FrameBuilder.cpp
@@ -203,7 +203,9 @@
         mCanvasState.setClippingOutline(mAllocator, &(properties.getOutline()));
     }
 
-    if (!mCanvasState.quickRejectConservative(0, 0, width, height)) {
+    bool quickRejected = properties.getClipToBounds()
+            && mCanvasState.quickRejectConservative(0, 0, width, height);
+    if (!quickRejected) {
         // not rejected, so defer render as either Layer, or direct (possibly wrapped in saveLayer)
         if (node.getLayer()) {
             // HW layer
diff --git a/libs/hwui/GradientCache.cpp b/libs/hwui/GradientCache.cpp
index e899ac7..eec9ed1 100644
--- a/libs/hwui/GradientCache.cpp
+++ b/libs/hwui/GradientCache.cpp
@@ -168,10 +168,13 @@
     texture->blend = info.hasAlpha;
     texture->generation = 1;
 
-    // Asume the cache is always big enough
+    // Assume the cache is always big enough
     const uint32_t size = info.width * 2 * bytesPerPixel();
     while (getSize() + size > mMaxSize) {
-        mCache.removeOldest();
+        LOG_ALWAYS_FATAL_IF(!mCache.removeOldest(),
+                "Ran out of things to remove from the cache? getSize() = %" PRIu32
+                ", size = %" PRIu32 ", mMaxSize = %" PRIu32 ", width = %" PRIu32,
+                getSize(), size, mMaxSize, info.width);
     }
 
     generateTexture(colors, positions, info.width, 2, texture);
diff --git a/libs/hwui/RenderNode.cpp b/libs/hwui/RenderNode.cpp
index d4588ed..bade216 100644
--- a/libs/hwui/RenderNode.cpp
+++ b/libs/hwui/RenderNode.cpp
@@ -381,6 +381,10 @@
     bool childFunctorsNeedLayer = mProperties.prepareForFunctorPresence(
             willHaveFunctor, functorsNeedLayer);
 
+    if (CC_UNLIKELY(mPositionListener.get())) {
+        mPositionListener->onPositionUpdated(*this, info);
+    }
+
     prepareLayer(info, animatorDirtyMask);
     if (info.mode == TreeInfo::MODE_FULL) {
         pushStagingDisplayListChanges(info);
diff --git a/libs/hwui/RenderNode.h b/libs/hwui/RenderNode.h
index 8e4a3df..f248de54 100644
--- a/libs/hwui/RenderNode.h
+++ b/libs/hwui/RenderNode.h
@@ -209,6 +209,19 @@
     OffscreenBuffer** getLayerHandle() { return &mLayer; } // ugh...
 #endif
 
+    class ANDROID_API PositionListener {
+    public:
+        virtual ~PositionListener() {}
+        virtual void onPositionUpdated(RenderNode& node, const TreeInfo& info) = 0;
+    };
+
+    // Note this is not thread safe, this needs to be called
+    // before the RenderNode is used for drawing.
+    // RenderNode takes ownership of the pointer
+    ANDROID_API void setPositionListener(PositionListener* listener) {
+        mPositionListener.reset(listener);
+    }
+
 private:
     typedef key_value_pair_t<float, DrawRenderNodeOp*> ZDrawRenderNodeOpPair;
 
@@ -317,6 +330,8 @@
     // This is *NOT* thread-safe, and should therefore only be tracking
     // mDisplayList, not mStagingDisplayList.
     uint32_t mParentCount;
+
+    std::unique_ptr<PositionListener> mPositionListener;
 }; // class RenderNode
 
 } /* namespace uirenderer */
diff --git a/libs/hwui/TessellationCache.cpp b/libs/hwui/TessellationCache.cpp
index 461e819..fd9fb852 100644
--- a/libs/hwui/TessellationCache.cpp
+++ b/libs/hwui/TessellationCache.cpp
@@ -242,23 +242,21 @@
             spotBuffer);
 }
 
-class ShadowProcessor : public TaskProcessor<TessellationCache::vertexBuffer_pair_t*> {
+class ShadowProcessor : public TaskProcessor<TessellationCache::vertexBuffer_pair_t> {
 public:
     ShadowProcessor(Caches& caches)
-            : TaskProcessor<TessellationCache::vertexBuffer_pair_t*>(&caches.tasks) {}
+            : TaskProcessor<TessellationCache::vertexBuffer_pair_t>(&caches.tasks) {}
     ~ShadowProcessor() {}
 
-    virtual void onProcess(const sp<Task<TessellationCache::vertexBuffer_pair_t*> >& task) override {
+    virtual void onProcess(const sp<Task<TessellationCache::vertexBuffer_pair_t> >& task) override {
         TessellationCache::ShadowTask* t = static_cast<TessellationCache::ShadowTask*>(task.get());
         ATRACE_NAME("shadow tessellation");
 
-        VertexBuffer* ambientBuffer = new VertexBuffer;
-        VertexBuffer* spotBuffer = new VertexBuffer;
         tessellateShadows(&t->drawTransform, &t->localClip, t->opaque, &t->casterPerimeter,
                 &t->transformXY, &t->transformZ, t->lightCenter, t->lightRadius,
-                *ambientBuffer, *spotBuffer);
+                t->ambientBuffer, t->spotBuffer);
 
-        t->setResult(new TessellationCache::vertexBuffer_pair_t(ambientBuffer, spotBuffer));
+        t->setResult(TessellationCache::vertexBuffer_pair_t(&t->ambientBuffer, &t->spotBuffer));
     }
 };
 
@@ -373,7 +371,7 @@
         task = static_cast<ShadowTask*>(mShadowCache.get(key));
     }
     LOG_ALWAYS_FATAL_IF(task == nullptr, "shadow not precached");
-    outBuffers = *(task->getResult());
+    outBuffers = task->getResult();
 }
 
 sp<TessellationCache::ShadowTask> TessellationCache::getShadowTask(
@@ -392,13 +390,6 @@
     return task;
 }
 
-TessellationCache::ShadowTask::~ShadowTask() {
-    TessellationCache::vertexBuffer_pair_t* bufferPair = getResult();
-    delete bufferPair->getFirst();
-    delete bufferPair->getSecond();
-    delete bufferPair;
-}
-
 ///////////////////////////////////////////////////////////////////////////////
 // Tessellation precaching
 ///////////////////////////////////////////////////////////////////////////////
diff --git a/libs/hwui/TessellationCache.h b/libs/hwui/TessellationCache.h
index 977c2d9e..6dcc8120 100644
--- a/libs/hwui/TessellationCache.h
+++ b/libs/hwui/TessellationCache.h
@@ -21,6 +21,7 @@
 #include "Matrix.h"
 #include "Rect.h"
 #include "Vector.h"
+#include "VertexBuffer.h"
 #include "thread/TaskProcessor.h"
 #include "utils/Macros.h"
 #include "utils/Pair.h"
@@ -89,7 +90,7 @@
         hash_t hash() const;
     };
 
-    class ShadowTask : public Task<TessellationCache::vertexBuffer_pair_t*> {
+    class ShadowTask : public Task<vertexBuffer_pair_t> {
     public:
         ShadowTask(const Matrix4* drawTransform, const Rect& localClip, bool opaque,
                 const SkPath* casterPerimeter, const Matrix4* transformXY, const Matrix4* transformZ,
@@ -104,13 +105,11 @@
             , lightRadius(lightRadius) {
         }
 
-        ~ShadowTask();
-
         /* Note - we deep copy all task parameters, because *even though* pointers into Allocator
          * controlled objects (like the SkPath and Matrix4s) should be safe for the entire frame,
          * certain Allocators are destroyed before trim() is called to flush incomplete tasks.
          *
-         * These deep copies could be avoided, long term, by cancelling or flushing outstanding
+         * These deep copies could be avoided, long term, by canceling or flushing outstanding
          * tasks before tearing down single-frame LinearAllocators.
          */
         const Matrix4 drawTransform;
@@ -121,6 +120,8 @@
         const Matrix4 transformZ;
         const Vector3 lightCenter;
         const float lightRadius;
+        VertexBuffer ambientBuffer;
+        VertexBuffer spotBuffer;
     };
 
     TessellationCache();
@@ -217,12 +218,12 @@
     ///////////////////////////////////////////////////////////////////////////////
     // Shadow tessellation caching
     ///////////////////////////////////////////////////////////////////////////////
-    sp<TaskProcessor<vertexBuffer_pair_t*> > mShadowProcessor;
+    sp<TaskProcessor<vertexBuffer_pair_t> > mShadowProcessor;
 
     // holds a pointer, and implicit strong ref to each shadow task of the frame
-    LruCache<ShadowDescription, Task<vertexBuffer_pair_t*>*> mShadowCache;
-    class BufferPairRemovedListener : public OnEntryRemoved<ShadowDescription, Task<vertexBuffer_pair_t*>*> {
-        void operator()(ShadowDescription& description, Task<vertexBuffer_pair_t*>*& bufferPairTask) override {
+    LruCache<ShadowDescription, Task<vertexBuffer_pair_t>*> mShadowCache;
+    class BufferPairRemovedListener : public OnEntryRemoved<ShadowDescription, Task<vertexBuffer_pair_t>*> {
+        void operator()(ShadowDescription& description, Task<vertexBuffer_pair_t>*& bufferPairTask) override {
             bufferPairTask->decStrong(nullptr);
         }
     };
diff --git a/libs/hwui/TreeInfo.h b/libs/hwui/TreeInfo.h
index be25516..accd303 100644
--- a/libs/hwui/TreeInfo.h
+++ b/libs/hwui/TreeInfo.h
@@ -86,6 +86,12 @@
 #endif
     ErrorHandler* errorHandler = nullptr;
 
+    // Frame number for use with synchronized surfaceview position updating
+    int64_t frameNumber = -1;
+    int32_t windowInsetLeft = 0;
+    int32_t windowInsetTop = 0;
+    bool updateWindowPositions = false;
+
     struct Out {
         bool hasFunctors = false;
         // This is only updated if evaluateAnimations is true
diff --git a/libs/hwui/VectorDrawable.cpp b/libs/hwui/VectorDrawable.cpp
index 541c799..2e3856f 100644
--- a/libs/hwui/VectorDrawable.cpp
+++ b/libs/hwui/VectorDrawable.cpp
@@ -324,7 +324,7 @@
     // Save the current clip information, which is local to this group.
     outCanvas->save();
     // Draw the group tree in the same order as the XML file.
-    for (Node* child : mChildren) {
+    for (auto& child : mChildren) {
         child->draw(outCanvas, stackedMatrix, scaleX, scaleY);
     }
     // Restore the previous clip information.
@@ -361,7 +361,7 @@
 }
 
 void Group::addChild(Node* child) {
-    mChildren.push_back(child);
+    mChildren.emplace_back(child);
 }
 
 bool Group::getProperties(float* outProperties, int length) {
diff --git a/libs/hwui/VectorDrawable.h b/libs/hwui/VectorDrawable.h
index f8f1ea6..36a8aeb 100644
--- a/libs/hwui/VectorDrawable.h
+++ b/libs/hwui/VectorDrawable.h
@@ -316,7 +316,7 @@
         // Count of the properties, must be at the end.
         Count,
     };
-    std::vector<Node*> mChildren;
+    std::vector< std::unique_ptr<Node> > mChildren;
     Properties mProperties;
 };
 
@@ -360,7 +360,7 @@
     float mViewportHeight = 0;
     float mRootAlpha = 1.0f;
 
-    Group* mRootNode;
+    std::unique_ptr<Group> mRootNode;
     SkRect mBounds;
     SkMatrix mCanvasMatrix;
     SkPaint mPaint;
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index d411621..ea702c0 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -92,18 +92,18 @@
     }
 }
 
-void CanvasContext::setSurface(ANativeWindow* window) {
+void CanvasContext::setSurface(Surface* surface) {
     ATRACE_CALL();
 
-    mNativeWindow = window;
+    mNativeSurface = surface;
 
     if (mEglSurface != EGL_NO_SURFACE) {
         mEglManager.destroySurface(mEglSurface);
         mEglSurface = EGL_NO_SURFACE;
     }
 
-    if (window) {
-        mEglSurface = mEglManager.createSurface(window);
+    if (surface) {
+        mEglSurface = mEglManager.createSurface(surface);
     }
 
     if (mEglSurface != EGL_NO_SURFACE) {
@@ -127,8 +127,8 @@
     mSwapBehavior = swapBehavior;
 }
 
-void CanvasContext::initialize(ANativeWindow* window) {
-    setSurface(window);
+void CanvasContext::initialize(Surface* surface) {
+    setSurface(surface);
 #if !HWUI_NEW_OPS
     if (mCanvas) return;
     mCanvas = new OpenGLRenderer(mRenderThread.renderState());
@@ -136,11 +136,11 @@
 #endif
 }
 
-void CanvasContext::updateSurface(ANativeWindow* window) {
-    setSurface(window);
+void CanvasContext::updateSurface(Surface* surface) {
+    setSurface(surface);
 }
 
-bool CanvasContext::pauseSurface(ANativeWindow* window) {
+bool CanvasContext::pauseSurface(Surface* surface) {
     return mRenderThread.removeFrameCallback(this);
 }
 
@@ -204,6 +204,10 @@
     info.renderer = mCanvas;
 #endif
 
+    if (CC_LIKELY(mNativeSurface.get())) {
+        info.frameNumber = static_cast<int64_t>(mNativeSurface->getNextFrameNumber());
+    }
+
     mAnimationContext->startFrame(info.mode);
     for (const sp<RenderNode>& node : mRenderNodes) {
         // Only the primary target node will be drawn full - all other nodes would get drawn in
@@ -219,7 +223,7 @@
     freePrefetechedLayers();
     GL_CHECKPOINT(MODERATE);
 
-    if (CC_UNLIKELY(!mNativeWindow.get())) {
+    if (CC_UNLIKELY(!mNativeSurface.get())) {
         mCurrentFrameInfo->addFlag(FrameInfoFlags::SkippedFrame);
         info.out.canDrawThisFrame = false;
         return;
@@ -242,8 +246,9 @@
         } else {
             // We're maybe behind? Find out for sure
             int runningBehind = 0;
-            mNativeWindow->query(mNativeWindow.get(),
-                    NATIVE_WINDOW_CONSUMER_RUNNING_BEHIND, &runningBehind);
+            // TODO: Have this method be on Surface, too, not just ANativeWindow...
+            ANativeWindow* window = mNativeSurface.get();
+            window->query(window, NATIVE_WINDOW_CONSUMER_RUNNING_BEHIND, &runningBehind);
             info.out.canDrawThisFrame = !runningBehind;
         }
     } else {
diff --git a/libs/hwui/renderthread/CanvasContext.h b/libs/hwui/renderthread/CanvasContext.h
index 63a7977..168166e 100644
--- a/libs/hwui/renderthread/CanvasContext.h
+++ b/libs/hwui/renderthread/CanvasContext.h
@@ -39,6 +39,7 @@
 #include <SkBitmap.h>
 #include <SkRect.h>
 #include <utils/Functor.h>
+#include <gui/Surface.h>
 
 #include <set>
 #include <string>
@@ -75,10 +76,10 @@
     // Won't take effect until next EGLSurface creation
     void setSwapBehavior(SwapBehavior swapBehavior);
 
-    void initialize(ANativeWindow* window);
-    void updateSurface(ANativeWindow* window);
-    bool pauseSurface(ANativeWindow* window);
-    bool hasSurface() { return mNativeWindow.get(); }
+    void initialize(Surface* surface);
+    void updateSurface(Surface* surface);
+    bool pauseSurface(Surface* surface);
+    bool hasSurface() { return mNativeSurface.get(); }
 
     void setup(int width, int height, float lightRadius,
             uint8_t ambientShadowAlpha, uint8_t spotShadowAlpha);
@@ -172,7 +173,7 @@
     // lifecycle tracking
     friend class android::uirenderer::RenderState;
 
-    void setSurface(ANativeWindow* window);
+    void setSurface(Surface* window);
     void requireSurface();
 
     void freePrefetechedLayers();
@@ -182,7 +183,7 @@
 
     RenderThread& mRenderThread;
     EglManager& mEglManager;
-    sp<ANativeWindow> mNativeWindow;
+    sp<Surface> mNativeSurface;
     EGLSurface mEglSurface = EGL_NO_SURFACE;
     bool mBufferPreserved = false;
     SwapBehavior mSwapBehavior = kSwap_default;
diff --git a/libs/hwui/renderthread/EglManager.cpp b/libs/hwui/renderthread/EglManager.cpp
index 466fef9d..805bec1 100644
--- a/libs/hwui/renderthread/EglManager.cpp
+++ b/libs/hwui/renderthread/EglManager.cpp
@@ -228,6 +228,13 @@
     LOG_ALWAYS_FATAL_IF(surface == EGL_NO_SURFACE,
             "Failed to create EGLSurface for window %p, eglErr = %s",
             (void*) window, egl_error_str());
+
+    if (mSwapBehavior != SwapBehavior::Preserved) {
+        LOG_ALWAYS_FATAL_IF(eglSurfaceAttrib(mEglDisplay, surface, EGL_SWAP_BEHAVIOR, EGL_BUFFER_DESTROYED) == EGL_FALSE,
+                            "Failed to set swap behavior to destroyed for window %p, eglErr = %s",
+                            (void*) window, egl_error_str());
+    }
+
     return surface;
 }
 
diff --git a/libs/hwui/renderthread/RenderProxy.cpp b/libs/hwui/renderthread/RenderProxy.cpp
index 1d1b144..7c6cd7e 100644
--- a/libs/hwui/renderthread/RenderProxy.cpp
+++ b/libs/hwui/renderthread/RenderProxy.cpp
@@ -139,38 +139,38 @@
     postAndWait(task); // block since name/value pointers owned by caller
 }
 
-CREATE_BRIDGE2(initialize, CanvasContext* context, ANativeWindow* window) {
-    args->context->initialize(args->window);
+CREATE_BRIDGE2(initialize, CanvasContext* context, Surface* surface) {
+    args->context->initialize(args->surface);
     return nullptr;
 }
 
-void RenderProxy::initialize(const sp<ANativeWindow>& window) {
+void RenderProxy::initialize(const sp<Surface>& surface) {
     SETUP_TASK(initialize);
     args->context = mContext;
-    args->window = window.get();
+    args->surface = surface.get();
     post(task);
 }
 
-CREATE_BRIDGE2(updateSurface, CanvasContext* context, ANativeWindow* window) {
-    args->context->updateSurface(args->window);
+CREATE_BRIDGE2(updateSurface, CanvasContext* context, Surface* surface) {
+    args->context->updateSurface(args->surface);
     return nullptr;
 }
 
-void RenderProxy::updateSurface(const sp<ANativeWindow>& window) {
+void RenderProxy::updateSurface(const sp<Surface>& surface) {
     SETUP_TASK(updateSurface);
     args->context = mContext;
-    args->window = window.get();
+    args->surface = surface.get();
     postAndWait(task);
 }
 
-CREATE_BRIDGE2(pauseSurface, CanvasContext* context, ANativeWindow* window) {
-    return (void*) args->context->pauseSurface(args->window);
+CREATE_BRIDGE2(pauseSurface, CanvasContext* context, Surface* surface) {
+    return (void*) args->context->pauseSurface(args->surface);
 }
 
-bool RenderProxy::pauseSurface(const sp<ANativeWindow>& window) {
+bool RenderProxy::pauseSurface(const sp<Surface>& surface) {
     SETUP_TASK(pauseSurface);
     args->context = mContext;
-    args->window = window.get();
+    args->surface = surface.get();
     return (bool) postAndWait(task);
 }
 
diff --git a/libs/hwui/renderthread/RenderProxy.h b/libs/hwui/renderthread/RenderProxy.h
index 4180d802..178724a 100644
--- a/libs/hwui/renderthread/RenderProxy.h
+++ b/libs/hwui/renderthread/RenderProxy.h
@@ -67,9 +67,9 @@
     ANDROID_API bool loadSystemProperties();
     ANDROID_API void setName(const char* name);
 
-    ANDROID_API void initialize(const sp<ANativeWindow>& window);
-    ANDROID_API void updateSurface(const sp<ANativeWindow>& window);
-    ANDROID_API bool pauseSurface(const sp<ANativeWindow>& window);
+    ANDROID_API void initialize(const sp<Surface>& surface);
+    ANDROID_API void updateSurface(const sp<Surface>& surface);
+    ANDROID_API bool pauseSurface(const sp<Surface>& surface);
     ANDROID_API void setup(int width, int height, float lightRadius,
             uint8_t ambientShadowAlpha, uint8_t spotShadowAlpha);
     ANDROID_API void setLightCenter(const Vector3& lightCenter);
diff --git a/libs/hwui/tests/macrobench/TestSceneRunner.cpp b/libs/hwui/tests/macrobench/TestSceneRunner.cpp
index a843e92..58c0876 100644
--- a/libs/hwui/tests/macrobench/TestSceneRunner.cpp
+++ b/libs/hwui/tests/macrobench/TestSceneRunner.cpp
@@ -112,9 +112,9 @@
             scene->doFrame(i);
             proxy->syncAndDrawFrame();
         }
-        proxy->fence();
-        nsecs_t done = systemTime(CLOCK_MONOTONIC);
         if (opts.reportFrametimeWeight) {
+            proxy->fence();
+            nsecs_t done = systemTime(CLOCK_MONOTONIC);
             avgMs.add((done - vsync) / 1000000.0);
             if (i % 10 == 9) {
                 printf("Average frametime %.3fms\n", avgMs.average());
diff --git a/libs/hwui/tests/unit/GradientCacheTests.cpp b/libs/hwui/tests/unit/GradientCacheTests.cpp
new file mode 100644
index 0000000..5ee1705
--- /dev/null
+++ b/libs/hwui/tests/unit/GradientCacheTests.cpp
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <gtest/gtest.h>
+
+#include "Extensions.h"
+#include "GradientCache.h"
+#include "tests/common/TestUtils.h"
+
+using namespace android;
+using namespace android::uirenderer;
+
+RENDERTHREAD_TEST(GradientCache, addRemove) {
+    Extensions extensions;
+    GradientCache cache(extensions);
+    cache.setMaxSize(5000);
+
+    SkColor colors[] = { 0xFF00FF00, 0xFFFF0000, 0xFF0000FF };
+    float positions[] = { 1, 2, 3 };
+    Texture* texture = cache.get(colors, positions, 3);
+    ASSERT_TRUE(texture);
+    ASSERT_FALSE(texture->cleanup);
+    ASSERT_EQ((uint32_t) texture->objectSize(), cache.getSize());
+    ASSERT_TRUE(cache.getSize());
+    cache.clear();
+    ASSERT_EQ(cache.getSize(), 0u);
+}
diff --git a/libs/hwui/tests/unit/RecordingCanvasTests.cpp b/libs/hwui/tests/unit/RecordingCanvasTests.cpp
index 01bfc5a..20d2f1f 100644
--- a/libs/hwui/tests/unit/RecordingCanvasTests.cpp
+++ b/libs/hwui/tests/unit/RecordingCanvasTests.cpp
@@ -455,6 +455,23 @@
     }
 }
 
+TEST(RecordingCanvas, firstClipWillReplace) {
+    auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
+        canvas.save(SaveFlags::MatrixClip);
+        // since no explicit clip set on canvas, this should be the one observed on op:
+        canvas.clipRect(-100, -100, 300, 300, SkRegion::kIntersect_Op);
+
+        SkPaint paint;
+        paint.setColor(SK_ColorWHITE);
+        canvas.drawRect(0, 0, 100, 100, paint);
+
+        canvas.restore();
+    });
+    ASSERT_EQ(1u, dl->getOps().size()) << "Must have one op";
+    // first clip must be preserved, even if it extends beyond canvas bounds
+    EXPECT_CLIP_RECT(Rect(-100, -100, 300, 300), dl->getOps()[0]->localClip);
+}
+
 TEST(RecordingCanvas, insertReorderBarrier) {
     auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
         canvas.drawRect(0, 0, 400, 400, SkPaint());
diff --git a/libs/hwui/tests/unit/SkiaBehaviorTests.cpp b/libs/hwui/tests/unit/SkiaBehaviorTests.cpp
new file mode 100644
index 0000000..586625b
--- /dev/null
+++ b/libs/hwui/tests/unit/SkiaBehaviorTests.cpp
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "tests/common/TestUtils.h"
+
+#include <gtest/gtest.h>
+#include <SkShader.h>
+
+using namespace android;
+using namespace android::uirenderer;
+
+/**
+ * 1x1 bitmaps must not be optimized into solid color shaders, since HWUI can't
+ * compose/render color shaders
+ */
+TEST(SkiaBehavior, CreateBitmapShader1x1) {
+    SkBitmap origBitmap = TestUtils::createSkBitmap(1, 1);
+    std::unique_ptr<SkShader> s(SkShader::CreateBitmapShader(
+            origBitmap,
+            SkShader::kClamp_TileMode,
+            SkShader::kRepeat_TileMode));
+
+    SkBitmap bitmap;
+    SkShader::TileMode xy[2];
+    ASSERT_TRUE(s->isABitmap(&bitmap, nullptr, xy))
+        << "1x1 bitmap shader must query as bitmap shader";
+    EXPECT_EQ(SkShader::kClamp_TileMode, xy[0]);
+    EXPECT_EQ(SkShader::kRepeat_TileMode, xy[1]);
+    EXPECT_EQ(origBitmap.pixelRef(), bitmap.pixelRef());
+}
diff --git a/media/java/android/media/AudioTrack.java b/media/java/android/media/AudioTrack.java
index 3007d86..b26b310 100644
--- a/media/java/android/media/AudioTrack.java
+++ b/media/java/android/media/AudioTrack.java
@@ -1784,9 +1784,9 @@
      * Note that the actual playback of this data might occur after this function returns.
      *
      * @param audioData the array that holds the data to play.
-     * @param offsetInBytes the offset expressed in bytes in audioData where the data to play
+     * @param offsetInBytes the offset expressed in bytes in audioData where the data to write
      *    starts.
-     * @param sizeInBytes the number of bytes to read in audioData after the offset.
+     * @param sizeInBytes the number of bytes to write in audioData after the offset.
      * @return zero or the positive number of bytes that were written, or
      *    {@link #ERROR_INVALID_OPERATION}
      *    if the track isn't properly initialized, or {@link #ERROR_BAD_VALUE} if
@@ -1821,9 +1821,9 @@
      * Note that the actual playback of this data might occur after this function returns.
      *
      * @param audioData the array that holds the data to play.
-     * @param offsetInBytes the offset expressed in bytes in audioData where the data to play
+     * @param offsetInBytes the offset expressed in bytes in audioData where the data to write
      *    starts.
-     * @param sizeInBytes the number of bytes to read in audioData after the offset.
+     * @param sizeInBytes the number of bytes to write in audioData after the offset.
      * @param writeMode one of {@link #WRITE_BLOCKING}, {@link #WRITE_NON_BLOCKING}. It has no
      *     effect in static mode.
      *     <br>With {@link #WRITE_BLOCKING}, the write will block until all data has been written
@@ -1920,8 +1920,8 @@
      * In static buffer mode, copies the data to the buffer starting at offset 0.
      * Note that the actual playback of this data might occur after this function returns.
      *
-     * @param audioData the array that holds the data to play.
-     * @param offsetInShorts the offset expressed in shorts in audioData where the data to play
+     * @param audioData the array that holds the data to write.
+     * @param offsetInShorts the offset expressed in shorts in audioData where the data to write
      *     starts.
      * @param sizeInShorts the number of shorts to read in audioData after the offset.
      * @param writeMode one of {@link #WRITE_BLOCKING}, {@link #WRITE_NON_BLOCKING}. It has no
@@ -1987,7 +1987,7 @@
      * and the write mode is ignored.
      * Note that the actual playback of this data might occur after this function returns.
      *
-     * @param audioData the array that holds the data to play.
+     * @param audioData the array that holds the data to write.
      *     The implementation does not clip for sample values within the nominal range
      *     [-1.0f, 1.0f], provided that all gains in the audio pipeline are
      *     less than or equal to unity (1.0f), and in the absence of post-processing effects
@@ -1998,8 +1998,8 @@
      *     and later processing in the audio path.  Therefore applications are encouraged
      *     to provide samples values within the nominal range.
      * @param offsetInFloats the offset, expressed as a number of floats,
-     *     in audioData where the data to play starts.
-     * @param sizeInFloats the number of floats to read in audioData after the offset.
+     *     in audioData where the data to write starts.
+     * @param sizeInFloats the number of floats to write in audioData after the offset.
      * @param writeMode one of {@link #WRITE_BLOCKING}, {@link #WRITE_NON_BLOCKING}. It has no
      *     effect in static mode.
      *     <br>With {@link #WRITE_BLOCKING}, the write will block until all data has been written
@@ -2070,7 +2070,7 @@
      * and the write mode is ignored.
      * Note that the actual playback of this data might occur after this function returns.
      *
-     * @param audioData the buffer that holds the data to play, starting at the position reported
+     * @param audioData the buffer that holds the data to write, starting at the position reported
      *     by <code>audioData.position()</code>.
      *     <BR>Note that upon return, the buffer position (<code>audioData.position()</code>) will
      *     have been advanced to reflect the amount of data that was successfully written to
@@ -2137,7 +2137,7 @@
     /**
      * Writes the audio data to the audio sink for playback in streaming mode on a HW_AV_SYNC track.
      * The blocking behavior will depend on the write mode.
-     * @param audioData the buffer that holds the data to play, starting at the position reported
+     * @param audioData the buffer that holds the data to write, starting at the position reported
      *     by <code>audioData.position()</code>.
      *     <BR>Note that upon return, the buffer position (<code>audioData.position()</code>) will
      *     have been advanced to reflect the amount of data that was successfully written to
diff --git a/media/java/android/media/audiopolicy/AudioMixingRule.java b/media/java/android/media/audiopolicy/AudioMixingRule.java
index f9fdd8d..54543ec 100644
--- a/media/java/android/media/audiopolicy/AudioMixingRule.java
+++ b/media/java/android/media/audiopolicy/AudioMixingRule.java
@@ -428,8 +428,17 @@
                     }
                 }
                 // rule didn't exist, add it
-                // FIXME doesn't work with RULE_MATCH_UID yet
-                mCriteria.add(new AttributeMatchCriterion(attrToMatch, rule));
+                switch (match_rule) {
+                    case RULE_MATCH_ATTRIBUTE_USAGE:
+                    case RULE_MATCH_ATTRIBUTE_CAPTURE_PRESET:
+                        mCriteria.add(new AttributeMatchCriterion(attrToMatch, rule));
+                        break;
+                    case RULE_MATCH_UID:
+                        mCriteria.add(new AttributeMatchCriterion(intProp, rule));
+                        break;
+                    default:
+                        throw new IllegalStateException("Unreachable code in addRuleInternal()");
+                }
             }
             return this;
         }
diff --git a/media/java/android/media/browse/MediaBrowser.java b/media/java/android/media/browse/MediaBrowser.java
index 80b3ffc..56b2514 100644
--- a/media/java/android/media/browse/MediaBrowser.java
+++ b/media/java/android/media/browse/MediaBrowser.java
@@ -117,6 +117,9 @@
      * to the media browse service when connecting and retrieving the root id
      * for browsing, or null if none. The contents of this bundle may affect
      * the information returned when browsing.
+     * @see android.service.media.MediaBrowserService.BrowserRoot#EXTRA_RECENT
+     * @see android.service.media.MediaBrowserService.BrowserRoot#EXTRA_OFFLINE
+     * @see android.service.media.MediaBrowserService.BrowserRoot#EXTRA_SUGGESTED
      */
     public MediaBrowser(Context context, ComponentName serviceComponent,
             ConnectionCallback callback, Bundle rootHints) {
diff --git a/media/java/android/service/media/MediaBrowserService.java b/media/java/android/service/media/MediaBrowserService.java
index 0393c94..3372524 100644
--- a/media/java/android/service/media/MediaBrowserService.java
+++ b/media/java/android/service/media/MediaBrowserService.java
@@ -351,6 +351,9 @@
      *            root id for browsing, or null if none. The contents of this
      *            bundle may affect the information returned when browsing.
      * @return The {@link BrowserRoot} for accessing this app's content or null.
+     * @see BrowserRoot#EXTRA_RECENT
+     * @see BrowserRoot#EXTRA_OFFLINE
+     * @see BrowserRoot#EXTRA_SUGGESTED
      */
     public abstract @Nullable BrowserRoot onGetRoot(@NonNull String clientPackageName,
             int clientUid, @Nullable Bundle rootHints);
@@ -667,6 +670,57 @@
      * when first connected.
      */
     public static final class BrowserRoot {
+        /**
+         * The lookup key for a boolean that indicates whether the browser service should return a
+         * browser root for recently played media items.
+         *
+         * <p>When creating a media browser for a given media browser service, this key can be
+         * supplied as a root hint for retrieving media items that are recently played.
+         * If the media browser service can provide such media items, the implementation must return
+         * the key in the root hint when {@link #onGetRoot(String, int, Bundle)} is called back.
+         *
+         * <p>The root hint may contain multiple keys.
+         *
+         * @see #EXTRA_OFFLINE
+         * @see #EXTRA_SUGGESTED
+         */
+        public static final String EXTRA_RECENT = "android.service.media.extra.RECENT";
+
+        /**
+         * The lookup key for a boolean that indicates whether the browser service should return a
+         * browser root for offline media items.
+         *
+         * <p>When creating a media browser for a given media browser service, this key can be
+         * supplied as a root hint for retrieving media items that are can be played without an
+         * internet connection.
+         * If the media browser service can provide such media items, the implementation must return
+         * the key in the root hint when {@link #onGetRoot(String, int, Bundle)} is called back.
+         *
+         * <p>The root hint may contain multiple keys.
+         *
+         * @see #EXTRA_RECENT
+         * @see #EXTRA_SUGGESTED
+         */
+        public static final String EXTRA_OFFLINE = "android.service.media.extra.OFFLINE";
+
+        /**
+         * The lookup key for a boolean that indicates whether the browser service should return a
+         * browser root for suggested media items.
+         *
+         * <p>When creating a media browser for a given media browser service, this key can be
+         * supplied as a root hint for retrieving the media items suggested by the media browser
+         * service. The list of media items passed in {@link android.media.browse.MediaBrowser.SubscriptionCallback#onChildrenLoaded(String, List)}
+         * is considered ordered by relevance, first being the top suggestion.
+         * If the media browser service can provide such media items, the implementation must return
+         * the key in the root hint when {@link #onGetRoot(String, int, Bundle)} is called back.
+         *
+         * <p>The root hint may contain multiple keys.
+         *
+         * @see #EXTRA_RECENT
+         * @see #EXTRA_OFFLINE
+         */
+        public static final String EXTRA_SUGGESTED = "android.service.media.extra.SUGGESTED";
+
         final private String mRootId;
         final private Bundle mExtras;
 
diff --git a/packages/DocumentsUI/res/drawable/cabinet.png b/packages/DocumentsUI/res/drawable/cabinet.png
deleted file mode 100644
index da44023..0000000
--- a/packages/DocumentsUI/res/drawable/cabinet.png
+++ /dev/null
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable/cabinet.xml b/packages/DocumentsUI/res/drawable/cabinet.xml
new file mode 100644
index 0000000..843ffc7
--- /dev/null
+++ b/packages/DocumentsUI/res/drawable/cabinet.xml
@@ -0,0 +1,81 @@
+<!--
+Copyright (C) 2016 The Android Open Source Project
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="672dp"
+        android:height="921dp"
+        android:viewportWidth="672.0"
+        android:viewportHeight="921.0">
+    <path
+        android:pathData="M286,0c5,0,10,0,15,0c0.1,1.8,1.5,1.8,2.8,2.1c11.1,2,22.1,4,33.2,6.1c31.8,6.1,63.7,12.3,95.5,18.5  c16.1,3.1,32.1,6.2,48.2,9.3c26,4.9,52.1,9.3,78,14.6c10.8,2.2,21.6,4.6,32.3,6.5c11.3,2,22.6,4.7,34,6c7.9,0.9,7.9,1.1,7.9,9.2  c0,237.3,0,474.5,0,711.8c-1.5,0.9,-3,2,-4.6,2.8c-18.3,8.3,-36.6,16.6,-54.8,25c-29.3,13.4,-58.5,26.8,-87.8,40.3  c-23.5,10.9,-47,21.8,-70.4,32.8c-2.1,1,-4.2,1.5,-6.3,1.1c-6.8,-1.3,-13.6,-2.5,-20.1,-4.9c5.9,-0.3,11.4,1.9,17.1,2.9c5.9,1.1,5.9,1,5.9,-4.9  c0,-17.1,0.1,-34.3,0,-51.4c-0.3,-68.9,-0.7,-137.8,-1,-206.7c0,-35.8,0,-71.6,0.1,-107.4c0,-3.8,-0.6,-5.2,-4.7,-3.7c-7.9,2.9,-16,5.4,-24.1,7.8  c-14.1,4.3,-27.8,10,-42.2,13.2c0,-64,0,-127.9,-0.1,-191.9c0,-4.1,1.3,-5.9,5.1,-7c21,-6.6,42,-13.4,63,-20.2c2.3,-0.8,4.4,-1.8,4,-4.9  c0,-59.3,0,-118.7,0,-178c0,-1.3,-0.7,-2,-2,-2c-2.6,-0.4,-5.2,-0.7,-7.8,-1.2c-30.2,-5.3,-60.5,-10.6,-90.7,-16c-31.9,-5.6,-63.7,-11.3,-95.6,-16.9  c-24.9,-4.4,-49.8,-8.7,-74.6,-13.1C117.1,75.6,93.1,71.3,69,67c-0.3,-0.3,-0.7,-0.7,-1,-1c17.4,-5.3,34.8,-10.7,52.3,-15.9  c29.4,-8.7,58.8,-17.2,88.2,-25.8c24.3,-7.1,48.6,-14.2,72.9,-21.4C283.1,2.4,285.6,2.7,286,0z"
+        android:fillColor="#EFEFEE"/>
+    <path
+        android:pathData="M412,307c0.4,3,-1.7,4.1,-4,4.9c-21,6.8,-42,13.6,-63,20.2c-3.8,1.2,-5.1,3,-5.1,7C340,403.1,340,467,340,531  c-11.8,-1.2,-23.3,-4.5,-34.9,-6.5c-10,-1.7,-19.9,-4.6,-30.1,-5.5c-0.7,-0.3,-1.4,-0.9,-2.2,-1c-19.8,-4,-39.5,-8,-59.3,-12c-12.2,-2.4,-24.3,-4.7,-36.5,-7  c-0.9,-0.3,-1.8,-0.8,-2.8,-1c-24.5,-4.9,-48.9,-9.9,-73.5,-14.6C89.3,481.3,78,477.1,66,478c-0.7,-1.6,-2.1,-1.8,-3.6,-2.1  c-11.1,-2.2,-22.2,-4.7,-33.3,-6.7c-9.7,-1.7,-19.1,-4.9,-29.1,-5.3c0,-64.3,0,-128.7,0,-193c0.8,-0.2,1.6,-0.4,2.4,-0.7c19,-7.8,37.9,-15.9,57.1,-23.4  c5.4,-2.1,6.7,-4.8,6.6,-10.2c-0.2,-55.1,-0.1,-110.2,-0.1,-165.4c0,-1.9,-1.4,-4.6,1.9,-5.4c0.3,0.3,0.7,0.7,1,1c-1.3,4.9,-1,9.9,-1,14.9  c0,51.1,0,102.3,0,153.4c0,1.2,0,2.3,0,3.5c0.1,3.5,1.2,5.9,5.3,6.5c7,1.1,14,2.6,21,3.9c22.1,4.3,44.1,8.6,66.2,12.8  c27.3,5.2,54.6,10.1,81.9,15.3c21.8,4.1,43.5,8.5,65.2,12.6c28.1,5.4,56.2,10.8,84.3,15.8C398.4,306.8,405.1,310.5,412,307z   M105,329c0,3.3,0,6.7,-0.1,10c-0.1,2.5,0.4,3.6,3.4,4.2c30,5.3,59.9,10.9,89.8,16.5c3.4,0.6,5.1,0.2,4.9,-3.7  c-0.2,-3.3,-0.1,-6.7,-0.1,-10c0.5,-3.6,-0.1,-6.3,-4.7,-6.1c-1.1,0.1,-2.2,-0.6,-3.4,-0.8c-28.3,-5,-56.6,-9.9,-84.9,-14.9  C105.6,323.4,104.5,325.2,105,329z M65.9,280.8c13.7,2.5,27.4,4.9,41.1,7.4c32.6,5.9,65.2,11.8,97.8,17.8  c41.4,7.6,82.8,15.2,124.2,22.8c6.8,1.2,13.3,1.4,20,-1.3c9.3,-3.6,18.9,-6.3,28.4,-9.4c6.4,-2.1,12.8,-4.2,19.2,-6.4  c-4.2,-2,-8.3,-3,-12.4,-3.8c-22.6,-4.2,-45.3,-8,-67.9,-12.6c-14.6,-3,-29.2,-5.6,-43.8,-8.5c-24,-4.6,-48,-9.2,-72,-13.7c-15.9,-3,-31.8,-6.2,-47.8,-9.2  c-19,-3.6,-38,-7.3,-57,-10.6c-9.9,-1.7,-19.6,-4.5,-29.7,-5.2C48,255.6,30.1,263,12.2,270.4c0,0.4,0,0.7,0,1.1  C30.1,274.6,48,277.7,65.9,280.8z"
+        android:fillColor="#EAEAEA"/>
+    <path
+        android:pathData="M672,782c-6,0.9,-11.1,4.3,-16.4,6.9c-30.8,15,-61.5,30.3,-92.3,45.4c-34.8,17.1,-69.5,34.3,-104.5,51.1  c-13,6.3,-26,12.8,-39,19.1c-1.5,0.7,-3.7,1,-3.9,3.4c-3.7,0,-7.3,0,-11,0c-0.4,-3,-3.1,-2.3,-4.7,-2.7c-19.3,-4.8,-38.6,-9.5,-57.9,-14.1  c-27.5,-6.5,-55.2,-12.7,-82.6,-19.4c-30.9,-7.5,-61.8,-15,-92.7,-22.1c-24.8,-5.8,-49.5,-12,-74.3,-18C70.8,826.5,48.9,821.3,27,816  c-1.1,-0.3,-2.3,-0.5,-3.3,-1c-3.2,-1.3,-3.5,-3.3,-0.7,-5.4c0.9,-0.7,2,-1.2,3.1,-1.7c12,-5.3,24,-10.7,36,-16c0.4,-0.2,0.9,-0.2,1.9,-0.3  c0,3.6,0,7,0,10.5c0,1.6,-0.5,3.5,2,3.9c0.7,2.8,3.2,2.5,5.2,3c39.3,9,78.7,18.1,118.1,27c43.9,10,87.7,20,131.6,29.9  c9.7,2.2,19.2,4.9,29.1,6c1.1,1.5,2.6,0.9,4,1l0,0c4.1,2,8.5,2.6,13,3l0,0c7.2,2.4,14.4,4.3,22,5l0,0c6.5,2.5,13.3,3.6,20.1,4.9  c2.1,0.4,4.2,-0.1,6.3,-1.1c23.5,-11,46.9,-21.9,70.4,-32.8c29.2,-13.5,58.5,-26.9,87.8,-40.3c18.3,-8.4,36.6,-16.6,54.8,-25  c1.6,-0.7,3.1,-1.9,4.6,-2.8c2,-2.9,1.1,-6.2,0.9,-9.2c-0.3,-4.7,1.9,-5.5,5.7,-4.7c10.8,2.2,21.6,4.6,32.5,6.9C672,778.7,672,780.3,672,782z  "
+        android:fillColor="#E6E4E4"/>
+    <path
+        android:pathData="M350,872c-9.9,-1.1,-19.4,-3.9,-29.1,-6C277,856,233.1,846,189.2,836c-39.4,-9,-78.7,-18,-118.1,-27  c-2,-0.4,-4.5,-0.2,-5.2,-3c0,-85.7,0,-171.4,0.1,-257.1c6.5,0.1,12.7,2.3,19,3.6c26.4,5.4,52.8,10.9,79.2,16.5c25.9,5.4,51.8,11,77.7,16.4  c26.2,5.5,52.5,11,78.7,16.5c30.1,6.3,60.2,12.6,90.3,19c0.3,68.9,0.7,137.8,1,206.7c0.1,17.1,0,34.3,0,51.4c0,5.9,0,6,-5.9,4.9  c-5.7,-1.1,-11.2,-3.2,-17.1,-2.9c0,0,0,0,0,0c-7,-3.1,-14.2,-5.4,-22,-5c0,0,0,0,0,0c-3.9,-2.9,-8.4,-2.9,-13,-3c0,0,0,0,0,0  C352.9,871.5,351.4,872.1,350,872z M177,687c0,3.2,0.1,6.3,0,9.5c-0.1,2.7,0.7,4,3.8,4.6c29.7,5.8,59.3,11.7,89,17.7  c2.4,0.5,4.7,0.1,4.9,-2.6c0.4,-3.7,1.2,-7.6,-0.6,-11.2c1,-3,1.2,-5.3,-3,-6c-29.6,-5.4,-59.2,-10.8,-88.7,-16.5C177.7,681.6,176.5,682.8,177,687  z"
+        android:fillColor="#E5E5E5"/>
+    <path
+        android:pathData="M411,621c-30.1,-6.3,-60.2,-12.6,-90.3,-19c-26.2,-5.5,-52.5,-11,-78.7,-16.5c-25.9,-5.5,-51.8,-11,-77.7,-16.4  c-26.4,-5.5,-52.8,-11.1,-79.2,-16.5c-6.3,-1.3,-12.5,-3.5,-19,-3.6c0,-23.6,0,-47.3,0,-70.9c12,-0.9,23.2,3.3,34.7,5.5c24.5,4.6,49,9.7,73.5,14.6  c1,0.2,1.9,0.6,2.8,1c0,3.3,0.7,6.7,0.7,9.9c0,5.6,2.4,7.5,7.5,8.4c15.3,2.7,30.5,5.8,45.8,8.7c12,2.3,24,4.4,36.1,6.6  c1.9,0.3,4.8,1.5,4.7,-1.4c-0.2,-4.6,1.7,-8.2,3.3,-12.1c10.2,0.9,20,3.8,30.1,5.5c11.7,2,23.1,5.3,34.9,6.5  c14.5,-3.2,28.1,-8.9,42.2,-13.2c8.1,-2.5,16.2,-5,24.1,-7.8c4.1,-1.5,4.8,-0.1,4.7,3.7C411,549.4,411,585.2,411,621z"
+        android:fillColor="#D9D9D9"/>
+    <path
+        android:pathData="M412,307c-6.9,3.5,-13.6,-0.2,-20.1,-1.3c-28.2,-5,-56.2,-10.4,-84.3,-15.8c-21.8,-4.1,-43.5,-8.5,-65.2,-12.6  c-27.3,-5.2,-54.6,-10.1,-81.9,-15.3c-22.1,-4.2,-44.1,-8.5,-66.2,-12.8c-7,-1.3,-13.9,-2.9,-21,-3.9c-4.1,-0.6,-5.2,-3,-5.3,-6.5c0,-1.2,0,-2.3,0,-3.5  c0,-51.1,0,-102.3,0,-153.4c0,-5,-0.3,-10,1,-14.9c24.1,4.3,48.1,8.6,72.2,12.8c24.9,4.4,49.8,8.7,74.6,13.1c31.9,5.6,63.7,11.3,95.6,16.9  c30.2,5.3,60.5,10.6,90.7,16c2.6,0.5,5.2,0.8,7.8,1.2c0,1.3,0.7,2,2,2C412,188.3,412,247.7,412,307z M409,217.4c0,-25.5,0,-51,0,-76.5  c0,-10.9,0.1,-11.2,-10.7,-13.2c-23.4,-4.4,-46.8,-8.5,-70.3,-12.6c-24.1,-4.3,-48.2,-8.4,-72.3,-12.6c-17.7,-3.1,-35.5,-6.3,-53.2,-9.4  c-22.1,-3.9,-44.3,-7.6,-66.4,-11.5c-20,-3.5,-40,-7.1,-60.1,-10.5c-6,-1,-6.1,-0.8,-6.1,5.6c0,53,0,105.9,0,158.9c0,1,0,2,0,3  c0.2,2.6,1,4.1,4,4.6c10.1,1.7,20.1,3.9,30.2,5.8c27.3,5.1,54.6,10.1,81.9,15.2c22.1,4.2,44.1,8.6,66.2,12.8  c27.3,5.2,54.6,10.2,81.9,15.3c22.7,4.3,45.5,8.6,68.2,12.8c6.5,1.2,6.5,1.1,6.5,-5.7C409,272,409,244.7,409,217.4z"
+        android:fillColor="#E8E8E8"/>
+    <path
+        android:pathData="M412,129c-1.3,0,-2,-0.7,-2,-2C411.3,127,412,127.7,412,129z"
+        android:fillColor="#EAEAEA"/>
+    <path
+        android:pathData="M65.8,248.3c10.1,0.7,19.8,3.5,29.7,5.2c19,3.3,38,7,57,10.6c15.9,3,31.8,6.2,47.8,9.2  c24,4.6,48,9.1,72,13.7c14.6,2.8,29.3,5.5,43.8,8.5c22.5,4.6,45.3,8.4,67.9,12.6c4.1,0.8,8.2,1.8,12.4,3.8  c-6.4,2.1,-12.8,4.3,-19.2,6.4c-9.5,3.1,-19.1,5.8,-28.4,9.4c-6.7,2.6,-13.3,2.5,-20,1.3c-41.4,-7.6,-82.8,-15.2,-124.2,-22.8  c-32.6,-6,-65.2,-11.9,-97.8,-17.8c-13.7,-2.5,-27.4,-4.9,-41.1,-7.4C65.9,270,65.9,259.1,65.8,248.3z"
+        android:fillColor="#E6A3A3"/>
+    <path
+        android:pathData="M275,519c-1.5,3.9,-3.5,7.5,-3.3,12.1c0.1,2.9,-2.8,1.7,-4.7,1.4c-12,-2.2,-24.1,-4.3,-36.1,-6.6  c-15.3,-2.9,-30.5,-6,-45.8,-8.7c-5.1,-0.9,-7.5,-2.8,-7.5,-8.4c0,-3.2,-0.8,-6.5,-0.7,-9.9c12.2,2.3,24.4,4.6,36.5,7c19.8,3.9,39.5,8,59.3,12  C273.6,518.2,274.3,518.7,275,519z"
+        android:fillColor="#CBCBCA"/>
+    <path
+        android:pathData="M202.9,345.9c0,3.3,-0.1,6.7,0.1,10c0.2,3.9,-1.4,4.3,-4.9,3.7c-29.9,-5.6,-59.8,-11.2,-89.8,-16.5  c-3,-0.5,-3.5,-1.7,-3.4,-4.2c0.1,-3.3,0.1,-6.7,0.1,-10c21.7,3.9,43.4,7.9,65.2,11.6C181.1,342.4,191.8,345.3,202.9,345.9z"
+        android:fillColor="#CFCFCE"/>
+    <path
+        android:pathData="M65.8,248.3c0,10.9,0,21.7,0,32.6c-17.9,-3.1,-35.8,-6.2,-53.7,-9.3c0,-0.4,0,-0.7,0,-1.1  C30.1,263,48,255.6,65.8,248.3z"
+        android:fillColor="#E57474"/>
+    <path
+        android:pathData="M202.9,345.9c-11.1,-0.6,-21.8,-3.5,-32.6,-5.4c-21.8,-3.7,-43.5,-7.7,-65.2,-11.6c-0.6,-3.8,0.6,-5.6,4.8,-4.8  c28.3,5,56.6,9.9,84.9,14.9c1.1,0.2,2.3,0.8,3.4,0.8C202.8,339.6,203.4,342.3,202.9,345.9z"
+        android:fillColor="#BDBDBD"/>
+    <path
+        android:pathData="M367,876c7.8,-0.4,15,1.9,22,5C381.4,880.3,374.2,878.4,367,876z"
+        android:fillColor="#EFEFEE"/>
+    <path
+        android:pathData="M354,873c4.5,0.1,9.1,0.1,13,3C362.5,875.6,358.1,875,354,873z"
+        android:fillColor="#EFEFEE"/>
+    <path
+        android:pathData="M350,872c1.4,0.1,3,-0.5,4,1C352.6,872.9,351,873.5,350,872z"
+        android:fillColor="#EFEFEE"/>
+    <path
+        android:pathData="M274.1,705c1.9,3.6,1,7.5,0.6,11.2c-0.3,2.8,-2.5,3.1,-4.9,2.6c-29.7,-5.9,-59.3,-11.9,-89,-17.7  c-3.1,-0.6,-3.9,-1.9,-3.8,-4.6c0.1,-3.2,0,-6.3,0,-9.5c1.2,0,2.4,-0.1,3.5,0.1c19.2,3.8,38.4,7.7,57.6,11.4  C250.1,700.8,261.9,703.8,274.1,705z"
+        android:fillColor="#D6D6D5"/>
+    <path
+        android:pathData="M274.1,705c-12.1,-1.2,-24,-4.2,-35.9,-6.5c-19.2,-3.7,-38.4,-7.6,-57.6,-11.4c-1.1,-0.2,-2.3,-0.1,-3.5,-0.1  c-0.5,-4.2,0.7,-5.4,5.3,-4.5c29.5,5.7,59.1,11.1,88.7,16.5C275.3,699.7,275.1,702,274.1,705z"
+        android:fillColor="#C9C9C8"/>
+    <path
+        android:pathData="M409,217.4c0,27.3,0,54.6,0,82c0,6.8,0,6.9,-6.5,5.7c-22.7,-4.2,-45.5,-8.6,-68.2,-12.8  c-27.3,-5.1,-54.6,-10.1,-81.9,-15.3c-22.1,-4.2,-44.1,-8.6,-66.2,-12.8c-27.3,-5.2,-54.6,-10.1,-81.9,-15.2c-10.1,-1.9,-20.1,-4.1,-30.2,-5.8  c-3,-0.5,-3.9,-2.1,-4,-4.6c-0.1,-1,0,-2,0,-3c0,-53,0,-105.9,0,-158.9c0,-6.4,0,-6.6,6.1,-5.6c20,3.4,40,7,60.1,10.5c22.1,3.9,44.3,7.6,66.4,11.5  c17.7,3.1,35.5,6.3,53.2,9.4c24.1,4.2,48.2,8.4,72.3,12.6c23.4,4.1,46.9,8.2,70.3,12.6c10.8,2,10.7,2.3,10.7,13.2  C409,166.4,409,191.9,409,217.4z M283.9,146.9c0.4,-3.2,-0.2,-5.3,-4,-6c-29.7,-5,-59.4,-9.9,-89,-15.3c-4.8,-0.9,-5.2,0.7,-4.8,4.4  c0,3.5,-0.1,7,0,10.5c0,1.3,-0.4,3.2,1.4,3.3c2.9,0.1,5.3,1.8,8.1,2.3c13.8,2.4,27.6,4.9,41.4,7.4c13.3,2.4,26.5,5.1,39.8,7.4  c6.6,1.2,7.3,0.4,7.3,-6.5C284,151.9,283.9,149.4,283.9,146.9z"
+        android:fillColor="#E8E7E7"/>
+    <path
+        android:pathData="M283.9,146.9c0,2.5,0.1,5,0.1,7.5c0,6.9,-0.7,7.7,-7.3,6.5c-13.3,-2.4,-26.5,-5,-39.8,-7.4  c-13.8,-2.5,-27.6,-5.1,-41.4,-7.4c-2.8,-0.5,-5.2,-2.2,-8.1,-2.3c-1.8,-0.1,-1.4,-2,-1.4,-3.3c0,-3.5,0,-7,0,-10.5c1.9,0.3,3.9,0.7,5.8,1  c21.6,4,43.1,8.1,64.7,11.8C265.6,144.4,274.5,147.1,283.9,146.9z"
+        android:fillColor="#CFCFCE"/>
+    <path
+        android:pathData="M283.9,146.9c-9.3,0.2,-18.3,-2.5,-27.3,-4.1c-21.6,-3.7,-43.1,-7.8,-64.7,-11.8c-1.9,-0.4,-3.9,-0.7,-5.8,-1  c-0.4,-3.6,-0.1,-5.2,4.8,-4.4c29.6,5.4,59.3,10.4,89,15.3C283.7,141.6,284.3,143.7,283.9,146.9z"
+        android:fillColor="#BDBDBD"/>
+</vector>
diff --git a/packages/DocumentsUI/res/drawable/hourglass.xml b/packages/DocumentsUI/res/drawable/hourglass.xml
new file mode 100644
index 0000000..9b8d0e2
--- /dev/null
+++ b/packages/DocumentsUI/res/drawable/hourglass.xml
@@ -0,0 +1,168 @@
+<!--
+Copyright (C) 2016 The Android Open Source Project
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="421dp"
+        android:height="909dp"
+        android:viewportWidth="421.0"
+        android:viewportHeight="909.0">
+    <path
+        android:pathData="M36,122.9c-2.8,-2.6,-5.7,-5.1,-8.3,-7.8c-5.6,-6,-9.2,-12.9,-8.8,-21.5c0.3,-7.5,0.6,-15,-0.1,-22.5   c-1.2,-14.1,5.5,-23.9,16,-31.9c16.7,-12.8,36.1,-19.6,56.1,-25.1c23.8,-6.5,48,-10.2,72.5,-12.3C168.6,1.3,174,2.2,179,0   c19.3,0,38.7,0,58,0c6,2.1,12.4,1.3,18.6,1.8c30.2,2.7,59.9,7.6,88.5,17.5c16.5,5.7,32.6,12.6,45.2,25.4c6.5,6.6,10.3,14,9.8,23.6   c-0.4,7.8,-0.5,15.7,0,23.4c0.6,10.3,-3.4,18.4,-10.6,25.2c-2.2,2,-4.4,4,-6.6,6c-3,2,-6.1,4,-9.1,5.9c-9.2,4.5,-18.5,9,-28.2,12.3   c-42.4,14.5,-86.3,18.8,-130.8,19.6c-10.9,0.2,-21.9,-0.4,-32.9,-0.7c-4.6,-0.4,-9.2,-0.8,-13.9,-1.1c-18.9,-1.1,-37.5,-3.9,-56,-7.6   c-15.3,-3.1,-30.2,-7.6,-44.9,-12.6c-7.6,-3.7,-15.3,-7.4,-22.9,-11.1C41.1,125.8,38.6,124.3,36,122.9z M41,72c2.9,6.9,7.1,12.6,13.1,17.2   c13,10,27.9,15.8,43.4,20.6c28.2,8.8,57.2,12.8,86.5,14c31.8,1.2,63.7,0.8,95.3,-4.6c25.3,-4.4,50.1,-10,72.9,-22.2   c10.8,-5.8,20,-13.1,24.7,-24.9c2.3,-11,-2.3,-19.5,-10.2,-26.4c-10.5,-9.2,-23.1,-14.9,-36.2,-19.6C295.2,13.4,258.4,9.7,221.2,8.1   c-11.1,-0.5,-22.2,0,-33.4,0.6c-21.4,1,-42.6,3.1,-63.6,7.3c-22.2,4.5,-44.1,10.3,-63.4,22.6C48.9,46.3,38.4,55.4,41,72z"
+        android:fillColor="#9F9F9F"/>
+    <path
+        android:pathData="M0,829c3.7,-2.8,4.7,-7.6,7.8,-10.9c2.6,-2.8,4.9,-5.7,9.2,-7.6c0,3.4,-0.1,6.5,0,9.5c0,1.5,-0.7,3.5,1.7,4   c0.4,3.3,1.4,6.4,2.9,9.4c3.8,7.7,10,13,16.8,17.9c9.2,6.7,19.7,10.8,29.8,15.5c-0.7,2.4,1.3,0.7,1.8,1.1l0,0c1.5,2.1,3.7,2.2,6,2   l0,0c0.8,0.6,1.5,1.4,2.4,1.7c9.5,2.7,18.9,5.8,28.7,7.4c3.6,0.6,7,3.5,10.9,1.1c2.4,0.4,4.8,0.8,7.1,1.2c0.2,1.5,1.3,1.6,2.5,1.8   c6.6,0.9,13.3,2.4,19.9,2.8c5.1,0.3,10.3,2.9,15.4,0.3c0.4,0,0.8,0.1,1.1,0.1c0.3,2.2,2.1,1.8,3.5,1.8c3.8,0,7.6,0,11.5,0   c1.1,1.4,2.7,1,4.1,1c17.2,0,34.5,0,51.7,0c1.4,0,3,0.4,4.1,-1c3.8,0,7.6,0,11.5,0c1.4,0,3.2,0.4,3.5,-1.8c9.4,-1,18.7,-2.1,28.1,-3.1   c6,1.3,11.6,0.4,16.9,-2.8c21.2,-4.2,42.1,-9.3,61.8,-18.4c15.8,-7.3,30.8,-15.8,38,-33.1c2.4,-2,1.9,-4.8,2.2,-7.4c0.3,-3,0,-6,0.1,-9   c0,-1,-0.3,-2.1,0.7,-2.7c1.1,-0.7,1.7,0.5,2.5,1c7.2,5,12.1,11.7,14.8,20c0.4,1.2,0.6,2.2,2.1,2.3c0,3.3,0,6.7,0,10   c-1.5,0,-1.8,1.1,-2.2,2.2c-3.8,10.2,-11.2,17.5,-20.1,23.3c-20.8,13.6,-44.1,21.2,-68.1,26.7c-29.2,6.7,-58.7,11,-88.7,11.7   c-1.6,0,-3.5,-0.5,-3.9,2c-18.7,0,-37.3,0,-56,0c-0.3,-2.5,-2.3,-1.9,-3.9,-2c-5.6,-0.1,-11.2,-0.5,-16.9,-0.8c-18.5,-1.2,-36.8,-3.8,-55,-7.3   C79.9,893.9,54,887,30.2,874C19,867.9,8.5,860.9,2.5,849C2,848,1.4,847,0,847C0,841,0,835,0,829z"
+        android:fillColor="#E6E4E4"/>
+    <path
+        android:pathData="M372.9,128.9c3,-2,6.1,-4,9.1,-5.9c-0.2,2.7,0.2,5.4,1,8c-1.5,1.6,-0.3,1.8,1,2c0.3,1,0.7,2,1,3   c-1.5,1.6,-0.3,1.8,1,2c0.7,2,1.3,4,2,6c-1.5,1.6,-0.3,1.8,1,2c0.3,1.7,0.7,3.3,1,5c-1,2.3,-0.6,4.1,2,5c4.9,23.8,9,47.6,8,72   c-3.5,1.5,-2.1,3.8,-1,6c-1,6,-2,12,-3,18c-1.3,1,-1.3,2,0,3c0,0.7,0,1.3,0,2c-2.1,0.4,-2.6,1.3,-1,3c0,0.3,0,0.7,0,1   c-1.3,0.2,-2.5,0.4,-1,2c0.4,2.1,-0.7,4,-1,6c-1.3,0.2,-2.5,0.4,-1,2c-3.7,9.3,-7.3,18.7,-11,28c-2.7,1.2,-4.2,2.9,-3,6   c-3.4,6.9,-7.8,13.3,-12.2,19.5c-6.1,8.6,-12.4,17.3,-19.4,25.2c-7.3,8.3,-15.5,15.8,-23.9,23c-11.9,10.3,-24.9,19.3,-38.1,27.7   c-12.2,7.8,-25.4,14.1,-38.4,20.5c-12.1,6,-18.5,15.8,-21,28.6c-1.5,7.8,-0.5,15.4,2,22.8c1.2,3.5,3.7,6.1,5.6,9.2   c5.4,8.6,14.8,10.5,22.6,15c15.3,9,30.8,17.7,45.3,28.1c14.4,10.4,28.2,21.5,40.5,34.1c10.1,10.4,18.5,22.2,26.8,34.3   c6.5,9.5,11.3,19.6,16.1,29.8c-1.5,1.6,-0.3,1.8,1,2c0.7,2,1.3,4,2,6c-1,2.3,-0.6,4.1,2,5c0.7,1.2,1.2,2.5,1,4c-1,2.3,-0.6,4.1,2,5   c1.2,12.3,4.6,24.1,5.7,36.5c0.8,8.4,1.4,16.8,1,25.1c-0.3,5.9,-1.1,12,-1.9,18c-1.2,8.7,-2.3,17.4,-4.2,25.9   c-1.5,6.6,-3.7,13.1,-5.6,19.6c-1.8,3.1,-2.9,6.5,-3.9,9.9c-3.4,6.4,-5.6,13.6,-11.9,18.2c-0.1,-3.8,1.6,-7.1,3,-10.4   c8.7,-20.9,13,-42.8,14.7,-65.1c1,-12.9,0.2,-25.8,-1.7,-38.7c-2.7,-18.5,-7.8,-36.2,-15.8,-53.1c-7.3,-15.4,-16.8,-29.3,-27.7,-42.4   c-2.7,-3.2,-6.3,-5.7,-9.6,-8.6c0.4,0.8,0.7,1.4,1,1.9c0.7,1.1,1.5,2.2,2.3,3.3c16.5,21.5,28.5,45.2,34.2,71.7c0.7,3.3,3.1,6.9,0.3,10.4   c-1,-1.9,-2.1,-3.7,-3.1,-5.6c-3.3,-6.3,-6.1,-12.9,-11.7,-17.6c-0.4,-0.9,-0.8,-1.8,-1.3,-2.6c-4,-6.3,-10.4,-10.4,-14.8,-16.2c0,-5.4,-2.7,-9.9,-4.8,-14.5   c-8.4,-18.6,-20.4,-34.9,-32.9,-50.8c-8.4,-10.8,-15.5,-22.8,-28.7,-28.8c-5.3,-2.4,-10,-6,-15.1,-8.6c-5.1,-2.6,-9.9,-6.4,-16.3,-5.2   c-5.2,1,-10.4,2.1,-15.3,4.1c-29.3,11.9,-48.4,34.1,-61.8,61.9c-0.3,0.3,-0.7,0.6,-1,1c-7.1,0.8,-13.9,2.9,-20.7,5.1   c-32.6,10.6,-61,27.4,-82.3,54.9c-9.2,11.6,-15.4,24.7,-18.9,39c-1.5,-1.1,-1.1,-2.7,-1.1,-4.1c-0.1,-9.6,0.3,-19.2,1.8,-28.7   c3.7,-22.6,11.8,-43.5,24,-62.8c12.6,-20,28.6,-36.9,47,-51.7c21.3,-17.3,44.6,-31.3,69.3,-42.9c14.5,-6.8,20,-18.8,21.8,-33.1   c1.8,-13.3,-4.9,-24.1,-12.2,-34.4c-3.6,-5,-7.4,-9.8,-13.2,-12.5c-5.8,-2.8,-11.6,-5.7,-17.4,-8.7c-22,-11.6,-42.6,-25.1,-61.1,-41.7   c-20.7,-18.6,-37.9,-40,-48.8,-65.9c-6.7,-15.7,-10.9,-32.1,-12,-49c-1.8,-27.1,2.1,-53.6,11.7,-79.1c3.8,-10.1,7.1,-20.4,13.3,-29.4   c14.7,5,29.6,9.5,44.9,12.6c18.5,3.7,37.2,6.5,56,7.6c4.6,0.3,9.3,0.7,13.9,1.1c0.2,3.6,-1.5,6.8,-2.6,10   c-11.9,33.6,-17.8,68.2,-17.2,103.8c0.2,9.7,1.3,19.4,2.7,29.1c3.8,24.6,11.4,47.7,26,68.1c12.2,17.1,28.4,28.6,49,33.4   c4.7,1.1,9.5,2.2,14.5,-0.5c18.7,-10.2,36.8,-21.2,53.7,-34.2c15.4,-11.9,29.4,-25.3,41.5,-40.5c12.9,-16.2,23.4,-33.8,30.4,-53.4   c6.4,-17.6,10.3,-35.6,10.9,-54.3c0.5,-15.9,0.2,-31.9,-3,-47.6C383.8,158.7,380.1,143.3,372.9,128.9z"
+        android:fillColor="#EDECEC"/>
+    <path
+        android:pathData="M383,780c1.1,-3.4,2.1,-6.8,3.9,-9.9c7.8,7.1,12.8,15.2,12.2,26.4c-0.6,10.7,-0.3,21.5,-0.5,32.3   c-7.2,17.3,-22.2,25.8,-38,33.1c-19.7,9.1,-40.6,14.1,-61.8,18.4c-5.6,0.9,-11.3,1.9,-16.9,2.8c-9.4,1,-18.7,2.1,-28.1,3.1   c-5,0.3,-9.9,0.7,-14.9,1c-20,1.2,-40,0.9,-60,0c-5,-0.3,-9.9,-0.7,-14.9,-1c-0.4,0,-0.8,-0.1,-1.1,-0.1c-12.6,-1.6,-25.2,-3.2,-37.9,-4.8   c-2.4,-0.4,-4.8,-0.8,-7.1,-1.2c-2.6,-0.6,-5.1,-1.3,-7.7,-1.8c-11.6,-2,-22.6,-6.3,-34.2,-8.4c0,0,0,0,0,0c-1.7,-1.6,-3.7,-2.2,-6,-2c0,0,0,0,0,0   c-0.2,-1,-1.1,-0.9,-1.8,-1.1c-10.2,-4.7,-20.6,-8.8,-29.8,-15.5c-6.8,-4.9,-13,-10.2,-16.8,-17.9c-1.5,-3,-2.4,-6.1,-2.9,-9.4   c0.1,-10.3,0,-20.6,0.2,-30.9c0.1,-8.3,3.5,-15,10,-20.2c2,3.5,3.4,7.1,4.1,11c-1.1,0.8,-1,2,-1.1,3.1c-0.6,8.2,2.9,14.9,8.3,20.6   c8.9,9.6,20.4,15.4,32.3,20.2c17.9,7.2,36.5,11.9,55.4,15.4c20.1,3.7,40.3,5.7,60.6,6.5c21.4,0.8,42.8,0.4,64.2,-1.6   c19.8,-1.9,39.4,-4.6,58.7,-9.3c19.9,-4.8,39.3,-11.2,56.4,-22.9c7.8,-5.3,15.2,-11.3,17.4,-21C386.4,790.1,388.3,784.3,383,780z"
+        android:fillColor="#9F9F9F"/>
+    <path
+        android:pathData="M378,305c-1.2,-3.1,0.3,-4.8,3,-6C380.6,301.3,379.7,303.3,378,305z"
+        android:fillColor="#F1F0F0"/>
+    <path
+        android:pathData="M399,234c-1.1,-2.2,-2.5,-4.5,1,-6C399.7,230,400.8,232.2,399,234z"
+        android:fillColor="#F1F0F0"/>
+    <path
+        android:pathData="M392,156c-2.6,-0.9,-3,-2.7,-2,-5C391.4,152.4,391.6,154.2,392,156z"
+        android:fillColor="#F1F0F0"/>
+    <path
+        android:pathData="M389,636c-2.6,-0.9,-3,-2.7,-2,-5C388.4,632.4,388.6,634.2,389,636z"
+        android:fillColor="#F1F0F0"/>
+    <path
+        android:pathData="M392,645c-2.6,-0.9,-3,-2.7,-2,-5C391.4,641.4,391.6,643.2,392,645z"
+        android:fillColor="#F1F0F0"/>
+    <path
+        android:pathData="M396,255c-1.3,-1,-1.3,-2,0,-3C397.3,253,397.3,254,396,255z"
+        android:fillColor="#F1F0F0"/>
+    <path
+        android:pathData="M395,260c-1.6,-1.7,-1.1,-2.6,1,-3C395.7,258,395.3,259,395,260z"
+        android:fillColor="#F1F0F0"/>
+    <path
+        android:pathData="M384,133c-1.3,-0.2,-2.5,-0.4,-1,-2C383.8,131.4,384,132.2,384,133z"
+        android:fillColor="#F1F0F0"/>
+    <path
+        android:pathData="M385,625c-1.3,-0.2,-2.5,-0.4,-1,-2C384.8,623.4,385,624.2,385,625z"
+        android:fillColor="#F1F0F0"/>
+    <path
+        android:pathData="M392,271c-1.5,-1.6,-0.3,-1.8,1,-2C393,269.8,392.8,270.6,392,271z"
+        android:fillColor="#F1F0F0"/>
+    <path
+        android:pathData="M394,263c-1.5,-1.6,-0.3,-1.8,1,-2C395,261.8,394.8,262.6,394,263z"
+        android:fillColor="#F1F0F0"/>
+    <path
+        android:pathData="M386,138c-1.3,-0.2,-2.5,-0.4,-1,-2C385.8,136.4,386,137.2,386,138z"
+        android:fillColor="#F1F0F0"/>
+    <path
+        android:pathData="M389,146c-1.3,-0.2,-2.5,-0.4,-1,-2C388.8,144.4,389,145.2,389,146z"
+        android:fillColor="#F1F0F0"/>
+    <path
+        android:pathData="M33.1,783.9c-0.8,-3.9,-2.2,-7.6,-4.1,-11c-3.7,-11.7,-7.2,-23.5,-9.2,-35.5c-1.3,-7.6,-1.9,-15.4,-2.7,-23.2   c-0.6,-6.2,-0.9,-12.4,-1,-18.5c-0.2,-7.9,1.9,-15.7,2.3,-23.4c0.5,-10.1,2.9,-19.5,5.5,-29c6.3,-23.1,17.3,-43.9,31.5,-62.8   c23.4,-31.1,53.3,-54.7,86.9,-74.1c10.1,-5.8,20.3,-11.6,30.9,-16.2c11.7,-5.2,16.3,-14.9,18.8,-26.3c3.2,-14.9,-2.8,-26.6,-12.7,-37.1   c-1.8,-1.9,-3.9,-3.1,-6.2,-4.2c-23.7,-11.4,-46.4,-24.4,-67.2,-40.7c-16.2,-12.6,-31.3,-26.5,-44.2,-42.6c-16.2,-20.3,-28.8,-42.5,-36.2,-67.5   c-3.1,-10.6,-5.4,-21.3,-6.2,-32.4c-0.7,-9.6,-3.2,-19.3,-2,-28.8c0.8,-6.6,1.5,-13.4,1.9,-20c1,-15.2,4.9,-29.6,9.2,-44c1.7,-5.7,4,-11.3,6.4,-16.8   c0.9,-2.2,1.3,-4.4,1.3,-6.8c2.6,1.4,5.1,2.9,7.2,4.9c-0.6,0.8,-1.4,1.4,-1.8,2.3c-11.2,26.2,-16.2,53.8,-17.4,82.2   c-0.4,8.8,1,17.5,1.9,26.2c2,19.7,7.4,38.4,15.6,56.3c17.9,39.2,46.6,69.1,81.3,93.6c18.5,13.1,38.1,24.3,58.5,34.1   c3.3,1.6,6,3.7,8.1,6.5c8,10.6,12.6,22.1,9.6,35.7c-2.1,9.4,-6.5,17.9,-14.8,22.7c-8.2,4.7,-16.9,8.5,-25.3,12.9   c-22.5,12,-43.8,25.8,-62.6,43c-21.9,19.9,-40.8,42.1,-53.7,69.2C26.3,646.5,20.6,682,24.1,719c1.8,18.7,7,36.7,12.7,54.6   c5.9,18.7,18.2,30.9,35.3,38.9c15.4,7.2,31.5,12.2,48.1,15.8c1.6,0.4,4.3,-0.3,4.8,2.6c-18,-2.8,-35.4,-7.5,-52.3,-14.5   c-12.2,-5.1,-23.4,-11.4,-32.4,-21.4C37.2,791.7,36.5,787,33.1,783.9z"
+        android:fillColor="#EDECEC"/>
+    <path
+        android:pathData="M372.9,128.9c7.2,14.3,10.9,29.8,14.1,45.4c3.2,15.7,3.5,31.6,3,47.6c-0.6,18.7,-4.6,36.7,-10.9,54.3   c-7.1,19.6,-17.6,37.2,-30.4,53.4c-12.1,15.2,-26.1,28.6,-41.5,40.5c-16.9,13,-35,24,-53.7,34.2c-5,2.7,-9.8,1.6,-14.5,0.5   c-20.6,-4.8,-36.8,-16.3,-49,-33.4c-14.6,-20.4,-22.3,-43.5,-26,-68.1c-1.5,-9.7,-2.6,-19.4,-2.7,-29.1c-0.6,-35.6,5.4,-70.2,17.2,-103.8   c1.2,-3.3,2.8,-6.4,2.6,-10c11,0.2,21.9,0.9,32.9,0.7c44.4,-0.8,88.4,-5.2,130.8,-19.6C354.4,137.9,363.7,133.5,372.9,128.9z"
+        android:fillColor="#F1F0F0"/>
+    <path
+        android:pathData="M377,72c-4.6,11.9,-13.9,19.1,-24.7,24.9c-22.9,12.2,-47.6,17.9,-72.9,22.2c-31.6,5.4,-63.5,5.9,-95.3,4.6   c-29.3,-1.1,-58.3,-5.1,-86.5,-14C82.1,105,67.2,99.1,54.2,89.2C48.2,84.6,43.9,78.8,41,72c3.9,-1.8,4.6,-6.2,7.3,-9   c10.3,-10.5,22.9,-16.9,36.5,-21.8c19.7,-7.1,40,-11.5,60.7,-14.5c19.4,-2.8,38.9,-3.9,58.4,-4.5c17.9,-0.6,35.8,0.8,53.6,2.8   c15,1.6,29.9,3.5,44.5,7.1c20,4.9,39.7,10.7,57.2,22.1C366.5,58.8,371.5,65.5,377,72z"
+        android:fillColor="#8D8E8E"/>
+    <path
+        android:pathData="M125,831c-0.4,-2.9,-3.2,-2.3,-4.8,-2.6c-16.6,-3.7,-32.7,-8.7,-48.1,-15.8c-17.1,-8,-29.4,-20.2,-35.3,-38.9   c-5.7,-17.9,-10.9,-35.9,-12.7,-54.6c-3.6,-37.1,2.1,-72.5,18.4,-106.4c13,-27.1,31.9,-49.3,53.7,-69.2c18.8,-17.1,40.2,-30.9,62.6,-43   c8.4,-4.5,17.1,-8.2,25.3,-12.9c8.4,-4.8,12.7,-13.3,14.8,-22.7c3.1,-13.6,-1.6,-25.1,-9.6,-35.7c-2.1,-2.8,-4.8,-4.9,-8.1,-6.5   c-20.4,-9.9,-40,-21.1,-58.5,-34.1c-34.7,-24.6,-63.4,-54.5,-81.3,-93.6c-8.2,-17.9,-13.6,-36.6,-15.6,-56.3c-0.9,-8.7,-2.3,-17.5,-1.9,-26.2   c1.2,-28.3,6.2,-55.9,17.4,-82.2c0.4,-0.9,1.2,-1.5,1.8,-2.3c7.6,3.7,15.3,7.4,22.9,11.1c-6.2,9,-9.5,19.3,-13.3,29.4   c-9.6,25.5,-13.5,52,-11.7,79.1c1.1,16.9,5.4,33.3,12,49c11,25.9,28.1,47.4,48.8,65.9c18.5,16.6,39.1,30.2,61.1,41.7   c5.7,3,11.5,5.9,17.4,8.7c5.8,2.8,9.7,7.6,13.2,12.5c7.3,10.3,14,21.1,12.2,34.4c-1.9,14.3,-7.4,26.3,-21.8,33.1   c-24.7,11.6,-48,25.7,-69.3,42.9c-18.3,14.9,-34.3,31.7,-47,51.7c-12.2,19.3,-20.3,40.2,-24,62.8c-1.6,9.6,-2,19.1,-1.8,28.7   c0,1.4,-0.4,3.1,1.1,4.1c-0.6,14.9,0.1,29.8,3,44.5c4.2,21.4,9.1,42.6,26,58.4c-0.2,2.3,0.9,4.1,2.2,5.9c8.8,12.3,22,18.6,35.3,24.1   c26.4,10.9,54.2,16.1,82.4,19.1c1.7,0.2,3,0.4,3,2.4c-4.5,0.7,-9,0.9,-13.4,0.5c-13.2,-1,-26.5,-1.9,-39.6,-4.1c-0.6,-2.4,-1.7,-2.3,-3.1,-0.6   c-1.3,-0.1,-2.6,-0.3,-3.9,-0.4c-0.6,-2.3,-1.6,-2.4,-3.1,-0.7c-0.6,-0.1,-1.3,-0.2,-1.9,-0.3c-0.6,-2.4,-1.7,-2.4,-3.1,-0.6   C126.2,831.2,125.6,831.1,125,831z"
+        android:fillColor="#E8E8E7"/>
+    <path
+        android:pathData="M377,72c-5.4,-6.4,-10.5,-13.2,-17.7,-17.9C341.7,42.7,322.1,36.9,302,32c-14.6,-3.6,-29.5,-5.5,-44.5,-7.1   c-17.8,-1.9,-35.7,-3.3,-53.6,-2.8c-19.5,0.6,-39,1.7,-58.4,4.5c-20.7,3,-41,7.4,-60.7,14.5C71.3,46.1,58.7,52.4,48.4,63   c-2.7,2.8,-3.5,7.2,-7.3,9c-2.6,-16.6,7.9,-25.6,19.8,-33.3c19.3,-12.3,41.2,-18.2,63.4,-22.6c21,-4.2,42.2,-6.3,63.6,-7.3   c11.1,-0.5,22.3,-1,33.4,-0.6c37.2,1.5,74,5.3,109.4,17.9c13.1,4.6,25.6,10.4,36.2,19.6C374.7,52.5,379.3,61,377,72z"
+        android:fillColor="#808080"/>
+    <path
+        android:pathData="M179,887.2c20,0.9,40,1.2,60,0c0,0.3,0,0.5,0,0.8c-1.1,1.4,-2.7,1,-4.1,1c-17.2,0,-34.5,0,-51.7,0   c-1.4,0,-3,0.4,-4.1,-1C179,887.7,179,887.5,179,887.2z"
+        android:fillColor="#F4F3F2"/>
+    <path
+        android:pathData="M76,869.9c11.6,2.2,22.6,6.5,34.2,8.4c2.6,0.4,5.2,1.2,7.7,1.8c-3.9,2.3,-7.3,-0.6,-10.9,-1.1   c-9.8,-1.6,-19.2,-4.7,-28.7,-7.4C77.5,871.3,76.8,870.5,76,869.9z"
+        android:fillColor="#F4F3F2"/>
+    <path
+        android:pathData="M125.1,881.3c12.6,1.6,25.2,3.2,37.9,4.8c-5.2,2.6,-10.3,0,-15.4,-0.3c-6.7,-0.4,-13.3,-1.9,-19.9,-2.8   C126.3,882.9,125.2,882.8,125.1,881.3z"
+        android:fillColor="#F4F3F2"/>
+    <path
+        android:pathData="M282,883.1c5.6,-0.9,11.3,-1.9,16.9,-2.8C293.7,883.4,288.1,884.4,282,883.1z"
+        android:fillColor="#F4F3F2"/>
+    <path
+        android:pathData="M179,887.2c0,0.3,0,0.5,0,0.8c-3.8,0,-7.6,0,-11.5,0c-1.4,0,-3.2,0.4,-3.5,-1.8C169,886.5,174,886.9,179,887.2z"
+        android:fillColor="#FFFFFF"/>
+    <path
+        android:pathData="M239,888c0,-0.3,0,-0.5,0,-0.8c5,-0.3,9.9,-0.7,14.9,-1c-0.3,2.2,-2.1,1.8,-3.5,1.8C246.6,888,242.8,888,239,888z"
+        android:fillColor="#FFFFFF"/>
+    <path
+        android:pathData="M70,867.9c2.3,-0.2,4.3,0.4,6,2C73.8,870.1,71.6,870,70,867.9z"
+        android:fillColor="#F4F3F2"/>
+    <path
+        android:pathData="M68.2,866.8c0.7,0.2,1.6,0.2,1.8,1.1C69.5,867.4,67.6,869.2,68.2,866.8z"
+        android:fillColor="#F4F3F2"/>
+    <path
+        android:pathData="M165,584c0.3,-0.3,0.7,-0.6,1,-1c10.5,-1.3,21,-3.3,31.5,-3.8c20.8,-1.1,41.4,0.7,61.7,5.4   c30.5,7,57.8,20.3,81.8,40.5c4.4,5.9,10.8,10,14.8,16.2c0.5,0.8,0.9,1.7,1.3,2.6c-2.4,4.1,-4.7,8.1,-7.1,12.2c-4,3.7,-6.3,8.9,-11,12   c-1.6,-0.1,-2.8,0.5,-4.1,1.5c-12.6,9.3,-26.7,15.6,-41.5,20.1c-31,9.4,-62.6,13.3,-95.1,11.6c-12.3,-0.6,-24.5,-1.5,-36.5,-3.4   c-4.5,-0.7,-5.3,0.5,-4.8,4.2c-0.5,0,-1,0.1,-1.5,0c-17.8,-3.9,-34.7,-10.3,-50.9,-18.7c-1,-0.5,-1.6,-1.1,-1.6,-2.3c2.3,-0.3,4.1,1.1,6.1,2   c14.2,6.2,28.9,10.6,44.1,13.3c2.5,0.4,3,0.2,2.4,-2.3c-2.5,-9.3,-3.7,-18.7,-4.8,-28.3c-1.5,-13.7,-0.3,-27.1,1.7,-40.5   C154.6,610.8,159.7,597.4,165,584z"
+        android:fillColor="#E57474"/>
+    <path
+        android:pathData="M103,681c0.1,1.1,0.7,1.8,1.6,2.3c16.2,8.4,33.1,14.8,50.9,18.7c0.5,0.1,1,0,1.5,0c0.6,0.4,1.3,0.7,1.9,1.1   c-0.1,2.7,1,5.2,1.9,7.6c6.5,17.8,16.5,33.6,27.5,48.8c12.5,17.1,27.1,32.1,45.1,43.6c6.6,4.2,13.7,7.3,20.5,11   c-15.6,2.8,-31.4,2.5,-47.1,2.4c-9.9,0,-19.9,-0.2,-29.8,-1.1c-18.2,-1.6,-36.2,-3.9,-54,-8.3c-18.1,-4.4,-35.9,-9.5,-51,-21.1   c-16.9,-15.8,-21.8,-37,-26,-58.4c-2.9,-14.7,-3.6,-29.6,-3,-44.5c3.5,-14.4,9.7,-27.4,18.9,-39c3.4,4.8,6.2,10.1,10.3,14.2   c6,6.1,11.6,13,19.7,16.8c0.5,0.8,1.2,1,2.1,1c0,0,0,0,0,0c0.3,0.3,0.7,0.7,1,1c0,0,0,0,0,0c0.3,0.3,0.7,0.7,1,1l0,0   c1,1.3,2.4,1.8,4,2l0,0C100.7,681.2,101.9,681,103,681L103,681z"
+        android:fillColor="#E6A3A3"/>
+    <path
+        android:pathData="M341,625c-24,-20.1,-51.3,-33.5,-81.8,-40.5c-20.3,-4.7,-41,-6.5,-61.7,-5.4c-10.5,0.6,-21,2.5,-31.5,3.8   c13.4,-27.8,32.5,-49.9,61.8,-61.9c4.9,-2,10.1,-3.1,15.3,-4.1c6.3,-1.2,11.2,2.6,16.3,5.2c5.2,2.6,9.9,6.2,15.1,8.6   c13.3,6,20.3,18,28.7,28.8c12.5,16,24.6,32.2,32.9,50.8C338.3,615.2,341,619.7,341,625z"
+        android:fillColor="#F1F0F0"/>
+    <path
+        android:pathData="M91.9,675c-8,-3.8,-13.6,-10.7,-19.7,-16.8c-4.1,-4.1,-6.9,-9.4,-10.3,-14.2c21.3,-27.5,49.8,-44.3,82.3,-54.9   c6.8,-2.2,13.6,-4.3,20.7,-5.1c-5.3,13.4,-10.4,26.9,-12.5,41.2c-2,13.4,-3.2,26.8,-1.7,40.5c1.1,9.6,2.3,19,4.8,28.3   c0.7,2.5,0.1,2.7,-2.4,2.3c-15.2,-2.6,-29.9,-7.1,-44.1,-13.3c-2,-0.9,-3.7,-2.3,-6.1,-2c0,0,0,0,0,0c-0.7,-1.2,-1.9,-1,-3,-1c0,0,0,0,0,0   c-0.3,-2.7,-2.4,-1.8,-4,-2c0,0,0,0,0,0c-0.3,-0.3,-0.7,-0.7,-1,-1c0,0,0,0,0,0c-0.3,-0.3,-0.7,-0.7,-1,-1c0,0,0,0,0,0   C93.6,675.2,92.8,675,91.9,675z"
+        android:fillColor="#D86868"/>
+    <path
+        android:pathData="M135,832.8c1.3,0.1,2.6,0.3,3.9,0.4c0.9,0.8,2,0.7,3.1,0.6c13.1,2.2,26.4,3,39.6,4.1   c4.5,0.3,9,0.2,13.4,-0.5c3.1,0.3,6.3,0.7,9.4,0.8c22.6,0.4,45.2,-0.9,67.6,-4.1c23.9,-3.3,47.4,-8.2,69.8,-17.6   c10.7,-4.5,21.4,-9.3,29.3,-18.3l0,0.1c6.2,-4.6,8.5,-11.8,11.9,-18.2c5.2,4.3,3.3,10.1,2.2,15c-2.2,9.7,-9.6,15.7,-17.4,21   c-17.1,11.7,-36.5,18.1,-56.4,22.9c-19.3,4.7,-38.9,7.4,-58.7,9.3c-21.4,2,-42.8,2.5,-64.2,1.6c-20.3,-0.8,-40.5,-2.8,-60.6,-6.5   c-19,-3.5,-37.6,-8.2,-55.4,-15.4c-11.9,-4.8,-23.4,-10.6,-32.3,-20.2c-5.3,-5.8,-8.9,-12.4,-8.3,-20.6c0.1,-1.2,0,-2.4,1.1,-3.1   c3.3,3.1,4.1,7.8,7.2,11.1c9,9.9,20.2,16.3,32.4,21.4c16.8,7,34.3,11.7,52.3,14.5c0.6,0.1,1.2,0.2,1.9,0.3c0.9,0.8,2,0.7,3.1,0.6   c0.6,0.1,1.3,0.2,1.9,0.3C132.8,833,133.9,833,135,832.8z"
+        android:fillColor="#999899"/>
+    <path
+        android:pathData="M371.9,667c2.8,-3.5,0.4,-7.1,-0.3,-10.4c-5.7,-26.6,-17.7,-50.3,-34.2,-71.7c-0.8,-1,-1.5,-2.2,-2.3,-3.3   c-0.3,-0.5,-0.6,-1.1,-1,-1.9c3.4,3,6.9,5.4,9.6,8.6c10.8,13.1,20.4,27,27.7,42.4c8,16.9,13.1,34.6,15.8,53.1   c1.9,12.9,2.6,25.8,1.7,38.7c-1.7,22.4,-6,44.3,-14.7,65.1c-1.4,3.3,-3.1,6.6,-3,10.4c0,0,0,-0.1,0,-0.1c-1.8,-0.3,-3.3,0.4,-4.7,1.2   c-6.3,3.7,-12.6,7.3,-19.4,10.2c-24,10.3,-48.8,14.5,-74.7,9.2c-4.2,-0.9,-9.4,-0.2,-12.4,-4.8c13.8,-2,27.6,-4.4,41.1,-8   c10,-2.7,20,-5.4,29,-10.8c1.5,-0.5,3.2,-0.9,4.6,-1.6c10.6,-5.1,19.5,-12.1,25.3,-22.6c4.7,-3.1,8.2,-10.7,6.9,-15c0.3,-1.4,0.6,-2.9,1,-4.3   c5.4,-16.2,7.5,-33.1,9,-50C378,689.7,375.5,678.3,371.9,667z"
+        android:fillColor="#F1F0F0"/>
+    <path
+        android:pathData="M371.9,667c3.6,11.3,6.1,22.7,5.1,34.6c-1.5,16.9,-3.6,33.8,-9,50c-0.5,1.4,-0.7,2.9,-1,4.3   c-2.3,5,-4.6,10,-6.9,15c-5.8,10.5,-14.7,17.5,-25.3,22.6c-1.5,0.7,-3.1,1.1,-4.6,1.6c-0.3,-2.1,0.3,-3.9,1.1,-5.7   c3.4,-7.4,6.4,-14.9,8.9,-22.6c6,-18.1,10,-36.5,12,-55.6c1.2,-11.6,0.9,-23.2,0.6,-34.8c-0.2,-6.8,-0.7,-13.8,-2.8,-20.5   c2.4,-4.1,4.7,-8.1,7.1,-12.2c5.6,4.7,8.4,11.3,11.7,17.6C369.8,663.3,370.8,665.2,371.9,667z"
+        android:fillColor="#E6A3A3"/>
+    <path
+        android:pathData="M260,814c2.9,4.6,8.1,3.9,12.4,4.8c25.9,5.3,50.7,1.1,74.7,-9.2c6.7,-2.9,13.1,-6.4,19.4,-10.2   c1.4,-0.9,2.9,-1.6,4.7,-1.2c-7.9,9.1,-18.6,13.8,-29.3,18.3c-22.3,9.4,-45.9,14.3,-69.8,17.6c-22.4,3.1,-45,4.4,-67.6,4.1   c-3.1,-0.1,-6.3,-0.5,-9.4,-0.8c-0.1,-2,-1.4,-2.2,-3,-2.4c-28.3,-3,-56,-8.2,-82.4,-19.1c-13.4,-5.5,-26.5,-11.8,-35.3,-24.1c-1.3,-1.8,-2.4,-3.6,-2.2,-5.9   c15.1,11.6,32.9,16.7,51,21.1c17.7,4.4,35.8,6.6,54,8.3c10,0.9,20,1.1,29.8,1.1c15.7,0.1,31.5,0.4,47.1,-2.4   C256,814,258,814,260,814z"
+        android:fillColor="#EDECEC"/>
+    <path
+        android:pathData="M130,831.9c-1.1,0.1,-2.2,0.2,-3.1,-0.6C128.3,829.5,129.4,829.5,130,831.9z"
+        android:fillColor="#EDECEC"/>
+    <path
+        android:pathData="M135,832.8c-1.1,0.1,-2.2,0.2,-3.1,-0.7C133.4,830.5,134.4,830.6,135,832.8z"
+        android:fillColor="#EDECEC"/>
+    <path
+        android:pathData="M142,833.8c-1.1,0.1,-2.2,0.2,-3.1,-0.6C140.3,831.5,141.4,831.5,142,833.8z"
+        android:fillColor="#EDECEC"/>
+    <path
+        android:pathData="M260,814c-2,0,-4,0,-6,0c-6.8,-3.7,-13.9,-6.8,-20.5,-11c-18,-11.5,-32.7,-26.4,-45.1,-43.6c-11,-15.2,-21,-31,-27.5,-48.8   c-0.9,-2.5,-2,-4.9,-1.9,-7.7c0.7,0,1.4,-0.1,2,0.1c16.2,4.6,33.1,5.3,49.6,5.3c10,0,20.1,-0.2,30.2,-1.3c19.5,-2,38.7,-5.3,57,-12.4   c15.6,-6,30.1,-13.9,41.3,-26.9c4.7,-3.1,7,-8.3,11,-12c2.1,6.7,2.6,13.7,2.8,20.5c0.3,11.6,0.6,23.1,-0.6,34.8c-2,19,-6,37.5,-12,55.6   c-2.6,7.7,-5.5,15.3,-8.9,22.6c-0.9,1.9,-1.5,3.7,-1.1,5.7c-9,5.4,-19,8.1,-29,10.8C287.6,809.6,273.8,811.9,260,814z"
+        android:fillColor="#E6A3A3"/>
+    <path
+        android:pathData="M339,668c-11.1,13,-25.7,20.8,-41.3,26.9c-18.3,7.1,-37.5,10.4,-57,12.4c-10.1,1,-20.3,1.3,-30.2,1.3   c-16.6,0,-33.4,-0.7,-49.6,-5.3c-0.6,-0.2,-1.3,-0.1,-2,-0.1c-0.6,-0.4,-1.3,-0.7,-1.9,-1.1c-0.4,-3.8,0.3,-5,4.8,-4.2c12.1,2,24.3,2.8,36.5,3.4   c32.4,1.7,64.1,-2.3,95.1,-11.6c14.8,-4.5,29,-10.8,41.5,-20.1C336.3,668.5,337.5,667.9,339,668z"
+        android:fillColor="#FFFFFF"/>
+    <path
+        android:pathData="M96,678c1.6,0.2,3.7,-0.7,4,2C98.4,679.8,97,679.3,96,678z"
+        android:fillColor="#FFFFFF"/>
+    <path
+        android:pathData="M100,680c1.1,0,2.3,-0.2,3,1C101.9,681,100.7,681.2,100,680z"
+        android:fillColor="#FFFFFF"/>
+    <path
+        android:pathData="M91.9,675c0.8,0,1.6,0.2,2.1,1C93.2,676,92.4,675.8,91.9,675z"
+        android:fillColor="#FFFFFF"/>
+    <path
+        android:pathData="M94,676c0.3,0.3,0.7,0.7,1,1C94.7,676.7,94.3,676.3,94,676z"
+        android:fillColor="#FFFFFF"/>
+    <path
+        android:pathData="M95,677c0.3,0.3,0.7,0.7,1,1C95.7,677.7,95.3,677.3,95,677z"
+        android:fillColor="#C5C5C5"/>
+    <path
+        android:pathData="M360,771c2.3,-5,4.6,-10,6.9,-15C368.2,760.3,364.7,767.8,360,771z"
+        android:fillColor="#EDECEC"/>
+</vector>
diff --git a/packages/DocumentsUI/res/layout/directory_cluster.xml b/packages/DocumentsUI/res/layout/directory_cluster.xml
index 8245e53..2fa09d3 100644
--- a/packages/DocumentsUI/res/layout/directory_cluster.xml
+++ b/packages/DocumentsUI/res/layout/directory_cluster.xml
@@ -25,7 +25,7 @@
         android:elevation="8dp"
         android:background="@color/material_grey_50"/>
 
-    <com.android.documentsui.DirectoryContainerView
+    <FrameLayout
         android:id="@+id/container_directory"
         android:layout_width="match_parent"
         android:layout_height="0dp"
diff --git a/packages/DocumentsUI/res/layout/drawer_layout.xml b/packages/DocumentsUI/res/layout/drawer_layout.xml
index 0146f14..e3def05 100644
--- a/packages/DocumentsUI/res/layout/drawer_layout.xml
+++ b/packages/DocumentsUI/res/layout/drawer_layout.xml
@@ -32,7 +32,7 @@
             android:layout_height="match_parent"
             android:orientation="vertical">
 
-            <com.android.documentsui.DocumentsToolBar
+            <com.android.documentsui.DocumentsToolbar
                 android:id="@+id/toolbar"
                 android:layout_width="match_parent"
                 android:layout_height="?android:attr/actionBarSize"
@@ -48,7 +48,7 @@
                     android:layout_marginStart="4dp"
                     android:overlapAnchor="true" />
 
-            </com.android.documentsui.DocumentsToolBar>
+            </com.android.documentsui.DocumentsToolbar>
 
             <include layout="@layout/directory_cluster"/>
 
diff --git a/packages/DocumentsUI/res/layout/fixed_layout.xml b/packages/DocumentsUI/res/layout/fixed_layout.xml
index 0dd4a33..8414feb 100644
--- a/packages/DocumentsUI/res/layout/fixed_layout.xml
+++ b/packages/DocumentsUI/res/layout/fixed_layout.xml
@@ -27,7 +27,7 @@
         android:layout_height="match_parent"
         android:orientation="vertical">
 
-        <com.android.documentsui.DocumentsToolBar
+        <com.android.documentsui.DocumentsToolbar
             android:id="@+id/toolbar"
             android:layout_width="match_parent"
             android:layout_height="?android:attr/actionBarSize"
@@ -43,7 +43,7 @@
                 android:layout_marginStart="4dp"
                 android:overlapAnchor="true" />
 
-        </com.android.documentsui.DocumentsToolBar>
+        </com.android.documentsui.DocumentsToolbar>
 
         <LinearLayout
             android:layout_width="match_parent"
diff --git a/packages/DocumentsUI/res/layout/single_pane_layout.xml b/packages/DocumentsUI/res/layout/single_pane_layout.xml
index c5a5745..f53d698 100644
--- a/packages/DocumentsUI/res/layout/single_pane_layout.xml
+++ b/packages/DocumentsUI/res/layout/single_pane_layout.xml
@@ -27,7 +27,7 @@
         android:layout_height="match_parent"
         android:orientation="vertical">
 
-        <com.android.documentsui.DocumentsToolBar
+        <com.android.documentsui.DocumentsToolbar
             android:id="@+id/toolbar"
             android:layout_width="match_parent"
             android:layout_height="?android:attr/actionBarSize"
@@ -43,7 +43,7 @@
                 android:layout_marginStart="4dp"
                 android:overlapAnchor="true" />
 
-        </com.android.documentsui.DocumentsToolBar>
+        </com.android.documentsui.DocumentsToolbar>
 
         <include layout="@layout/directory_cluster"/>
 
diff --git a/packages/DocumentsUI/res/menu/activity.xml b/packages/DocumentsUI/res/menu/activity.xml
index b791ef1..73571af 100644
--- a/packages/DocumentsUI/res/menu/activity.xml
+++ b/packages/DocumentsUI/res/menu/activity.xml
@@ -32,23 +32,6 @@
         android:imeOptions="actionSearch"
         android:visible="false" />
     <item
-        android:id="@+id/menu_sort"
-        android:title="@string/menu_sort"
-        android:icon="@drawable/ic_menu_sortby"
-        android:showAsAction="always">
-        <menu>
-            <item
-                android:id="@+id/menu_sort_name"
-                android:title="@string/sort_name" />
-            <item
-                android:id="@+id/menu_sort_date"
-                android:title="@string/sort_date" />
-            <item
-                android:id="@+id/menu_sort_size"
-                android:title="@string/sort_size" />
-        </menu>
-    </item>
-    <item
         android:id="@+id/menu_grid"
         android:title="@string/menu_grid"
         android:icon="@drawable/ic_menu_view_grid"
@@ -70,7 +53,7 @@
         android:title="@string/menu_create_dir"
         android:icon="@drawable/ic_menu_new_folder"
         android:alphabeticShortcut="e"
-        android:showAsAction="always"
+        android:showAsAction="never"
         android:visible="false" />
     <item
         android:id="@+id/menu_paste_from_clipboard"
@@ -80,6 +63,23 @@
         android:visible="false" />
     <!-- Copy action is defined in mode_directory.xml -->
     <item
+        android:id="@+id/menu_sort"
+        android:title="@string/menu_sort"
+        android:icon="@drawable/ic_menu_sortby"
+        android:showAsAction="never">
+        <menu>
+            <item
+                android:id="@+id/menu_sort_name"
+                android:title="@string/sort_name" />
+            <item
+                android:id="@+id/menu_sort_date"
+                android:title="@string/sort_date" />
+            <item
+                android:id="@+id/menu_sort_size"
+                android:title="@string/sort_size" />
+        </menu>
+    </item>
+    <item
         android:id="@+id/menu_file_size"
         android:showAsAction="never"
         android:visible="false" />
diff --git a/packages/DocumentsUI/res/values/layouts.xml b/packages/DocumentsUI/res/values/layouts.xml
index 8ac1ac2..c9308a1 100644
--- a/packages/DocumentsUI/res/values/layouts.xml
+++ b/packages/DocumentsUI/res/values/layouts.xml
@@ -15,7 +15,7 @@
 -->
 
 <resources>
-    <item name="docs_activity" type="layout">@layout/drawer_layout</item>
+    <item name="documents_activity" type="layout">@layout/drawer_layout</item>
     <item name="files_activity" type="layout">@layout/drawer_layout</item>
-    <item name="manage_roots_activity" type="layout">@layout/single_pane_layout</item>
+    <item name="downloads_activity" type="layout">@layout/single_pane_layout</item>
 </resources>
diff --git a/packages/DocumentsUI/res/values/strings.xml b/packages/DocumentsUI/res/values/strings.xml
index 3c49f16..b97918e 100644
--- a/packages/DocumentsUI/res/values/strings.xml
+++ b/packages/DocumentsUI/res/values/strings.xml
@@ -101,7 +101,7 @@
     <!-- Toast shown when creating a folder failed with an error [CHAR LIMIT=48] -->
     <string name="create_error">Failed to create folder</string>
     <!-- Error message shown when querying for a list of documents failed [CHAR LIMIT=48] -->
-    <string name="query_error">Failed to query documents</string>
+    <string name="query_error">Can\u2019t load content at the moment</string>
 
     <!-- Title of storage root location that contains recently modified or used documents [CHAR LIMIT=24] -->
     <string name="root_recent">Recent</string>
@@ -123,7 +123,7 @@
     <string name="no_results">No matches in %1$s</string>
 
     <!-- Toast shown when no app can be found to open the selected document [CHAR LIMIT=48] -->
-    <string name="toast_no_application">Can\'t open file</string>
+    <string name="toast_no_application">Can\u2019t open file</string>
     <!-- Toast shown when some of the selected documents failed to be deleted [CHAR LIMIT=48] -->
     <string name="toast_failed_delete">Unable to delete some documents</string>
 
@@ -160,27 +160,27 @@
     <string name="delete_preparing">Preparing for delete\u2026</string>
     <!-- Title of the copy error notification [CHAR LIMIT=48] -->
     <plurals name="copy_error_notification_title">
-        <item quantity="one">Couldn\'t copy <xliff:g id="count" example="1">%1$d</xliff:g> file</item>
-        <item quantity="other">Couldn\'t copy <xliff:g id="count" example="2">%1$d</xliff:g> files</item>
+        <item quantity="one">Couldn\u2019t copy <xliff:g id="count" example="1">%1$d</xliff:g> file</item>
+        <item quantity="other">Couldn\u2019t copy <xliff:g id="count" example="2">%1$d</xliff:g> files</item>
     </plurals>
     <!-- Title of the move error notification [CHAR LIMIT=48] -->
     <plurals name="move_error_notification_title">
-        <item quantity="one">Couldn\'t move <xliff:g id="count" example="1">%1$d</xliff:g> file</item>
-        <item quantity="other">Couldn\'t move <xliff:g id="count" example="2">%1$d</xliff:g> files</item>
+        <item quantity="one">Couldn\u2019t move <xliff:g id="count" example="1">%1$d</xliff:g> file</item>
+        <item quantity="other">Couldn\u2019t move <xliff:g id="count" example="2">%1$d</xliff:g> files</item>
     </plurals>
     <!-- Title of the delete error notification [CHAR LIMIT=48] -->
     <plurals name="delete_error_notification_title">
-        <item quantity="one">Couldn\'t delete <xliff:g id="count" example="1">%1$d</xliff:g> file</item>
-        <item quantity="other">Couldn\'t delete <xliff:g id="count" example="2">%1$d</xliff:g> files</item>
+        <item quantity="one">Couldn\u2019t delete <xliff:g id="count" example="1">%1$d</xliff:g> file</item>
+        <item quantity="other">Couldn\u2019t delete <xliff:g id="count" example="2">%1$d</xliff:g> files</item>
     </plurals>
     <!-- Second line for notifications saying that more information will be shown after touching [CHAR LIMIT=48] -->
     <string name="notification_touch_for_details">Tap to view details</string>
     <!-- Label of the close dialog button.[CHAR LIMIT=24] -->
     <string name="close">Close</string>
     <!-- Contents of the copying failure alert dialog. [CHAR LIMIT=48] -->
-    <string name="copy_failure_alert_content">These files weren\'t copied: <xliff:g id="list">%1$s</xliff:g></string>
+    <string name="copy_failure_alert_content">These files weren\u2019t copied: <xliff:g id="list">%1$s</xliff:g></string>
     <!-- Contents of the moving failure alert dialog. [CHAR LIMIT=48] -->
-    <string name="move_failure_alert_content">These files weren\'t moved: <xliff:g id="list">%1$s</xliff:g></string>
+    <string name="move_failure_alert_content">These files weren\u2019t moved: <xliff:g id="list">%1$s</xliff:g></string>
     <!-- Contents of the copying warning dialog due to converted files. [CHAR LIMIT=64] -->
     <string name="copy_converted_warning_content">These files were converted to another format: <xliff:g id="list" example="Document.pdf, Photo.jpg, Song.ogg">%1$s</xliff:g></string>
     <!-- Toast shown when a user copies files to clipboard. -->
diff --git a/packages/DocumentsUI/src/com/android/documentsui/BaseActivity.java b/packages/DocumentsUI/src/com/android/documentsui/BaseActivity.java
index d77fc14..3c21a21 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/BaseActivity.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/BaseActivity.java
@@ -42,88 +42,93 @@
 import android.support.annotation.LayoutRes;
 import android.support.annotation.Nullable;
 import android.util.Log;
-import android.view.LayoutInflater;
 import android.view.Menu;
 import android.view.MenuItem;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.AdapterView;
-import android.widget.AdapterView.OnItemSelectedListener;
-import android.widget.BaseAdapter;
-import android.widget.ImageView;
-import android.widget.TextView;
+import android.widget.Spinner;
 
-import com.android.documentsui.RecentsProvider.ResumeColumns;
 import com.android.documentsui.SearchManager.SearchManagerListener;
 import com.android.documentsui.State.ViewMode;
 import com.android.documentsui.dirlist.DirectoryFragment;
 import com.android.documentsui.model.DocumentInfo;
 import com.android.documentsui.model.DocumentStack;
-import com.android.documentsui.model.DurableUtils;
 import com.android.documentsui.model.RootInfo;
 import com.android.internal.util.Preconditions;
 
-import libcore.io.IoUtils;
-
 import java.io.FileNotFoundException;
-import java.io.IOException;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.List;
 import java.util.concurrent.Executor;
 
-public abstract class BaseActivity extends Activity implements SearchManagerListener {
+public abstract class BaseActivity extends Activity
+        implements SearchManagerListener, NavigationView.Environment {
 
     static final String EXTRA_STATE = "state";
 
+    // See comments where this const is referenced for details.
+    private static final int DRAWER_NO_FIDDLE_DELAY = 1500;
+
     State mState;
     RootsCache mRoots;
     SearchManager mSearchManager;
     DrawerController mDrawer;
-    boolean mProductivityDevice;
+    NavigationView mNavigator;
 
     private final String mTag;
+
     @LayoutRes
     private int mLayoutId;
-    private DirectoryContainerView mDirectoryContainer;
+
+    // Track the time we opened the drawer in response to back being pressed.
+    // We use the time gap to figure out whether to close app or reopen the drawer.
+    private long mDrawerLastFiddled;
 
     public abstract void onDocumentPicked(DocumentInfo doc, @Nullable SiblingProvider siblings);
     public abstract void onDocumentsPicked(List<DocumentInfo> docs);
 
     abstract void onTaskFinished(Uri... uris);
     abstract void refreshDirectory(int anim);
-    abstract void updateActionBar();
-    abstract void saveStackBlocking();
-    abstract State buildState();
+    /** Allows sub-classes to include information in a newly created State instance. */
+    abstract void includeState(State initialState);
 
     public BaseActivity(@LayoutRes int layoutId, String tag) {
         mLayoutId = layoutId;
         mTag = tag;
     }
 
+    @CallSuper
     @Override
     public void onCreate(Bundle icicle) {
         super.onCreate(icicle);
 
-        mState = (icicle != null)
-                ? icicle.<State>getParcelable(EXTRA_STATE)
-                        : buildState();
-
-        Metrics.logActivityLaunch(this, mState, getIntent());
-
         setContentView(mLayoutId);
 
+        mDrawer = DrawerController.create(this);
+        mState = getState(icicle);
+        Metrics.logActivityLaunch(this, mState, getIntent());
+
         mRoots = DocumentsApplication.getRootsCache(this);
+
         mRoots.setOnCacheUpdateListener(
                 new RootsCache.OnCacheUpdateListener() {
                     @Override
                     public void onCacheUpdate() {
-                        new HandleRootsChangedTask().execute(getCurrentRoot());
+                        new HandleRootsChangedTask(BaseActivity.this)
+                                .execute(getCurrentRoot());
                     }
                 });
-        mDirectoryContainer = (DirectoryContainerView) findViewById(R.id.container_directory);
+
         mSearchManager = new SearchManager(this);
 
+        DocumentsToolbar toolbar = (DocumentsToolbar) findViewById(R.id.toolbar);
+        setActionBar(toolbar);
+        mNavigator = new NavigationView(
+                mDrawer,
+                toolbar,
+                (Spinner) findViewById(R.id.stack),
+                mState,
+                this);
+
         // Base classes must update result in their onCreate.
         setResult(Activity.RESULT_CANCELED);
     }
@@ -133,7 +138,7 @@
         boolean showMenu = super.onCreateOptionsMenu(menu);
 
         getMenuInflater().inflate(R.menu.activity, menu);
-        mSearchManager.install((DocumentsToolBar) findViewById(R.id.toolbar));
+        mSearchManager.install((DocumentsToolbar) findViewById(R.id.toolbar));
 
         return showMenu;
     }
@@ -178,7 +183,20 @@
         super.onDestroy();
     }
 
-    State buildDefaultState() {
+    private State getState(@Nullable Bundle icicle) {
+        if (icicle != null) {
+            State state = icicle.<State>getParcelable(EXTRA_STATE);
+            if (DEBUG) Log.d(mTag, "Recovered existing state object: " + state);
+            return state;
+        }
+
+        State state = createSharedState();
+        includeState(state);
+        if (DEBUG) Log.d(mTag, "Created new state object: " + state);
+        return state;
+    }
+
+    private State createSharedState() {
         State state = new State();
 
         final Intent intent = getIntent();
@@ -218,22 +236,7 @@
         if (mRoots.isRecentsRoot(root)) {
             refreshCurrentRootAndDirectory(ANIM_NONE);
         } else {
-            new PickRootTask(root).executeOnExecutor(getExecutorForCurrentDirectory());
-        }
-    }
-
-    void expandMenus(Menu menu) {
-        for (int i = 0; i < menu.size(); i++) {
-            final MenuItem item = menu.getItem(i);
-            switch (item.getItemId()) {
-                case R.id.menu_advanced:
-                case R.id.menu_file_size:
-                case R.id.menu_new_window:
-                case R.id.menu_search:
-                    break;
-                default:
-                    item.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);
-            }
+            new PickRootTask(this, root).executeOnExecutor(getExecutorForCurrentDirectory());
         }
     }
 
@@ -341,10 +344,10 @@
      * The current directory name and selection will get updated.
      * @param anim
      */
-    final void refreshCurrentRootAndDirectory(int anim) {
+    @Override
+    public final void refreshCurrentRootAndDirectory(int anim) {
         mSearchManager.cancelSearch();
 
-        mDirectoryContainer.setDrawDisappearingFirst(anim == ANIM_ENTER);
         refreshDirectory(anim);
 
         final RootsFragment roots = RootsFragment.get(getFragmentManager());
@@ -352,8 +355,7 @@
             roots.onCurrentRootChanged();
         }
 
-        updateActionBar();
-
+        mNavigator.update();
         invalidateOptionsMenu();
     }
 
@@ -364,7 +366,6 @@
      */
     @Override
     public void onSearchChanged() {
-        mDirectoryContainer.setDrawDisappearingFirst(false);
         refreshDirectory(ANIM_NONE);
     }
 
@@ -489,6 +490,12 @@
         super.onRestoreInstanceState(state);
     }
 
+    @Override
+    public boolean isSearchExpanded() {
+        return mSearchManager.isExpanded();
+    }
+
+    @Override
     public RootInfo getCurrentRoot() {
         if (mState.stack.root != null) {
             return mState.stack.root;
@@ -527,16 +534,35 @@
             return;
         }
 
-        final int size = mState.stack.size();
+        int size = mState.stack.size();
 
-        if (mDrawer.isOpen()) {
-            mDrawer.setOpen(false);
-        } else if (size > 1) {
+        // Do some "do what a I want" drawer fiddling, but don't
+        // do it if user already hit back recently and we recently
+        // did some fiddling.
+        if ((System.currentTimeMillis() - mDrawerLastFiddled) > DRAWER_NO_FIDDLE_DELAY) {
+            // Close drawer if it is open.
+            if (mDrawer.isOpen()) {
+                mDrawer.setOpen(false);
+                mDrawerLastFiddled = System.currentTimeMillis();
+                return;
+            }
+
+            // Open the Close drawer if it is closed and we're at the top of a root.
+            if (size == 1) {
+                mDrawer.setOpen(true);
+                // Remember so we don't just close it again if back is pressed again.
+                mDrawerLastFiddled = System.currentTimeMillis();
+                return;
+            }
+        }
+
+        if (size > 1) {
             mState.stack.pop();
             refreshCurrentRootAndDirectory(ANIM_LEAVE);
-        } else {
-            super.onBackPressed();
+            return;
         }
+
+        super.onBackPressed();
     }
 
     public void onStackPicked(DocumentStack stack) {
@@ -562,115 +588,40 @@
         }
     }
 
-    final class PickRootTask extends AsyncTask<Void, Void, DocumentInfo> {
+    private static final class PickRootTask extends PairedTask<BaseActivity, Void, DocumentInfo> {
         private RootInfo mRoot;
 
-        public PickRootTask(RootInfo root) {
+        public PickRootTask(BaseActivity activity, RootInfo root) {
+            super(activity);
             mRoot = root;
         }
 
         @Override
-        protected DocumentInfo doInBackground(Void... params) {
-            return getRootDocumentBlocking(mRoot);
+        protected DocumentInfo run(Void... params) {
+            return mOwner.getRootDocumentBlocking(mRoot);
         }
 
         @Override
-        protected void onPostExecute(DocumentInfo result) {
-            if (result != null && !isDestroyed()) {
-                openContainerDocument(result);
+        protected void finish(DocumentInfo result) {
+            if (result != null) {
+                mOwner.openContainerDocument(result);
             }
         }
     }
 
-    final class RestoreStackTask extends AsyncTask<Void, Void, Void> {
-        private volatile boolean mRestoredStack;
-        private volatile boolean mExternal;
-
-        @Override
-        protected Void doInBackground(Void... params) {
-            if (DEBUG && !mState.stack.isEmpty()) {
-                Log.w(mTag, "Overwriting existing stack.");
-            }
-            RootsCache roots = DocumentsApplication.getRootsCache(BaseActivity.this);
-
-            // Restore last stack for calling package
-            final String packageName = getCallingPackageMaybeExtra();
-            final Cursor cursor = getContentResolver()
-                    .query(RecentsProvider.buildResume(packageName), null, null, null, null);
-            try {
-                if (cursor.moveToFirst()) {
-                    mExternal = cursor.getInt(cursor.getColumnIndex(ResumeColumns.EXTERNAL)) != 0;
-                    final byte[] rawStack = cursor.getBlob(
-                            cursor.getColumnIndex(ResumeColumns.STACK));
-                    DurableUtils.readFromArray(rawStack, mState.stack);
-                    mRestoredStack = true;
-                }
-            } catch (IOException e) {
-                Log.w(mTag, "Failed to resume: " + e);
-            } finally {
-                IoUtils.closeQuietly(cursor);
-            }
-
-            if (mRestoredStack) {
-                // Update the restored stack to ensure we have freshest data
-                final Collection<RootInfo> matchingRoots = roots.getMatchingRootsBlocking(mState);
-                try {
-                    mState.stack.updateRoot(matchingRoots);
-                    mState.stack.updateDocuments(getContentResolver());
-                } catch (FileNotFoundException e) {
-                    Log.w(mTag, "Failed to restore stack: " + e);
-                    mState.stack.reset();
-                    mRestoredStack = false;
-                }
-            }
-
-            return null;
-        }
-
-        @Override
-        protected void onPostExecute(Void result) {
-            if (isDestroyed()) return;
-            mState.restored = true;
-            refreshCurrentRootAndDirectory(ANIM_NONE);
-            onStackRestored(mRestoredStack, mExternal);
-        }
-    }
-
-    final class RestoreRootTask extends AsyncTask<Void, Void, RootInfo> {
-        private Uri mRootUri;
-
-        public RestoreRootTask(Uri rootUri) {
-            mRootUri = rootUri;
-        }
-
-        @Override
-        protected RootInfo doInBackground(Void... params) {
-            final String rootId = DocumentsContract.getRootId(mRootUri);
-            return mRoots.getRootOneshot(mRootUri.getAuthority(), rootId);
-        }
-
-        @Override
-        protected void onPostExecute(RootInfo root) {
-            if (isDestroyed()) return;
-            mState.restored = true;
-
-            if (root != null) {
-                onRootPicked(root);
-            } else {
-                Log.w(mTag, "Failed to find root: " + mRootUri);
-                finish();
-            }
-        }
-    }
-
-    final class HandleRootsChangedTask extends AsyncTask<RootInfo, Void, RootInfo> {
+    private static final class HandleRootsChangedTask
+            extends PairedTask<BaseActivity, RootInfo, RootInfo> {
         DocumentInfo mHome;
 
+        public HandleRootsChangedTask(BaseActivity activity) {
+            super(activity);
+        }
+
         @Override
-        protected RootInfo doInBackground(RootInfo... roots) {
+        protected RootInfo run(RootInfo... roots) {
             checkArgument(roots.length == 1);
             final RootInfo currentRoot = roots[0];
-            final Collection<RootInfo> cachedRoots = mRoots.getRootsBlocking();
+            final Collection<RootInfo> cachedRoots = mOwner.mRoots.getRootsBlocking();
             RootInfo homeRoot = null;
             for (final RootInfo root : cachedRoots) {
                 if (root.isHome()) {
@@ -682,107 +633,21 @@
                 }
             }
             Preconditions.checkNotNull(homeRoot);
-            mHome = getRootDocumentBlocking(homeRoot);
+            mHome = mOwner.getRootDocumentBlocking(homeRoot);
             return homeRoot;
         }
 
         @Override
-        protected void onPostExecute(RootInfo homeRoot) {
-            if (homeRoot != null && mHome != null && !isDestroyed()) {
+        protected void finish(RootInfo homeRoot) {
+            if (homeRoot != null && mHome != null) {
                 // Clear entire backstack and start in new root
-                mState.onRootChanged(homeRoot);
-                mSearchManager.update(homeRoot);
-                openContainerDocument(mHome);
+                mOwner.mState.onRootChanged(homeRoot);
+                mOwner.mSearchManager.update(homeRoot);
+                mOwner.openContainerDocument(mHome);
             }
         }
     }
 
-    final class ItemSelectedListener implements OnItemSelectedListener {
-
-        boolean mIgnoreNextNavigation;
-
-        @Override
-        public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
-            if (mIgnoreNextNavigation) {
-                mIgnoreNextNavigation = false;
-                return;
-            }
-
-            while (mState.stack.size() > position + 1) {
-                mState.popDocument();
-            }
-            refreshCurrentRootAndDirectory(ANIM_LEAVE);
-        }
-
-        @Override
-        public void onNothingSelected(AdapterView<?> parent) {
-            // Ignored
-        }
-    }
-
-    /**
-     * Class providing toolbar with runtime access to useful activity data.
-     */
-    final class StackAdapter extends BaseAdapter {
-        @Override
-        public int getCount() {
-            return mState.stack.size();
-        }
-
-        @Override
-        public DocumentInfo getItem(int position) {
-            return mState.stack.get(mState.stack.size() - position - 1);
-        }
-
-        @Override
-        public long getItemId(int position) {
-            return position;
-        }
-
-        @Override
-        public View getView(int position, View convertView, ViewGroup parent) {
-            if (convertView == null) {
-                convertView = LayoutInflater.from(parent.getContext())
-                        .inflate(R.layout.item_subdir_title, parent, false);
-            }
-
-            final TextView title = (TextView) convertView.findViewById(android.R.id.title);
-            final DocumentInfo doc = getItem(position);
-
-            if (position == 0) {
-                final RootInfo root = getCurrentRoot();
-                title.setText(root.title);
-            } else {
-                title.setText(doc.displayName);
-            }
-
-            return convertView;
-        }
-
-        @Override
-        public View getDropDownView(int position, View convertView, ViewGroup parent) {
-            if (convertView == null) {
-                convertView = LayoutInflater.from(parent.getContext())
-                        .inflate(R.layout.item_subdir, parent, false);
-            }
-
-            final ImageView subdir = (ImageView) convertView.findViewById(R.id.subdir);
-            final TextView title = (TextView) convertView.findViewById(android.R.id.title);
-            final DocumentInfo doc = getItem(position);
-
-            if (position == 0) {
-                final RootInfo root = getCurrentRoot();
-                title.setText(root.title);
-                subdir.setVisibility(View.GONE);
-            } else {
-                title.setText(doc.displayName);
-                subdir.setVisibility(View.VISIBLE);
-            }
-
-            return convertView;
-        }
-    }
-
     /**
      * Interface providing access to current view of documents
      * even when all documents are not homed to the same parent.
diff --git a/packages/DocumentsUI/src/com/android/documentsui/DirectoryContainerView.java b/packages/DocumentsUI/src/com/android/documentsui/DirectoryContainerView.java
deleted file mode 100644
index 71ea8a9..0000000
--- a/packages/DocumentsUI/src/com/android/documentsui/DirectoryContainerView.java
+++ /dev/null
@@ -1,61 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.documentsui;
-
-import android.content.Context;
-import android.graphics.Canvas;
-import android.util.AttributeSet;
-import android.view.View;
-import android.widget.FrameLayout;
-
-import java.util.ArrayList;
-
-public class DirectoryContainerView extends FrameLayout {
-    private boolean mDisappearingFirst = false;
-
-    public DirectoryContainerView(Context context) {
-        super(context);
-    }
-
-    public DirectoryContainerView(Context context, AttributeSet attrs) {
-        super(context, attrs);
-    }
-
-    @Override
-    protected void dispatchDraw(Canvas canvas) {
-        final ArrayList<View> disappearing = mDisappearingChildren;
-        if (mDisappearingFirst && disappearing != null) {
-            for (int i = 0; i < disappearing.size(); i++) {
-                super.drawChild(canvas, disappearing.get(i), getDrawingTime());
-            }
-        }
-        super.dispatchDraw(canvas);
-    }
-
-    @Override
-    protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
-        if (mDisappearingFirst && mDisappearingChildren != null
-                && mDisappearingChildren.contains(child)) {
-            return false;
-        }
-        return super.drawChild(canvas, child, drawingTime);
-    }
-
-    public void setDrawDisappearingFirst(boolean disappearingFirst) {
-        mDisappearingFirst = disappearingFirst;
-    }
-}
diff --git a/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java b/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java
index b933d0a..3485fe4 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java
@@ -16,6 +16,7 @@
 
 package com.android.documentsui;
 
+import static com.android.documentsui.Shared.DEBUG;
 import static com.android.documentsui.State.ACTION_CREATE;
 import static com.android.documentsui.State.ACTION_GET_CONTENT;
 import static com.android.documentsui.State.ACTION_OPEN;
@@ -31,11 +32,12 @@
 import android.content.ContentProviderClient;
 import android.content.ContentResolver;
 import android.content.ContentValues;
+import android.content.Context;
 import android.content.Intent;
 import android.content.pm.ResolveInfo;
 import android.content.res.Resources;
+import android.database.Cursor;
 import android.net.Uri;
-import android.os.AsyncTask;
 import android.os.Bundle;
 import android.os.Parcelable;
 import android.provider.DocumentsContract;
@@ -43,10 +45,6 @@
 import android.util.Log;
 import android.view.Menu;
 import android.view.MenuItem;
-import android.view.View;
-import android.widget.BaseAdapter;
-import android.widget.Spinner;
-import android.widget.Toolbar;
 
 import com.android.documentsui.RecentsProvider.RecentColumns;
 import com.android.documentsui.RecentsProvider.ResumeColumns;
@@ -56,23 +54,20 @@
 import com.android.documentsui.model.RootInfo;
 import com.android.documentsui.services.FileOperationService;
 
+import libcore.io.IoUtils;
+
+import java.io.FileNotFoundException;
+import java.io.IOException;
 import java.util.Arrays;
+import java.util.Collection;
 import java.util.List;
 
 public class DocumentsActivity extends BaseActivity {
     private static final int CODE_FORWARD = 42;
     private static final String TAG = "DocumentsActivity";
 
-    private Toolbar mToolbar;
-    private Spinner mToolbarStack;
-
-    private Toolbar mRootsToolbar;
-
-    private ItemSelectedListener mStackListener;
-    private BaseAdapter mStackAdapter;
-
     public DocumentsActivity() {
-        super(R.layout.docs_activity, TAG);
+        super(R.layout.documents_activity, TAG);
     }
 
     @Override
@@ -81,18 +76,6 @@
 
         final Resources res = getResources();
 
-        mDrawer = DrawerController.create(this);
-        mToolbar = (Toolbar) findViewById(R.id.toolbar);
-
-        mStackAdapter = new StackAdapter();
-        mStackListener = new ItemSelectedListener();
-        mToolbarStack = (Spinner) findViewById(R.id.stack);
-        mToolbarStack.setOnItemSelectedListener(mStackListener);
-
-        mRootsToolbar = (Toolbar) findViewById(R.id.roots_toolbar);
-
-        setActionBar(mToolbar);
-
         if (mState.action == ACTION_CREATE) {
             final String mimeType = getIntent().getType();
             final String title = getIntent().getStringExtra(Intent.EXTRA_TITLE);
@@ -118,16 +101,14 @@
             // In this case, we set the activity title in AsyncTask.onPostExecute().  To prevent
             // talkback from reading aloud the default title, we clear it here.
             setTitle("");
-            new RestoreStackTask().execute();
+            new RestoreStackTask(this).execute();
         } else {
             refreshCurrentRootAndDirectory(ANIM_NONE);
         }
     }
 
     @Override
-    State buildState() {
-        State state = buildDefaultState();
-
+    void includeState(State state) {
         final Intent intent = getIntent();
         final String action = intent.getAction();
         if (Intent.ACTION_OPEN_DOCUMENT.equals(action)) {
@@ -158,8 +139,6 @@
             state.transferMode = intent.getIntExtra(FileOperationService.EXTRA_OPERATION,
                     FileOperationService.OPERATION_COPY);
         }
-
-        return state;
     }
 
     @Override
@@ -180,7 +159,7 @@
         }
 
         if (showDrawer) {
-            setRootsDrawerOpen(true);
+            mNavigator.revealRootsDrawer(true);
         }
     }
 
@@ -217,74 +196,28 @@
     @Override
     protected void onPostCreate(Bundle savedInstanceState) {
         super.onPostCreate(savedInstanceState);
-        mDrawer.syncState();
-        updateActionBar();
-    }
-
-    public void setRootsDrawerOpen(boolean open) {
-        mDrawer.setOpen(open);
+        mDrawer.update();
+        mNavigator.update();
     }
 
     @Override
-    public void updateActionBar() {
-        if (mRootsToolbar != null) {
-            final String prompt = getIntent().getStringExtra(DocumentsContract.EXTRA_PROMPT);
-            if (prompt != null) {
-                mRootsToolbar.setTitle(prompt);
+    public String getDrawerTitle() {
+        String title = getIntent().getStringExtra(DocumentsContract.EXTRA_PROMPT);
+        if (title == null) {
+            if (mState.action == ACTION_OPEN ||
+                mState.action == ACTION_GET_CONTENT ||
+                mState.action == ACTION_OPEN_TREE) {
+                title = getResources().getString(R.string.title_open);
+            } else if (mState.action == ACTION_CREATE ||
+                       mState.action == ACTION_PICK_COPY_DESTINATION) {
+                title = getResources().getString(R.string.title_save);
             } else {
-                if (mState.action == ACTION_OPEN ||
-                    mState.action == ACTION_GET_CONTENT ||
-                    mState.action == ACTION_OPEN_TREE) {
-                    mRootsToolbar.setTitle(R.string.title_open);
-                } else if (mState.action == ACTION_CREATE ||
-                           mState.action == ACTION_PICK_COPY_DESTINATION) {
-                    mRootsToolbar.setTitle(R.string.title_save);
-                }
+                // If all else fails, just call it "Files".
+                title = getResources().getString(R.string.files_label);
             }
         }
 
-        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);
-                        }
-                    });
-        } else {
-            mToolbar.setNavigationIcon(null);
-            mToolbar.setNavigationContentDescription(R.string.drawer_open);
-            mToolbar.setNavigationOnClickListener(null);
-        }
-
-        if (mSearchManager.isExpanded()) {
-            mToolbar.setTitle(null);
-            mToolbarStack.setVisibility(View.GONE);
-            mToolbarStack.setAdapter(null);
-        } else {
-            if (mState.stack.size() <= 1) {
-                mToolbar.setTitle(getCurrentRoot().title);
-                mToolbarStack.setVisibility(View.GONE);
-                mToolbarStack.setAdapter(null);
-            } else {
-                mToolbar.setTitle(null);
-                mToolbarStack.setVisibility(View.VISIBLE);
-                mToolbarStack.setAdapter(mStackAdapter);
-
-                mStackListener.mIgnoreNextNavigation = true;
-                mToolbarStack.setSelection(mStackAdapter.getCount() - 1);
-            }
-        }
-    }
-
-    @Override
-    public boolean onCreateOptionsMenu(Menu menu) {
-        boolean showMenu = super.onCreateOptionsMenu(menu);
-
-        expandMenus(menu);
-        return showMenu;
+        return title;
     }
 
     @Override
@@ -331,11 +264,6 @@
     }
 
     @Override
-    public boolean onOptionsItemSelected(MenuItem item) {
-        return mDrawer.onOptionsItemSelected(item) || super.onOptionsItemSelected(item);
-    }
-
-    @Override
     void refreshDirectory(int anim) {
         final FragmentManager fm = getFragmentManager();
         final RootInfo root = getCurrentRoot();
@@ -386,17 +314,23 @@
     }
 
     void onSaveRequested(DocumentInfo replaceTarget) {
-        new ExistingFinishTask(replaceTarget.derivedUri).executeOnExecutor(getExecutorForCurrentDirectory());
+        new ExistingFinishTask(this, replaceTarget.derivedUri)
+                .executeOnExecutor(getExecutorForCurrentDirectory());
     }
 
     void onSaveRequested(String mimeType, String displayName) {
-        new CreateFinishTask(mimeType, displayName).executeOnExecutor(getExecutorForCurrentDirectory());
+        new CreateFinishTask(this, mimeType, displayName)
+                .executeOnExecutor(getExecutorForCurrentDirectory());
     }
 
     @Override
     void onRootPicked(RootInfo root) {
         super.onRootPicked(root);
-        setRootsDrawerOpen(false);
+        mNavigator.revealRootsDrawer(false);
+    }
+
+    public void setRootsDrawerOpen(boolean open) {
+        mNavigator.revealRootsDrawer(open);
     }
 
     @Override
@@ -406,7 +340,8 @@
             openContainerDocument(doc);
         } else if (mState.action == ACTION_OPEN || mState.action == ACTION_GET_CONTENT) {
             // Explicit file picked, return
-            new ExistingFinishTask(doc.derivedUri).executeOnExecutor(getExecutorForCurrentDirectory());
+            new ExistingFinishTask(this, doc.derivedUri)
+                    .executeOnExecutor(getExecutorForCurrentDirectory());
         } else if (mState.action == ACTION_CREATE) {
             // Replace selected file
             SaveFragment.get(fm).setReplaceTarget(doc);
@@ -421,7 +356,8 @@
             for (int i = 0; i < size; i++) {
                 uris[i] = docs.get(i).derivedUri;
             }
-            new ExistingFinishTask(uris).executeOnExecutor(getExecutorForCurrentDirectory());
+            new ExistingFinishTask(this, uris)
+                    .executeOnExecutor(getExecutorForCurrentDirectory());
         }
     }
 
@@ -436,11 +372,10 @@
             // Should not be reached.
             throw new IllegalStateException("Invalid mState.action.");
         }
-        new PickFinishTask(result).executeOnExecutor(getExecutorForCurrentDirectory());
+        new PickFinishTask(this, result).executeOnExecutor(getExecutorForCurrentDirectory());
     }
 
-    @Override
-    void saveStackBlocking() {
+    void writeStackToRecentsBlocking() {
         final ContentResolver resolver = getContentResolver();
         final ContentValues values = new ContentValues();
 
@@ -501,69 +436,137 @@
         finish();
     }
 
+
     public static DocumentsActivity get(Fragment fragment) {
         return (DocumentsActivity) fragment.getActivity();
     }
 
-    private final class PickFinishTask extends AsyncTask<Void, Void, Void> {
+    /**
+     * Restores the stack from Recents for the specified package.
+     */
+    private static final class RestoreStackTask
+            extends PairedTask<DocumentsActivity, Void, Void> {
+
+        private volatile boolean mRestoredStack;
+        private volatile boolean mExternal;
+        private State mState;
+
+        public RestoreStackTask(DocumentsActivity activity) {
+            super(activity);
+            mState = activity.mState;
+        }
+
+        @Override
+        protected Void run(Void... params) {
+            if (DEBUG && !mState.stack.isEmpty()) {
+                Log.w(TAG, "Overwriting existing stack.");
+            }
+            RootsCache roots = DocumentsApplication.getRootsCache(mOwner);
+
+            String packageName = mOwner.getCallingPackageMaybeExtra();
+            Uri resumeUri = RecentsProvider.buildResume(packageName);
+            Cursor cursor = mOwner.getContentResolver().query(resumeUri, null, null, null, null);
+            try {
+                if (cursor.moveToFirst()) {
+                    mExternal = cursor.getInt(cursor.getColumnIndex(ResumeColumns.EXTERNAL)) != 0;
+                    final byte[] rawStack = cursor.getBlob(
+                            cursor.getColumnIndex(ResumeColumns.STACK));
+                    DurableUtils.readFromArray(rawStack, mState.stack);
+                    mRestoredStack = true;
+                }
+            } catch (IOException e) {
+                Log.w(TAG, "Failed to resume: " + e);
+            } finally {
+                IoUtils.closeQuietly(cursor);
+            }
+
+            if (mRestoredStack) {
+                // Update the restored stack to ensure we have freshest data
+                final Collection<RootInfo> matchingRoots = roots.getMatchingRootsBlocking(mState);
+                try {
+                    mState.stack.updateRoot(matchingRoots);
+                    mState.stack.updateDocuments(mOwner.getContentResolver());
+                } catch (FileNotFoundException e) {
+                    Log.w(TAG, "Failed to restore stack for package: " + packageName
+                            + " because of error: "+ e);
+                    mState.stack.reset();
+                    mRestoredStack = false;
+                }
+            }
+
+            return null;
+        }
+
+        @Override
+        protected void finish(Void result) {
+            mState.restored = true;
+            mOwner.refreshCurrentRootAndDirectory(ANIM_NONE);
+            mOwner.onStackRestored(mRestoredStack, mExternal);
+        }
+    }
+
+    private static final class PickFinishTask extends PairedTask<DocumentsActivity, Void, Void> {
         private final Uri mUri;
 
-        public PickFinishTask(Uri uri) {
+        public PickFinishTask(DocumentsActivity activity, Uri uri) {
+            super(activity);
             mUri = uri;
         }
 
         @Override
-        protected Void doInBackground(Void... params) {
-            saveStackBlocking();
+        protected Void run(Void... params) {
+            mOwner.writeStackToRecentsBlocking();
             return null;
         }
 
         @Override
-        protected void onPostExecute(Void result) {
-            onTaskFinished(mUri);
+        protected void finish(Void result) {
+            mOwner.onTaskFinished(mUri);
         }
     }
 
-    final class ExistingFinishTask extends AsyncTask<Void, Void, Void> {
+    private static final class ExistingFinishTask extends PairedTask<DocumentsActivity, Void, Void> {
         private final Uri[] mUris;
 
-        public ExistingFinishTask(Uri... uris) {
+        public ExistingFinishTask(DocumentsActivity activity, Uri... uris) {
+            super(activity);
             mUris = uris;
         }
 
         @Override
-        protected Void doInBackground(Void... params) {
-            saveStackBlocking();
+        protected Void run(Void... params) {
+            mOwner.writeStackToRecentsBlocking();
             return null;
         }
 
         @Override
-        protected void onPostExecute(Void result) {
-            onTaskFinished(mUris);
+        protected void finish(Void result) {
+            mOwner.onTaskFinished(mUris);
         }
     }
 
     /**
      * Task that creates a new document in the background.
      */
-    final class CreateFinishTask extends AsyncTask<Void, Void, Uri> {
+    private static final class CreateFinishTask extends PairedTask<DocumentsActivity, Void, Uri> {
         private final String mMimeType;
         private final String mDisplayName;
 
-        public CreateFinishTask(String mimeType, String displayName) {
+        public CreateFinishTask(DocumentsActivity activity, String mimeType, String displayName) {
+            super(activity);
             mMimeType = mimeType;
             mDisplayName = displayName;
         }
 
         @Override
-        protected void onPreExecute() {
-            setPending(true);
+        protected void prepare() {
+            mOwner.setPending(true);
         }
 
         @Override
-        protected Uri doInBackground(Void... params) {
-            final ContentResolver resolver = getContentResolver();
-            final DocumentInfo cwd = getCurrentDirectory();
+        protected Uri run(Void... params) {
+            final ContentResolver resolver = mOwner.getContentResolver();
+            final DocumentInfo cwd = mOwner.getCurrentDirectory();
 
             ContentProviderClient client = null;
             Uri childUri = null;
@@ -579,22 +582,22 @@
             }
 
             if (childUri != null) {
-                saveStackBlocking();
+                mOwner.writeStackToRecentsBlocking();
             }
 
             return childUri;
         }
 
         @Override
-        protected void onPostExecute(Uri result) {
+        protected void finish(Uri result) {
             if (result != null) {
-                onTaskFinished(result);
+                mOwner.onTaskFinished(result);
             } else {
                 Snackbars.makeSnackbar(
-                    DocumentsActivity.this, R.string.save_error, Snackbar.LENGTH_SHORT).show();
+                        mOwner, R.string.save_error, Snackbar.LENGTH_SHORT).show();
             }
 
-            setPending(false);
+            mOwner.setPending(false);
         }
     }
 }
diff --git a/packages/DocumentsUI/src/com/android/documentsui/DocumentsToolBar.java b/packages/DocumentsUI/src/com/android/documentsui/DocumentsToolbar.java
similarity index 86%
rename from packages/DocumentsUI/src/com/android/documentsui/DocumentsToolBar.java
rename to packages/DocumentsUI/src/com/android/documentsui/DocumentsToolbar.java
index 36b7646..7742cbf 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/DocumentsToolBar.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/DocumentsToolbar.java
@@ -24,28 +24,28 @@
 /**
  * ToolBar of Documents UI.
  */
-public class DocumentsToolBar extends Toolbar {
+public class DocumentsToolbar extends Toolbar {
     interface OnActionViewCollapsedListener {
         void onActionViewCollapsed();
     }
 
     private OnActionViewCollapsedListener mOnActionViewCollapsedListener;
 
-    public DocumentsToolBar(Context context, AttributeSet attrs,
+    public DocumentsToolbar(Context context, AttributeSet attrs,
             int defStyleAttr, int defStyleRes) {
         super(context, attrs, defStyleAttr, defStyleRes);
     }
 
-    public DocumentsToolBar(Context context, AttributeSet attrs,
+    public DocumentsToolbar(Context context, AttributeSet attrs,
             int defStyleAttr) {
         super(context, attrs, defStyleAttr);
     }
 
-    public DocumentsToolBar(Context context, AttributeSet attrs) {
+    public DocumentsToolbar(Context context, AttributeSet attrs) {
         super(context, attrs);
     }
 
-    public DocumentsToolBar(Context context) {
+    public DocumentsToolbar(Context context) {
         super(context);
     }
 
diff --git a/packages/DocumentsUI/src/com/android/documentsui/DownloadsActivity.java b/packages/DocumentsUI/src/com/android/documentsui/DownloadsActivity.java
index 5cc677c..d589d5e 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/DownloadsActivity.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/DownloadsActivity.java
@@ -24,8 +24,6 @@
 import android.app.FragmentManager;
 import android.content.ActivityNotFoundException;
 import android.content.ClipData;
-import android.content.ContentResolver;
-import android.content.ContentValues;
 import android.content.Context;
 import android.content.Intent;
 import android.net.Uri;
@@ -35,15 +33,10 @@
 import android.util.Log;
 import android.view.Menu;
 import android.view.MenuItem;
-import android.view.View;
-import android.widget.BaseAdapter;
-import android.widget.Spinner;
 import android.widget.Toolbar;
 
-import com.android.documentsui.RecentsProvider.ResumeColumns;
 import com.android.documentsui.dirlist.DirectoryFragment;
 import com.android.documentsui.model.DocumentInfo;
-import com.android.documentsui.model.DurableUtils;
 import com.android.documentsui.model.RootInfo;
 import com.android.internal.util.Preconditions;
 
@@ -56,14 +49,8 @@
 public class DownloadsActivity extends BaseActivity {
     private static final String TAG = "DownloadsActivity";
 
-    private Toolbar mToolbar;
-    private Spinner mToolbarStack;
-
-    private ItemSelectedListener mStackListener;
-    private BaseAdapter mStackAdapter;
-
     public DownloadsActivity() {
-        super(R.layout.manage_roots_activity, TAG);
+        super(R.layout.downloads_activity, TAG);
     }
 
     @Override
@@ -72,73 +59,39 @@
 
         final Context context = this;
 
-        mDrawer = DrawerController.createDummy();
-
-        mToolbar = (Toolbar) findViewById(R.id.toolbar);
-        mToolbar.setTitleTextAppearance(context,
+        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
+        toolbar.setTitleTextAppearance(context,
                 android.R.style.TextAppearance_DeviceDefault_Widget_ActionBar_Title);
 
-        mStackAdapter = new StackAdapter();
-        mStackListener = new ItemSelectedListener();
-        mToolbarStack = (Spinner) findViewById(R.id.stack);
-        mToolbarStack.setOnItemSelectedListener(mStackListener);
-
-        setActionBar(mToolbar);
-
         if (!mState.restored) {
             // In this case, we set the activity title in AsyncTask.onPostExecute(). To prevent
             // talkback from reading aloud the default title, we clear it here.
             setTitle("");
             final Uri rootUri = getIntent().getData();
-            new RestoreRootTask(rootUri).executeOnExecutor(getExecutorForCurrentDirectory());
+            new RestoreRootTask(this, rootUri).executeOnExecutor(getExecutorForCurrentDirectory());
         } else {
             refreshCurrentRootAndDirectory(ANIM_NONE);
         }
     }
 
     @Override
-    State buildState() {
-        State state = buildDefaultState();
-
+    void includeState(State state) {
         state.action = ACTION_MANAGE;
         state.acceptMimes = new String[] { "*/*" };
         state.allowMultiple = true;
         state.showSize = true;
         state.excludedAuthorities = getExcludedAuthorities();
-
-        return state;
     }
 
     @Override
     protected void onPostCreate(Bundle savedInstanceState) {
         super.onPostCreate(savedInstanceState);
-        updateActionBar();
+        mNavigator.update();
     }
 
     @Override
-    public void updateActionBar() {
-        // No navigation in manage root mode.
-        mToolbar.setNavigationIcon(null);
-        mToolbar.setNavigationOnClickListener(null);
-
-        if (mSearchManager.isExpanded()) {
-            mToolbar.setTitle(null);
-            mToolbarStack.setVisibility(View.GONE);
-            mToolbarStack.setAdapter(null);
-        } else {
-            if (mState.stack.size() <= 1) {
-                mToolbar.setTitle(getCurrentRoot().title);
-                mToolbarStack.setVisibility(View.GONE);
-                mToolbarStack.setAdapter(null);
-            } else {
-                mToolbar.setTitle(null);
-                mToolbarStack.setVisibility(View.VISIBLE);
-                mToolbarStack.setAdapter(mStackAdapter);
-
-                mStackListener.mIgnoreNextNavigation = true;
-                mToolbarStack.setSelection(mStackAdapter.getCount() - 1);
-            }
-        }
+    public String getDrawerTitle() {
+        return null;  // being and nothingness
     }
 
     @Override
@@ -147,14 +100,12 @@
 
         final MenuItem advanced = menu.findItem(R.id.menu_advanced);
         final MenuItem createDir = menu.findItem(R.id.menu_create_dir);
-        final MenuItem newWindow = menu.findItem(R.id.menu_new_window);
         final MenuItem pasteFromCb = menu.findItem(R.id.menu_paste_from_clipboard);
         final MenuItem fileSize = menu.findItem(R.id.menu_file_size);
 
         advanced.setVisible(false);
         createDir.setVisible(false);
         pasteFromCb.setEnabled(false);
-        newWindow.setEnabled(false);
         fileSize.setVisible(false);
 
         Menus.disableHiddenItems(menu);
@@ -209,21 +160,6 @@
     public void onDocumentsPicked(List<DocumentInfo> docs) {}
 
     @Override
-    void saveStackBlocking() {
-        final ContentResolver resolver = getContentResolver();
-        final ContentValues values = new ContentValues();
-
-        final byte[] rawStack = DurableUtils.writeToArrayOrNull(mState.stack);
-
-        // Remember location for next app launch
-        final String packageName = getCallingPackageMaybeExtra();
-        values.clear();
-        values.put(ResumeColumns.STACK, rawStack);
-        values.put(ResumeColumns.EXTERNAL, 0);
-        resolver.insert(RecentsProvider.buildResume(packageName), values);
-    }
-
-    @Override
     void onTaskFinished(Uri... uris) {
         Log.d(TAG, "onFinished() " + Arrays.toString(uris));
 
diff --git a/packages/DocumentsUI/src/com/android/documentsui/DrawerController.java b/packages/DocumentsUI/src/com/android/documentsui/DrawerController.java
index df3ac1b..bcf69c4 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/DrawerController.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/DrawerController.java
@@ -22,8 +22,8 @@
 import android.support.v4.app.ActionBarDrawerToggle;
 import android.support.v4.widget.DrawerLayout;
 import android.support.v4.widget.DrawerLayout.DrawerListener;
-import android.view.MenuItem;
 import android.view.View;
+import android.widget.Toolbar;
 
 /**
  * A facade over the various pieces comprising "roots fragment in a Drawer".
@@ -33,13 +33,10 @@
 abstract class DrawerController implements DrawerListener {
 
     abstract void setOpen(boolean open);
-    abstract void lockOpen();
-    abstract void lockClosed();
     abstract boolean isPresent();
     abstract boolean isOpen();
-    abstract boolean isUnlocked();
-    abstract void syncState();
-    abstract boolean onOptionsItemSelected(MenuItem item);
+    abstract void setTitle(String title);
+    abstract void update();
 
     /**
      * Returns a controller suitable for {@code Layout}.
@@ -53,6 +50,8 @@
         }
 
         View drawer = activity.findViewById(R.id.drawer_roots);
+        Toolbar toolbar = (Toolbar) activity.findViewById(R.id.roots_toolbar);
+
         ActionBarDrawerToggle toggle = new ActionBarDrawerToggle(
                 activity,
                 layout,
@@ -60,7 +59,7 @@
                 R.string.drawer_open,
                 R.string.drawer_close);
 
-        return new RuntimeDrawerController(layout, drawer, toggle);
+        return new RuntimeDrawerController(layout, drawer, toggle, toolbar);
     }
 
     /**
@@ -78,9 +77,12 @@
         private final ActionBarDrawerToggle mToggle;
         private DrawerLayout mLayout;
         private View mDrawer;
+        private Toolbar mToolbar;
 
         public RuntimeDrawerController(
-                DrawerLayout layout, View drawer, ActionBarDrawerToggle toggle) {
+                DrawerLayout layout, View drawer, ActionBarDrawerToggle toggle,
+                Toolbar drawerToolbar) {
+            mToolbar = drawerToolbar;
             checkArgument(layout != null);
 
             mLayout = layout;
@@ -110,31 +112,16 @@
         }
 
         @Override
-        void syncState() {
+        void setTitle(String title) {
+            mToolbar.setTitle(title);
+        }
+
+        @Override
+        void update() {
             mToggle.syncState();
         }
 
         @Override
-        boolean isUnlocked() {
-            return mLayout.getDrawerLockMode(mDrawer) == DrawerLayout.LOCK_MODE_UNLOCKED;
-        }
-
-        @Override
-        void lockOpen() {
-            mLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_OPEN);
-        }
-
-        @Override
-        void lockClosed() {
-            mLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED);
-        }
-
-        @Override
-        boolean onOptionsItemSelected(MenuItem item) {
-            return false;
-        }
-
-        @Override
         public void onDrawerSlide(View drawerView, float slideOffset) {
             mToggle.onDrawerSlide(drawerView, slideOffset);
         }
@@ -163,14 +150,6 @@
         @Override
         void setOpen(boolean open) {}
 
-        @Override
-        void syncState() {}
-
-        @Override
-        void lockOpen() {}
-
-        @Override
-        void lockClosed() {}
 
         @Override
         boolean isOpen() {
@@ -178,19 +157,15 @@
         }
 
         @Override
-        boolean isUnlocked() {
-            return true;
-        }
-
-        @Override
         boolean isPresent() {
             return false;
         }
 
         @Override
-        boolean onOptionsItemSelected(MenuItem item) {
-            return false;
-        }
+        void setTitle(String title) {}
+
+        @Override
+        void update() {}
 
         @Override
         public void onDrawerSlide(View drawerView, float slideOffset) {}
diff --git a/packages/DocumentsUI/src/com/android/documentsui/FilesActivity.java b/packages/DocumentsUI/src/com/android/documentsui/FilesActivity.java
index 064535a..c81f342 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/FilesActivity.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/FilesActivity.java
@@ -30,7 +30,6 @@
 import android.content.ContentValues;
 import android.content.Intent;
 import android.net.Uri;
-import android.os.AsyncTask;
 import android.os.Bundle;
 import android.os.Parcelable;
 import android.provider.DocumentsContract;
@@ -40,10 +39,6 @@
 import android.view.KeyEvent;
 import android.view.Menu;
 import android.view.MenuItem;
-import android.view.View;
-import android.widget.BaseAdapter;
-import android.widget.Spinner;
-import android.widget.Toolbar;
 
 import com.android.documentsui.OperationDialogFragment.DialogType;
 import com.android.documentsui.RecentsProvider.ResumeColumns;
@@ -67,10 +62,6 @@
 
     public static final String TAG = "FilesActivity";
 
-    private Toolbar mToolbar;
-    private Spinner mToolbarStack;
-    private ItemSelectedListener mStackListener;
-    private BaseAdapter mStackAdapter;
     private DocumentClipper mClipper;
 
     public FilesActivity() {
@@ -81,17 +72,7 @@
     public void onCreate(Bundle icicle) {
         super.onCreate(icicle);
 
-        mToolbar = (Toolbar) findViewById(R.id.toolbar);
-
-        mStackAdapter = new StackAdapter();
-        mStackListener = new ItemSelectedListener();
-        mToolbarStack = (Spinner) findViewById(R.id.stack);
-        mToolbarStack.setOnItemSelectedListener(mStackListener);
-
-        setActionBar(mToolbar);
-
         mClipper = new DocumentClipper(this);
-        mDrawer = DrawerController.create(this);
 
         RootsFragment.show(getFragmentManager(), null);
 
@@ -116,19 +97,19 @@
             refreshCurrentRootAndDirectory(ANIM_NONE);
         } else if (intent.getAction() == Intent.ACTION_VIEW) {
             checkArgument(uri != null);
-            new OpenUriForViewTask().executeOnExecutor(
+            new OpenUriForViewTask(this).executeOnExecutor(
                     ProviderExecutor.forAuthority(uri.getAuthority()), uri);
         } else if (DocumentsContract.isRootUri(this, uri)) {
             if (DEBUG) Log.d(TAG, "Launching with root URI.");
             // If we've got a specific root to display, restore that root using a dedicated
             // authority. That way a misbehaving provider won't result in an ANR.
-            new RestoreRootTask(uri).executeOnExecutor(
+            new RestoreRootTask(this, uri).executeOnExecutor(
                     ProviderExecutor.forAuthority(uri.getAuthority()));
         } else {
             if (DEBUG) Log.d(TAG, "Launching into Home directory.");
             // If all else fails, try to load "Home" directory.
             final Uri homeUri = DocumentsContract.buildHomeUri();
-            new RestoreRootTask(homeUri).executeOnExecutor(
+            new RestoreRootTask(this, homeUri).executeOnExecutor(
                     ProviderExecutor.forAuthority(homeUri.getAuthority()));
         }
 
@@ -152,9 +133,7 @@
     }
 
     @Override
-    State buildState() {
-        State state = buildDefaultState();
-
+    void includeState(State state) {
         final Intent intent = getIntent();
 
         state.action = State.ACTION_BROWSE;
@@ -167,8 +146,6 @@
         if (stack != null) {
             state.stack = stack;
         }
-
-        return state;
     }
 
     @Override
@@ -179,11 +156,11 @@
         // serach. Why? Because this avoid an early (undesired) load of
         // the recents root...which is the default root in other activities.
         // In Files app "Home" is the default, but it is loaded async.
-        // updateActionBar will be called once Home root is loaded.
+        // update will be called once Home root is loaded.
         // Except while searching we need this call to ensure the
         // search bits get layed out correctly.
         if (mSearchManager.isSearching()) {
-            updateActionBar();
+            mNavigator.update();
         }
     }
 
@@ -203,52 +180,8 @@
     }
 
     @Override
-    public void updateActionBar() {
-        final RootInfo root = getCurrentRoot();
-
-        if (mDrawer.isPresent()) {
-            mToolbar.setNavigationIcon(R.drawable.ic_hamburger);
-            mToolbar.setNavigationContentDescription(R.string.drawer_open);
-            mToolbar.setNavigationOnClickListener(
-                    new View.OnClickListener() {
-                        @Override
-                        public void onClick(View v) {
-                            mDrawer.setOpen(true);
-                        }
-                    });
-        } else {
-            mToolbar.setNavigationIcon(
-                    root != null ? root.loadToolbarIcon(mToolbar.getContext()) : null);
-            mToolbar.setNavigationContentDescription(R.string.drawer_open);
-            mToolbar.setNavigationOnClickListener(null);
-        }
-
-        if (mSearchManager.isExpanded()) {
-            mToolbar.setTitle(null);
-            mToolbarStack.setVisibility(View.GONE);
-            mToolbarStack.setAdapter(null);
-        } else {
-            if (mState.stack.size() <= 1) {
-                mToolbar.setTitle(root.title);
-                mToolbarStack.setVisibility(View.GONE);
-                mToolbarStack.setAdapter(null);
-            } else {
-                mToolbar.setTitle(null);
-                mToolbarStack.setVisibility(View.VISIBLE);
-                mToolbarStack.setAdapter(mStackAdapter);
-
-                mStackListener.mIgnoreNextNavigation = true;
-                mToolbarStack.setSelection(mStackAdapter.getCount() - 1);
-            }
-        }
-    }
-
-    @Override
-    public boolean onCreateOptionsMenu(Menu menu) {
-        boolean showMenu = super.onCreateOptionsMenu(menu);
-
-        expandMenus(menu);
-        return showMenu;
+    public String getDrawerTitle() {
+        return getResources().getString(R.string.files_label);
     }
 
     @Override
@@ -260,15 +193,13 @@
         final MenuItem createDir = menu.findItem(R.id.menu_create_dir);
         final MenuItem pasteFromCb = menu.findItem(R.id.menu_paste_from_clipboard);
         final MenuItem settings = menu.findItem(R.id.menu_settings);
+        final MenuItem newWindow = menu.findItem(R.id.menu_new_window);
 
         createDir.setVisible(true);
         createDir.setEnabled(canCreateDirectory());
         pasteFromCb.setEnabled(mClipper.hasItemsToPaste());
         settings.setVisible(root.hasSettings());
-
-        // TODO: For some reason settings menu item is not
-        // honoring the "showAsAction=never" setting in activity.xml.
-        settings.setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER);
+        newWindow.setVisible(true);
 
         Menus.disableHiddenItems(menu, pasteFromCb);
         return true;
@@ -417,13 +348,15 @@
         }
     }
 
-    @Override
-    void saveStackBlocking() {
+    // Turns out only DocumentsActivity was ever calling saveStackBlocking.
+    // There may be a  case where we want to contribute entries from
+    // Behavior here in FilesActivity, but it isn't yet obvious.
+    // TODO: Contribute to recents, or remove this.
+    void writeStackToRecentsBlocking() {
         final ContentResolver resolver = getContentResolver();
         final ContentValues values = new ContentValues();
 
-        final byte[] rawStack = DurableUtils.writeToArrayOrNull(
-                getDisplayState().stack);
+        final byte[] rawStack = DurableUtils.writeToArrayOrNull(mState.stack);
 
         // Remember location for next app launch
         final String packageName = getCallingPackageMaybeExtra();
@@ -462,12 +395,19 @@
      * to know which root to select. Also, the stack doesn't contain intermediate directories.
      * It's primarly used for opening ZIP archives from Downloads app.
      */
-    final class OpenUriForViewTask extends AsyncTask<Uri, Void, Void> {
+    private static final class OpenUriForViewTask extends PairedTask<FilesActivity, Uri, Void> {
+
+        private final State mState;
+        public OpenUriForViewTask(FilesActivity activity) {
+            super(activity);
+            mState = activity.mState;
+        }
+
         @Override
-        protected Void doInBackground(Uri... params) {
+        protected Void run(Uri... params) {
             final Uri uri = params[0];
 
-            final RootsCache rootsCache = DocumentsApplication.getRootsCache(FilesActivity.this);
+            final RootsCache rootsCache = DocumentsApplication.getRootsCache(mOwner);
             final String authority = uri.getAuthority();
 
             final Collection<RootInfo> roots =
@@ -480,20 +420,17 @@
             final RootInfo root = roots.iterator().next();
             mState.stack.root = root;
             try {
-                mState.stack.add(DocumentInfo.fromUri(getContentResolver(), uri));
+                mState.stack.add(DocumentInfo.fromUri(mOwner.getContentResolver(), uri));
             } catch (FileNotFoundException e) {
                 Log.e(TAG, "Failed to resolve DocumentInfo from Uri: " + uri);
             }
-            mState.stack.add(getRootDocumentBlocking(root));
+            mState.stack.add(mOwner.getRootDocumentBlocking(root));
             return null;
         }
 
         @Override
-        protected void onPostExecute(Void result) {
-            if (isDestroyed()) {
-                return;
-            }
-            refreshCurrentRootAndDirectory(ANIM_NONE);
+        protected void finish(Void result) {
+            mOwner.refreshCurrentRootAndDirectory(ANIM_NONE);
         }
     }
 }
diff --git a/packages/DocumentsUI/src/com/android/documentsui/NavigationView.java b/packages/DocumentsUI/src/com/android/documentsui/NavigationView.java
new file mode 100644
index 0000000..ff1940a
--- /dev/null
+++ b/packages/DocumentsUI/src/com/android/documentsui/NavigationView.java
@@ -0,0 +1,246 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.documentsui;
+
+import static android.view.View.GONE;
+import static android.view.View.VISIBLE;
+import static com.android.documentsui.Shared.DEBUG;
+import static com.android.documentsui.dirlist.DirectoryFragment.ANIM_LEAVE;
+
+import android.annotation.Nullable;
+import android.graphics.drawable.Drawable;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.AdapterView;
+import android.widget.AdapterView.OnItemSelectedListener;
+import android.widget.BaseAdapter;
+import android.widget.ImageView;
+import android.widget.Spinner;
+import android.widget.TextView;
+
+import com.android.documentsui.model.DocumentInfo;
+import com.android.documentsui.model.RootInfo;
+
+/**
+ * A facade over the portions of the app and drawer toolbars.
+ */
+class NavigationView {
+
+    private static final String TAG = "NavigationView";
+
+    private final DrawerController mDrawer;
+    private final DocumentsToolbar mToolbar;
+    private final Spinner mBreadcrumb;
+    private final State mState;
+    private final NavigationView.Environment mEnv;
+    private final BreadcrumbAdapter mBreadcrumbAdapter;
+
+    private boolean mIgnoreNextNavigation;
+
+    public NavigationView(
+            DrawerController drawer,
+            DocumentsToolbar toolbar,
+            Spinner breadcrumb,
+            State state,
+            NavigationView.Environment env) {
+
+        mToolbar = toolbar;
+        mBreadcrumb = breadcrumb;
+        mDrawer = drawer;
+        mState = state;
+        mEnv = env;
+
+        mBreadcrumbAdapter = new BreadcrumbAdapter(mState, mEnv);
+        mToolbar.setNavigationOnClickListener(
+                new View.OnClickListener() {
+                    @Override
+                    public void onClick(View v) {
+                        onNavigationIconClicked();
+                    }
+
+                });
+
+        mBreadcrumb.setOnItemSelectedListener(
+                new OnItemSelectedListener() {
+                    @Override
+                    public void onItemSelected(
+                            AdapterView<?> parent, View view, int position, long id) {
+                        onBreadcrumbItemSelected(position);
+                    }
+
+                    @Override
+                    public void onNothingSelected(AdapterView<?> parent) {}
+                });
+
+    }
+
+    private void onNavigationIconClicked() {
+        if (mDrawer.isPresent()) {
+            mDrawer.setOpen(true);
+        }
+    }
+
+    private void onBreadcrumbItemSelected(int position) {
+        if (mIgnoreNextNavigation) {
+            mIgnoreNextNavigation = false;
+            return;
+        }
+
+        while (mState.stack.size() > position + 1) {
+            mState.popDocument();
+        }
+        mEnv.refreshCurrentRootAndDirectory(ANIM_LEAVE);
+    }
+
+    void update() {
+
+        // TODO: Looks to me like this block is never getting hit.
+        if (mEnv.isSearchExpanded()) {
+            mToolbar.setTitle(null);
+            mBreadcrumb.setVisibility(View.GONE);
+            mBreadcrumb.setAdapter(null);
+            return;
+        }
+
+        mDrawer.setTitle(mEnv.getDrawerTitle());
+
+        mToolbar.setNavigationIcon(getActionBarIcon());
+        mToolbar.setNavigationContentDescription(R.string.drawer_open);
+
+        if (mState.stack.size() <= 1) {
+            showBreadcrumb(false);
+            String title = mEnv.getCurrentRoot().title;
+            if (DEBUG) Log.d(TAG, "New toolbar title is: " + title);
+            mToolbar.setTitle(title);
+        } else {
+            showBreadcrumb(true);
+            mToolbar.setTitle(null);
+            mIgnoreNextNavigation = true;
+            mBreadcrumb.setSelection(mBreadcrumbAdapter.getCount() - 1);
+        }
+
+        if (DEBUG) Log.d(TAG, "Final toolbar title is: " + mToolbar.getTitle());
+    }
+
+    private void showBreadcrumb(boolean visibility) {
+        if (visibility) {
+            mBreadcrumb.setVisibility(VISIBLE);
+            mBreadcrumb.setAdapter(mBreadcrumbAdapter);
+        } else {
+            mBreadcrumb.setVisibility(GONE);
+            mBreadcrumb.setAdapter(null);
+        }
+    }
+
+    // Hamburger if drawer is present, else root icon, or sad nullness.
+    private @Nullable Drawable getActionBarIcon() {
+        if (mDrawer.isPresent()) {
+            return mToolbar.getContext().getDrawable(R.drawable.ic_hamburger);
+        } else {
+            RootInfo root = mEnv.getCurrentRoot();
+            if (root != null) {
+                return root.loadToolbarIcon(mToolbar.getContext());
+            }
+        }
+        return null;
+    }
+
+    void revealRootsDrawer(boolean open) {
+        mDrawer.setOpen(open);
+    }
+
+    /**
+     * Class providing toolbar with runtime access to useful activity data.
+     */
+    static final class BreadcrumbAdapter extends BaseAdapter {
+
+        private Environment mEnv;
+        private State mState;
+
+        public BreadcrumbAdapter(State state, Environment env) {
+            mState = state;
+            mEnv = env;
+        }
+
+        @Override
+        public int getCount() {
+            return mState.stack.size();
+        }
+
+        @Override
+        public DocumentInfo getItem(int position) {
+            return mState.stack.get(mState.stack.size() - position - 1);
+        }
+
+        @Override
+        public long getItemId(int position) {
+            return position;
+        }
+
+        @Override
+        public View getView(int position, View convertView, ViewGroup parent) {
+            if (convertView == null) {
+                convertView = LayoutInflater.from(parent.getContext())
+                        .inflate(R.layout.item_subdir_title, parent, false);
+            }
+
+            final TextView title = (TextView) convertView.findViewById(android.R.id.title);
+            final DocumentInfo doc = getItem(position);
+
+            if (position == 0) {
+                final RootInfo root = mEnv.getCurrentRoot();
+                title.setText(root.title);
+            } else {
+                title.setText(doc.displayName);
+            }
+
+            return convertView;
+        }
+
+        @Override
+        public View getDropDownView(int position, View convertView, ViewGroup parent) {
+            if (convertView == null) {
+                convertView = LayoutInflater.from(parent.getContext())
+                        .inflate(R.layout.item_subdir, parent, false);
+            }
+
+            final ImageView subdir = (ImageView) convertView.findViewById(R.id.subdir);
+            final TextView title = (TextView) convertView.findViewById(android.R.id.title);
+            final DocumentInfo doc = getItem(position);
+
+            if (position == 0) {
+                final RootInfo root = mEnv.getCurrentRoot();
+                title.setText(root.title);
+                subdir.setVisibility(View.GONE);
+            } else {
+                title.setText(doc.displayName);
+                subdir.setVisibility(View.VISIBLE);
+            }
+
+            return convertView;
+        }
+    }
+
+    interface Environment {
+        RootInfo getCurrentRoot();
+        String getDrawerTitle();
+        void refreshCurrentRootAndDirectory(int animation);
+        boolean isSearchExpanded();
+    }
+}
diff --git a/packages/DocumentsUI/src/com/android/documentsui/PairedTask.java b/packages/DocumentsUI/src/com/android/documentsui/PairedTask.java
new file mode 100644
index 0000000..b74acb8
--- /dev/null
+++ b/packages/DocumentsUI/src/com/android/documentsui/PairedTask.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.documentsui;
+
+import android.app.Activity;
+import android.os.AsyncTask;
+
+/**
+ * An {@link AsyncTask} that guards work with checks that a paired {@link Activity}
+ * is still alive. Instances of this class make no progress.
+ *
+ * <p>Use this type of task for greater safety when executing tasks that might complete
+ * after an Activity is destroyed.
+ *
+ * <p>Also useful as tasks can be static, limiting scope, but still have access to
+ * the owning class (by way the A template and the mActivity field).
+ *
+ * @template Owner Activity type.
+ * @template Input input type
+ * @template Output output type
+ */
+abstract class PairedTask<Owner extends Activity, Input, Output>
+        extends AsyncTask<Input, Void, Output> {
+
+    protected final Owner mOwner;
+
+    public PairedTask(Owner owner) {
+        mOwner = owner;
+    }
+
+    /** Called prior to run being executed. Analogous to {@link AsyncTask#onPreExecute} */
+    void prepare() {}
+
+    /** Analogous to {@link AsyncTask#doInBackground} */
+    abstract Output run(Input... input);
+
+    /** Analogous to {@link AsyncTask#onPostExecute} */
+    abstract void finish(Output output);
+
+    @Override
+    final protected void onPreExecute() {
+        if (mOwner.isDestroyed()) {
+            return;
+        }
+        prepare();
+    }
+
+    @Override
+    final protected Output doInBackground(Input... input) {
+        if (mOwner.isDestroyed()) {
+            return null;
+        }
+        return run(input);
+    }
+
+    @Override
+    final protected void onPostExecute(Output result) {
+        if (mOwner.isDestroyed()) {
+            return;
+        }
+        finish(result);
+    }
+}
diff --git a/packages/DocumentsUI/src/com/android/documentsui/RestoreRootTask.java b/packages/DocumentsUI/src/com/android/documentsui/RestoreRootTask.java
new file mode 100644
index 0000000..9048b9d
--- /dev/null
+++ b/packages/DocumentsUI/src/com/android/documentsui/RestoreRootTask.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.documentsui;
+
+import android.net.Uri;
+import android.provider.DocumentsContract;
+import android.util.Log;
+
+import com.android.documentsui.model.RootInfo;
+
+final class RestoreRootTask extends PairedTask<BaseActivity, Void, RootInfo> {
+    private static final String TAG = "RestoreRootTask";
+
+    private final Uri mRootUri;
+
+    public RestoreRootTask(BaseActivity activity, Uri rootUri) {
+        super(activity);
+        mRootUri = rootUri;
+    }
+
+    @Override
+    protected RootInfo run(Void... params) {
+        String rootId = DocumentsContract.getRootId(mRootUri);
+        return mOwner.mRoots.getRootOneshot(mRootUri.getAuthority(), rootId);
+    }
+
+    @Override
+    protected void finish(RootInfo root) {
+        mOwner.mState.restored = true;
+
+        if (root != null) {
+            mOwner.onRootPicked(root);
+        } else {
+            Log.w(TAG, "Failed to find root: " + mRootUri);
+            mOwner.finish();
+        }
+    }
+}
diff --git a/packages/DocumentsUI/src/com/android/documentsui/SearchManager.java b/packages/DocumentsUI/src/com/android/documentsui/SearchManager.java
index fb585a6..69f54c7 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/SearchManager.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/SearchManager.java
@@ -47,7 +47,7 @@
     private boolean mSearchExpanded;
     private boolean mIgnoreNextClose;
 
-    private DocumentsToolBar mActionBar;
+    private DocumentsToolbar mActionBar;
     private MenuItem mMenu;
     private SearchView mView;
 
@@ -59,7 +59,7 @@
         mListener = listener;
     }
 
-    public void install(DocumentsToolBar actionBar) {
+    public void install(DocumentsToolbar actionBar) {
         assert (mActionBar == null);
         mActionBar = actionBar;
         mMenu = actionBar.getSearchMenu();
diff --git a/packages/DocumentsUI/src/com/android/documentsui/dirlist/DirectoryFragment.java b/packages/DocumentsUI/src/com/android/documentsui/dirlist/DirectoryFragment.java
index 5f04eac..70bee3c 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/dirlist/DirectoryFragment.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/dirlist/DirectoryFragment.java
@@ -151,7 +151,6 @@
     private String mStateKey;
 
     private int mLastSortOrder = SORT_ORDER_UNKNOWN;
-    private boolean mLastShowSize;
     private DocumentsAdapter mAdapter;
     private LoaderCallbacks<DirectoryResult> mCallbacks;
     private FragmentTuner mTuner;
@@ -428,7 +427,6 @@
 
     private void updateDisplayState() {
         State state = getDisplayState();
-        mLastShowSize = state.showSize;
         updateLayout(state.derivedMode);
         mRecView.setAdapter(mAdapter);
     }
@@ -855,27 +853,28 @@
     }
 
     private void showEmptyDirectory() {
-        showEmptyView(R.string.empty);
+        showEmptyView(R.string.empty, R.drawable.cabinet);
     }
 
     private void showNoResults(RootInfo root) {
         CharSequence msg = getContext().getResources().getText(R.string.no_results);
-        showEmptyView(String.format(String.valueOf(msg), root.title));
+        showEmptyView(String.format(String.valueOf(msg), root.title), R.drawable.cabinet);
     }
 
-    // Shows an error indicating documents couldn't be queried.
     private void showQueryError() {
-        showEmptyView(R.string.query_error);
+        showEmptyView(R.string.query_error, R.drawable.hourglass);
     }
 
-    private void showEmptyView(@StringRes int id) {
-        showEmptyView(getContext().getResources().getText(id));
+    private void showEmptyView(@StringRes int id, int drawable) {
+        showEmptyView(getContext().getResources().getText(id), drawable);
     }
 
-    private void showEmptyView(CharSequence msg) {
+    private void showEmptyView(CharSequence msg, int drawable) {
         View content = mEmptyView.findViewById(R.id.content);
         TextView msgView = (TextView) mEmptyView.findViewById(R.id.message);
+        ImageView imageView = (ImageView) mEmptyView.findViewById(R.id.artwork);
         msgView.setText(msg);
+        imageView.setImageResource(drawable);
 
         content.animate().cancel();  // cancel any ongoing animations
 
diff --git a/packages/DocumentsUI/tests/src/com/android/documentsui/DownloadsActivityUiTest.java b/packages/DocumentsUI/tests/src/com/android/documentsui/DownloadsActivityUiTest.java
index 0a91427..243c357 100644
--- a/packages/DocumentsUI/tests/src/com/android/documentsui/DownloadsActivityUiTest.java
+++ b/packages/DocumentsUI/tests/src/com/android/documentsui/DownloadsActivityUiTest.java
@@ -30,7 +30,7 @@
 import android.support.test.uiautomator.Configurator;
 import android.support.test.uiautomator.UiDevice;
 import android.support.test.uiautomator.Until;
-import android.test.InstrumentationTestCase;
+import android.test.ActivityInstrumentationTestCase2;
 import android.test.suitebuilder.annotation.LargeTest;
 import android.util.Log;
 import android.view.MotionEvent;
@@ -38,7 +38,7 @@
 import com.android.documentsui.model.RootInfo;
 
 @LargeTest
-public class DownloadsActivityUiTest extends InstrumentationTestCase {
+public class DownloadsActivityUiTest extends ActivityInstrumentationTestCase2<DownloadsActivity> {
 
     private static final int TIMEOUT = 5000;
     private static final String TAG = "DownloadsActivityUiTest";
@@ -53,6 +53,10 @@
     private ContentProviderClient mClient;
     private RootInfo mRoot;
 
+    public DownloadsActivityUiTest() {
+        super(DownloadsActivity.class);
+    }
+
     public void setUp() throws Exception {
         // Initialize UiDevice instance.
         Instrumentation instrumentation = getInstrumentation();
@@ -79,7 +83,8 @@
         intent.setDataAndType(mRoot.getUri(), DocumentsContract.Root.MIME_TYPE_ITEM);
         intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
         intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
-        mContext.startActivity(intent);
+        setActivityIntent(intent);
+        getActivity();  // Start the activity.
 
         // Wait for the app to appear.
         mDevice.wait(Until.hasObject(By.pkg(TARGET_PKG).depth(0)), TIMEOUT);
@@ -92,11 +97,11 @@
 
     @Override
     protected void tearDown() throws Exception {
-        // Need to kill off the task we started.
-        super.tearDown();
         Log.d(TAG, "Resetting storage from setUp");
         resetStorage();
         mClient.release();
+
+        super.tearDown();
     }
 
     private void resetStorage() throws RemoteException {
diff --git a/packages/DocumentsUI/tests/src/com/android/documentsui/FilesActivityUiTest.java b/packages/DocumentsUI/tests/src/com/android/documentsui/FilesActivityUiTest.java
index 2535a80e..868dbbb 100644
--- a/packages/DocumentsUI/tests/src/com/android/documentsui/FilesActivityUiTest.java
+++ b/packages/DocumentsUI/tests/src/com/android/documentsui/FilesActivityUiTest.java
@@ -30,7 +30,7 @@
 import android.support.test.uiautomator.Configurator;
 import android.support.test.uiautomator.UiDevice;
 import android.support.test.uiautomator.Until;
-import android.test.InstrumentationTestCase;
+import android.test.ActivityInstrumentationTestCase2;
 import android.test.suitebuilder.annotation.LargeTest;
 import android.util.Log;
 import android.view.MotionEvent;
@@ -38,7 +38,7 @@
 import com.android.documentsui.model.RootInfo;
 
 @LargeTest
-public class FilesActivityUiTest extends InstrumentationTestCase {
+public class FilesActivityUiTest extends ActivityInstrumentationTestCase2<FilesActivity> {
 
     private static final int TIMEOUT = 5000;
     private static final String TAG = "FilesActivityUiTest";
@@ -54,6 +54,10 @@
     private RootInfo mRoot_0;
     private RootInfo mRoot_1;
 
+    public FilesActivityUiTest() {
+        super(FilesActivity.class);
+    }
+
     public void setUp() throws Exception {
         // Initialize UiDevice instance.
         Instrumentation instrumentation = getInstrumentation();
@@ -71,12 +75,14 @@
         mResolver = mContext.getContentResolver();
 
         mClient = mResolver.acquireUnstableContentProviderClient(DEFAULT_AUTHORITY);
+        assertNotNull("Failed to acquire ContentProviderClient.", mClient);
         mDocsHelper = new DocumentsProviderHelper(DEFAULT_AUTHORITY, mClient);
 
         // Launch app.
         Intent intent = mContext.getPackageManager().getLaunchIntentForPackage(TARGET_PKG);
         intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
-        mContext.startActivity(intent);
+        setActivityIntent(intent);
+        getActivity();  // Start the activity.
 
         // Wait for the app to appear.
         mDevice.wait(Until.hasObject(By.pkg(TARGET_PKG).depth(0)), TIMEOUT);
@@ -89,10 +95,11 @@
 
     @Override
     protected void tearDown() throws Exception {
-        super.tearDown();
         Log.d(TAG, "Resetting storage from setUp");
         resetStorage();
         mClient.release();
+
+        super.tearDown();
     }
 
     private void resetStorage() throws RemoteException {
diff --git a/packages/DocumentsUI/tests/src/com/android/documentsui/StubProvider.java b/packages/DocumentsUI/tests/src/com/android/documentsui/StubProvider.java
index 9855427..2527650 100644
--- a/packages/DocumentsUI/tests/src/com/android/documentsui/StubProvider.java
+++ b/packages/DocumentsUI/tests/src/com/android/documentsui/StubProvider.java
@@ -16,10 +16,6 @@
 
 package com.android.documentsui;
 
-import static com.android.documentsui.Shared.TAG;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
 import android.content.Context;
 import android.content.SharedPreferences;
 import android.content.pm.ProviderInfo;
@@ -471,6 +467,14 @@
 
     @Override
     public Bundle call(String method, String arg, Bundle extras) {
+        // We're not supposed to override any of the default DocumentsProvider
+        // methods that are supported by "call", so javadoc asks that we
+        // always call super.call first and return if response is not null.
+        Bundle result = super.call(method, arg, extras);
+        if (result != null) {
+            return result;
+        }
+
         switch (method) {
             case "clear":
                 clearCacheAndBuildRoots();
@@ -484,11 +488,10 @@
                 simulateReadErrorsForFile(arg);
                 return null;
             case "createDocumentWithFlags":
-                Bundle bundle = dispatchCreateDocumentWithFlags(extras);
-                return bundle;
-            default:
-                return super.call(method, arg, extras);
+                return dispatchCreateDocumentWithFlags(extras);
         }
+
+        return null;
     }
 
     private Bundle createVirtualFileFromBundle(Bundle extras) {
diff --git a/packages/MtpDocumentsProvider/AndroidManifest.xml b/packages/MtpDocumentsProvider/AndroidManifest.xml
index 7ea54c9..2dd49ab 100644
--- a/packages/MtpDocumentsProvider/AndroidManifest.xml
+++ b/packages/MtpDocumentsProvider/AndroidManifest.xml
@@ -30,16 +30,12 @@
         </activity>
 
         <receiver android:name=".UsbIntentReceiver" android:exported="true">
-            <!-- TODO: Remove an intent-filter for USB_DEVICE_ATTACHED after the provider becomes to
-                       open devices on demand. -->
             <intent-filter>
                 <action android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED" />
+                <action android:name="android.hardware.usb.action.USB_DEVICE_DETACHED" />
             </intent-filter>
             <meta-data android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED"
                        android:resource="@xml/device_filter" />
-            <intent-filter>
-                <action android:name="android.hardware.usb.action.USB_DEVICE_DETACHED" />
-            </intent-filter>
         </receiver>
     </application>
 </manifest>
diff --git a/packages/MtpDocumentsProvider/src/com/android/mtp/DocumentLoader.java b/packages/MtpDocumentsProvider/src/com/android/mtp/DocumentLoader.java
index 1829746b..ef1e8e2 100644
--- a/packages/MtpDocumentsProvider/src/com/android/mtp/DocumentLoader.java
+++ b/packages/MtpDocumentsProvider/src/com/android/mtp/DocumentLoader.java
@@ -24,9 +24,11 @@
 import android.os.Bundle;
 import android.os.Process;
 import android.provider.DocumentsContract;
+import android.util.Log;
 
 import java.io.FileNotFoundException;
 import java.io.IOException;
+import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Date;
 import java.util.LinkedList;
@@ -56,11 +58,17 @@
 
     private static MtpObjectInfo[] loadDocuments(MtpManager manager, int deviceId, int[] handles)
             throws IOException {
-        final MtpObjectInfo[] objectInfos = new MtpObjectInfo[handles.length];
+        final ArrayList<MtpObjectInfo> objects = new ArrayList<>();
         for (int i = 0; i < handles.length; i++) {
-            objectInfos[i] = manager.getObjectInfo(deviceId, handles[i]);
+            final MtpObjectInfo info = manager.getObjectInfo(deviceId, handles[i]);
+            if (info == null) {
+                Log.e(MtpDocumentsProvider.TAG,
+                        "Failed to obtain object info handle=" + handles[i]);
+                continue;
+            }
+            objects.add(info);
         }
-        return objectInfos;
+        return objects.toArray(new MtpObjectInfo[objects.size()]);
     }
 
     synchronized Cursor queryChildDocuments(String[] columnNames, Identifier parent)
diff --git a/packages/MtpDocumentsProvider/src/com/android/mtp/Identifier.java b/packages/MtpDocumentsProvider/src/com/android/mtp/Identifier.java
index 76a1fad..124d207 100644
--- a/packages/MtpDocumentsProvider/src/com/android/mtp/Identifier.java
+++ b/packages/MtpDocumentsProvider/src/com/android/mtp/Identifier.java
@@ -44,11 +44,38 @@
             return false;
         final Identifier other = (Identifier) obj;
         return mDeviceId == other.mDeviceId && mStorageId == other.mStorageId &&
-                mObjectHandle == other.mObjectHandle && mDocumentId == other.mDocumentId;
+                mObjectHandle == other.mObjectHandle && mDocumentId.equals(other.mDocumentId);
     }
 
     @Override
     public int hashCode() {
         return Objects.hash(mDeviceId, mStorageId, mObjectHandle, mDocumentId);
     }
+
+    @Override
+    public String toString() {
+        final StringBuilder builder = new StringBuilder();
+        builder.append("Identifier { ");
+
+        builder.append("mDeviceId: ");
+        builder.append(mDeviceId);
+        builder.append(", ");
+
+        builder.append("mStorageId: ");
+        builder.append(mStorageId);
+        builder.append(", ");
+
+        builder.append("mObjectHandle: ");
+        builder.append(mObjectHandle);
+        builder.append(", ");
+
+        builder.append("mDocumentId: ");
+        builder.append(mDocumentId);
+        builder.append(", ");
+
+        builder.append("mDocumentType: ");
+        builder.append(mDocumentType);
+        builder.append(" }");
+        return builder.toString();
+    }
 }
diff --git a/packages/MtpDocumentsProvider/src/com/android/mtp/Mapper.java b/packages/MtpDocumentsProvider/src/com/android/mtp/Mapper.java
index ac47067..3faa8f4 100644
--- a/packages/MtpDocumentsProvider/src/com/android/mtp/Mapper.java
+++ b/packages/MtpDocumentsProvider/src/com/android/mtp/Mapper.java
@@ -20,11 +20,9 @@
 
 import android.annotation.Nullable;
 import android.content.ContentValues;
-import android.content.res.Resources;
 import android.database.Cursor;
 import android.database.DatabaseUtils;
 import android.database.sqlite.SQLiteDatabase;
-import android.database.sqlite.SQLiteException;
 import android.mtp.MtpObjectInfo;
 import android.provider.DocumentsContract.Document;
 import android.provider.DocumentsContract.Root;
@@ -87,12 +85,10 @@
     /**
      * Puts root information to database.
      * @param parentDocumentId Document ID of device document.
-     * @param resources Resources required to localize root name.
      * @param roots List of root information.
      * @return If roots are added or removed from the database.
      */
-    synchronized boolean putStorageDocuments(
-            String parentDocumentId, Resources resources, MtpRoot[] roots) {
+    synchronized boolean putStorageDocuments(String parentDocumentId, MtpRoot[] roots) {
         final SQLiteDatabase database = mDatabase.getSQLiteDatabase();
         database.beginTransaction();
         try {
@@ -117,7 +113,7 @@
                 valuesList[i] = new ContentValues();
                 extraValuesList[i] = new ContentValues();
                 MtpDatabase.getStorageDocumentValues(
-                        valuesList[i], extraValuesList[i], resources, parentDocumentId, roots[i]);
+                        valuesList[i], extraValuesList[i], parentDocumentId, roots[i]);
             }
             final boolean changed = putDocuments(
                     valuesList,
diff --git a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDatabase.java b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDatabase.java
index 59d85c1..8a3ebefa 100644
--- a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDatabase.java
+++ b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDatabase.java
@@ -108,7 +108,7 @@
      * @param columnNames Column names defined in {@link android.provider.DocumentsContract.Root}.
      * @return Database cursor.
      */
-    Cursor queryRoots(String[] columnNames) {
+    Cursor queryRoots(Resources resources, String[] columnNames) {
         final String selection =
                 COLUMN_ROW_STATE + " IN (?, ?) AND " + COLUMN_DOCUMENT_TYPE + " = ?";
         final Cursor deviceCursor = mDatabase.query(
@@ -183,10 +183,14 @@
                     }
                     if (storageCursor.getCount() == 1 && values.containsKey(Root.COLUMN_TITLE)) {
                         storageCursor.moveToFirst();
+                        // Add storage name to device name if we have only 1 storage.
                         values.put(
                                 Root.COLUMN_TITLE,
-                                storageCursor.getString(
-                                        storageCursor.getColumnIndex(Root.COLUMN_TITLE)));
+                                resources.getString(
+                                        R.string.root_name,
+                                        values.getAsString(Root.COLUMN_TITLE),
+                                        storageCursor.getString(
+                                                storageCursor.getColumnIndex(Root.COLUMN_TITLE))));
                     }
                 } finally {
                     storageCursor.close();
@@ -533,13 +537,11 @@
     /**
      * Gets {@link ContentValues} for the given root.
      * @param values {@link ContentValues} that receives values.
-     * @param resources Resources used to get localized root name.
      * @param root Root to be converted {@link ContentValues}.
      */
     static void getStorageDocumentValues(
             ContentValues values,
             ContentValues extraValues,
-            Resources resources,
             String parentDocumentId,
             MtpRoot root) {
         values.clear();
@@ -550,13 +552,12 @@
         values.put(COLUMN_ROW_STATE, ROW_STATE_VALID);
         values.put(COLUMN_DOCUMENT_TYPE, DOCUMENT_TYPE_STORAGE);
         values.put(Document.COLUMN_MIME_TYPE, Document.MIME_TYPE_DIR);
-        values.put(Document.COLUMN_DISPLAY_NAME, root.getRootName(resources));
+        values.put(Document.COLUMN_DISPLAY_NAME, root.mDescription);
         values.putNull(Document.COLUMN_SUMMARY);
         values.putNull(Document.COLUMN_LAST_MODIFIED);
         values.put(Document.COLUMN_ICON, R.drawable.ic_root_mtp);
         values.put(Document.COLUMN_FLAGS, 0);
-        values.put(Document.COLUMN_SIZE,
-                (int) Math.min(root.mMaxCapacity - root.mFreeSpace, Integer.MAX_VALUE));
+        values.put(Document.COLUMN_SIZE, root.mMaxCapacity - root.mFreeSpace);
 
         extraValues.put(
                 Root.COLUMN_FLAGS,
@@ -576,9 +577,7 @@
     static void getObjectDocumentValues(
             ContentValues values, int deviceId, String parentId, MtpObjectInfo info) {
         values.clear();
-        final String mimeType = info.getFormat() == MtpConstants.FORMAT_ASSOCIATION ?
-                DocumentsContract.Document.MIME_TYPE_DIR :
-                MediaFile.getMimeTypeForFormatCode(info.getFormat());
+        final String mimeType = getMimeType(info);
         int flag = 0;
         if (info.getProtectionStatus() == 0) {
             flag |= Document.FLAG_SUPPORTS_DELETE |
@@ -607,6 +606,17 @@
         values.put(Document.COLUMN_SIZE, info.getCompressedSize());
     }
 
+    private static String getMimeType(MtpObjectInfo info) {
+        if (info.getFormat() == MtpConstants.FORMAT_ASSOCIATION) {
+            return DocumentsContract.Document.MIME_TYPE_DIR;
+        }
+        final String formatCodeMimeType = MediaFile.getMimeTypeForFormatCode(info.getFormat());
+        if (formatCodeMimeType != null) {
+            return formatCodeMimeType;
+        }
+        return MediaFile.getMimeTypeForFile(info.getName());
+    }
+
     static String[] strings(Object... args) {
         final String[] results = new String[args.length];
         for (int i = 0; i < args.length; i++) {
diff --git a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDocumentsProvider.java b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDocumentsProvider.java
index 775f976..0338454 100644
--- a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDocumentsProvider.java
+++ b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDocumentsProvider.java
@@ -58,6 +58,8 @@
             Document.COLUMN_FLAGS, Document.COLUMN_SIZE,
     };
 
+    static final boolean DEBUG = false;
+
     private final Object mDeviceListLock = new Object();
 
     private static MtpDocumentsProvider sSingleton;
@@ -70,6 +72,7 @@
     private Resources mResources;
     private MtpDatabase mDatabase;
     private AppFuse mAppFuse;
+    private ServiceIntentSender mIntentSender;
 
     /**
      * Provides singleton instance to MtpDocumentsService.
@@ -86,8 +89,9 @@
         mResolver = getContext().getContentResolver();
         mDeviceToolkits = new HashMap<Integer, DeviceToolkit>();
         mDatabase = new MtpDatabase(getContext(), MtpDatabaseConstants.FLAG_DATABASE_IN_FILE);
-        mRootScanner = new RootScanner(mResolver, mResources, mMtpManager, mDatabase);
+        mRootScanner = new RootScanner(mResolver, mMtpManager, mDatabase);
         mAppFuse = new AppFuse(TAG, new AppFuseCallback());
+        mIntentSender = new ServiceIntentSender(getContext());
         // TODO: Mount AppFuse on demands.
         try {
             mAppFuse.mount(getContext().getSystemService(StorageManager.class));
@@ -105,14 +109,16 @@
             MtpManager mtpManager,
             ContentResolver resolver,
             MtpDatabase database,
-            StorageManager storageManager) {
+            StorageManager storageManager,
+            ServiceIntentSender intentSender) {
         mResources = resources;
         mMtpManager = mtpManager;
         mResolver = resolver;
         mDeviceToolkits = new HashMap<Integer, DeviceToolkit>();
         mDatabase = database;
-        mRootScanner = new RootScanner(mResolver, mResources, mMtpManager, mDatabase);
+        mRootScanner = new RootScanner(mResolver, mMtpManager, mDatabase);
         mAppFuse = new AppFuse(TAG, new AppFuseCallback());
+        mIntentSender = intentSender;
         // TODO: Mount AppFuse on demands.
         try {
             mAppFuse.mount(storageManager);
@@ -129,7 +135,7 @@
         if (projection == null) {
             projection = MtpDocumentsProvider.DEFAULT_ROOT_PROJECTION;
         }
-        final Cursor cursor = mDatabase.queryRoots(projection);
+        final Cursor cursor = mDatabase.queryRoots(mResources, projection);
         cursor.setNotificationUri(
                 mResolver, DocumentsContract.buildRootsUri(MtpDocumentsProvider.AUTHORITY));
         return cursor;
@@ -147,11 +153,15 @@
     @Override
     public Cursor queryChildDocuments(String parentDocumentId,
             String[] projection, String sortOrder) throws FileNotFoundException {
+        if (DEBUG) {
+            Log.d(TAG, "queryChildDocuments: " + parentDocumentId);
+        }
         if (projection == null) {
             projection = MtpDocumentsProvider.DEFAULT_DOCUMENT_PROJECTION;
         }
         Identifier parentIdentifier = mDatabase.createIdentifier(parentDocumentId);
         try {
+            openDevice(parentIdentifier.mDeviceId);
             if (parentIdentifier.mDocumentType == MtpDatabaseConstants.DOCUMENT_TYPE_DEVICE) {
                 final Identifier singleStorageIdentifier =
                         mDatabase.getSingleStorageIdentifier(parentDocumentId);
@@ -174,13 +184,17 @@
     public ParcelFileDescriptor openDocument(
             String documentId, String mode, CancellationSignal signal)
                     throws FileNotFoundException {
+        if (DEBUG) {
+            Log.d(TAG, "openDocument: " + documentId);
+        }
         final Identifier identifier = mDatabase.createIdentifier(documentId);
         try {
+            openDevice(identifier.mDeviceId);
             switch (mode) {
                 case "r":
                     final long fileSize = getFileSize(documentId);
-                    // MTP getPartialObject operation does not support files that are larger than 4GB.
-                    // Fallback to non-seekable file descriptor.
+                    // MTP getPartialObject operation does not support files that are larger than
+                    // 4GB. Fallback to non-seekable file descriptor.
                     // TODO: Use getPartialObject64 for MTP devices that support Android vendor
                     // extension.
                     if (fileSize <= 0xffffffffl) {
@@ -213,6 +227,7 @@
             CancellationSignal signal) throws FileNotFoundException {
         final Identifier identifier = mDatabase.createIdentifier(documentId);
         try {
+            openDevice(identifier.mDeviceId);
             return new AssetFileDescriptor(
                     getPipeManager(identifier).readThumbnail(mMtpManager, identifier),
                     0,  // Start offset.
@@ -227,6 +242,7 @@
     public void deleteDocument(String documentId) throws FileNotFoundException {
         try {
             final Identifier identifier = mDatabase.createIdentifier(documentId);
+            openDevice(identifier.mDeviceId);
             final Identifier parentIdentifier = mDatabase.getParentIdentifier(documentId);
             mMtpManager.deleteDocument(identifier.mDeviceId, identifier.mObjectHandle);
             mDatabase.deleteDocument(documentId);
@@ -257,8 +273,12 @@
     @Override
     public String createDocument(String parentDocumentId, String mimeType, String displayName)
             throws FileNotFoundException {
+        if (DEBUG) {
+            Log.d(TAG, "createDocument: " + displayName);
+        }
         try {
             final Identifier parentId = mDatabase.createIdentifier(parentDocumentId);
+            openDevice(parentId.mDeviceId);
             final ParcelFileDescriptor pipe[] = ParcelFileDescriptor.createReliablePipe();
             pipe[0].close();  // 0 bytes for a new document.
             final int formatCode = Document.MIME_TYPE_DIR.equals(mimeType) ?
@@ -286,11 +306,22 @@
 
     void openDevice(int deviceId) throws IOException {
         synchronized (mDeviceListLock) {
+            if (mDeviceToolkits.containsKey(deviceId)) {
+                return;
+            }
+            if (DEBUG) {
+                Log.d(TAG, "Open device " + deviceId);
+            }
             mMtpManager.openDevice(deviceId);
             mDeviceToolkits.put(
                     deviceId, new DeviceToolkit(mMtpManager, mResolver, mDatabase));
+            mIntentSender.sendUpdateNotificationIntent();
+            try {
+                mRootScanner.resume().await();
+            } catch (InterruptedException error) {
+                Log.e(TAG, "openDevice", error);
+            }
         }
-        mRootScanner.resume();
     }
 
     void closeDevice(int deviceId) throws IOException, InterruptedException {
@@ -298,6 +329,7 @@
             closeDeviceInternal(deviceId);
         }
         mRootScanner.resume();
+        mIntentSender.sendUpdateNotificationIntent();
     }
 
     int[] getOpenedDeviceIds() {
@@ -318,6 +350,13 @@
     }
 
     /**
+     * Resumes root scanner to handle the update of device list.
+     */
+    void resumeRootScanner() {
+        mRootScanner.resume();
+    }
+
+    /**
      * Finalize the content provider for unit tests.
      */
     @Override
@@ -356,6 +395,12 @@
 
     private void closeDeviceInternal(int deviceId) throws IOException, InterruptedException {
         // TODO: Flush the device before closing (if not closed externally).
+        if (!mDeviceToolkits.containsKey(deviceId)) {
+            return;
+        }
+        if (DEBUG) {
+            Log.d(TAG, "Close device " + deviceId);
+        }
         getDeviceToolkit(deviceId).mDocumentLoader.clearTasks();
         mDeviceToolkits.remove(deviceId);
         mMtpManager.closeDevice(deviceId);
diff --git a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDocumentsService.java b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDocumentsService.java
index 5bede86..9c4952b 100644
--- a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDocumentsService.java
+++ b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDocumentsService.java
@@ -19,13 +19,12 @@
 import android.app.Notification;
 import android.app.Service;
 import android.app.NotificationManager;
+import android.content.ComponentName;
+import android.content.Context;
 import android.content.Intent;
-import android.hardware.usb.UsbDevice;
 import android.os.IBinder;
 import android.util.Log;
 
-import com.android.internal.util.Preconditions;
-
 import java.io.IOException;
 
 /**
@@ -36,6 +35,7 @@
 public class MtpDocumentsService extends Service {
     static final String ACTION_OPEN_DEVICE = "com.android.mtp.OPEN_DEVICE";
     static final String ACTION_CLOSE_DEVICE = "com.android.mtp.CLOSE_DEVICE";
+    static final String ACTION_UPDATE_NOTIFICATION = "com.android.mtp.UPDATE_NOTIFICATION";
     static final String EXTRA_DEVICE = "device";
 
     NotificationManager mNotificationManager;
@@ -55,32 +55,10 @@
     @Override
     public int onStartCommand(Intent intent, int flags, int startId) {
         // If intent is null, the service was restarted.
-        if (intent != null) {
-            final MtpDocumentsProvider provider = MtpDocumentsProvider.getInstance();
-            final UsbDevice device = intent.<UsbDevice>getParcelableExtra(EXTRA_DEVICE);
-            try {
-                Preconditions.checkNotNull(device);
-                switch (intent.getAction()) {
-                    case ACTION_OPEN_DEVICE:
-                        provider.openDevice(device.getDeviceId());
-                        break;
-
-                    case ACTION_CLOSE_DEVICE:
-                        mNotificationManager.cancel(device.getDeviceId());
-                        provider.closeDevice(device.getDeviceId());
-                        break;
-
-                    default:
-                        throw new IllegalArgumentException("Received unknown intent action.");
-                }
-            } catch (IOException | InterruptedException | IllegalArgumentException error) {
-                logErrorMessage(error);
-            }
-        } else {
-            // TODO: Fetch devices again.
+        if (intent == null || ACTION_UPDATE_NOTIFICATION.equals(intent.getAction())) {
+            return updateForegroundState() ? START_STICKY : START_NOT_STICKY;
         }
-
-        return updateForegroundState() ? START_STICKY : START_NOT_STICKY;
+        return START_NOT_STICKY;
     }
 
     /**
@@ -92,6 +70,7 @@
         final int[] deviceIds = provider.getOpenedDeviceIds();
         int notificationId = 0;
         Notification notification = null;
+        // TODO: Hide notification if the device has already been removed.
         for (final int deviceId : deviceIds) {
             try {
                 final String title = getResources().getString(
diff --git a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpManager.java b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpManager.java
index efe5ff1..5519efd 100644
--- a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpManager.java
+++ b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpManager.java
@@ -273,9 +273,7 @@
             final MtpRoot[] results = new MtpRoot[storageIds.length];
             for (int i = 0; i < storageIds.length; i++) {
                 results[i] = new MtpRoot(
-                        device.getDeviceId(),
-                        device.getDeviceInfo().getModel(),
-                        device.getStorageInfo(storageIds[i]));
+                        device.getDeviceId(), device.getStorageInfo(storageIds[i]));
             }
             return results;
         }
diff --git a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpRoot.java b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpRoot.java
index ec338c3..8530aaf 100644
--- a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpRoot.java
+++ b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpRoot.java
@@ -16,7 +16,6 @@
 
 package com.android.mtp;
 
-import android.content.res.Resources;
 import android.mtp.MtpStorageInfo;
 
 import com.android.internal.annotations.VisibleForTesting;
@@ -24,7 +23,6 @@
 class MtpRoot {
     final int mDeviceId;
     final int mStorageId;
-    final String mDeviceModelName;
     final String mDescription;
     final long mFreeSpace;
     final long mMaxCapacity;
@@ -33,24 +31,21 @@
     @VisibleForTesting
     MtpRoot(int deviceId,
             int storageId,
-            String deviceName,
             String description,
             long freeSpace,
             long maxCapacity,
             String volumeIdentifier) {
         mDeviceId = deviceId;
         mStorageId = storageId;
-        mDeviceModelName = deviceName;
         mDescription = description;
         mFreeSpace = freeSpace;
         mMaxCapacity = maxCapacity;
         mVolumeIdentifier = volumeIdentifier;
     }
 
-    MtpRoot(int deviceId, String deviceModelName, MtpStorageInfo storageInfo) {
+    MtpRoot(int deviceId, MtpStorageInfo storageInfo) {
         mDeviceId = deviceId;
         mStorageId = storageInfo.getStorageId();
-        mDeviceModelName = deviceModelName;
         mDescription = storageInfo.getDescription();
         mFreeSpace = storageInfo.getFreeSpace();
         mMaxCapacity = storageInfo.getMaxCapacity();
@@ -64,7 +59,6 @@
         final MtpRoot other = (MtpRoot) object;
         return mDeviceId == other.mDeviceId &&
                 mStorageId == other.mStorageId &&
-                mDeviceModelName.equals(other.mDeviceModelName) &&
                 mDescription.equals(other.mDescription) &&
                 mFreeSpace == other.mFreeSpace &&
                 mMaxCapacity == other.mMaxCapacity &&
@@ -73,19 +67,12 @@
 
     @Override
     public int hashCode() {
-        return mDeviceId ^ mStorageId ^ mDeviceModelName.hashCode() ^ mDescription.hashCode() ^
+        return mDeviceId ^ mStorageId ^ mDescription.hashCode() ^
                 ((int) mFreeSpace) ^ ((int) mMaxCapacity) ^ mVolumeIdentifier.hashCode();
     }
-    
+
     @Override
     public String toString() {
-        return "MtpRoot{Name: " + mDeviceModelName + " " + mDescription + "}";
-    }
-    
-    String getRootName(Resources resources) {
-        return String.format(
-                resources.getString(R.string.root_name),
-                mDeviceModelName,
-                mDescription);
+        return "MtpRoot{Name: " + mDescription + "}";
     }
 }
diff --git a/packages/MtpDocumentsProvider/src/com/android/mtp/RootScanner.java b/packages/MtpDocumentsProvider/src/com/android/mtp/RootScanner.java
index 15b8ef3..a4c3cf4 100644
--- a/packages/MtpDocumentsProvider/src/com/android/mtp/RootScanner.java
+++ b/packages/MtpDocumentsProvider/src/com/android/mtp/RootScanner.java
@@ -1,12 +1,28 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
 package com.android.mtp;
 
 import android.content.ContentResolver;
-import android.content.res.Resources;
 import android.net.Uri;
 import android.os.Process;
 import android.provider.DocumentsContract;
 import android.util.Log;
 
+import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
 import java.util.concurrent.FutureTask;
@@ -35,7 +51,6 @@
     private final static long AWAIT_TERMINATION_TIMEOUT = 2000;
 
     final ContentResolver mResolver;
-    final Resources mResources;
     final MtpManager mManager;
     final MtpDatabase mDatabase;
 
@@ -44,11 +59,9 @@
 
     RootScanner(
             ContentResolver resolver,
-            Resources resources,
             MtpManager manager,
             MtpDatabase database) {
         mResolver = resolver;
-        mResources = resources;
         mManager = manager;
         mDatabase = database;
     }
@@ -64,9 +77,8 @@
 
     /**
      * Starts to check new changes right away.
-     * If the background thread has already gone, it restarts another background thread.
      */
-    synchronized void resume() {
+    synchronized CountDownLatch resume() {
         if (mExecutor == null) {
             // Only single thread updates the database.
             mExecutor = Executors.newSingleThreadExecutor();
@@ -75,8 +87,10 @@
             // Cancel previous task.
             mCurrentTask.cancel(true);
         }
-        mCurrentTask = new FutureTask<Void>(new UpdateRootsRunnable(), null);
+        final UpdateRootsRunnable runnable = new UpdateRootsRunnable();
+        mCurrentTask = new FutureTask<Void>(runnable, null);
         mExecutor.submit(mCurrentTask);
+        return runnable.mFirstScanCompleted;
     }
 
     /**
@@ -98,6 +112,8 @@
      * Runnable to scan roots and update the database information.
      */
     private final class UpdateRootsRunnable implements Runnable {
+        final CountDownLatch mFirstScanCompleted = new CountDownLatch(1);
+
         @Override
         public void run() {
             Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
@@ -124,8 +140,7 @@
                         continue;
                     }
                     mDatabase.getMapper().startAddingDocuments(documentId);
-                    if (mDatabase.getMapper().putStorageDocuments(
-                            documentId, mResources, device.roots)) {
+                    if (mDatabase.getMapper().putStorageDocuments(documentId, device.roots)) {
                         changed = true;
                     }
                     if (mDatabase.getMapper().stopAddingDocuments(documentId)) {
@@ -136,6 +151,7 @@
                 if (changed) {
                     notifyChange();
                 }
+                mFirstScanCompleted.countDown();
                 pollingCount++;
                 try {
                     // Use SHORT_POLLING_PERIOD for the first SHORT_POLLING_TIMES because it is
diff --git a/packages/MtpDocumentsProvider/src/com/android/mtp/ServiceIntentSender.java b/packages/MtpDocumentsProvider/src/com/android/mtp/ServiceIntentSender.java
new file mode 100644
index 0000000..a1bb2c1
--- /dev/null
+++ b/packages/MtpDocumentsProvider/src/com/android/mtp/ServiceIntentSender.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.mtp;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+
+/**
+ * Sends intent to MtpDocumentsService.
+ */
+class ServiceIntentSender {
+    private Context mContext;
+
+    ServiceIntentSender(Context context) {
+        mContext = context;
+    }
+
+    void sendUpdateNotificationIntent() {
+        final Intent intent = new Intent(MtpDocumentsService.ACTION_UPDATE_NOTIFICATION);
+        intent.setComponent(new ComponentName(mContext, MtpDocumentsService.class));
+        mContext.startService(intent);
+    }
+}
diff --git a/packages/MtpDocumentsProvider/src/com/android/mtp/UsbIntentReceiver.java b/packages/MtpDocumentsProvider/src/com/android/mtp/UsbIntentReceiver.java
index 0ac130e..0489ea8 100644
--- a/packages/MtpDocumentsProvider/src/com/android/mtp/UsbIntentReceiver.java
+++ b/packages/MtpDocumentsProvider/src/com/android/mtp/UsbIntentReceiver.java
@@ -21,7 +21,9 @@
 import android.content.Intent;
 import android.hardware.usb.UsbDevice;
 import android.hardware.usb.UsbManager;
-import android.net.Uri;
+import android.util.Log;
+
+import java.io.IOException;
 
 public class UsbIntentReceiver extends BroadcastReceiver {
     @Override
@@ -29,17 +31,15 @@
         final UsbDevice device = intent.getExtras().getParcelable(UsbManager.EXTRA_DEVICE);
         switch (intent.getAction()) {
             case UsbManager.ACTION_USB_DEVICE_ATTACHED:
-                startService(context, MtpDocumentsService.ACTION_OPEN_DEVICE, device);
+                MtpDocumentsProvider.getInstance().resumeRootScanner();
                 break;
             case UsbManager.ACTION_USB_DEVICE_DETACHED:
-                startService(context, MtpDocumentsService.ACTION_CLOSE_DEVICE, device);
+                try {
+                    MtpDocumentsProvider.getInstance().closeDevice(device.getDeviceId());
+                } catch (IOException | InterruptedException e) {
+                    Log.e(MtpDocumentsProvider.TAG, "Failed to close device", e);
+                }
                 break;
         }
     }
-
-    private void startService(Context context, String action, UsbDevice device) {
-        final Intent intent = new Intent(action, Uri.EMPTY, context, MtpDocumentsService.class);
-        intent.putExtra(MtpDocumentsService.EXTRA_DEVICE, device);
-        context.startService(intent);
-    }
 }
diff --git a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/DocumentLoaderTest.java b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/DocumentLoaderTest.java
index 5f71606..af1fed4 100644
--- a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/DocumentLoaderTest.java
+++ b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/DocumentLoaderTest.java
@@ -42,8 +42,8 @@
     public void setUp() {
         mDatabase = new MtpDatabase(getContext(), MtpDatabaseConstants.FLAG_DATABASE_IN_MEMORY);
         mDatabase.getMapper().startAddingDocuments("deviceDocId");
-        mDatabase.getMapper().putStorageDocuments("deviceDocId", new TestResources(), new MtpRoot[] {
-                new MtpRoot(0, 0, "Device", "Storage", 1000, 1000, "")
+        mDatabase.getMapper().putStorageDocuments("deviceDocId", new MtpRoot[] {
+                new MtpRoot(0, 0, "Storage", 1000, 1000, "")
         });
         mDatabase.getMapper().stopAddingDocuments("deviceDocId");
         mManager = new BlockableTestMtpManager(getContext());
diff --git a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDatabaseTest.java b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDatabaseTest.java
index 4022886..a49dc67 100644
--- a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDatabaseTest.java
+++ b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDatabaseTest.java
@@ -81,8 +81,8 @@
         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().putStorageDocuments("1", new MtpRoot[] {
+                new MtpRoot(0, 1, "Storage", 1000, 2000, "")
         });
         mDatabase.getMapper().stopAddingDocuments("1");
 
@@ -96,7 +96,7 @@
             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));
+            assertEquals("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));
@@ -109,7 +109,7 @@
         }
 
         {
-            final Cursor cursor = mDatabase.queryRoots(new String [] {
+            final Cursor cursor = mDatabase.queryRoots(resources, new String [] {
                     Root.COLUMN_ROOT_ID,
                     Root.COLUMN_FLAGS,
                     Root.COLUMN_ICON,
@@ -139,10 +139,10 @@
 
     public void testPutStorageDocuments() throws Exception {
         mDatabase.getMapper().startAddingDocuments("deviceDocId");
-        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,"")
+        mDatabase.getMapper().putStorageDocuments("deviceDocId", new MtpRoot[] {
+                new MtpRoot(0, 1, "Storage", 1000, 2000, ""),
+                new MtpRoot(0, 2, "Storage", 2000, 4000, ""),
+                new MtpRoot(0, 3, "/@#%&<>Storage", 3000, 6000,"")
         });
 
         {
@@ -155,7 +155,7 @@
             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));
+            assertEquals("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));
@@ -166,11 +166,11 @@
 
             cursor.moveToNext();
             assertEquals(2, getInt(cursor, COLUMN_DOCUMENT_ID));
-            assertEquals("Device Storage", getString(cursor, COLUMN_DISPLAY_NAME));
+            assertEquals("Storage", getString(cursor, COLUMN_DISPLAY_NAME));
 
             cursor.moveToNext();
             assertEquals(3, getInt(cursor, COLUMN_DOCUMENT_ID));
-            assertEquals("Device /@#%&<>Storage", getString(cursor, COLUMN_DISPLAY_NAME));
+            assertEquals("/@#%&<>Storage", getString(cursor, COLUMN_DISPLAY_NAME));
 
             cursor.close();
         }
@@ -264,9 +264,9 @@
         };
 
         mDatabase.getMapper().startAddingDocuments("deviceDocId");
-        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, "")
+        mDatabase.getMapper().putStorageDocuments("deviceDocId", new MtpRoot[] {
+                new MtpRoot(0, 100, "Storage A", 1000, 0, ""),
+                new MtpRoot(0, 101, "Storage B", 1001, 0, "")
         });
 
         {
@@ -275,11 +275,11 @@
             cursor.moveToNext();
             assertEquals(1, getInt(cursor, COLUMN_DOCUMENT_ID));
             assertEquals(100, getInt(cursor, COLUMN_STORAGE_ID));
-            assertEquals("Device Storage A", getString(cursor, COLUMN_DISPLAY_NAME));
+            assertEquals("Storage A", getString(cursor, COLUMN_DISPLAY_NAME));
             cursor.moveToNext();
             assertEquals(2, getInt(cursor, COLUMN_DOCUMENT_ID));
             assertEquals(101, getInt(cursor, COLUMN_STORAGE_ID));
-            assertEquals("Device Storage B", getString(cursor, COLUMN_DISPLAY_NAME));
+            assertEquals("Storage B", getString(cursor, COLUMN_DISPLAY_NAME));
             cursor.close();
         }
 
@@ -291,18 +291,18 @@
             cursor.moveToNext();
             assertEquals(1, getInt(cursor, COLUMN_DOCUMENT_ID));
             assertTrue(isNull(cursor, COLUMN_STORAGE_ID));
-            assertEquals("Device Storage A", getString(cursor, COLUMN_DISPLAY_NAME));
+            assertEquals("Storage A", getString(cursor, COLUMN_DISPLAY_NAME));
             cursor.moveToNext();
             assertEquals(2, getInt(cursor, COLUMN_DOCUMENT_ID));
             assertTrue(isNull(cursor, COLUMN_STORAGE_ID));
-            assertEquals("Device Storage B", getString(cursor, COLUMN_DISPLAY_NAME));
+            assertEquals("Storage B", getString(cursor, COLUMN_DISPLAY_NAME));
             cursor.close();
         }
 
         mDatabase.getMapper().startAddingDocuments("deviceDocId");
-        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, "")
+        mDatabase.getMapper().putStorageDocuments("deviceDocId", new MtpRoot[] {
+                new MtpRoot(0, 200, "Storage A", 2000, 0, ""),
+                new MtpRoot(0, 202, "Storage C", 2002, 0, "")
         });
 
         {
@@ -311,15 +311,15 @@
             cursor.moveToNext();
             assertEquals(1, getInt(cursor, COLUMN_DOCUMENT_ID));
             assertTrue(isNull(cursor, COLUMN_STORAGE_ID));
-            assertEquals("Device Storage A", getString(cursor, COLUMN_DISPLAY_NAME));
+            assertEquals("Storage A", getString(cursor, COLUMN_DISPLAY_NAME));
             cursor.moveToNext();
             assertEquals(2, getInt(cursor, COLUMN_DOCUMENT_ID));
             assertTrue(isNull(cursor, COLUMN_STORAGE_ID));
-            assertEquals("Device Storage B", getString(cursor, COLUMN_DISPLAY_NAME));
+            assertEquals("Storage B", getString(cursor, COLUMN_DISPLAY_NAME));
             cursor.moveToNext();
             assertEquals(4, getInt(cursor, COLUMN_DOCUMENT_ID));
             assertEquals(202, getInt(cursor, COLUMN_STORAGE_ID));
-            assertEquals("Device Storage C", getString(cursor, COLUMN_DISPLAY_NAME));
+            assertEquals("Storage C", getString(cursor, COLUMN_DISPLAY_NAME));
             cursor.close();
         }
 
@@ -331,11 +331,11 @@
             cursor.moveToNext();
             assertEquals(1, getInt(cursor, COLUMN_DOCUMENT_ID));
             assertEquals(200, getInt(cursor, COLUMN_STORAGE_ID));
-            assertEquals("Device Storage A", getString(cursor, COLUMN_DISPLAY_NAME));
+            assertEquals("Storage A", getString(cursor, COLUMN_DISPLAY_NAME));
             cursor.moveToNext();
             assertEquals(4, getInt(cursor, COLUMN_DOCUMENT_ID));
             assertEquals(202, getInt(cursor, COLUMN_STORAGE_ID));
-            assertEquals("Device Storage C", getString(cursor, COLUMN_DISPLAY_NAME));
+            assertEquals("Storage C", getString(cursor, COLUMN_DISPLAY_NAME));
             cursor.close();
         }
     }
@@ -432,11 +432,11 @@
 
         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().putStorageDocuments("1", new MtpRoot[] {
+                new MtpRoot(0, 100, "Storage", 0, 0, "")
         });
-        mDatabase.getMapper().putStorageDocuments("2", resources, new MtpRoot[] {
-                new MtpRoot(1, 100, "Device B", "Storage", 0, 0, "")
+        mDatabase.getMapper().putStorageDocuments("2", new MtpRoot[] {
+                new MtpRoot(1, 100, "Storage", 0, 0, "")
         });
 
         {
@@ -445,16 +445,16 @@
             cursor.moveToNext();
             assertEquals(3, getInt(cursor, COLUMN_DOCUMENT_ID));
             assertEquals(100, getInt(cursor, COLUMN_STORAGE_ID));
-            assertEquals("Device A Storage", getString(cursor, COLUMN_DISPLAY_NAME));
+            assertEquals("Storage", getString(cursor, COLUMN_DISPLAY_NAME));
             cursor.moveToNext();
             assertEquals(4, getInt(cursor, COLUMN_DOCUMENT_ID));
             assertEquals(100, getInt(cursor, COLUMN_STORAGE_ID));
-            assertEquals("Device B Storage", getString(cursor, COLUMN_DISPLAY_NAME));
+            assertEquals("Storage", getString(cursor, COLUMN_DISPLAY_NAME));
             cursor.close();
         }
 
         {
-            final Cursor cursor = mDatabase.queryRoots(rootColumns);
+            final Cursor cursor = mDatabase.queryRoots(resources, rootColumns);
             assertEquals(2, cursor.getCount());
             cursor.moveToNext();
             assertEquals(1, getInt(cursor, Root.COLUMN_ROOT_ID));
@@ -469,11 +469,11 @@
 
         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().putStorageDocuments("1", new MtpRoot[] {
+                new MtpRoot(0, 200, "Storage", 2000, 0, "")
         });
-        mDatabase.getMapper().putStorageDocuments("2", resources, new MtpRoot[] {
-                new MtpRoot(1, 300, "Device", "Storage", 3000, 0, "")
+        mDatabase.getMapper().putStorageDocuments("2", new MtpRoot[] {
+                new MtpRoot(1, 300, "Storage", 3000, 0, "")
         });
         mDatabase.getMapper().stopAddingDocuments("1");
         mDatabase.getMapper().stopAddingDocuments("2");
@@ -482,18 +482,18 @@
             final Cursor cursor = mDatabase.queryRootDocuments(columns);
             assertEquals(2, cursor.getCount());
             cursor.moveToNext();
-            assertEquals(5, getInt(cursor, COLUMN_DOCUMENT_ID));
+            assertEquals(3, getInt(cursor, COLUMN_DOCUMENT_ID));
             assertEquals(200, getInt(cursor, COLUMN_STORAGE_ID));
-            assertEquals("Device Storage", getString(cursor, COLUMN_DISPLAY_NAME));
+            assertEquals("Storage", getString(cursor, COLUMN_DISPLAY_NAME));
             cursor.moveToNext();
-            assertEquals(6, getInt(cursor, COLUMN_DOCUMENT_ID));
+            assertEquals(4, getInt(cursor, COLUMN_DOCUMENT_ID));
             assertEquals(300, getInt(cursor, COLUMN_STORAGE_ID));
-            assertEquals("Device Storage", getString(cursor, COLUMN_DISPLAY_NAME));
+            assertEquals("Storage", getString(cursor, COLUMN_DISPLAY_NAME));
             cursor.close();
         }
 
         {
-            final Cursor cursor = mDatabase.queryRoots(rootColumns);
+            final Cursor cursor = mDatabase.queryRoots(resources, rootColumns);
             assertEquals(2, cursor.getCount());
             cursor.moveToNext();
             assertEquals(1, getInt(cursor, Root.COLUMN_ROOT_ID));
@@ -562,24 +562,24 @@
 
         mDatabase.getMapper().startAddingDocuments(null);
         mDatabase.getMapper().putDeviceDocument(
-                new MtpDeviceRecord(0, "Device",  false,  new MtpRoot[0], null, null));
+                new MtpDeviceRecord(0, "Device", false,  new MtpRoot[0], null, null));
         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().putStorageDocuments("1", new MtpRoot[] {
+                new MtpRoot(0, 100, "Storage", 0, 0, ""),
         });
         mDatabase.getMapper().clearMapping();
 
         mDatabase.getMapper().startAddingDocuments("1");
-        mDatabase.getMapper().putStorageDocuments("1", resources, new MtpRoot[] {
-                new MtpRoot(0, 200, "Device", "Storage", 2000, 0, ""),
+        mDatabase.getMapper().putStorageDocuments("1", new MtpRoot[] {
+                new MtpRoot(0, 200, "Storage", 2000, 0, ""),
         });
         mDatabase.getMapper().clearMapping();
 
         mDatabase.getMapper().startAddingDocuments("1");
-        mDatabase.getMapper().putStorageDocuments("1", resources, new MtpRoot[] {
-                new MtpRoot(0, 300, "Device", "Storage", 3000, 0, ""),
+        mDatabase.getMapper().putStorageDocuments("1", new MtpRoot[] {
+                new MtpRoot(0, 300, "Storage", 3000, 0, ""),
         });
         mDatabase.getMapper().stopAddingDocuments("1");
 
@@ -589,11 +589,11 @@
             cursor.moveToNext();
             assertEquals(2, getInt(cursor, COLUMN_DOCUMENT_ID));
             assertEquals(300, getInt(cursor, COLUMN_STORAGE_ID));
-            assertEquals("Device Storage", getString(cursor, COLUMN_DISPLAY_NAME));
+            assertEquals("Storage", getString(cursor, COLUMN_DISPLAY_NAME));
             cursor.close();
         }
         {
-            final Cursor cursor = mDatabase.queryRoots(rootColumns);
+            final Cursor cursor = mDatabase.queryRoots(resources, rootColumns);
             assertEquals(1, cursor.getCount());
             cursor.moveToNext();
             assertEquals(1, getInt(cursor, Root.COLUMN_ROOT_ID));
@@ -610,15 +610,15 @@
         };
 
         mDatabase.getMapper().startAddingDocuments("deviceDocId");
-        mDatabase.getMapper().putStorageDocuments("deviceDocId", resources, new MtpRoot[] {
-                new MtpRoot(0, 100, "Device", "Storage", 0, 0, ""),
+        mDatabase.getMapper().putStorageDocuments("deviceDocId", new MtpRoot[] {
+                new MtpRoot(0, 100, "Storage", 0, 0, ""),
         });
         mDatabase.getMapper().clearMapping();
 
         mDatabase.getMapper().startAddingDocuments("deviceDocId");
-        mDatabase.getMapper().putStorageDocuments("deviceDocId", resources, new MtpRoot[] {
-                new MtpRoot(0, 200, "Device", "Storage", 2000, 0, ""),
-                new MtpRoot(0, 201, "Device", "Storage", 2001, 0, ""),
+        mDatabase.getMapper().putStorageDocuments("deviceDocId", new MtpRoot[] {
+                new MtpRoot(0, 200, "Storage", 2000, 0, ""),
+                new MtpRoot(0, 201, "Storage", 2001, 0, ""),
         });
         mDatabase.getMapper().stopAddingDocuments("deviceDocId");
 
@@ -628,11 +628,11 @@
             cursor.moveToNext();
             assertEquals(2, getInt(cursor, COLUMN_DOCUMENT_ID));
             assertEquals(200, getInt(cursor, COLUMN_STORAGE_ID));
-            assertEquals("Device Storage", getString(cursor, COLUMN_DISPLAY_NAME));
+            assertEquals("Storage", getString(cursor, COLUMN_DISPLAY_NAME));
             cursor.moveToNext();
             assertEquals(3, getInt(cursor, COLUMN_DOCUMENT_ID));
             assertEquals(201, getInt(cursor, COLUMN_STORAGE_ID));
-            assertEquals("Device Storage", getString(cursor, COLUMN_DISPLAY_NAME));
+            assertEquals("Storage", getString(cursor, COLUMN_DISPLAY_NAME));
             cursor.close();
         }
     }
@@ -646,14 +646,14 @@
         // The client code should be able to replace existing rows with new information.
         // Add one.
         mDatabase.getMapper().startAddingDocuments("1");
-        mDatabase.getMapper().putStorageDocuments("1", resources, new MtpRoot[] {
-                new MtpRoot(0, 100, "Device", "Storage A", 0, 0, ""),
+        mDatabase.getMapper().putStorageDocuments("1", new MtpRoot[] {
+                new MtpRoot(0, 100, "Storage A", 0, 0, ""),
         });
         mDatabase.getMapper().stopAddingDocuments("1");
         // Replace it.
         mDatabase.getMapper().startAddingDocuments("1");
-        mDatabase.getMapper().putStorageDocuments("1", resources, new MtpRoot[] {
-                new MtpRoot(0, 100, "Device", "Storage B", 1000, 1000, ""),
+        mDatabase.getMapper().putStorageDocuments("1", new MtpRoot[] {
+                new MtpRoot(0, 100, "Storage B", 1000, 1000, ""),
         });
         mDatabase.getMapper().stopAddingDocuments("1");
         {
@@ -667,7 +667,7 @@
             cursor.moveToNext();
             assertEquals(2, getInt(cursor, COLUMN_DOCUMENT_ID));
             assertEquals(100, getInt(cursor, COLUMN_STORAGE_ID));
-            assertEquals("Device Storage B", getString(cursor, COLUMN_DISPLAY_NAME));
+            assertEquals("Storage B", getString(cursor, COLUMN_DISPLAY_NAME));
             cursor.close();
         }
         {
@@ -676,7 +676,7 @@
                     Root.COLUMN_TITLE,
                     Root.COLUMN_AVAILABLE_BYTES
             };
-            final Cursor cursor = mDatabase.queryRoots(columns);
+            final Cursor cursor = mDatabase.queryRoots(resources, columns);
             assertEquals(1, cursor.getCount());
             cursor.moveToNext();
             assertEquals(1, getInt(cursor, Root.COLUMN_ROOT_ID));
@@ -695,21 +695,21 @@
         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().putStorageDocuments("1", new MtpRoot[] {
+                new MtpRoot(0, 100, "Storage A", 0, 0, ""),
         });
         mDatabase.getMapper().clearMapping();
-        final Cursor oldCursor = mDatabase.queryRoots(strings(Root.COLUMN_ROOT_ID));
+        final Cursor oldCursor = mDatabase.queryRoots(resources, strings(Root.COLUMN_ROOT_ID));
         assertEquals(1, oldCursor.getCount());
 
         // Add one.
         mDatabase.getMapper().startAddingDocuments("1");
-        mDatabase.getMapper().putStorageDocuments("1", resources, new MtpRoot[] {
-                new MtpRoot(0, 101, "Device", "Storage B", 1000, 1000, ""),
+        mDatabase.getMapper().putStorageDocuments("1", new MtpRoot[] {
+                new MtpRoot(0, 101, "Storage B", 1000, 1000, ""),
         });
         // Add one more before resolving unmapped documents.
-        mDatabase.getMapper().putStorageDocuments("1", resources, new MtpRoot[] {
-                new MtpRoot(0, 102, "Device", "Storage B", 1000, 1000, ""),
+        mDatabase.getMapper().putStorageDocuments("1", new MtpRoot[] {
+                new MtpRoot(0, 102, "Storage B", 1000, 1000, ""),
         });
         mDatabase.getMapper().stopAddingDocuments("1");
 
@@ -729,15 +729,15 @@
 
     public void testQueryDocuments() {
         mDatabase.getMapper().startAddingDocuments("deviceDocId");
-        mDatabase.getMapper().putStorageDocuments("deviceDocId", resources, new MtpRoot[] {
-                new MtpRoot(0, 100, "Device", "Storage A", 0, 0, ""),
+        mDatabase.getMapper().putStorageDocuments("deviceDocId", new MtpRoot[] {
+                new MtpRoot(0, 100, "Storage A", 0, 0, ""),
         });
         mDatabase.getMapper().stopAddingDocuments("deviceDocId");
 
         final Cursor cursor = mDatabase.queryDocument("1", strings(Document.COLUMN_DISPLAY_NAME));
         assertEquals(1, cursor.getCount());
         cursor.moveToNext();
-        assertEquals("Device Storage A", getString(cursor, Document.COLUMN_DISPLAY_NAME));
+        assertEquals("Storage A", getString(cursor, Document.COLUMN_DISPLAY_NAME));
         cursor.close();
     }
 
@@ -750,7 +750,7 @@
 
         // It the device does not have storages, it shows a device root.
         {
-            final Cursor cursor = mDatabase.queryRoots(strings(Root.COLUMN_TITLE));
+            final Cursor cursor = mDatabase.queryRoots(resources, strings(Root.COLUMN_TITLE));
             assertEquals(1, cursor.getCount());
             cursor.moveToNext();
             assertEquals("Device", cursor.getString(0));
@@ -758,14 +758,14 @@
         }
 
         mDatabase.getMapper().startAddingDocuments("1");
-        mDatabase.getMapper().putStorageDocuments("1", resources, new MtpRoot[] {
-                new MtpRoot(0, 100, "Device", "Storage A", 0, 0, "")
+        mDatabase.getMapper().putStorageDocuments("1", new MtpRoot[] {
+                new MtpRoot(0, 100, "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));
+            final Cursor cursor = mDatabase.queryRoots(resources, strings(Root.COLUMN_TITLE));
             assertEquals(1, cursor.getCount());
             cursor.moveToNext();
             assertEquals("Device Storage A", cursor.getString(0));
@@ -773,15 +773,15 @@
         }
 
         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().putStorageDocuments("1", new MtpRoot[] {
+                new MtpRoot(0, 100, "Storage A", 0, 0, ""),
+                new MtpRoot(0, 101, "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));
+            final Cursor cursor = mDatabase.queryRoots(resources, strings(Root.COLUMN_TITLE));
             assertEquals(1, cursor.getCount());
             cursor.moveToNext();
             assertEquals("Device", cursor.getString(0));
@@ -791,8 +791,8 @@
 
     public void testGetParentId() throws FileNotFoundException {
         mDatabase.getMapper().startAddingDocuments("deviceDocId");
-        mDatabase.getMapper().putStorageDocuments("deviceDocId", resources, new MtpRoot[] {
-                new MtpRoot(0, 100, "Device", "Storage A", 0, 0, ""),
+        mDatabase.getMapper().putStorageDocuments("deviceDocId", new MtpRoot[] {
+                new MtpRoot(0, 100, "Storage A", 0, 0, ""),
         });
         mDatabase.getMapper().stopAddingDocuments("deviceDocId");
 
@@ -810,8 +810,8 @@
 
     public void testDeleteDocument() {
         mDatabase.getMapper().startAddingDocuments("deviceDocId");
-        mDatabase.getMapper().putStorageDocuments("deviceDocId", resources, new MtpRoot[] {
-                new MtpRoot(0, 100, "Device", "Storage A", 0, 0, ""),
+        mDatabase.getMapper().putStorageDocuments("deviceDocId", new MtpRoot[] {
+                new MtpRoot(0, 100, "Storage A", 0, 0, ""),
         });
         mDatabase.getMapper().stopAddingDocuments("deviceDocId");
 
@@ -854,8 +854,8 @@
 
     public void testPutNewDocument() {
         mDatabase.getMapper().startAddingDocuments("deviceDocId");
-        mDatabase.getMapper().putStorageDocuments("deviceDocId", resources, new MtpRoot[] {
-                new MtpRoot(0, 100, "Device", "Storage A", 0, 0, ""),
+        mDatabase.getMapper().putStorageDocuments("deviceDocId", new MtpRoot[] {
+                new MtpRoot(0, 100, "Storage A", 0, 0, ""),
         });
         mDatabase.getMapper().stopAddingDocuments("deviceDocId");
 
@@ -911,7 +911,7 @@
                 DocumentsContract.Root.COLUMN_TITLE,
                 DocumentsContract.Root.COLUMN_AVAILABLE_BYTES
         };
-        try (final Cursor cursor = mDatabase.queryRoots(columns)) {
+        try (final Cursor cursor = mDatabase.queryRoots(resources, columns)) {
             assertEquals(1, cursor.getCount());
             assertTrue(cursor.moveToNext());
             assertEquals(1, cursor.getLong(0));
diff --git a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDocumentsProviderTest.java b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDocumentsProviderTest.java
index 3606612..5b0f557 100644
--- a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDocumentsProviderTest.java
+++ b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDocumentsProviderTest.java
@@ -23,13 +23,11 @@
 import android.os.ParcelFileDescriptor;
 import android.os.storage.StorageManager;
 import android.provider.DocumentsContract.Root;
-import android.system.ErrnoException;
 import android.system.Os;
 import android.system.OsConstants;
 import android.provider.DocumentsContract;
 import android.test.AndroidTestCase;
 import android.test.suitebuilder.annotation.MediumTest;
-import android.util.Log;
 
 import java.io.FileNotFoundException;
 import java.io.IOException;
@@ -64,13 +62,12 @@
         setupProvider(MtpDatabaseConstants.FLAG_DATABASE_IN_MEMORY);
         mMtpManager.addValidDevice(new MtpDeviceRecord(
                 0,
-                "Device",
+                "Device A",
                 false /* unopened */,
                 new MtpRoot[] {
                     new MtpRoot(
                             0 /* deviceId */,
                             1 /* storageId */,
-                            "Device A" /* device model name */,
                             "Storage A" /* volume description */,
                             1024 /* free space */,
                             2048 /* total space */,
@@ -79,11 +76,14 @@
                 null,
                 null));
 
-        mProvider.openDevice(0);
+        mProvider.resumeRootScanner();
         mResolver.waitForNotification(ROOTS_URI, 1);
 
-        mProvider.closeDevice(0);
+        mProvider.openDevice(0);
         mResolver.waitForNotification(ROOTS_URI, 2);
+
+        mProvider.closeDevice(0);
+        mResolver.waitForNotification(ROOTS_URI, 3);
     }
 
     public void testOpenAndCloseErrorDevice() throws Exception {
@@ -94,24 +94,17 @@
         } catch (Throwable error) {
             assertTrue(error instanceof IOException);
         }
-
-        try {
-            mProvider.closeDevice(1);
-            fail();
-        } catch (Throwable error) {
-            assertTrue(error instanceof IOException);
-        }
+        assertEquals(0, mProvider.getOpenedDeviceIds().length);
 
         // Check if the following notification is the first one or not.
         mMtpManager.addValidDevice(new MtpDeviceRecord(
                 0,
-                "Device",
+                "Device A",
                 false /* unopened */,
                 new MtpRoot[] {
                     new MtpRoot(
                             0 /* deviceId */,
                             1 /* storageId */,
-                            "Device A" /* device model name */,
                             "Storage A" /* volume description */,
                             1024 /* free space */,
                             2048 /* total space */,
@@ -119,21 +112,66 @@
                 },
                 null,
                 null));
-        mProvider.openDevice(0);
+        mProvider.resumeRootScanner();
         mResolver.waitForNotification(ROOTS_URI, 1);
+        mProvider.openDevice(0);
+        mResolver.waitForNotification(ROOTS_URI, 2);
+    }
+
+    public void testOpenDeviceOnDemand() throws Exception {
+        setupProvider(MtpDatabaseConstants.FLAG_DATABASE_IN_MEMORY);
+        mMtpManager.addValidDevice(new MtpDeviceRecord(
+                0,
+                "Device A",
+                false /* unopened */,
+                new MtpRoot[] {
+                    new MtpRoot(
+                            0 /* deviceId */,
+                            1 /* storageId */,
+                            "Storage A" /* volume description */,
+                            1024 /* free space */,
+                            2048 /* total space */,
+                            "" /* no volume identifier */)
+                },
+                null,
+                null));
+        mMtpManager.setObjectHandles(0, 1, -1, new int[0]);
+        mProvider.resumeRootScanner();
+        mResolver.waitForNotification(ROOTS_URI, 1);
+        final String[] columns = new String[] {
+                DocumentsContract.Root.COLUMN_TITLE,
+                DocumentsContract.Root.COLUMN_DOCUMENT_ID
+        };
+        try (final Cursor cursor = mProvider.queryRoots(columns)) {
+            assertEquals(1, cursor.getCount());
+            assertTrue(cursor.moveToNext());
+            assertEquals("Device A", cursor.getString(0));
+            assertEquals(1, cursor.getLong(1));
+        }
+        {
+            final int [] openedDevice = mProvider.getOpenedDeviceIds();
+            assertEquals(0, openedDevice.length);
+        }
+        // Device is opened automatically when querying its children.
+        try (final Cursor cursor = mProvider.queryChildDocuments("1", null, null)) {}
+
+        {
+            final int [] openedDevice = mProvider.getOpenedDeviceIds();
+            assertEquals(1, openedDevice.length);
+            assertEquals(0, openedDevice[0]);
+        }
     }
 
     public void testQueryRoots() throws Exception {
         setupProvider(MtpDatabaseConstants.FLAG_DATABASE_IN_MEMORY);
         mMtpManager.addValidDevice(new MtpDeviceRecord(
                 0,
-                "Device",
+                "Device A",
                 false /* unopened */,
                 new MtpRoot[] {
                         new MtpRoot(
                                 0 /* deviceId */,
                                 1 /* storageId */,
-                                "Device A" /* device model name */,
                                 "Storage A" /* volume description */,
                                 1024 /* free space */,
                                 2048 /* total space */,
@@ -143,13 +181,12 @@
                 null));
         mMtpManager.addValidDevice(new MtpDeviceRecord(
                 1,
-                "Device",
+                "Device B",
                 false /* unopened */,
                 new MtpRoot[] {
                     new MtpRoot(
                             1 /* deviceId */,
                             1 /* storageId */,
-                            "Device B" /* device model name */,
                             "Storage B" /* volume description */,
                             2048 /* free space */,
                             4096 /* total space */,
@@ -194,13 +231,12 @@
                 0, "Device A", false /* unopened */, new MtpRoot[0], null, null));
         mMtpManager.addValidDevice(new MtpDeviceRecord(
                 1,
-                "Device",
+                "Device B",
                 false /* unopened */,
                 new MtpRoot[] {
                     new MtpRoot(
                             1 /* deviceId */,
                             1 /* storageId */,
-                            "Device B" /* device model name */,
                             "Storage B" /* volume description */,
                             2048 /* free space */,
                             4096 /* total space */,
@@ -210,9 +246,13 @@
                 null));
         {
             mProvider.openDevice(0);
-            mProvider.openDevice(1);
+            mProvider.resumeRootScanner();
             mResolver.waitForNotification(ROOTS_URI, 1);
 
+            mProvider.openDevice(1);
+            mProvider.resumeRootScanner();
+            mResolver.waitForNotification(ROOTS_URI, 2);
+
             final Cursor cursor = mProvider.queryRoots(null);
             assertEquals(2, cursor.getCount());
 
@@ -236,7 +276,7 @@
 
     public void testQueryDocument() throws IOException, InterruptedException, TimeoutException {
         setupProvider(MtpDatabaseConstants.FLAG_DATABASE_IN_MEMORY);
-        setupRoots(0, new MtpRoot[] { new MtpRoot(0, 0, "Device", "Storage", 1000, 1000, "") });
+        setupRoots(0, new MtpRoot[] { new MtpRoot(0, 0, "Storage", 1000, 1000, "") });
         setupDocuments(
                 0,
                 0,
@@ -273,7 +313,7 @@
     public void testQueryDocument_directory()
             throws IOException, InterruptedException, TimeoutException {
         setupProvider(MtpDatabaseConstants.FLAG_DATABASE_IN_MEMORY);
-        setupRoots(0, new MtpRoot[] { new MtpRoot(0, 0, "Device", "Storage", 1000, 1000, "") });
+        setupRoots(0, new MtpRoot[] { new MtpRoot(0, 0, "Storage", 1000, 1000, "") });
         setupDocuments(
                 0,
                 0,
@@ -312,7 +352,6 @@
                 new MtpRoot(
                         0 /* deviceId */,
                         1 /* storageId */,
-                        "Device A" /* device model name */,
                         "Storage A" /* volume description */,
                         1024 /* free space */,
                         4096 /* total space */,
@@ -324,7 +363,7 @@
         cursor.moveToNext();
         assertEquals("2", cursor.getString(0));
         assertEquals(DocumentsContract.Document.MIME_TYPE_DIR, cursor.getString(1));
-        assertEquals("Device A Storage A", cursor.getString(2));
+        assertEquals("Storage A", cursor.getString(2));
         assertTrue(cursor.isNull(3));
         assertEquals(0, cursor.getInt(4));
         assertEquals(3072, cursor.getInt(5));
@@ -332,7 +371,7 @@
 
     public void testQueryChildDocuments() throws Exception {
         setupProvider(MtpDatabaseConstants.FLAG_DATABASE_IN_MEMORY);
-        setupRoots(0, new MtpRoot[] { new MtpRoot(0, 0, "Device", "Storage", 1000, 1000, "") });
+        setupRoots(0, new MtpRoot[] { new MtpRoot(0, 0, "Storage", 1000, 1000, "") });
         setupDocuments(
                 0,
                 0,
@@ -375,7 +414,7 @@
 
     public void testQueryChildDocuments_documentError() throws Exception {
         setupProvider(MtpDatabaseConstants.FLAG_DATABASE_IN_MEMORY);
-        setupRoots(0, new MtpRoot[] { new MtpRoot(0, 0, "Device", "Storage", 1000, 1000, "") });
+        setupRoots(0, new MtpRoot[] { new MtpRoot(0, 0, "Storage", 1000, 1000, "") });
         mMtpManager.setObjectHandles(0, 0, -1, new int[] { 1 });
         try {
             mProvider.queryChildDocuments("1", null, null);
@@ -388,7 +427,7 @@
     public void testDeleteDocument() throws IOException, InterruptedException, TimeoutException {
         setupProvider(MtpDatabaseConstants.FLAG_DATABASE_IN_MEMORY);
         setupRoots(0, new MtpRoot[] {
-                new MtpRoot(0, 0, "Device", "Storage", 0, 0, "")
+                new MtpRoot(0, 0, "Storage", 0, 0, "")
         });
         setupDocuments(0, 0, MtpManager.OBJECT_HANDLE_ROOT_CHILDREN, "1", new MtpObjectInfo[] {
                 new MtpObjectInfo.Builder()
@@ -408,7 +447,7 @@
             throws IOException, InterruptedException, TimeoutException {
         setupProvider(MtpDatabaseConstants.FLAG_DATABASE_IN_MEMORY);
         setupRoots(0, new MtpRoot[] {
-                new MtpRoot(0, 0, "Device", "Storage", 0, 0, "")
+                new MtpRoot(0, 0, "Storage", 0, 0, "")
         });
         setupDocuments(0, 0, MtpManager.OBJECT_HANDLE_ROOT_CHILDREN, "1", new MtpObjectInfo[] {
                 new MtpObjectInfo.Builder()
@@ -431,7 +470,7 @@
     public void testOpenDocument() throws Exception {
         setupProvider(MtpDatabaseConstants.FLAG_DATABASE_IN_MEMORY);
         setupRoots(0, new MtpRoot[] {
-                new MtpRoot(0, 0, "Device", "Storage", 0, 0, "")
+                new MtpRoot(0, 0, "Storage", 0, 0, "")
         });
         final byte[] bytes = "Hello world".getBytes();
         setupDocuments(0, 0, MtpManager.OBJECT_HANDLE_ROOT_CHILDREN, "1", new MtpObjectInfo[] {
@@ -469,7 +508,7 @@
         };
         setupProvider(MtpDatabaseConstants.FLAG_DATABASE_IN_MEMORY);
         setupRoots(0, new MtpRoot[] {
-                new MtpRoot(0, 0, "Device", "Storage", 0, 0, "")
+                new MtpRoot(0, 0, "Storage", 0, 0, "")
         });
         final byte[] bytes = "Hello world".getBytes();
         setupDocuments(0, 0, MtpManager.OBJECT_HANDLE_ROOT_CHILDREN, "1", new MtpObjectInfo[] {
@@ -492,7 +531,12 @@
         mProvider = new MtpDocumentsProvider();
         final StorageManager storageManager = getContext().getSystemService(StorageManager.class);
         assertTrue(mProvider.onCreateForTesting(
-                mResources, mMtpManager, mResolver, mDatabase, storageManager));
+                mResources,
+                mMtpManager,
+                mResolver,
+                mDatabase,
+                storageManager,
+                new TestServiceIntentSender()));
     }
 
     private String[] getStrings(Cursor cursor) {
diff --git a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/TestResources.java b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/TestResources.java
index eb80e3b..b23038b 100644
--- a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/TestResources.java
+++ b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/TestResources.java
@@ -27,4 +27,9 @@
         }
         throw new NotFoundException();
     }
+
+    @Override
+    public String getString(int id, Object... formatArgs) throws NotFoundException {
+        return String.format(getString(id), formatArgs);
+    }
 }
diff --git a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/TestServiceIntentSender.java b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/TestServiceIntentSender.java
new file mode 100644
index 0000000..d4a4a48
--- /dev/null
+++ b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/TestServiceIntentSender.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.mtp;
+
+class TestServiceIntentSender extends ServiceIntentSender {
+    TestServiceIntentSender() {
+        super(null);
+    }
+
+    @Override
+    void sendUpdateNotificationIntent() {}
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtils.java b/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtils.java
index 78c530c..f885110 100644
--- a/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtils.java
@@ -155,7 +155,7 @@
             for (UserInfo userInfo : um.getProfiles(userId)) {
                 final List<ComponentName> admins = dpm.getActiveAdminsAsUser(userInfo.id);
                 if (admins == null) {
-                    return null;
+                    continue;
                 }
                 final boolean isSeparateProfileChallengeEnabled =
                         lockPatternUtils.isSeparateProfileChallengeEnabled(userInfo.id);
@@ -209,16 +209,7 @@
         IPackageManager ipm = AppGlobals.getPackageManager();
         try {
             if (ipm.getBlockUninstallForUser(packageName, userId)) {
-                DevicePolicyManager dpm = (DevicePolicyManager) context.getSystemService(
-                        Context.DEVICE_POLICY_SERVICE);
-                if (dpm == null) {
-                    return null;
-                }
-                ComponentName admin = dpm.getProfileOwner();
-                if (admin == null) {
-                    admin = dpm.getDeviceOwnerComponentOnCallingUser();
-                }
-                return new EnforcedAdmin(admin, UserHandle.myUserId());
+                return getProfileOrDeviceOwner(context, userId);
             }
         } catch (RemoteException e) {
             // Nothing to do
@@ -238,7 +229,7 @@
         try {
             ApplicationInfo ai = ipm.getApplicationInfo(packageName, 0, userId);
             if (ai != null && ((ai.flags & ApplicationInfo.FLAG_SUSPENDED) != 0)) {
-                return getProfileOrDeviceOwnerOnCallingUser(context);
+                return getProfileOrDeviceOwner(context, userId);
             }
         } catch (RemoteException e) {
             // Nothing to do
@@ -246,6 +237,80 @@
         return null;
     }
 
+    public static EnforcedAdmin checkIfInputMethodDisallowed(Context context,
+            String packageName, int userId) {
+        DevicePolicyManager dpm = (DevicePolicyManager) context.getSystemService(
+                Context.DEVICE_POLICY_SERVICE);
+        if (dpm == null) {
+            return null;
+        }
+        EnforcedAdmin admin = getProfileOrDeviceOwner(context, userId);
+        boolean permitted = true;
+        if (admin != null) {
+            permitted = dpm.isInputMethodPermittedByAdmin(admin.component,
+                    packageName, userId);
+        }
+        int managedProfileId = getManagedProfileId(context, userId);
+        EnforcedAdmin profileAdmin = getProfileOrDeviceOwner(context, managedProfileId);
+        boolean permittedByProfileAdmin = true;
+        if (profileAdmin != null) {
+            permittedByProfileAdmin = dpm.isInputMethodPermittedByAdmin(profileAdmin.component,
+                    packageName, managedProfileId);
+        }
+        if (!permitted && !permittedByProfileAdmin) {
+            return EnforcedAdmin.MULTIPLE_ENFORCED_ADMIN;
+        } else if (!permitted) {
+            return admin;
+        } else if (!permittedByProfileAdmin) {
+            return profileAdmin;
+        }
+        return null;
+    }
+
+    public static EnforcedAdmin checkIfAccessibilityServiceDisallowed(Context context,
+            String packageName, int userId) {
+        DevicePolicyManager dpm = (DevicePolicyManager) context.getSystemService(
+                Context.DEVICE_POLICY_SERVICE);
+        if (dpm == null) {
+            return null;
+        }
+        EnforcedAdmin admin = getProfileOrDeviceOwner(context, userId);
+        boolean permitted = true;
+        if (admin != null) {
+            permitted = dpm.isAccessibilityServicePermittedByAdmin(admin.component,
+                    packageName, userId);
+        }
+        int managedProfileId = getManagedProfileId(context, userId);
+        EnforcedAdmin profileAdmin = getProfileOrDeviceOwner(context, managedProfileId);
+        boolean permittedByProfileAdmin = true;
+        if (profileAdmin != null) {
+            permittedByProfileAdmin = dpm.isAccessibilityServicePermittedByAdmin(
+                    profileAdmin.component, packageName, managedProfileId);
+        }
+        if (!permitted && !permittedByProfileAdmin) {
+            return EnforcedAdmin.MULTIPLE_ENFORCED_ADMIN;
+        } else if (!permitted) {
+            return admin;
+        } else if (!permittedByProfileAdmin) {
+            return profileAdmin;
+        }
+        return null;
+    }
+
+    private static int getManagedProfileId(Context context, int userId) {
+        UserManager um = (UserManager) context.getSystemService(Context.USER_SERVICE);
+        List<UserInfo> userProfiles = um.getProfiles(userId);
+        for (UserInfo uInfo : userProfiles) {
+            if (uInfo.id == userId) {
+                continue;
+            }
+            if (uInfo.isManagedProfile()) {
+                return uInfo.id;
+            }
+        }
+        return UserHandle.USER_NULL;
+    }
+
     /**
      * Check if account management for a specific type of account is disabled by admin.
      * Only a profile or device owner can disable account management. So, we check if account
@@ -255,7 +320,7 @@
      * or {@code null} if the account management is not disabled.
      */
     public static EnforcedAdmin checkIfAccountManagementDisabled(Context context,
-            String accountType) {
+            String accountType, int userId) {
         if (accountType == null) {
             return null;
         }
@@ -265,7 +330,7 @@
             return null;
         }
         boolean isAccountTypeDisabled = false;
-        String[] disabledTypes = dpm.getAccountTypesWithManagementDisabled();
+        String[] disabledTypes = dpm.getAccountTypesWithManagementDisabledAsUser(userId);
         for (String type : disabledTypes) {
             if (accountType.equals(type)) {
                 isAccountTypeDisabled = true;
@@ -275,7 +340,7 @@
         if (!isAccountTypeDisabled) {
             return null;
         }
-        return getProfileOrDeviceOwnerOnCallingUser(context);
+        return getProfileOrDeviceOwner(context, userId);
     }
 
     /**
@@ -296,7 +361,7 @@
     }
 
     /**
-     * Checks if an admin has enforced minimum password quality requirements on the device.
+     * Checks if an admin has enforced minimum password quality requirements on the given user.
      *
      * @return EnforcedAdmin Object containing the enforced admin component and admin user details,
      * or {@code null} if no quality requirements are set. If the requirements are set by
@@ -304,35 +369,73 @@
      * {@link UserHandle#USER_NULL}.
      *
      */
-    public static EnforcedAdmin checkIfPasswordQualityIsSet(Context context) {
+    public static EnforcedAdmin checkIfPasswordQualityIsSet(Context context, int userId) {
         final DevicePolicyManager dpm = (DevicePolicyManager) context.getSystemService(
                 Context.DEVICE_POLICY_SERVICE);
         if (dpm == null) {
             return null;
         }
-        boolean isDisabledByMultipleAdmins = false;
-        ComponentName adminComponent = null;
-        List<ComponentName> admins = dpm.getActiveAdmins();
-        int quality;
-        if (admins != null) {
+
+        LockPatternUtils lockPatternUtils = new LockPatternUtils(context);
+        EnforcedAdmin enforcedAdmin = null;
+        if (lockPatternUtils.isSeparateProfileChallengeEnabled(userId)) {
+            // userId is managed profile and has a separate challenge, only consider
+            // the admins in that user.
+            final List<ComponentName> admins = dpm.getActiveAdminsAsUser(userId);
+            if (admins == null) {
+                return null;
+            }
             for (ComponentName admin : admins) {
-                quality = dpm.getPasswordQuality(admin);
-                if (quality >= DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED) {
-                    if (adminComponent == null) {
-                        adminComponent = admin;
+                if (dpm.getPasswordQuality(admin, userId)
+                        > DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED) {
+                    if (enforcedAdmin == null) {
+                        enforcedAdmin = new EnforcedAdmin(admin, userId);
                     } else {
-                        isDisabledByMultipleAdmins = true;
-                        break;
+                        return EnforcedAdmin.MULTIPLE_ENFORCED_ADMIN;
                     }
                 }
             }
-        }
-        EnforcedAdmin enforcedAdmin = null;
-        if (adminComponent != null) {
-            if (!isDisabledByMultipleAdmins) {
-                enforcedAdmin = new EnforcedAdmin(adminComponent, UserHandle.myUserId());
-            } else {
-                enforcedAdmin = new EnforcedAdmin();
+        } else {
+            // Return all admins for this user and the profiles that are visible from this
+            // user that do not use a separate work challenge.
+            final UserManager um = (UserManager) context.getSystemService(Context.USER_SERVICE);
+            for (UserInfo userInfo : um.getProfiles(userId)) {
+                final List<ComponentName> admins = dpm.getActiveAdminsAsUser(userInfo.id);
+                if (admins == null) {
+                    continue;
+                }
+                final boolean isSeparateProfileChallengeEnabled =
+                        lockPatternUtils.isSeparateProfileChallengeEnabled(userInfo.id);
+                for (ComponentName admin : admins) {
+                    if (!isSeparateProfileChallengeEnabled) {
+                        if (dpm.getPasswordQuality(admin, userInfo.id)
+                                > DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED) {
+                            if (enforcedAdmin == null) {
+                                enforcedAdmin = new EnforcedAdmin(admin, userInfo.id);
+                            } else {
+                                return EnforcedAdmin.MULTIPLE_ENFORCED_ADMIN;
+                            }
+                            // This same admins could have set policies both on the managed profile
+                            // and on the parent. So, if the admin has set the policy on the
+                            // managed profile here, we don't need to further check if that admin
+                            // has set policy on the parent admin.
+                            continue;
+                        }
+                    }
+                    if (userInfo.isManagedProfile()) {
+                        // If userInfo.id is a managed profile, we also need to look at
+                        // the policies set on the parent.
+                        DevicePolicyManager parentDpm = dpm.getParentProfileInstance(userInfo);
+                        if (parentDpm.getPasswordQuality(admin, userInfo.id)
+                                > DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED) {
+                            if (enforcedAdmin == null) {
+                                enforcedAdmin = new EnforcedAdmin(admin, userInfo.id);
+                            } else {
+                                return EnforcedAdmin.MULTIPLE_ENFORCED_ADMIN;
+                            }
+                        }
+                    }
+                }
             }
         }
         return enforcedAdmin;
@@ -352,7 +455,8 @@
         EnforcedAdmin enforcedAdmin = null;
         final int userId = UserHandle.myUserId();
         if (lockPatternUtils.isSeparateProfileChallengeEnabled(userId)) {
-            // If the user has a separate challenge, only consider the admins in that user.
+            // userId is managed profile and has a separate challenge, only consider
+            // the admins in that user.
             final List<ComponentName> admins = dpm.getActiveAdminsAsUser(userId);
             if (admins == null) {
                 return null;
@@ -373,7 +477,7 @@
             for (UserInfo userInfo : um.getProfiles(userId)) {
                 final List<ComponentName> admins = dpm.getActiveAdminsAsUser(userInfo.id);
                 if (admins == null) {
-                    return null;
+                    continue;
                 }
                 final boolean isSeparateProfileChallengeEnabled =
                         lockPatternUtils.isSeparateProfileChallengeEnabled(userInfo.id);
@@ -410,19 +514,24 @@
         return enforcedAdmin;
     }
 
-    public static EnforcedAdmin getProfileOrDeviceOwnerOnCallingUser(Context context) {
+    public static EnforcedAdmin getProfileOrDeviceOwner(Context context, int userId) {
+        if (userId == UserHandle.USER_NULL) {
+            return null;
+        }
         final DevicePolicyManager dpm = (DevicePolicyManager) context.getSystemService(
                 Context.DEVICE_POLICY_SERVICE);
         if (dpm == null) {
             return null;
         }
-        ComponentName adminComponent = dpm.getDeviceOwnerComponentOnCallingUser();
+        ComponentName adminComponent = dpm.getProfileOwnerAsUser(userId);
         if (adminComponent != null) {
-            return new EnforcedAdmin(adminComponent, UserHandle.myUserId());
+            return new EnforcedAdmin(adminComponent, userId);
         }
-        adminComponent = dpm.getProfileOwner();
-        if (adminComponent != null) {
-            return new EnforcedAdmin(adminComponent, UserHandle.myUserId());
+        if (dpm.getDeviceOwnerUserId() == userId) {
+            adminComponent = dpm.getDeviceOwnerComponentOnAnyUser();
+            if (adminComponent != null) {
+                return new EnforcedAdmin(adminComponent, userId);
+            }
         }
         return null;
     }
diff --git a/packages/SettingsLib/src/com/android/settingslib/TetherUtil.java b/packages/SettingsLib/src/com/android/settingslib/TetherUtil.java
index f5a2aae..d368de9 100644
--- a/packages/SettingsLib/src/com/android/settingslib/TetherUtil.java
+++ b/packages/SettingsLib/src/com/android/settingslib/TetherUtil.java
@@ -15,40 +15,19 @@
  */
 package com.android.settingslib;
 
-import android.app.ActivityManager;
-import android.content.ComponentName;
-import android.content.ContentResolver;
 import android.content.Context;
-import android.content.res.Resources;
-import android.net.ConnectivityManager;
 import android.net.wifi.WifiManager;
 import android.os.SystemProperties;
-import android.os.UserManager;
-import android.provider.Settings;
 import android.telephony.CarrierConfigManager;
 
 public class TetherUtil {
 
-    // Extras used for communicating with the TetherService.
-    public static final String EXTRA_ADD_TETHER_TYPE = "extraAddTetherType";
-    public static final String EXTRA_REM_TETHER_TYPE = "extraRemTetherType";
-    public static final String EXTRA_SET_ALARM = "extraSetAlarm";
-    /**
-     * Tells the service to run a provision check now.
-     */
-    public static final String EXTRA_RUN_PROVISION = "extraRunProvision";
-
     public static boolean setWifiTethering(boolean enable, Context context) {
         final WifiManager wifiManager =
                 (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
         return wifiManager.setWifiApEnabled(null, enable);
     }
 
-    public static boolean isWifiTetherEnabled(Context context) {
-        WifiManager wifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
-        return wifiManager.getWifiApState() == WifiManager.WIFI_AP_STATE_ENABLED;
-    }
-
     private static boolean isEntitlementCheckRequired(Context context) {
         final CarrierConfigManager configManager = (CarrierConfigManager) context
              .getSystemService(Context.CARRIER_CONFIG_SERVICE);
@@ -71,13 +50,4 @@
         }
         return (provisionApp.length == 2);
     }
-
-    public static boolean isTetheringSupported(Context context) {
-        final ConnectivityManager cm =
-                (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
-        final boolean isAdminUser =
-                UserManager.get(context).isUserAdmin(ActivityManager.getCurrentUser());
-        return isAdminUser && cm.isTetheringSupported();
-    }
-
 }
diff --git a/packages/SystemUI/res/anim/recents_from_app_enter.xml b/packages/SystemUI/res/anim/recents_from_app_enter.xml
deleted file mode 100644
index 10ddce6..0000000
--- a/packages/SystemUI/res/anim/recents_from_app_enter.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-** Copyright 2012, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-**     http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
--->
-<!-- Recents Activity -->
-<set xmlns:android="http://schemas.android.com/apk/res/android"
-     android:shareInterpolator="false"
-     android:zAdjustment="top">
-  <alpha android:fromAlpha="1.0" android:toAlpha="1.0"
-         android:fillEnabled="true"
-         android:fillBefore="true" android:fillAfter="true"
-         android:interpolator="@android:interpolator/fast_out_slow_in"
-         android:duration="0"/>
-</set>
diff --git a/packages/SystemUI/res/anim/recents_from_app_exit.xml b/packages/SystemUI/res/anim/recents_from_app_exit.xml
deleted file mode 100644
index c98ecf4..0000000
--- a/packages/SystemUI/res/anim/recents_from_app_exit.xml
+++ /dev/null
@@ -1,30 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-** Copyright 2012, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-**     http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
--->
-<!-- Incoming Activity -->
-<set xmlns:android="http://schemas.android.com/apk/res/android"
-     android:shareInterpolator="false"
-     android:zAdjustment="normal">
-
-    <!-- Animate the view out only after recents is visible -->
-    <alpha android:fromAlpha="1.0" android:toAlpha="0.0"
-           android:fillEnabled="true"
-           android:fillBefore="true" android:fillAfter="true"
-           android:interpolator="@android:interpolator/fast_out_slow_in"
-           android:duration="1"/>
-</set>
diff --git a/packages/SystemUI/res/anim/recents_from_launcher_enter.xml b/packages/SystemUI/res/anim/recents_from_launcher_enter.xml
index b191e62..00b3dfd 100644
--- a/packages/SystemUI/res/anim/recents_from_launcher_enter.xml
+++ b/packages/SystemUI/res/anim/recents_from_launcher_enter.xml
@@ -23,6 +23,6 @@
   <alpha android:fromAlpha="0.0" android:toAlpha="1.0"
          android:fillEnabled="true"
          android:fillBefore="true" android:fillAfter="true"
-         android:interpolator="@android:interpolator/linear"
+         android:interpolator="@android:interpolator/linear_out_slow_in"
          android:duration="150"/>
 </set>
diff --git a/packages/SystemUI/res/anim/recents_from_launcher_exit.xml b/packages/SystemUI/res/anim/recents_from_launcher_exit.xml
index fa6caf2..33831b8 100644
--- a/packages/SystemUI/res/anim/recents_from_launcher_exit.xml
+++ b/packages/SystemUI/res/anim/recents_from_launcher_exit.xml
@@ -23,6 +23,6 @@
   <alpha android:fromAlpha="1.0" android:toAlpha="0.0"
          android:fillEnabled="true"
          android:fillBefore="true" android:fillAfter="true"
-         android:interpolator="@android:interpolator/linear_out_slow_in"
-         android:duration="150"/>
+         android:interpolator="@interpolator/recents_from_launcher_exit_interpolator"
+         android:duration="133"/>
 </set>
diff --git a/packages/SystemUI/res/anim/recents_from_search_launcher_exit.xml b/packages/SystemUI/res/anim/recents_from_search_launcher_exit.xml
index e0e2fc8..23cedf8 100644
--- a/packages/SystemUI/res/anim/recents_from_search_launcher_exit.xml
+++ b/packages/SystemUI/res/anim/recents_from_search_launcher_exit.xml
@@ -23,6 +23,6 @@
   <alpha android:fromAlpha="1.0" android:toAlpha="0.0"
          android:fillEnabled="true"
          android:fillBefore="true" android:fillAfter="true"
-         android:interpolator="@android:interpolator/linear_out_slow_in"
-         android:duration="@integer/recents_enter_from_home_transition_duration"/>
+         android:interpolator="@interpolator/recents_from_launcher_exit_interpolator"
+         android:duration="133"/>
 </set>
diff --git a/packages/SystemUI/res/anim/recents_launch_from_launcher_enter.xml b/packages/SystemUI/res/anim/recents_launch_from_launcher_enter.xml
deleted file mode 100644
index 1135bc0..0000000
--- a/packages/SystemUI/res/anim/recents_launch_from_launcher_enter.xml
+++ /dev/null
@@ -1,30 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-** Copyright 2012, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-**     http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
--->
-
-<set xmlns:android="http://schemas.android.com/apk/res/android"
-     android:shareInterpolator="false"
-     android:zAdjustment="normal">
-  <!--scale android:fromXScale="2.0" android:toXScale="1.0"
-         android:fromYScale="2.0" android:toYScale="1.0"
-         android:interpolator="@android:interpolator/decelerate_cubic"
-         android:fillEnabled="true"
-         android:fillBefore="true" android:fillAfter="true"
-         android:pivotX="50%p" android:pivotY="50%p"
-         android:duration="250" /-->
-</set>
diff --git a/packages/SystemUI/res/anim/recents_launch_from_launcher_exit.xml b/packages/SystemUI/res/anim/recents_launch_from_launcher_exit.xml
deleted file mode 100644
index fa28cf4..0000000
--- a/packages/SystemUI/res/anim/recents_launch_from_launcher_exit.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-** Copyright 2012, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-**     http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
--->
-
-<set xmlns:android="http://schemas.android.com/apk/res/android"
-     android:shareInterpolator="false"
-     android:zAdjustment="top">
-  <alpha android:fromAlpha="1.0" android:toAlpha="0.0"
-         android:fillEnabled="true"
-         android:fillBefore="true" android:fillAfter="true"
-         android:interpolator="@android:interpolator/decelerate_cubic"
-         android:duration="250"/>
-</set>
diff --git a/packages/SystemUI/res/anim/recents_return_to_launcher_exit.xml b/packages/SystemUI/res/anim/recents_return_to_launcher_exit.xml
deleted file mode 100644
index e95e667..0000000
--- a/packages/SystemUI/res/anim/recents_return_to_launcher_exit.xml
+++ /dev/null
@@ -1,31 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-** Copyright 2012, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-**     http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
--->
-
-<set xmlns:android="http://schemas.android.com/apk/res/android"
-     android:shareInterpolator="false"
-     android:zAdjustment="normal">
-  <!--scale android:fromXScale="1.0" android:toXScale="2.0"
-         android:fromYScale="1.0" android:toYScale="2.0"
-         android:interpolator="@android:interpolator/decelerate_cubic"
-         android:pivotX="50%p" android:pivotY="50%p"
-         android:duration="250" /-->
-  <alpha android:fromAlpha="1.0" android:toAlpha="0.0"
-         android:interpolator="@android:interpolator/decelerate_cubic"
-         android:duration="250"/>
-</set>
diff --git a/packages/SystemUI/res/anim/recents_to_launcher_enter.xml b/packages/SystemUI/res/anim/recents_to_launcher_enter.xml
index b191e62..544ec88 100644
--- a/packages/SystemUI/res/anim/recents_to_launcher_enter.xml
+++ b/packages/SystemUI/res/anim/recents_to_launcher_enter.xml
@@ -23,6 +23,6 @@
   <alpha android:fromAlpha="0.0" android:toAlpha="1.0"
          android:fillEnabled="true"
          android:fillBefore="true" android:fillAfter="true"
-         android:interpolator="@android:interpolator/linear"
-         android:duration="150"/>
+         android:interpolator="@interpolator/recents_to_launcher_enter_interpolator"
+         android:duration="133"/>
 </set>
diff --git a/packages/SystemUI/res/anim/recents_to_launcher_exit.xml b/packages/SystemUI/res/anim/recents_to_launcher_exit.xml
index fa6caf2..226edb8 100644
--- a/packages/SystemUI/res/anim/recents_to_launcher_exit.xml
+++ b/packages/SystemUI/res/anim/recents_to_launcher_exit.xml
@@ -24,5 +24,5 @@
          android:fillEnabled="true"
          android:fillBefore="true" android:fillAfter="true"
          android:interpolator="@android:interpolator/linear_out_slow_in"
-         android:duration="150"/>
+         android:duration="1"/>
 </set>
diff --git a/packages/SystemUI/res/anim/recents_to_search_launcher_enter.xml b/packages/SystemUI/res/anim/recents_to_search_launcher_enter.xml
index ea82835..657b216 100644
--- a/packages/SystemUI/res/anim/recents_to_search_launcher_enter.xml
+++ b/packages/SystemUI/res/anim/recents_to_search_launcher_enter.xml
@@ -23,6 +23,6 @@
   <alpha android:fromAlpha="0.0" android:toAlpha="1.0"
          android:fillEnabled="true"
          android:fillBefore="true" android:fillAfter="true"
-         android:interpolator="@android:interpolator/linear"
-         android:duration="100"/>
+         android:interpolator="@interpolator/recents_to_launcher_enter_interpolator"
+         android:duration="133"/>
 </set>
diff --git a/packages/SystemUI/res/anim/recents_to_search_launcher_exit.xml b/packages/SystemUI/res/anim/recents_to_search_launcher_exit.xml
index a8bdc8e..5182cab2 100644
--- a/packages/SystemUI/res/anim/recents_to_search_launcher_exit.xml
+++ b/packages/SystemUI/res/anim/recents_to_search_launcher_exit.xml
@@ -24,5 +24,5 @@
          android:fillEnabled="true"
          android:fillBefore="true" android:fillAfter="true"
          android:interpolator="@android:interpolator/linear"
-         android:duration="100"/>
+         android:duration="1"/>
 </set>
diff --git a/packages/SystemUI/res/anim/wallpaper_recents_launch_from_launcher_enter.xml b/packages/SystemUI/res/anim/wallpaper_recents_launch_from_launcher_enter.xml
deleted file mode 100644
index 73ae9f2..0000000
--- a/packages/SystemUI/res/anim/wallpaper_recents_launch_from_launcher_enter.xml
+++ /dev/null
@@ -1,36 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-** Copyright 2012, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-**     http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
--->
-
-<set xmlns:android="http://schemas.android.com/apk/res/android"
-     android:detachWallpaper="true"
-     android:shareInterpolator="false"
-     android:zAdjustment="normal">
-  <!--scale android:fromXScale="2.0" android:toXScale="1.0"
-         android:fromYScale="2.0" android:toYScale="1.0"
-         android:interpolator="@android:interpolator/decelerate_cubic"
-         android:fillEnabled="true"
-         android:fillBefore="true" android:fillAfter="true"
-         android:pivotX="50%p" android:pivotY="50%p"
-         android:duration="250" /-->
-  <alpha android:fromAlpha="0.0" android:toAlpha="1.0"
-         android:fillEnabled="true"
-         android:fillBefore="true" android:fillAfter="true"
-         android:interpolator="@android:interpolator/decelerate_cubic"
-         android:duration="250"/>
-</set>
diff --git a/packages/SystemUI/res/anim/wallpaper_recents_launch_from_launcher_exit.xml b/packages/SystemUI/res/anim/wallpaper_recents_launch_from_launcher_exit.xml
deleted file mode 100644
index 7e257d9..0000000
--- a/packages/SystemUI/res/anim/wallpaper_recents_launch_from_launcher_exit.xml
+++ /dev/null
@@ -1,29 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-** Copyright 2012, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-**     http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
--->
-
-<set xmlns:android="http://schemas.android.com/apk/res/android"
-     android:detachWallpaper="true"
-     android:shareInterpolator="false"
-     android:zAdjustment="top">
-  <alpha android:fromAlpha="1.0" android:toAlpha="0.0"
-         android:fillEnabled="true"
-         android:fillBefore="true" android:fillAfter="true"
-         android:interpolator="@android:interpolator/decelerate_cubic"
-         android:duration="250"/>
-</set>
diff --git a/packages/SystemUI/res/anim/recents_return_to_launcher_enter.xml b/packages/SystemUI/res/interpolator/recents_from_launcher_exit_interpolator.xml
similarity index 62%
rename from packages/SystemUI/res/anim/recents_return_to_launcher_enter.xml
rename to packages/SystemUI/res/interpolator/recents_from_launcher_exit_interpolator.xml
index efa9019..4a7fff6 100644
--- a/packages/SystemUI/res/anim/recents_return_to_launcher_enter.xml
+++ b/packages/SystemUI/res/interpolator/recents_from_launcher_exit_interpolator.xml
@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="utf-8"?>
 <!--
 /*
-** Copyright 2012, The Android Open Source Project
+** 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.
@@ -16,11 +16,8 @@
 ** limitations under the License.
 */
 -->
-
-<set xmlns:android="http://schemas.android.com/apk/res/android"
-     android:shareInterpolator="false"
-     android:zAdjustment="normal">
-  <alpha android:fromAlpha="0.0" android:toAlpha="1.0"
-         android:interpolator="@android:interpolator/decelerate_cubic"
-         android:duration="250"/>
-</set>
+<pathInterpolator xmlns:android="http://schemas.android.com/apk/res/android"
+    android:controlX1="0"
+    android:controlY1="0"
+    android:controlX2="0.8"
+    android:controlY2="1" />
diff --git a/packages/SystemUI/res/anim/recents_return_to_launcher_enter.xml b/packages/SystemUI/res/interpolator/recents_to_launcher_enter_interpolator.xml
similarity index 62%
copy from packages/SystemUI/res/anim/recents_return_to_launcher_enter.xml
copy to packages/SystemUI/res/interpolator/recents_to_launcher_enter_interpolator.xml
index efa9019..c61dfd8 100644
--- a/packages/SystemUI/res/anim/recents_return_to_launcher_enter.xml
+++ b/packages/SystemUI/res/interpolator/recents_to_launcher_enter_interpolator.xml
@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="utf-8"?>
 <!--
 /*
-** Copyright 2012, The Android Open Source Project
+** 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.
@@ -16,11 +16,8 @@
 ** limitations under the License.
 */
 -->
-
-<set xmlns:android="http://schemas.android.com/apk/res/android"
-     android:shareInterpolator="false"
-     android:zAdjustment="normal">
-  <alpha android:fromAlpha="0.0" android:toAlpha="1.0"
-         android:interpolator="@android:interpolator/decelerate_cubic"
-         android:duration="250"/>
-</set>
+<pathInterpolator xmlns:android="http://schemas.android.com/apk/res/android"
+    android:controlX1="0.4"
+    android:controlY1="0"
+    android:controlX2="1"
+    android:controlY2="1" />
diff --git a/packages/SystemUI/res/layout/keyboard_shortcuts_wrapper.xml b/packages/SystemUI/res/layout/keyboard_shortcuts_category_separator.xml
similarity index 66%
rename from packages/SystemUI/res/layout/keyboard_shortcuts_wrapper.xml
rename to packages/SystemUI/res/layout/keyboard_shortcuts_category_separator.xml
index 802acfe..778ef8f 100644
--- a/packages/SystemUI/res/layout/keyboard_shortcuts_wrapper.xml
+++ b/packages/SystemUI/res/layout/keyboard_shortcuts_category_separator.xml
@@ -15,8 +15,11 @@
   ~ limitations under the License
   -->
 
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
-              android:orientation="vertical"
-              android:layout_width="match_parent"
-              android:layout_height="wrap_content">
-</LinearLayout>
+<View xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="1dp"
+    android:layout_marginStart="24dp"
+    android:layout_marginTop="8dp"
+    android:layout_marginEnd="0dp"
+    android:layout_marginBottom="20dp"
+    android:background="?android:attr/dividerHorizontal" />
diff --git a/packages/SystemUI/res/layout/keyboard_shortcuts_container.xml b/packages/SystemUI/res/layout/keyboard_shortcuts_container.xml
index fa07eb1..7ca3b95 100644
--- a/packages/SystemUI/res/layout/keyboard_shortcuts_container.xml
+++ b/packages/SystemUI/res/layout/keyboard_shortcuts_container.xml
@@ -16,7 +16,7 @@
   -->
 
 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
-              android:orientation="horizontal"
-              android:layout_width="match_parent"
-              android:layout_height="wrap_content">
-</LinearLayout>
+    android:orientation="vertical"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content" />
+
diff --git a/packages/SystemUI/res/layout/keyboard_shortcuts_view.xml b/packages/SystemUI/res/layout/keyboard_shortcuts_view.xml
index 77b1264..f73ee15 100644
--- a/packages/SystemUI/res/layout/keyboard_shortcuts_view.xml
+++ b/packages/SystemUI/res/layout/keyboard_shortcuts_view.xml
@@ -14,25 +14,35 @@
   ~ See the License for the specific language governing permissions and
   ~ limitations under the License
   -->
-<LinearLayout
-        xmlns:android="http://schemas.android.com/apk/res/android"
-        android:id="@+id/keyboard_shortcuts_wrapper"
-        android:layout_width="488dp"
+
+<FrameLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="488dp"
+    android:layout_height="wrap_content">
+    <LinearLayout
+        android:layout_width="match_parent"
         android:layout_height="wrap_content"
-        android:focusable="true">
-    <ScrollView
+        android:orientation="vertical">
+        <ScrollView
             android:id="@+id/keyboard_shortcuts_scroll_view"
-            android:layout_width="0dp"
-            android:layout_height="0dp"
-            android:layout_weight="1">
-        <LinearLayout
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content">
+            <LinearLayout
                 android:id="@+id/keyboard_shortcuts_container"
                 android:layout_width="match_parent"
                 android:layout_height="wrap_content"
                 android:orientation="vertical"/>
-    </ScrollView>
-    <View
+        </ScrollView>
+        <!-- Required for stretching to full available height when the items in the scroll view
+             occupy less space then the full height -->
+        <View
             android:layout_width="match_parent"
-            android:layout_height="1dp"
-            android:background="?android:attr/listDivider"/>
-</LinearLayout>
+            android:layout_height="0dp"
+            android:layout_weight="1"/>
+    </LinearLayout>
+    <View
+        android:layout_gravity="bottom"
+        android:layout_width="match_parent"
+        android:layout_height="1dp"
+        android:background="?android:attr/dividerHorizontal"/>
+</FrameLayout>
diff --git a/packages/SystemUI/res/layout/qs_add_tile_layout.xml b/packages/SystemUI/res/layout/qs_add_tile_layout.xml
deleted file mode 100644
index 962b00e..0000000
--- a/packages/SystemUI/res/layout/qs_add_tile_layout.xml
+++ /dev/null
@@ -1,43 +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.
--->
-
-<FrameLayout
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:layout_height="wrap_content"
-    android:layout_width="wrap_content"
-    android:paddingTop="20dp"
-    android:paddingStart="7dp"
-    android:paddingEnd="7dp">
-    <LinearLayout
-        android:layout_height="wrap_content"
-        android:layout_width="80dp"
-        android:orientation="vertical">
-        <ImageView
-            android:id="@+id/tile_icon"
-            android:layout_gravity="center"
-            android:layout_width="@dimen/qs_tile_icon_size"
-            android:layout_height="@dimen/qs_tile_icon_size" />
-        <TextView
-            android:id="@+id/tile_label"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:layout_gravity="center|bottom"
-            android:paddingTop="10dp"
-            android:gravity="center"
-            android:textSize="@dimen/qs_tile_text_size" />
-    </LinearLayout>
-</FrameLayout>
diff --git a/packages/SystemUI/res/layout/qs_customize_layout.xml b/packages/SystemUI/res/layout/qs_customize_divider.xml
similarity index 63%
rename from packages/SystemUI/res/layout/qs_customize_layout.xml
rename to packages/SystemUI/res/layout/qs_customize_divider.xml
index 0b8e02f..71ad85b 100644
--- a/packages/SystemUI/res/layout/qs_customize_layout.xml
+++ b/packages/SystemUI/res/layout/qs_customize_divider.xml
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
 <!--
-     Copyright (C) 2015 The Android Open Source Project
+     Copyright (C) 2016 The Android Open Source Project
 
      Licensed under the Apache License, Version 2.0 (the "License");
      you may not use this file except in compliance with the License.
@@ -14,18 +14,16 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-<com.android.systemui.qs.customize.NonPagedTileLayout
+
+<TextView
     xmlns:android="http://schemas.android.com/apk/res/android"
-    android:id="@+id/tiles_container"
+    android:id="@android:id/title"
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
-    android:orientation="vertical">
-
-    <view
-        class="com.android.systemui.qs.PagedTileLayout$TilePage"
-        android:id="@+id/tile_page"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content" />
-
-</com.android.systemui.qs.customize.NonPagedTileLayout>
-
+    android:paddingTop="20dp"
+    android:paddingStart="16dp"
+    android:paddingEnd="8dp"
+    android:paddingBottom="13dp"
+    android:textAppearance="@android:style/TextAppearance.Material.Body2"
+    android:textColor="?android:attr/colorAccent"
+    android:text="@string/drag_to_add_tiles" />
diff --git a/packages/SystemUI/res/layout/qs_customize_panel.xml b/packages/SystemUI/res/layout/qs_customize_panel.xml
index e56431b..73a92d9 100644
--- a/packages/SystemUI/res/layout/qs_customize_panel.xml
+++ b/packages/SystemUI/res/layout/qs_customize_panel.xml
@@ -22,85 +22,53 @@
     android:background="@drawable/qs_customizer_background"
     android:gravity="center_horizontal">
 
-    <FrameLayout
+    <LinearLayout
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
-        android:background="@drawable/notification_header_bg">
+        android:paddingTop="28dp"
+        android:paddingEnd="8dp">
 
-        <LinearLayout
-            android:id="@+id/drag_buttons"
-            android:layout_width="match_parent"
-            android:layout_height="fill_parent"
-            android:orientation="horizontal">
-            <FrameLayout
-                android:layout_width="0dp"
-                android:layout_height="fill_parent"
-                android:layout_weight="1">
-                <com.android.systemui.qs.customize.DropButton
-                    android:id="@+id/info_button"
-                    android:layout_width="wrap_content"
-                    android:layout_height="wrap_content"
-                    android:layout_gravity="center"
-                    android:gravity="center"
-                    android:drawableStart="@drawable/ic_info"
-                    android:drawablePadding="10dp"
-                    android:textAppearance="?android:attr/textAppearanceMedium"
-                    android:textColor="@android:color/white"
-                    android:text="@string/qs_customize_info" />
-            </FrameLayout>
-            <FrameLayout
-                android:layout_width="0dp"
-                android:layout_height="fill_parent"
-                android:layout_weight="1">
-                <com.android.systemui.qs.customize.DropButton
-                    android:id="@+id/remove_button"
-                    android:layout_width="wrap_content"
-                    android:layout_height="wrap_content"
-                    android:layout_gravity="center"
-                    android:gravity="center"
-                    android:drawableStart="@drawable/ic_close_white"
-                    android:drawablePadding="10dp"
-                    android:textAppearance="?android:attr/textAppearanceMedium"
-                    android:textColor="@android:color/white"
-                    android:text="@string/qs_customize_remove" />
-            </FrameLayout>
-        </LinearLayout>
+        <ImageView
+            android:id="@+id/close"
+            android:layout_width="56dp"
+            android:layout_height="56dp"
+            android:padding="16dp"
+            android:src="@drawable/ic_close_white" />
 
-        <Toolbar
-            android:id="@*android:id/action_bar"
-            android:layout_width="match_parent"
+        <Space
+            android:layout_width="0dp"
+            android:layout_height="0dp"
+            android:layout_weight="1" />
+
+        <Button
+            android:id="@+id/save"
+            android:layout_width="wrap_content"
             android:layout_height="wrap_content"
-            android:navigationContentDescription="@*android:string/action_bar_up_description"
-            android:background="@drawable/notification_header_bg"
-            style="?android:attr/toolbarStyle" />
-    </FrameLayout>
+            android:paddingStart="12dp"
+            android:paddingEnd="12dp"
+            android:background="?android:attr/selectableItemBackground"
+            android:textAppearance="@android:style/TextAppearance.Material.Widget.Button.Inverse"
+            android:textColor="?android:attr/colorAccent"
+            android:text="@string/save" />
 
-    <com.android.systemui.tuner.AutoScrollView
+        <Button
+            android:id="@+id/reset"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:paddingStart="12dp"
+            android:paddingEnd="12dp"
+            android:background="?android:attr/selectableItemBackground"
+            android:textAppearance="@android:style/TextAppearance.Material.Widget.Button.Inverse"
+            android:textColor="?android:attr/colorAccent"
+            android:text="@*android:string/reset" />
+
+    </LinearLayout>
+
+    <android.support.v7.widget.RecyclerView
+        android:id="@android:id/list"
         android:layout_width="@dimen/notification_panel_width"
         android:layout_height="0dp"
-        android:layout_weight="1"
-        android:paddingTop="12dp"
-        android:paddingBottom="8dp"
-        android:elevation="2dp">
-
-        <com.android.systemui.qs.customize.CustomQSPanel
-            android:id="@+id/quick_settings_panel"
-            android:background="#0000"
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content" />
-
-    </com.android.systemui.tuner.AutoScrollView>
-
-    <com.android.systemui.qs.customize.FloatingActionButton
-        android:id="@+id/fab"
-        android:clickable="true"
-        android:layout_width="@dimen/fab_size"
-        android:layout_height="@dimen/fab_size"
-        android:layout_gravity="bottom|end"
-        android:layout_marginEnd="@dimen/fab_margin"
-        android:layout_marginBottom="@dimen/fab_margin"
-        android:elevation="@dimen/fab_elevation"
-        android:background="@drawable/fab_background" />
+        android:layout_weight="1" />
 
     <View
         android:layout_width="match_parent"
diff --git a/packages/SystemUI/res/layout/qs_customize_tile_frame.xml b/packages/SystemUI/res/layout/qs_customize_tile_frame.xml
new file mode 100644
index 0000000..aaa84fd
--- /dev/null
+++ b/packages/SystemUI/res/layout/qs_customize_tile_frame.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2016 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<FrameLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_height="wrap_content"
+    android:layout_width="wrap_content"
+    android:paddingStart="8dp"
+    android:paddingEnd="8dp"
+    android:paddingBottom="16dp" />
diff --git a/packages/SystemUI/res/layout/status_bar_no_notifications.xml b/packages/SystemUI/res/layout/status_bar_no_notifications.xml
index dd501d4..6f87184 100644
--- a/packages/SystemUI/res/layout/status_bar_no_notifications.xml
+++ b/packages/SystemUI/res/layout/status_bar_no_notifications.xml
@@ -25,9 +25,9 @@
             android:id="@+id/no_notifications"
             android:layout_width="match_parent"
             android:layout_height="64dp"
-            android:paddingTop="12dp"
+            android:paddingTop="28dp"
             android:gravity="top|center_horizontal"
-            android:textColor="#ffffff"
-            android:textSize="20sp"
+            android:textColor="#e6ffffff"
+            android:textSize="16sp"
             android:text="@string/empty_shade_text"/>
 </com.android.systemui.statusbar.EmptyShadeView>
diff --git a/packages/SystemUI/res/layout/status_bar_notification_dismiss_all.xml b/packages/SystemUI/res/layout/status_bar_notification_dismiss_all.xml
index dc7577a..efb273f 100644
--- a/packages/SystemUI/res/layout/status_bar_notification_dismiss_all.xml
+++ b/packages/SystemUI/res/layout/status_bar_notification_dismiss_all.xml
@@ -19,15 +19,16 @@
         xmlns:android="http://schemas.android.com/apk/res/android"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
-        android:visibility="gone"
-        android:clipChildren="false"
-        android:clipToPadding="false">
+        android:paddingEnd="8dp"
+        android:visibility="gone">
     <com.android.systemui.statusbar.DismissViewButton
+            style="@android:style/Widget.Material.Button.Borderless"
             android:id="@+id/dismiss_text"
-            android:layout_width="48dp"
-            android:layout_height="48dp"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
             android:layout_gravity="end"
             android:focusable="true"
-            android:background="@drawable/ripple_drawable"
-            android:contentDescription="@string/accessibility_clear_all"/>
+            android:contentDescription="@string/accessibility_clear_all"
+            android:text="@string/clear_all_notifications_text"
+            android:textAllCaps="true"/>
 </com.android.systemui.statusbar.DismissView>
diff --git a/packages/SystemUI/res/values-land/config.xml b/packages/SystemUI/res/values-land/config.xml
index e0affa1..43e7bac 100644
--- a/packages/SystemUI/res/values-land/config.xml
+++ b/packages/SystemUI/res/values-land/config.xml
@@ -38,6 +38,4 @@
          while the stack is not focused. -->
     <item name="recents_layout_unfocused_range_min" format="float" type="integer">-2</item>
     <item name="recents_layout_unfocused_range_max" format="float" type="integer">1.5</item>
-
-    <integer name="quick_settings_num_columns">4</integer>
 </resources>
diff --git a/packages/SystemUI/res/values-land/dimens.xml b/packages/SystemUI/res/values-land/dimens.xml
index c75a89f..26a81c8 100644
--- a/packages/SystemUI/res/values-land/dimens.xml
+++ b/packages/SystemUI/res/values-land/dimens.xml
@@ -19,8 +19,7 @@
     <!-- thickness (width) of the navigation bar on phones that require it -->
     <dimen name="navigation_bar_size">@*android:dimen/navigation_bar_width</dimen>
 
-    <!-- Standard notification width + gravity -->
-    <dimen name="notification_panel_width">@dimen/standard_notification_panel_width</dimen>
+    <!-- Standard notification gravity -->
     <integer name="notification_panel_layout_gravity">@integer/standard_notification_panel_layout_gravity</integer>
 
     <dimen name="docked_divider_handle_width">2dp</dimen>
diff --git a/packages/SystemUI/res/values-sw600dp/dimens.xml b/packages/SystemUI/res/values-sw600dp/dimens.xml
index 71f92fd..c0652d8 100644
--- a/packages/SystemUI/res/values-sw600dp/dimens.xml
+++ b/packages/SystemUI/res/values-sw600dp/dimens.xml
@@ -93,4 +93,6 @@
 
     <dimen name="navigation_key_width">128dp</dimen>
     <dimen name="navigation_key_padding">25dp</dimen>
+
+    <dimen name="qs_expand_margin">0dp</dimen>
 </resources>
diff --git a/packages/SystemUI/res/values-w550dp-land/config.xml b/packages/SystemUI/res/values-w550dp-land/config.xml
new file mode 100644
index 0000000..71e54a1
--- /dev/null
+++ b/packages/SystemUI/res/values-w550dp-land/config.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 2016, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License"); 
+** you may not use this file except in compliance with the License. 
+** You may obtain a copy of the License at 
+**
+**     http://www.apache.org/licenses/LICENSE-2.0 
+**
+** Unless required by applicable law or agreed to in writing, software 
+** distributed under the License is distributed on an "AS IS" BASIS, 
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
+** See the License for the specific language governing permissions and 
+** limitations under the License.
+*/
+-->
+
+<!-- These resources are around just to allow their values to be customized
+     for different hardware and product builds. -->
+<resources>
+    <integer name="quick_settings_num_columns">4</integer>
+</resources>
diff --git a/packages/SystemUI/res/values-w550dp-land/dimens.xml b/packages/SystemUI/res/values-w550dp-land/dimens.xml
new file mode 100644
index 0000000..4160c83
--- /dev/null
+++ b/packages/SystemUI/res/values-w550dp-land/dimens.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (c) 2016, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+*/
+-->
+<resources>
+    <!-- Standard notification width + gravity -->
+    <dimen name="notification_panel_width">544dp</dimen>
+
+    <dimen name="qs_expand_margin">32dp</dimen>
+</resources>
diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml
index 9bb6dc6..a9b8df2 100644
--- a/packages/SystemUI/res/values/colors.xml
+++ b/packages/SystemUI/res/values/colors.xml
@@ -158,5 +158,4 @@
     <!-- Keyboard shortcuts colors -->
     <color name="ksh_system_group_color">#ff00bcd4</color>
     <color name="ksh_application_group_color">#fff44336</color>
-    <color name="ksh_dialog_background_color">#ffffffff</color>
 </resources>
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index e8df01b..a6ba8b5 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -103,7 +103,7 @@
 
     <!-- The default tiles to display in QuickSettings -->
     <string name="quick_settings_tiles_default" translatable="false">
-        wifi,bt,flashlight,dnd,cell,battery,rotation,airplane,location,cast,work
+        wifi,cell,battery,dnd,flashlight,rotation,bt,airplane,location
     </string>
 
     <!-- The tiles to display in QuickSettings -->
@@ -138,12 +138,6 @@
     <!-- The duration in seconds to wait before the dismiss buttons are shown. -->
     <integer name="recents_task_bar_dismiss_delay_seconds">1000</integer>
 
-    <!-- The duration of the window transition when coming to Recents from an app.
-         In order to defer the in-app animations until after the transition is complete,
-         we also need to use this value as the starting delay when animating the first
-         task decorations in. -->
-    <integer name="recents_enter_from_app_transition_duration">325</integer>
-
     <!-- The duration for animating the task decorations in after transitioning from an app. -->
     <integer name="recents_task_enter_from_app_duration">200</integer>
 
@@ -153,12 +147,6 @@
     <!-- The duration for animating the task decorations out before transitioning to an app. -->
     <integer name="recents_task_exit_to_app_duration">125</integer>
 
-    <!-- The duration of the window transition when coming to Recents from the Launcher.
-         In order to defer the in-app animations until after the transition is complete,
-         we also need to use this value as the starting delay when animating the task views
-         in from the bottom of the screen. -->
-    <integer name="recents_enter_from_home_transition_duration">100</integer>
-
     <!-- The min animation duration for animating the nav bar scrim in. -->
     <integer name="recents_nav_bar_scrim_enter_duration">400</integer>
 
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index e79a82a..e5e5710 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -186,6 +186,7 @@
     <dimen name="qs_detail_empty_text_size">14sp</dimen>
     <dimen name="qs_data_usage_text_size">14sp</dimen>
     <dimen name="qs_data_usage_usage_text_size">36sp</dimen>
+    <dimen name="qs_expand_margin">0dp</dimen>
 
     <dimen name="segmented_button_spacing">0dp</dimen>
     <dimen name="borderless_button_radius">2dp</dimen>
@@ -606,4 +607,7 @@
 
     <dimen name="docked_divider_handle_width">16dp</dimen>
     <dimen name="docked_divider_handle_height">2dp</dimen>
+
+    <dimen name="battery_height">14.5dp</dimen>
+    <dimen name="battery_width">9.5dp</dimen>
 </resources>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index c6c448d0..6ff9be1 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -1044,9 +1044,6 @@
     <!-- VolumeUI restoration notification: text -->
     <string name="volumeui_notification_text">Touch to restore the original.</string>
 
-    <!-- Describes the way 2 names are concatenated. An example would be ", " to produce "Peter Muller, Paul Curry". Please also include a space here if it's appropriate in the language and if it's a RTL language include it on the left. The translation should start and end with " to keep the white space if desired [CHAR LIMIT=5] -->
-    <string name="group_summary_concadenation">", "</string>
-
     <!-- Toast shown when user unlocks screen and managed profile activity is in the foreground -->
     <string name="managed_profile_foreground_toast">You\'re using your work profile</string>
 
@@ -1187,6 +1184,11 @@
     <!-- Description for the toggle to set the initial scroll state to be paging or stack. DO NOT TRANSLATE -->
     <string name="overview_initial_state_paging_desc">Determines whether Overview will initially be in a stacked or paged state</string>
 
+    <!-- Toggle to enable the gesture to enter split-screen by swiping up from the Overview button. [CHAR LIMIT=60]-->
+    <string name="overview_nav_bar_gesture">Enable split-screen swipe-up accelerator</string>
+    <!-- Description for the toggle to enable the gesture to enter split-screen by swiping up from the Overview button. [CHAR LIMIT=NONE]-->
+    <string name="overview_nav_bar_gesture_desc">Enable gesture to enter split-screen by swiping up from the Overview button</string>
+
     <!-- Category in the System UI Tuner settings, where new/experimental
          settings are -->
     <string name="experimental">Experimental</string>
@@ -1400,4 +1402,7 @@
     <!-- SysUI Tuner: Label for preview area in navigation bar tuner [CHAR LIMIT=NONE] -->
     <string name="preview">Preview</string>
 
+    <!-- Label for area where tiles can be dragged out of [CHAR LIMIT=60] -->
+    <string name="drag_to_add_tiles">Drag to add tiles</string>
+
 </resources>
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index 9931ab9..60a9fc2 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -16,10 +16,6 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android">
 
-    <style name="RecentsStyle" parent="@android:style/Theme.DeviceDefault.Wallpaper.NoTitleBar">
-        <item name="android:windowAnimationStyle">@style/Animation.RecentsActivity</item>
-    </style>
-
     <style name="RecentsTheme" parent="@android:style/Theme.Material">
         <!-- NoTitle -->
         <item name="android:windowNoTitle">true</item>
@@ -27,38 +23,23 @@
         <item name="android:statusBarColor">@android:color/transparent</item>
         <item name="android:navigationBarColor">@android:color/transparent</item>
         <item name="android:windowDrawsSystemBarBackgrounds">true</item>
-        <item name="android:windowAnimationStyle">@style/Animation.RecentsActivity</item>
+        <item name="android:windowAnimationStyle">@null</item>
         <item name="android:ambientShadowAlpha">0.35</item>
     </style>
 
 
-    <!-- Alternate Recents theme -->
+    <!-- Recents theme -->
     <style name="RecentsTheme.Wallpaper">
-        <!-- Wallpaper -->
         <item name="android:windowBackground">@color/transparent</item>
         <item name="android:colorBackgroundCacheHint">@null</item>
         <item name="android:windowShowWallpaper">true</item>
     </style>
 
-    <!-- Performance optimized alternate Recents theme (no wallpaper) -->
+    <!-- Performance optimized Recents theme (no wallpaper) -->
     <style name="RecentsTheme.NoWallpaper">
         <item name="android:windowBackground">@android:color/black</item>
     </style>
 
-    <!-- Animations for a non-full-screen window or activity. -->
-    <style name="Animation.RecentsActivity" parent="@android:style/Animation.Activity">
-        <item name="android:activityOpenEnterAnimation">@anim/recents_launch_from_launcher_enter</item>
-        <item name="android:activityOpenExitAnimation">@anim/recents_launch_from_launcher_exit</item>
-        <item name="android:taskOpenEnterAnimation">@anim/recents_launch_from_launcher_enter</item>
-        <item name="android:taskOpenExitAnimation">@anim/recents_launch_from_launcher_exit</item>
-        <item name="android:taskToFrontEnterAnimation">@anim/recents_launch_from_launcher_enter</item>
-        <item name="android:taskToFrontExitAnimation">@anim/recents_launch_from_launcher_exit</item>
-        <item name="android:wallpaperOpenEnterAnimation">@anim/recents_launch_from_launcher_enter</item>
-        <item name="android:wallpaperOpenExitAnimation">@anim/recents_launch_from_launcher_exit</item>
-        <item name="android:wallpaperIntraOpenEnterAnimation">@anim/wallpaper_recents_launch_from_launcher_enter</item>
-        <item name="android:wallpaperIntraOpenExitAnimation">@anim/wallpaper_recents_launch_from_launcher_exit</item>
-    </style>
-
     <style name="TextAppearance.StatusBar.HeadsUp"
         parent="@*android:style/TextAppearance.StatusBar">
     </style>
@@ -241,6 +222,10 @@
         parent="@*android:style/TextAppearance.Material.Notification.Info">
     </style>
 
+    <style name="TextAppearance.Material.Notification.HybridNotificationDivider"
+        parent="@*android:style/TextAppearance.Material.Notification">
+    </style>
+
     <style name="SearchPanelCircle">
         <item name="android:layout_width">match_parent</item>
         <item name="android:layout_height">match_parent</item>
diff --git a/packages/SystemUI/res/xml/tuner_prefs.xml b/packages/SystemUI/res/xml/tuner_prefs.xml
index febe518..4de4ced 100644
--- a/packages/SystemUI/res/xml/tuner_prefs.xml
+++ b/packages/SystemUI/res/xml/tuner_prefs.xml
@@ -122,6 +122,11 @@
             android:title="@string/overview_fast_toggle_via_button"
             android:summary="@string/overview_fast_toggle_via_button_desc" />
 
+        <com.android.systemui.tuner.TunerSwitch
+            android:key="overview_nav_bar_gesture"
+            android:title="@string/overview_nav_bar_gesture"
+            android:summary="@string/overview_nav_bar_gesture_desc" />
+
     </PreferenceScreen>
 
     <SwitchPreference
diff --git a/packages/SystemUI/src/com/android/systemui/BatteryMeterDrawable.java b/packages/SystemUI/src/com/android/systemui/BatteryMeterDrawable.java
index a0dbad4..454d1ce 100755
--- a/packages/SystemUI/src/com/android/systemui/BatteryMeterDrawable.java
+++ b/packages/SystemUI/src/com/android/systemui/BatteryMeterDrawable.java
@@ -51,6 +51,8 @@
     private static final float BOLT_LEVEL_THRESHOLD = 0.3f;  // opaque bolt below this fraction
 
     private final int[] mColors;
+    private final int mIntrinsicWidth;
+    private final int mIntrinsicHeight;
 
     private boolean mShowPercent;
     private float mButtonHeightFraction;
@@ -161,12 +163,26 @@
         mLightModeBackgroundColor =
                 context.getColor(R.color.light_mode_icon_color_dual_tone_background);
         mLightModeFillColor = context.getColor(R.color.light_mode_icon_color_dual_tone_fill);
+
+        mIntrinsicWidth = context.getResources().getDimensionPixelSize(R.dimen.battery_width);
+        mIntrinsicHeight = context.getResources().getDimensionPixelSize(R.dimen.battery_height);
+    }
+
+    @Override
+    public int getIntrinsicHeight() {
+        return mIntrinsicHeight;
+    }
+
+    @Override
+    public int getIntrinsicWidth() {
+        return mIntrinsicWidth;
     }
 
     public void startListening() {
         mListening = true;
         mContext.getContentResolver().registerContentObserver(
                 Settings.System.getUriFor(SHOW_PERCENT_SETTING), false, mSettingObserver);
+        updateShowPercent();
         if (mDemoMode) return;
         mBatteryController.addStateChangedCallback(this);
     }
@@ -178,6 +194,11 @@
         mBatteryController.removeStateChangedCallback(this);
     }
 
+    public void disableShowPercent() {
+        mShowPercent = false;
+        postInvalidate();
+    }
+
     private void postInvalidate() {
         mHandler.post(new Runnable() {
             @Override
diff --git a/packages/SystemUI/src/com/android/systemui/Interpolators.java b/packages/SystemUI/src/com/android/systemui/Interpolators.java
index cd6dce0..5e33a9f 100644
--- a/packages/SystemUI/src/com/android/systemui/Interpolators.java
+++ b/packages/SystemUI/src/com/android/systemui/Interpolators.java
@@ -34,4 +34,10 @@
     public static final Interpolator LINEAR = new LinearInterpolator();
     public static final Interpolator ACCELERATE_DECELERATE = new AccelerateDecelerateInterpolator();
     public static final Interpolator DECELERATE_QUINT = new DecelerateInterpolator(2.5f);
+
+    /**
+     * Interpolator to be used when animating a move based on a click. Pair with enough duration.
+     */
+    public static final Interpolator TOUCH_RESPONSE =
+            new PathInterpolator(0.3f, 0f, 0.1f, 1f);
 }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSIconView.java b/packages/SystemUI/src/com/android/systemui/qs/QSIconView.java
index ed90904..1df372b 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSIconView.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSIconView.java
@@ -24,6 +24,7 @@
 import android.view.ViewGroup;
 import android.widget.ImageView;
 
+import android.widget.ImageView.ScaleType;
 import com.android.systemui.R;
 
 import java.util.Objects;
@@ -96,7 +97,7 @@
     protected View createIcon() {
         final ImageView icon = new ImageView(mContext);
         icon.setId(android.R.id.icon);
-        icon.setScaleType(ImageView.ScaleType.CENTER_INSIDE);
+        icon.setScaleType(ScaleType.FIT_CENTER);
         return icon;
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSTile.java b/packages/SystemUI/src/com/android/systemui/qs/QSTile.java
index 35000d3..d79f4d4 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSTile.java
@@ -76,7 +76,7 @@
 
     private String mTileSpec;
 
-    abstract protected TState newTileState();
+    public abstract TState newTileState();
     abstract protected void handleClick();
     abstract protected void handleUpdateState(TState state, Object arg);
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java
index e4b8a6c..f208470 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java
@@ -18,6 +18,7 @@
 
 import android.content.Context;
 import android.content.res.ColorStateList;
+import android.content.res.Configuration;
 import android.util.AttributeSet;
 import android.view.Gravity;
 import android.view.View;
@@ -102,6 +103,8 @@
 
     private static class HeaderTileLayout extends LinearLayout implements QSTileLayout {
 
+        private final ImageView mDownArrow;
+
         public HeaderTileLayout(Context context) {
             super(context);
             setClipChildren(false);
@@ -111,17 +114,31 @@
 
             int padding =
                     mContext.getResources().getDimensionPixelSize(R.dimen.qs_quick_tile_padding);
-            ImageView downArrow = new ImageView(context);
-            downArrow.setImageResource(R.drawable.ic_expand_more);
-            downArrow.setImageTintList(ColorStateList.valueOf(context.getResources().getColor(
+            mDownArrow = new ImageView(context);
+            mDownArrow.setImageResource(R.drawable.ic_expand_more);
+            mDownArrow.setImageTintList(ColorStateList.valueOf(context.getResources().getColor(
                     android.R.color.white, null)));
-            downArrow.setLayoutParams(generateLayoutParams());
-            downArrow.setPadding(padding, padding, padding, padding);
-            addView(downArrow);
+            mDownArrow.setLayoutParams(generateLayoutParams());
+            mDownArrow.setPadding(padding, padding, padding, padding);
+            updateDownArrowMargin();
+            addView(mDownArrow);
             setOrientation(LinearLayout.HORIZONTAL);
         }
 
         @Override
+        protected void onConfigurationChanged(Configuration newConfig) {
+            super.onConfigurationChanged(newConfig);
+            updateDownArrowMargin();
+        }
+
+        private void updateDownArrowMargin() {
+            LayoutParams params = (LayoutParams) mDownArrow.getLayoutParams();
+            params.setMarginStart(mContext.getResources().getDimensionPixelSize(
+                    R.dimen.qs_expand_margin));
+            mDownArrow.setLayoutParams(params);
+        }
+
+        @Override
         public void addTile(TileRecord tile) {
             addView(tile.tileView, getChildCount() - 1 /* Leave icon at end */,
                     generateLayoutParams());
diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/BlankCustomTile.java b/packages/SystemUI/src/com/android/systemui/qs/customize/BlankCustomTile.java
deleted file mode 100644
index 36bed0d..0000000
--- a/packages/SystemUI/src/com/android/systemui/qs/customize/BlankCustomTile.java
+++ /dev/null
@@ -1,91 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.qs.customize;
-
-import android.content.ComponentName;
-import android.content.pm.PackageManager;
-import android.content.pm.ServiceInfo;
-import android.graphics.drawable.Drawable;
-
-import com.android.internal.logging.MetricsLogger;
-import com.android.internal.logging.MetricsProto.MetricsEvent;
-import com.android.systemui.R;
-import com.android.systemui.qs.QSTile;
-
-public class BlankCustomTile extends QSTile<QSTile.State> {
-    public static final String PREFIX = "custom(";
-
-    private final ComponentName mComponent;
-
-    private BlankCustomTile(Host host, String action) {
-        super(host);
-        mComponent = ComponentName.unflattenFromString(action);
-    }
-
-    public static QSTile<?> create(Host host, String spec) {
-        if (spec == null || !spec.startsWith(PREFIX) || !spec.endsWith(")")) {
-            throw new IllegalArgumentException("Bad custom tile spec: " + spec);
-        }
-        final String action = spec.substring(PREFIX.length(), spec.length() - 1);
-        if (action.isEmpty()) {
-            throw new IllegalArgumentException("Empty custom tile spec action");
-        }
-        return new BlankCustomTile(host, action);
-    }
-
-    @Override
-    public void setListening(boolean listening) {
-    }
-
-    @Override
-    protected State newTileState() {
-        return new State();
-    }
-
-    @Override
-    protected void handleUserSwitch(int newUserId) {
-        super.handleUserSwitch(newUserId);
-    }
-
-    @Override
-    protected void handleClick() {
-        MetricsLogger.action(mContext, getMetricsCategory(), mComponent.getPackageName());
-    }
-
-    @Override
-    protected void handleLongClick() {
-    }
-
-    @Override
-    protected void handleUpdateState(State state, Object arg) {
-        try {
-            PackageManager pm = mContext.getPackageManager();
-            ServiceInfo info = pm.getServiceInfo(mComponent, 0);
-            Drawable drawable = info.loadIcon(pm);
-            drawable.setTint(mContext.getColor(R.color.qs_tile_tint_active));
-            state.icon = new DrawableIcon(drawable);
-            state.label = info.loadLabel(pm).toString();
-            state.contentDescription = state.label;
-        } catch (Exception e) {
-        }
-    }
-
-    @Override
-    public int getMetricsCategory() {
-        return MetricsEvent.QS_INTENT;
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/CustomQSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/customize/CustomQSPanel.java
deleted file mode 100644
index 286748b..0000000
--- a/packages/SystemUI/src/com/android/systemui/qs/customize/CustomQSPanel.java
+++ /dev/null
@@ -1,204 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.systemui.qs.customize;
-
-import android.app.ActivityManager;
-import android.content.ClipData;
-import android.content.Context;
-import android.content.Intent;
-import android.os.Handler;
-import android.os.UserHandle;
-import android.provider.Settings.Secure;
-import android.text.TextUtils;
-import android.util.AttributeSet;
-import android.util.Log;
-import android.view.LayoutInflater;
-import android.view.View;
-
-import com.android.systemui.R;
-import com.android.systemui.qs.QSPanel;
-import com.android.systemui.qs.QSTile;
-import com.android.systemui.qs.external.CustomTile;
-import com.android.systemui.qs.external.TileLifecycleManager;
-import com.android.systemui.statusbar.phone.QSTileHost;
-import com.android.systemui.tuner.TunerService;
-
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.List;
-
-/**
- * A version of QSPanel that allows tiles to be dragged around rather than
- * clicked on.  Dragging starting and receiving is handled in the NonPagedTileLayout,
- * and the saving/ordering is handled by the CustomQSTileHost.
- */
-public class CustomQSPanel extends QSPanel {
-    
-    private static final String TAG = "CustomQSPanel";
-    private static final boolean DEBUG = false;
-
-    private List<String> mSavedTiles = Collections.emptyList();
-    private ArrayList<String> mStash;
-    private List<String> mTiles = new ArrayList<>();
-
-    private ArrayList<QSTile<?>> mCurrentTiles = new ArrayList<>();
-
-    public CustomQSPanel(Context context, AttributeSet attrs) {
-        super(context, attrs);
-        mTileLayout = (QSTileLayout) LayoutInflater.from(mContext)
-                .inflate(R.layout.qs_customize_layout, mQsContainer, false);
-        mQsContainer.addView((View) mTileLayout, 1 /* Between brightness and footer */);
-        ((NonPagedTileLayout) mTileLayout).setCustomQsPanel(this);
-        removeView(mFooter.getView());
-
-        if (DEBUG) Log.d(TAG, "new CustomQSPanel", new Throwable());
-        TunerService.get(mContext).addTunable(this, QSTileHost.TILES_SETTING);
-    }
-
-    @Override
-    protected void showDetail(boolean show, Record r) {
-        // No detail here.
-    }
-
-    @Override
-    protected void onDetachedFromWindow() {
-        // Don't allow the super to unregister the tunable.
-    }
-
-    @Override
-    public void onTuningChanged(String key, String newValue) {
-        if (key.equals(QS_SHOW_BRIGHTNESS)) {
-            // No Brightness for you.
-            super.onTuningChanged(key, "0");
-        }
-        if (QSTileHost.TILES_SETTING.equals(key)) {
-            mSavedTiles = Collections.unmodifiableList(
-                    QSTileHost.loadTileSpecs(mContext, newValue));
-            if (DEBUG) Log.d(TAG, "New saved tiles " + TextUtils.join(",", mSavedTiles));
-        }
-    }
-
-    @Override
-    protected void createCustomizePanel() {
-        // Already in CustomizePanel.
-    }
-
-    public void tileSelected(QSTile<?> tile, ClipData currentClip) {
-        String sourceSpec = getSpec(currentClip);
-        String destSpec = tile.getTileSpec();
-        if (!sourceSpec.equals(destSpec)) {
-            moveTo(sourceSpec, destSpec);
-        }
-    }
-
-    public ClipData getClip(QSTile<?> tile) {
-        String tileSpec = tile.getTileSpec();
-        // TODO: Something better than plain text.
-        // TODO: Once using something better than plain text, stop listening to non-QS drag events.
-        return ClipData.newPlainText(tileSpec, tileSpec);
-    }
-
-    public String getSpec(ClipData data) {
-        return data.getItemAt(0).getText().toString();
-    }
-
-    public void setSavedTiles() {
-        if (DEBUG) Log.d(TAG, "setSavedTiles " + TextUtils.join(",", mSavedTiles));
-        setTiles(mSavedTiles);
-    }
-
-    public void saveCurrentTiles() {
-        mHost.changeTiles(mSavedTiles, mTiles);
-    }
-
-    public void stashCurrentTiles() {
-        mStash = new ArrayList<>(mTiles);
-    }
-
-    public void unstashTiles() {
-        setTiles(mStash);
-    }
-
-    @Override
-    public void setTiles(Collection<QSTile<?>> tiles) {
-        setTilesInternal();
-    }
-
-    private void setTilesInternal() {
-        if (DEBUG) Log.d(TAG, "Set tiles internal");
-        for (int i = 0; i < mCurrentTiles.size(); i++) {
-            mCurrentTiles.get(i).destroy();
-        }
-        mCurrentTiles.clear();
-        for (int i = 0; i < mTiles.size(); i++) {
-            if (mTiles.get(i).startsWith(CustomTile.PREFIX)) {
-                QSTile<?> tile = BlankCustomTile.create(mHost, mTiles.get(i));
-                tile.setTileSpec(mTiles.get(i));
-                mCurrentTiles.add(tile);
-            } else {
-                QSTile<?> tile = mHost.createTile(mTiles.get(i));
-                if (tile != null) {
-                    tile.setTileSpec(mTiles.get(i));
-                    mCurrentTiles.add(tile);
-                } else {
-                    if (DEBUG) Log.d(TAG, "Skipping " + mTiles.get(i));
-                }
-            }
-        }
-        super.setTiles(mCurrentTiles);
-    }
-
-    public void addTile(String spec) {
-        if (DEBUG) Log.d(TAG, "addTile " + spec);
-        mTiles.add(spec);
-        setTilesInternal();
-    }
-
-    public void moveTo(String from, String to) {
-        if (DEBUG) Log.d(TAG, "moveTo " + from + " " + to);
-        int fromIndex = mTiles.indexOf(from);
-        if (fromIndex < 0) {
-            Log.e(TAG, "Unknown from tile " + from);
-            return;
-        }
-        int index = mTiles.indexOf(to);
-        if (index < 0) {
-            Log.e(TAG, "Unknown to tile " + to);
-            return;
-        }
-        mTiles.remove(fromIndex);
-        mTiles.add(index, from);
-        setTilesInternal();
-    }
-
-    public void remove(String spec) {
-        if (!mTiles.remove(spec)) {
-            Log.e(TAG, "Unknown remove spec " + spec);
-        }
-        setTilesInternal();
-    }
-
-    public void setTiles(List<String> tiles) {
-        if (DEBUG) Log.d(TAG, "Set tiles " + TextUtils.join(",", tiles));
-        mTiles = new ArrayList<>(tiles);
-        setTilesInternal();
-    }
-
-    public Collection<QSTile<?>> getTiles() {
-        return mCurrentTiles;
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/DropButton.java b/packages/SystemUI/src/com/android/systemui/qs/customize/DropButton.java
deleted file mode 100644
index 3135408..0000000
--- a/packages/SystemUI/src/com/android/systemui/qs/customize/DropButton.java
+++ /dev/null
@@ -1,70 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
-*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-*      http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
-package com.android.systemui.qs.customize;
-
-import android.content.ClipData;
-import android.content.Context;
-import android.util.AttributeSet;
-import android.view.DragEvent;
-import android.view.View;
-import android.view.View.OnDragListener;
-import android.widget.TextView;
-
-public class DropButton extends TextView implements OnDragListener {
-
-    private OnDropListener mListener;
-
-    public DropButton(Context context, AttributeSet attrs) {
-        super(context, attrs);
-    }
-
-    @Override
-    protected void onAttachedToWindow() {
-        super.onAttachedToWindow();
-        // TODO: Don't do this, instead make this view the right size...
-        ((View) getParent()).setOnDragListener(this);
-    }
-
-    public void setOnDropListener(OnDropListener listener) {
-        mListener = listener;
-    }
-
-    private void setHovering(boolean hovering) {
-        setAlpha(hovering ? .3f : 1);
-    }
-
-    @Override
-    public boolean onDrag(View v, DragEvent event) {
-        switch (event.getAction()) {
-            case DragEvent.ACTION_DRAG_ENTERED:
-                setHovering(true);
-                break;
-            case DragEvent.ACTION_DROP:
-                if (mListener != null) {
-                    mListener.onDrop(this, event.getClipData());
-                }
-            case DragEvent.ACTION_DRAG_EXITED:
-            case DragEvent.ACTION_DRAG_ENDED:
-                setHovering(false);
-                break;
-        }
-        return true;
-    }
-
-    public interface OnDropListener {
-        void onDrop(View v, ClipData data);
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/FloatingActionButton.java b/packages/SystemUI/src/com/android/systemui/qs/customize/FloatingActionButton.java
deleted file mode 100644
index 8791a10..0000000
--- a/packages/SystemUI/src/com/android/systemui/qs/customize/FloatingActionButton.java
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.qs.customize;
-
-import android.animation.AnimatorInflater;
-import android.content.Context;
-import android.graphics.Outline;
-import android.util.AttributeSet;
-import android.view.View;
-import android.view.ViewOutlineProvider;
-import android.widget.ImageView;
-
-import com.android.systemui.R;
-
-public class FloatingActionButton extends ImageView {
-
-    public FloatingActionButton(Context context, AttributeSet attrs) {
-        super(context, attrs);
-        setScaleType(ScaleType.CENTER);
-        setStateListAnimator(AnimatorInflater.loadStateListAnimator(context, R.anim.fab_elevation));
-        setOutlineProvider(new ViewOutlineProvider() {
-            @Override
-            public void getOutline(View view, Outline outline) {
-                outline.setOval(0, 0, getWidth(), getHeight());
-            }
-        });
-        setClipToOutline(true);
-    }
-
-    @Override
-    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
-        super.onSizeChanged(w, h, oldw, oldh);
-        invalidateOutline();
-    }
-}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/NonPagedTileLayout.java b/packages/SystemUI/src/com/android/systemui/qs/customize/NonPagedTileLayout.java
deleted file mode 100644
index 98c7be4..0000000
--- a/packages/SystemUI/src/com/android/systemui/qs/customize/NonPagedTileLayout.java
+++ /dev/null
@@ -1,184 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.systemui.qs.customize;
-
-import android.content.ClipData;
-import android.content.Context;
-import android.graphics.Rect;
-import android.util.AttributeSet;
-import android.view.DragEvent;
-import android.view.LayoutInflater;
-import android.view.MotionEvent;
-import android.view.View;
-import android.view.View.OnTouchListener;
-import android.widget.LinearLayout;
-
-import com.android.systemui.R;
-import com.android.systemui.qs.PagedTileLayout;
-import com.android.systemui.qs.PagedTileLayout.TilePage;
-import com.android.systemui.qs.QSPanel.QSTileLayout;
-import com.android.systemui.qs.QSPanel.TileRecord;
-import com.android.systemui.qs.QSTile;
-
-import java.util.ArrayList;
-
-/**
- * Similar to PagedTileLayout, except that instead of pages it lays them out
- * vertically and expects to be inside a ScrollView.
- * @see CustomQSPanel
- */
-public class NonPagedTileLayout extends LinearLayout implements QSTileLayout, OnTouchListener {
-
-    private final ArrayList<TilePage> mPages = new ArrayList<>();
-    private final ArrayList<TileRecord> mTiles = new ArrayList<TileRecord>();
-    private CustomQSPanel mPanel;
-    private final Rect mHitRect = new Rect();
-
-    private ClipData mCurrentClip;
-    private View mCurrentView;
-
-    public NonPagedTileLayout(Context context, AttributeSet attrs) {
-        super(context, attrs);
-    }
-
-    @Override
-    protected void onFinishInflate() {
-        super.onFinishInflate();
-        TilePage page = (PagedTileLayout.TilePage) findViewById(R.id.tile_page);
-        page.setMaxRows(3 /* First page only gets 3 */);
-        mPages.add(page);
-    }
-
-    public void setCustomQsPanel(CustomQSPanel qsPanel) {
-        mPanel = qsPanel;
-    }
-
-    @Override
-    public void addTile(TileRecord record) {
-        mTiles.add(record);
-        distributeTiles();
-        if (record.tileView.getTag() == record.tile) {
-            return;
-        }
-        record.tileView.setTag(record.tile);
-        record.tileView.setVisibility(View.VISIBLE);
-        record.tileView.init(null, null);
-        record.tileView.setOnTouchListener(this);
-        if (mCurrentClip != null && mCurrentClip.getItemAt(0)
-                .getText().toString().equals(record.tile.getTileSpec())) {
-            record.tileView.setAlpha(.3f);
-            mCurrentView = record.tileView;
-        }
-    }
-
-    @Override
-    public void removeTile(TileRecord tile) {
-        if (mTiles.remove(tile)) {
-            distributeTiles();
-        }
-    }
-
-    private void distributeTiles() {
-        final int NP = mPages.size();
-        for (int i = 0; i < NP; i++) {
-            mPages.get(i).removeAllViews();
-        }
-        int index = 0;
-        final int NT = mTiles.size();
-        for (int i = 0; i < NT; i++) {
-            TileRecord tile = mTiles.get(i);
-            mPages.get(index).addTile(tile);
-            // Keep everything in one layout for now.
-            if (false && mPages.get(index).isFull()) {
-                if (++index == mPages.size()) {
-                    LayoutInflater inflater = LayoutInflater.from(mContext);
-                    inflater.inflate(R.layout.horizontal_divider, this);
-                    mPages.add((TilePage) inflater.inflate(R.layout.qs_paged_page, this, false));
-                    addView(mPages.get(mPages.size() - 1));
-                }
-            }
-        }
-    }
-
-    @Override
-    public int getOffsetTop(TileRecord tile) {
-        // No touch feedback, so this isn't required.
-        return 0;
-    }
-
-    @Override
-    public boolean updateResources() {
-        return false;
-    }
-
-    @Override
-    public boolean onDragEvent(DragEvent event) {
-        switch (event.getAction()) {
-            case DragEvent.ACTION_DRAG_LOCATION:
-                float x = event.getX();
-                float y = event.getY();
-                final int NP = mPages.size();
-                for (int i = 0; i < NP; i++) {
-                    TilePage page = mPages.get(i);
-                    if (contains(page, x, y)) {
-                        x -= page.getLeft();
-                        y -= page.getTop();
-                        final int NC = page.getChildCount();
-                        for (int j = 0; j < NC; j++) {
-                            View child = page.getChildAt(j);
-                            if (contains(child, x, y)) {
-                                mPanel.tileSelected((QSTile<?>) child.getTag(), mCurrentClip);
-                            }
-                        }
-                        break;
-                    }
-                }
-                break;
-            case DragEvent.ACTION_DRAG_ENDED:
-                onDragEnded();
-                break;
-        }
-        return true;
-    }
-
-    @Override
-    public boolean onTouch(View v, MotionEvent event) {
-        switch (event.getAction()) {
-            case MotionEvent.ACTION_DOWN:
-                // Stash the current tiles, in case the drop is on info, that we can restore
-                // the previous state.
-                mPanel.stashCurrentTiles();
-                mCurrentView = v;
-                mCurrentClip = mPanel.getClip((QSTile<?>) v.getTag());
-                View.DragShadowBuilder shadow = new View.DragShadowBuilder(v);
-                ((View) getParent().getParent()).startDrag(mCurrentClip, shadow, null, 0);
-                v.setAlpha(.3f);
-                return true;
-        }
-        return false;
-    }
-
-    public void onDragEnded() {
-        mCurrentView.setAlpha(1f);
-        mCurrentView = null;
-        mCurrentClip = null;
-    }
-
-    private boolean contains(View v, float x, float y) {
-        v.getHitRect(mHitRect);
-        return mHitRect.contains((int) x, (int) y);
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java b/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java
index a6c7fe4..edcccac 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java
@@ -16,38 +16,25 @@
 package com.android.systemui.qs.customize;
 
 import android.animation.Animator;
-import android.content.ClipData;
+import android.animation.Animator.AnimatorListener;
 import android.content.Context;
-import android.content.DialogInterface;
-import android.content.DialogInterface.OnCancelListener;
-import android.content.DialogInterface.OnDismissListener;
+import android.support.v7.widget.DefaultItemAnimator;
+import android.support.v7.widget.GridLayoutManager;
+import android.support.v7.widget.RecyclerView;
+import android.support.v7.widget.helper.ItemTouchHelper;
 import android.util.AttributeSet;
-import android.util.Log;
-import android.util.TypedValue;
 import android.view.ContextThemeWrapper;
-import android.view.DragEvent;
-import android.view.LayoutInflater;
-import android.view.Menu;
-import android.view.MenuItem;
 import android.view.View;
 import android.view.View.OnClickListener;
-import android.view.ViewGroup;
-import android.view.WindowManager;
 import android.widget.LinearLayout;
-import android.widget.ListView;
-import android.widget.Toolbar;
-import android.widget.Toolbar.OnMenuItemClickListener;
-
 import com.android.systemui.R;
 import com.android.systemui.qs.QSDetailClipper;
-import com.android.systemui.qs.QSTile.Host.Callback;
-import com.android.systemui.qs.customize.DropButton.OnDropListener;
-import com.android.systemui.qs.customize.TileAdapter.TileSelectedListener;
+import com.android.systemui.qs.QSTile;
 import com.android.systemui.statusbar.phone.PhoneStatusBar;
 import com.android.systemui.statusbar.phone.QSTileHost;
-import com.android.systemui.statusbar.phone.SystemUIDialog;
 
 import java.util.ArrayList;
+import java.util.List;
 
 /**
  * Allows full-screen customization of QS, through show() and hide().
@@ -55,26 +42,19 @@
  * This adds itself to the status bar window, so it can appear on top of quick settings and
  * *someday* do fancy animations to get into/out of it.
  */
-public class QSCustomizer extends LinearLayout implements OnMenuItemClickListener, Callback,
-        OnDropListener, OnClickListener, Animator.AnimatorListener, TileSelectedListener,
-        OnCancelListener, OnDismissListener {
+public class QSCustomizer extends LinearLayout implements AnimatorListener, OnClickListener {
 
-    private static final int MENU_SAVE = Menu.FIRST;
-    private static final int MENU_RESET = Menu.FIRST + 1;
     private final QSDetailClipper mClipper;
 
     private PhoneStatusBar mPhoneStatusBar;
 
-    private Toolbar mToolbar;
-    private ViewGroup mDragButtons;
-    private CustomQSPanel mQsPanel;
-
     private boolean isShown;
-    private DropButton mInfoButton;
-    private DropButton mRemoveButton;
-    private FloatingActionButton mFab;
-    private SystemUIDialog mDialog;
     private QSTileHost mHost;
+    private RecyclerView mRecyclerView;
+    private TileAdapter mTileAdapter;
+    private View mClose;
+    private View mSave;
+    private View mReset;
 
     public QSCustomizer(Context context, AttributeSet attrs) {
         super(new ContextThemeWrapper(context, android.R.style.Theme_Material), attrs);
@@ -83,59 +63,42 @@
 
     public void setHost(QSTileHost host) {
         mHost = host;
-        mHost.addCallback(this);
         mPhoneStatusBar = host.getPhoneStatusBar();
-        mQsPanel.setTiles(mHost.getTiles());
-        mQsPanel.setHost(mHost);
-        mQsPanel.setSavedTiles();
     }
 
     @Override
     protected void onFinishInflate() {
         super.onFinishInflate();
-        mToolbar = (Toolbar) findViewById(com.android.internal.R.id.action_bar);
-        TypedValue value = new TypedValue();
-        mContext.getTheme().resolveAttribute(android.R.attr.homeAsUpIndicator, value, true);
-        mToolbar.setNavigationIcon(
-                getResources().getDrawable(R.drawable.ic_close_white, mContext.getTheme()));
-        mToolbar.setNavigationOnClickListener(new OnClickListener() {
-            @Override
-            public void onClick(View v) {
-                hide(0, 0);
-            }
-        });
-        mToolbar.setOnMenuItemClickListener(this);
-        mToolbar.getMenu().add(Menu.NONE, MENU_SAVE, 0, mContext.getString(R.string.save))
-                .setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);
-        mToolbar.getMenu().add(Menu.NONE, MENU_RESET, 0,
-                mContext.getString(com.android.internal.R.string.reset));
+        mClose = findViewById(R.id.close);
+        mSave = findViewById(R.id.save);
+        mReset = findViewById(R.id.reset);
+        mClose.setOnClickListener(this);
+        mSave.setOnClickListener(this);
+        mReset.setOnClickListener(this);
 
-        mQsPanel = (CustomQSPanel) findViewById(R.id.quick_settings_panel);
-
-        mDragButtons = (ViewGroup) findViewById(R.id.drag_buttons);
-        setDragging(false);
-
-        mInfoButton = (DropButton) findViewById(R.id.info_button);
-        mInfoButton.setOnDropListener(this);
-        mRemoveButton = (DropButton) findViewById(R.id.remove_button);
-        mRemoveButton.setOnDropListener(this);
-
-        mFab = (FloatingActionButton) findViewById(R.id.fab);
-        mFab.setImageResource(R.drawable.ic_add);
-        mFab.setOnClickListener(this);
+        mRecyclerView = (RecyclerView) findViewById(android.R.id.list);
+        mTileAdapter = new TileAdapter(getContext());
+        mRecyclerView.setAdapter(mTileAdapter);
+        new ItemTouchHelper(mTileAdapter.getCallback()).attachToRecyclerView(mRecyclerView);
+        GridLayoutManager layout = new GridLayoutManager(getContext(), 3);
+        layout.setSpanSizeLookup(mTileAdapter.getSizeLookup());
+        mRecyclerView.setLayoutManager(layout);
+        mRecyclerView.addItemDecoration(mTileAdapter.getItemDecoration());
+        DefaultItemAnimator animator = new DefaultItemAnimator();
+        animator.setMoveDuration(TileAdapter.MOVE_DURATION);
+        mRecyclerView.setItemAnimator(animator);
     }
 
     public void show(int x, int y) {
         isShown = true;
-        mQsPanel.setSavedTiles();
         mPhoneStatusBar.getStatusBarWindow().addView(this);
-        mQsPanel.setListening(true);
+        setTileSpecs();
         mClipper.animateCircularClip(x, y, true, this);
+        new TileQueryHelper(mContext, mHost).setListener(mTileAdapter);
     }
 
     public void hide(int x, int y) {
         isShown = false;
-        mQsPanel.setListening(false);
         mClipper.animateCircularClip(x, y, false, this);
     }
 
@@ -149,109 +112,35 @@
         for (String tile : defTiles.split(",")) {
             tiles.add(tile);
         }
-        mQsPanel.setTiles(tiles);
+        mTileAdapter.setTileSpecs(tiles);
     }
 
-    private void setDragging(boolean dragging) {
-        mToolbar.setVisibility(!dragging ? View.VISIBLE : View.INVISIBLE);
+    private void setTileSpecs() {
+        List<String> specs = new ArrayList<>();
+        for (QSTile tile : mHost.getTiles()) {
+            specs.add(tile.getTileSpec());
+        }
+        mTileAdapter.setTileSpecs(specs);
     }
 
     private void save() {
-        Log.d("CustomQSPanel", "Save!");
-        mQsPanel.saveCurrentTiles();
-        // TODO: At save button.
-        hide(0, 0);
-    }
-
-    @Override
-    public boolean onMenuItemClick(MenuItem item) {
-        switch (item.getItemId()) {
-            case MENU_SAVE:
-                Log.d("CustomQSPanel", "Save...");
-                save();
-                break;
-            case MENU_RESET:
-                reset();
-                break;
-        }
-        return true;
-    }
-
-    @Override
-    public void onTileSelected(String spec) {
-        if (mDialog != null) {
-            mQsPanel.addTile(spec);
-            mDialog.dismiss();
-        }
-    }
-
-    @Override
-    public void onTilesChanged() {
-        mQsPanel.setTiles(mHost.getTiles());
-    }
-
-    public boolean onDragEvent(DragEvent event) {
-        switch (event.getAction()) {
-            case DragEvent.ACTION_DRAG_STARTED:
-                setDragging(true);
-                break;
-            case DragEvent.ACTION_DRAG_ENDED:
-                setDragging(false);
-                break;
-        }
-        return true;
-    }
-
-    public void onDrop(View v, ClipData data) {
-        if (v == mRemoveButton) {
-            mQsPanel.remove(mQsPanel.getSpec(data));
-        } else if (v == mInfoButton) {
-            mQsPanel.unstashTiles();
-            SystemUIDialog dialog = new SystemUIDialog(mContext);
-            dialog.setTitle(mQsPanel.getSpec(data));
-            dialog.setPositiveButton(R.string.ok, null);
-            dialog.show();
-        }
+        mTileAdapter.saveSpecs(mHost);
+        hide((int) mSave.getX() + mSave.getWidth() / 2, (int) mSave.getY() + mSave.getHeight() / 2);
     }
 
     @Override
     public void onClick(View v) {
-        if (mFab == v) {
-            mDialog = new SystemUIDialog(mContext,
-                    android.R.style.Theme_Material_Dialog);
-            View view = LayoutInflater.from(mContext).inflate(R.layout.qs_add_tiles_list, null);
-            ListView listView = (ListView) view.findViewById(android.R.id.list);
-            TileAdapter adapter = new TileAdapter(mContext, mQsPanel.getTiles(), mHost);
-            adapter.setListener(this);
-            listView.setDivider(null);
-            listView.setDividerHeight(0);
-            listView.setAdapter(adapter);
-            listView.setEmptyView(view.findViewById(R.id.empty_text));
-            mDialog.setView(view);
-            mDialog.setOnDismissListener(this);
-            mDialog.setOnCancelListener(this);
-            mDialog.show();
-            // Too lazy to figure out what this will be now, but it should probably be something
-            // besides just a dialog.
-            // For now, just make it big.
-            WindowManager.LayoutParams params = mDialog.getWindow().getAttributes();
-            params.width = WindowManager.LayoutParams.MATCH_PARENT;
-            params.height = WindowManager.LayoutParams.WRAP_CONTENT;
-            mDialog.getWindow().setAttributes(params);
+        if (v == mClose) {
+            hide((int) mClose.getX() + mClose.getWidth() / 2,
+                    (int) mClose.getY() + mClose.getHeight() / 2);
+        } else if (v == mSave) {
+            save();
+        } else if (v == mReset) {
+            reset();
         }
     }
 
     @Override
-    public void onDismiss(DialogInterface dialog) {
-        mDialog = null;
-    }
-
-    @Override
-    public void onCancel(DialogInterface dialog) {
-        mDialog = null;
-    }
-
-    @Override
     public void onAnimationEnd(Animator animation) {
         if (!isShown) {
             mPhoneStatusBar.getStatusBarWindow().removeView(this);
@@ -274,4 +163,4 @@
     public void onAnimationRepeat(Animator animation) {
         // Don't care.
     }
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java b/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java
index b72789e..fb3818c 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java
@@ -1,262 +1,293 @@
 /*
- * Copyright (C) 2015 The Android Open Source Project
+ * Copyright (C) 2016 The Android Open Source Project
  *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License at
  *
  *      http://www.apache.org/licenses/LICENSE-2.0
  *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
+ * Unless required by applicable law or agreed to in writing, software distributed under the
+ * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
  */
 
 package com.android.systemui.qs.customize;
 
-import android.content.ComponentName;
 import android.content.Context;
-import android.content.Intent;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageManager;
-import android.content.pm.PackageManager.NameNotFoundException;
-import android.content.pm.ResolveInfo;
-import android.graphics.drawable.Drawable;
-import android.os.AsyncTask;
-import android.os.Handler;
-import android.os.Looper;
-import android.service.quicksettings.TileService;
-import android.util.Log;
+import android.graphics.Canvas;
+import android.graphics.drawable.ColorDrawable;
+import android.support.v4.view.ViewCompat;
+import android.support.v7.widget.GridLayoutManager.SpanSizeLookup;
+import android.support.v7.widget.RecyclerView;
+import android.support.v7.widget.RecyclerView.ItemDecoration;
+import android.support.v7.widget.RecyclerView.State;
+import android.support.v7.widget.RecyclerView.ViewHolder;
+import android.support.v7.widget.helper.ItemTouchHelper;
+import android.support.v7.widget.helper.ItemTouchHelper.Callback;
 import android.view.LayoutInflater;
 import android.view.View;
-import android.view.View.OnClickListener;
 import android.view.ViewGroup;
-import android.widget.BaseAdapter;
-import android.widget.GridLayout;
-import android.widget.ImageView;
-import android.widget.TextView;
-
+import android.widget.FrameLayout;
 import com.android.systemui.R;
-import com.android.systemui.qs.QSTile;
-import com.android.systemui.qs.QSTile.Icon;
-import com.android.systemui.qs.external.CustomTile;
+import com.android.systemui.qs.QSIconView;
+import com.android.systemui.qs.QSTileView;
+import com.android.systemui.qs.customize.TileAdapter.Holder;
+import com.android.systemui.qs.customize.TileQueryHelper.TileInfo;
+import com.android.systemui.qs.customize.TileQueryHelper.TileStateListener;
 import com.android.systemui.statusbar.phone.QSTileHost;
 
 import java.util.ArrayList;
-import java.util.Collection;
-import java.util.HashMap;
 import java.util.List;
 
-public class TileAdapter extends BaseAdapter {
+public class TileAdapter extends RecyclerView.Adapter<Holder> implements TileStateListener {
 
-    private static final String TAG = "TileAdapter";
+    private static final long DRAG_LENGTH = 100;
+    private static final float DRAG_SCALE = 1.2f;
+    public static final long MOVE_DURATION = 150;
 
-    private final ArrayList<TileGroup> mGroups = new ArrayList<>();
+    private static final int TYPE_TILE = 0;
+    private static final int TYPE_EDIT = 1;
+
     private final Context mContext;
 
-    private TileSelectedListener mListener;
-    private ArrayList<String> mCurrentTiles;
+    private final List<TileInfo> mTiles = new ArrayList<>();
+    private int mDividerIndex;
+    private List<String> mCurrentSpecs;
+    private List<TileInfo> mOtherTiles;
+    private List<TileInfo> mAllTiles;
 
-    public TileAdapter(Context context, Collection<QSTile<?>> currentTiles, QSTileHost host) {
+    private Holder mCurrentDrag;
+
+    public TileAdapter(Context context) {
         mContext = context;
-        addSystemTiles(currentTiles, host);
-        // TODO: Live?
-    }
-
-    private void addSystemTiles(Collection<QSTile<?>> currentTiles, QSTileHost host) {
-        try {
-            ArrayList<String> tileSpecs = new ArrayList<>();
-            for (QSTile<?> tile : currentTiles) {
-                tileSpecs.add(tile.getTileSpec());
-            }
-            mCurrentTiles = tileSpecs;
-            final TileGroup group = new TileGroup("com.android.settings", mContext);
-            boolean hasColorMod = host.getDisplayController().isEnabled();
-            String possible = mContext.getString(R.string.quick_settings_tiles_default)
-                    + ",hotspot,inversion,saver" + (hasColorMod ? ",colors" : "");
-            String[] possibleTiles = possible.split(",");
-            for (int i = 0; i < possibleTiles.length; i++) {
-                final String spec = possibleTiles[i];
-                if (spec.startsWith("q")) {
-                    // Quick tiles can't be customized.
-                    continue;
-                }
-                if (tileSpecs.contains(spec)) {
-                    Log.d(TAG, "Skipping " + spec);
-                    continue;
-                }
-                Log.d(TAG, "Trying " + spec);
-                final QSTile<?> tile = host.createTile(spec);
-                if (tile == null) {
-                    continue;
-                }
-                // Bad, bad, very bad.
-                tile.setListening(true);
-                tile.clearState();
-                tile.refreshState();
-                tile.setListening(false);
-                new Handler(host.getLooper()).post(new Runnable() {
-                    @Override
-                    public void run() {
-                        group.addTile(spec, tile.getState().icon, tile.getState().label, mContext);
-                    }
-                });
-            }
-            // Error: Badness (10000).
-            // Serialize this work after the host's looper's queue is empty.
-            new Handler(host.getLooper()).post(new Runnable() {
-                @Override
-                public void run() {
-                    new Handler(Looper.getMainLooper()).post(new Runnable() {
-                        @Override
-                        public void run() {
-                            if (group.mTiles.size() > 0) {
-                                mGroups.add(group);
-                                notifyDataSetChanged();
-                            }
-                            new QueryTilesTask().execute();
-                        }
-                    });
-                }
-            });
-        } catch (NameNotFoundException e) {
-            Log.e(TAG, "Couldn't load system tiles", e);
-        }
-    }
-
-    public void setListener(TileSelectedListener listener) {
-        mListener = listener;
-    }
-
-    @Override
-    public int getCount() {
-        return mGroups.size();
-    }
-
-    @Override
-    public Object getItem(int position) {
-        return mGroups.get(position);
+        setHasStableIds(true);
     }
 
     @Override
     public long getItemId(int position) {
-        return position;
+        return mTiles.get(position) != null ? mAllTiles.indexOf(mTiles.get(position)) : -1;
+    }
+
+    public Callback getCallback() {
+        return mCallbacks;
+    }
+
+    public ItemDecoration getItemDecoration() {
+        return mDecoration;
+    }
+
+    public void saveSpecs(QSTileHost host) {
+        List<String> newSpecs = new ArrayList<>();
+        for (int i = 0; mTiles.get(i) != null; i++) {
+            newSpecs.add(mTiles.get(i).spec);
+        }
+        host.changeTiles(mCurrentSpecs, newSpecs);
+        setTileSpecs(newSpecs);
+    }
+
+    public void setTileSpecs(List<String> currentSpecs) {
+        mCurrentSpecs = currentSpecs;
+        recalcSpecs();
     }
 
     @Override
-    public View getView(int position, View convertView, ViewGroup parent) {
-        return mGroups.get(position).getView(mContext, convertView, parent, mListener);
+    public void onTilesChanged(List<TileInfo> tiles) {
+        mAllTiles = tiles;
+        recalcSpecs();
     }
 
-    private static class TileGroup {
-        private final ArrayList<TileInfo> mTiles = new ArrayList<>();
-        private CharSequence mLabel;
-        private Drawable mIcon;
-
-        public TileGroup(String pkg, Context context) throws NameNotFoundException {
-            PackageManager pm = context.getPackageManager();
-            ApplicationInfo info = pm.getApplicationInfo(pkg, 0);
-            mLabel = info.loadLabel(pm);
-            mIcon = info.loadIcon(pm);
-            Log.d(TAG, "Added " + mLabel);
+    private void recalcSpecs() {
+        if (mCurrentSpecs == null || mAllTiles == null) {
+            return;
         }
-
-        private void addTile(String spec, Drawable icon, CharSequence label) {
-            TileInfo info = new TileInfo();
-            info.label = label;
-            info.drawable = icon;
-            info.spec = spec;
-            mTiles.add(info);
+        mOtherTiles = new ArrayList<TileInfo>(mAllTiles);
+        mTiles.clear();
+        for (int i = 0; i < mCurrentSpecs.size(); i++) {
+            mTiles.add(getAndRemoveOther(mCurrentSpecs.get(i)));
         }
+        mTiles.add(null);
+        mTiles.addAll(mOtherTiles);
+        mDividerIndex = mTiles.indexOf(null);
+        notifyDataSetChanged();
+    }
 
-        private void addTile(String spec, Icon icon, CharSequence label, Context context) {
-            addTile(spec, icon != null ? icon.getDrawable(context) : null, label);
-        }
-
-        private View getView(Context context, View convertView, ViewGroup parent,
-                final TileSelectedListener listener) {
-            if (convertView == null) {
-                convertView = LayoutInflater.from(context).inflate(R.layout.tile_listing, parent,
-                        false);
+    private TileInfo getAndRemoveOther(String s) {
+        for (int i = 0; i < mOtherTiles.size(); i++) {
+            if (mOtherTiles.get(i).spec.equals(s)) {
+                return mOtherTiles.remove(i);
             }
-            ((TextView) convertView.findViewById(android.R.id.title)).setText(mLabel);
-            ((ImageView) convertView.findViewById(android.R.id.icon)).setImageDrawable(mIcon);
-            GridLayout grid = (GridLayout) convertView.findViewById(R.id.tile_grid);
-            final int N = mTiles.size();
-            if (grid.getChildCount() != N) {
-                grid.removeAllViews();
+        }
+        return null;
+    }
+
+    @Override
+    public int getItemViewType(int position) {
+        if (mTiles.get(position) == null) {
+            return TYPE_EDIT;
+        }
+        return TYPE_TILE;
+    }
+
+    @Override
+    public Holder onCreateViewHolder(ViewGroup parent, int viewType) {
+        final Context context = parent.getContext();
+        LayoutInflater inflater = LayoutInflater.from(context);
+        if (viewType == 1) {
+            return new Holder(inflater.inflate(R.layout.qs_customize_divider, parent, false));
+        }
+        FrameLayout frame = (FrameLayout) inflater.inflate(R.layout.qs_customize_tile_frame, parent,
+                false);
+        frame.addView(new QSTileView(context, new QSIconView(context)));
+        return new Holder(frame);
+    }
+
+    @Override
+    public int getItemCount() {
+        return mTiles.size();
+    }
+
+    @Override
+    public void onBindViewHolder(Holder holder, int position) {
+        if (holder.getItemViewType() == TYPE_EDIT) return;
+
+        TileInfo info = mTiles.get(position);
+        holder.mTileView.onStateChanged(info.state);
+    }
+
+    public SpanSizeLookup getSizeLookup() {
+        return mSizeLookup;
+    }
+
+    public class Holder extends ViewHolder {
+        private QSTileView mTileView;
+
+        public Holder(View itemView) {
+            super(itemView);
+            if (itemView instanceof FrameLayout) {
+                mTileView = (QSTileView) ((FrameLayout) itemView).getChildAt(0);
             }
-            for (int i = 0; i < N; i++) {
-                if (grid.getChildCount() <= i) {
-                    grid.addView(createTile(context));
-                }
-                View view = grid.getChildAt(i);
-                final TileInfo tileInfo = mTiles.get(i);
-                ((ImageView) view.findViewById(R.id.tile_icon)).setImageDrawable(tileInfo.drawable);
-                ((TextView) view.findViewById(R.id.tile_label)).setText(tileInfo.label);
-                view.setClickable(true);
-                view.setOnClickListener(new OnClickListener() {
-                    @Override
-                    public void onClick(View v) {
-                        listener.onTileSelected(tileInfo.spec);
-                    }
-                });
-            }
-            return convertView;
         }
 
-        private View createTile(Context context) {
-            return LayoutInflater.from(context).inflate(R.layout.qs_add_tile_layout, null);
+        public void startDrag() {
+            itemView.animate()
+                    .setDuration(DRAG_LENGTH)
+                    .scaleX(DRAG_SCALE)
+                    .scaleY(DRAG_SCALE);
+            mTileView.findViewById(R.id.tile_label).animate()
+                    .setDuration(DRAG_LENGTH)
+                    .alpha(0);
+        }
+
+        public void stopDrag() {
+            itemView.animate()
+                    .setDuration(DRAG_LENGTH)
+                    .scaleX(1)
+                    .scaleY(1);
+            mTileView.findViewById(R.id.tile_label).animate()
+                    .setDuration(DRAG_LENGTH)
+                    .alpha(1);
         }
     }
 
-    private static class TileInfo {
-        private String spec;
-        private Drawable drawable;
-        private CharSequence label;
-    }
-
-    private class QueryTilesTask extends AsyncTask<Void, Void, Collection<TileGroup>> {
+    private final SpanSizeLookup mSizeLookup = new SpanSizeLookup() {
         @Override
-        protected Collection<TileGroup> doInBackground(Void... params) {
-            HashMap<String, TileGroup> pkgMap = new HashMap<>();
-            PackageManager pm = mContext.getPackageManager();
-            // TODO: Handle userness.
-            List<ResolveInfo> services = pm.queryIntentServices(
-                    new Intent(TileService.ACTION_QS_TILE), 0);
-            for (ResolveInfo info : services) {
-                String packageName = info.serviceInfo.packageName;
-                ComponentName componentName = new ComponentName(packageName, info.serviceInfo.name);
-                String spec = CustomTile.toSpec(componentName);
-                if (mCurrentTiles.contains(spec)) {
+        public int getSpanSize(int position) {
+            return getItemViewType(position) == TYPE_EDIT ? 3 : 1;
+        }
+    };
+
+    private final ItemDecoration mDecoration = new ItemDecoration() {
+        // TODO: Move this to resource.
+        private final ColorDrawable mDrawable = new ColorDrawable(0xff384248);
+
+        @Override
+        public void onDraw(Canvas c, RecyclerView parent, State state) {
+            super.onDraw(c, parent, state);
+
+            final int childCount = parent.getChildCount();
+            final int width = parent.getWidth();
+            final int bottom = parent.getBottom();
+            for (int i = 0; i < childCount; i++) {
+                final View child = parent.getChildAt(i);
+                final ViewHolder holder = parent.getChildViewHolder(child);
+                if (holder.getAdapterPosition() < mDividerIndex) {
                     continue;
                 }
-                try {
-                    TileGroup group = pkgMap.get(packageName);
-                    if (group == null) {
-                        group = new TileGroup(packageName, mContext);
-                        pkgMap.put(packageName, group);
-                    }
-                    Drawable icon = info.serviceInfo.loadIcon(pm);
-                    CharSequence label = info.serviceInfo.loadLabel(pm);
-                    group.addTile(spec, icon, label != null ? label.toString() : "null");
-                } catch (NameNotFoundException e) {
-                    Log.w(TAG, "Couldn't find resolved package... " + packageName, e);
-                }
+
+                final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child
+                        .getLayoutParams();
+                final int top = child.getTop() + params.topMargin +
+                        Math.round(ViewCompat.getTranslationY(child));
+                // Draw full width, in case there aren't tiles all the way across.
+                mDrawable.setBounds(0, top, width, bottom);
+                mDrawable.draw(c);
+                break;
             }
-            return pkgMap.values();
+        }
+    };
+
+    private final ItemTouchHelper.Callback mCallbacks = new ItemTouchHelper.Callback() {
+
+        @Override
+        public boolean isLongPressDragEnabled() {
+            return true;
         }
 
         @Override
-        protected void onPostExecute(Collection<TileGroup> result) {
-            mGroups.addAll(result);
-            notifyDataSetChanged();
+        public boolean isItemViewSwipeEnabled() {
+            return false;
         }
-    }
 
-    public interface TileSelectedListener {
-        void onTileSelected(String spec);
-    }
+        @Override
+        public void onSelectedChanged(ViewHolder viewHolder, int actionState) {
+            super.onSelectedChanged(viewHolder, actionState);
+            if (mCurrentDrag != null) {
+                mCurrentDrag.stopDrag();
+            }
+            if (viewHolder != null) {
+                mCurrentDrag = (Holder) viewHolder;
+                mCurrentDrag.startDrag();
+            }
+        }
+
+        @Override
+        public int getMovementFlags(RecyclerView recyclerView, ViewHolder viewHolder) {
+            if (viewHolder.getItemViewType() == TYPE_EDIT) {
+                return makeMovementFlags(0, 0);
+            }
+            int dragFlags = ItemTouchHelper.UP | ItemTouchHelper.DOWN | ItemTouchHelper.RIGHT
+                    | ItemTouchHelper.LEFT;
+            return makeMovementFlags(dragFlags, 0);
+        }
+
+        @Override
+        public boolean onMove(RecyclerView recyclerView, ViewHolder viewHolder, ViewHolder target) {
+            int from = viewHolder.getAdapterPosition();
+            int to = target.getAdapterPosition();
+            if (to > mDividerIndex) {
+                if (from < mDividerIndex) {
+                    to = mDividerIndex;
+                } else {
+                    return false;
+                }
+            }
+            if (target.getItemViewType() == TYPE_EDIT && from < mDividerIndex) {
+                to++;
+            }
+            move(from, to, mTiles);
+            mDividerIndex = mTiles.indexOf(null);
+            notifyItemMoved(from, to);
+            return true;
+        }
+
+        private <T> void move(int from, int to, List<T> list) {
+            list.add(from > to ? to : to + 1, list.get(from));
+            list.remove(from > to ? from + 1 : from);
+        }
+
+        @Override
+        public void onSwiped(ViewHolder viewHolder, int direction) {
+        }
+    };
 }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java b/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java
new file mode 100644
index 0000000..29f8af2
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java
@@ -0,0 +1,155 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.customize;
+
+import android.app.ActivityManager;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.graphics.drawable.Drawable;
+import android.os.AsyncTask;
+import android.os.Handler;
+import android.os.Looper;
+import android.service.quicksettings.TileService;
+import com.android.systemui.R;
+import com.android.systemui.qs.QSTile;
+import com.android.systemui.qs.QSTile.DrawableIcon;
+import com.android.systemui.qs.external.CustomTile;
+import com.android.systemui.statusbar.phone.QSTileHost;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+public class TileQueryHelper {
+
+    private static final String TAG = "TileQueryHelper";
+
+    private final ArrayList<TileInfo> mTiles = new ArrayList<>();
+    private final ArrayList<String> mSpecs = new ArrayList<>();
+    private final Context mContext;
+    private TileStateListener mListener;
+
+    public TileQueryHelper(Context context, QSTileHost host) {
+        mContext = context;
+        addSystemTiles(host);
+        // TODO: Live?
+    }
+
+    private void addSystemTiles(QSTileHost host) {
+        boolean hasColorMod = host.getDisplayController().isEnabled();
+        String possible = mContext.getString(R.string.quick_settings_tiles_default)
+                + ",hotspot,inversion,saver" + (hasColorMod ? ",colors" : "");
+        String[] possibleTiles = possible.split(",");
+        final Handler qsHandler = new Handler(host.getLooper());
+        final Handler mainHandler = new Handler(Looper.getMainLooper());
+        for (int i = 0; i < possibleTiles.length; i++) {
+            final String spec = possibleTiles[i];
+            final QSTile<?> tile = host.createTile(spec);
+            if (tile == null) {
+                continue;
+            }
+            tile.setListening(true);
+            tile.clearState();
+            tile.refreshState();
+            tile.setListening(false);
+            qsHandler.post(new Runnable() {
+                @Override
+                public void run() {
+                    final QSTile.State state = tile.newTileState();
+                    tile.getState().copyTo(state);
+                    mainHandler.post(new Runnable() {
+                        @Override
+                        public void run() {
+                            addTile(spec, state);
+                            mListener.onTilesChanged(mTiles);
+                        }
+                    });
+                }
+            });
+        }
+        qsHandler.post(new Runnable() {
+            @Override
+            public void run() {
+                new QueryTilesTask().execute();
+            }
+        });
+    }
+
+    public void setListener(TileStateListener listener) {
+        mListener = listener;
+    }
+
+    private void addTile(String spec, QSTile.State state) {
+        if (mSpecs.contains(spec)) {
+            return;
+        }
+        TileInfo info = new TileInfo();
+        info.state = state;
+        info.spec = spec;
+        mTiles.add(info);
+        mSpecs.add(spec);
+    }
+
+    private void addTile(String spec, Drawable drawable, CharSequence label, Context context) {
+        QSTile.State state = new QSTile.State();
+        state.label = label;
+        state.contentDescription = label;
+        state.icon = new DrawableIcon(drawable);
+        addTile(spec, state);
+    }
+
+    public static class TileInfo {
+        public String spec;
+        public QSTile.State state;
+    }
+
+    private class QueryTilesTask extends AsyncTask<Void, Void, Collection<TileInfo>> {
+        @Override
+        protected Collection<TileInfo> doInBackground(Void... params) {
+            List<TileInfo> tiles = new ArrayList<>();
+            PackageManager pm = mContext.getPackageManager();
+            List<ResolveInfo> services = pm.queryIntentServicesAsUser(
+                    new Intent(TileService.ACTION_QS_TILE), 0, ActivityManager.getCurrentUser());
+            for (ResolveInfo info : services) {
+                String packageName = info.serviceInfo.packageName;
+                ComponentName componentName = new ComponentName(packageName, info.serviceInfo.name);
+                String spec = CustomTile.toSpec(componentName);
+                Drawable icon = info.serviceInfo.loadIcon(pm);
+                if (icon != null) {
+                    icon.mutate();
+                    icon.setTint(mContext.getColor(android.R.color.white));
+                }
+                CharSequence label = info.serviceInfo.loadLabel(pm);
+                addTile(spec, icon, label != null ? label.toString() : "null", mContext);
+            }
+            return tiles;
+        }
+
+        @Override
+        protected void onPostExecute(Collection<TileInfo> result) {
+            mTiles.addAll(result);
+            mListener.onTilesChanged(mTiles);
+        }
+    }
+
+    public interface TileStateListener {
+        void onTilesChanged(List<TileInfo> tiles);
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java b/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java
index df3b5de..3cd9e67 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java
@@ -151,7 +151,7 @@
     }
 
     @Override
-    protected State newTileState() {
+    public State newTileState() {
         return new State();
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/AirplaneModeTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/AirplaneModeTile.java
index d78d6ff..5222e61 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/AirplaneModeTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/AirplaneModeTile.java
@@ -51,7 +51,7 @@
     }
 
     @Override
-    protected BooleanState newTileState() {
+    public BooleanState newTileState() {
         return new BooleanState();
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/BatteryTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/BatteryTile.java
index 64b3a6c..72cdf18 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/BatteryTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/BatteryTile.java
@@ -54,7 +54,7 @@
     }
 
     @Override
-    protected State newTileState() {
+    public State newTileState() {
         return new QSTile.State();
     }
 
@@ -153,6 +153,7 @@
         private void bindView() {
             mDrawable.onBatteryLevelChanged(100, false, false);
             mDrawable.onPowerSaveChanged(true);
+            mDrawable.disableShowPercent();
             ((ImageView) mCurrentView.findViewById(android.R.id.icon)).setImageDrawable(mDrawable);
             Checkable checkbox = (Checkable) mCurrentView.findViewById(android.R.id.toggle);
             checkbox.setChecked(mPowerSave);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java
index 874fc3e..1dce053 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java
@@ -56,7 +56,7 @@
     }
 
     @Override
-    protected BooleanState newTileState() {
+    public BooleanState newTileState() {
         return new BooleanState();
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java
index 18eb7a1..15e082a 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java
@@ -60,7 +60,7 @@
     }
 
     @Override
-    protected BooleanState newTileState() {
+    public BooleanState newTileState() {
         return new BooleanState();
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java
index aacdbc9..c3a2ebe 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java
@@ -54,7 +54,7 @@
     }
 
     @Override
-    protected SignalState newTileState() {
+    public SignalState newTileState() {
         return new SignalState();
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/ColorInversionTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/ColorInversionTile.java
index 6e843e9..e98734c 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/ColorInversionTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/ColorInversionTile.java
@@ -54,7 +54,7 @@
     }
 
     @Override
-    protected BooleanState newTileState() {
+    public BooleanState newTileState() {
         return new BooleanState();
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/DataSaverTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/DataSaverTile.java
index 1aeb0fe..c6a98b4 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/DataSaverTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/DataSaverTile.java
@@ -30,7 +30,7 @@
     }
 
     @Override
-    protected BooleanState newTileState() {
+    public BooleanState newTileState() {
         return new BooleanState();
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java
index f99a3e4..58872ec 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java
@@ -94,7 +94,7 @@
     }
 
     @Override
-    protected BooleanState newTileState() {
+    public BooleanState newTileState() {
         return new BooleanState();
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java
index 1d9f15b..f06634e 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java
@@ -47,7 +47,7 @@
     }
 
     @Override
-    protected BooleanState newTileState() {
+    public BooleanState newTileState() {
         return new BooleanState();
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java
index 2f37943..943b502 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java
@@ -44,7 +44,7 @@
     }
 
     @Override
-    protected BooleanState newTileState() {
+    public BooleanState newTileState() {
         return new BooleanState();
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/IntentTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/IntentTile.java
index e1dc9f2..bdf95d8 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/IntentTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/IntentTile.java
@@ -75,7 +75,7 @@
     }
 
     @Override
-    protected State newTileState() {
+    public State newTileState() {
         return new State();
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/LocationTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/LocationTile.java
index 8328897..9f41f9a 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/LocationTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/LocationTile.java
@@ -45,7 +45,7 @@
     }
 
     @Override
-    protected BooleanState newTileState() {
+    public BooleanState newTileState() {
         return new BooleanState();
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/RotationLockTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/RotationLockTile.java
index f920d48..c94cf5a 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/RotationLockTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/RotationLockTile.java
@@ -46,7 +46,7 @@
     }
 
     @Override
-    protected BooleanState newTileState() {
+    public BooleanState newTileState() {
         return new BooleanState();
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/UserTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/UserTile.java
index 1565b6f..ba7ea4d 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/UserTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/UserTile.java
@@ -37,7 +37,7 @@
     }
 
     @Override
-    protected State newTileState() {
+    public State newTileState() {
         return new QSTile.State();
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java
index 42296f2..ac4dfd5 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java
@@ -60,7 +60,7 @@
     }
 
     @Override
-    protected SignalState newTileState() {
+    public SignalState newTileState() {
         return new SignalState();
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/WorkModeTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/WorkModeTile.java
index 508490f..a94973c 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/WorkModeTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/WorkModeTile.java
@@ -53,7 +53,7 @@
     }
 
     @Override
-    protected BooleanState newTileState() {
+    public BooleanState newTileState() {
         return new BooleanState();
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
index d01a288..3f482c8 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
@@ -45,7 +45,6 @@
 import com.android.systemui.recents.events.activity.CancelEnterRecentsWindowAnimationEvent;
 import com.android.systemui.recents.events.activity.DebugFlagsChangedEvent;
 import com.android.systemui.recents.events.activity.DismissRecentsToHomeAnimationStarted;
-import com.android.systemui.recents.events.activity.EnterRecentsTaskStackAnimationCompletedEvent;
 import com.android.systemui.recents.events.activity.EnterRecentsWindowAnimationCompletedEvent;
 import com.android.systemui.recents.events.activity.EnterRecentsWindowLastAnimationFrameEvent;
 import com.android.systemui.recents.events.activity.ExitRecentsWindowFirstAnimationFrameEvent;
@@ -130,6 +129,7 @@
          */
         public FinishRecentsRunnable(Intent launchIntent, ActivityOptions opts) {
             mLaunchIntent = launchIntent;
+            mOpts = opts;
         }
 
         @Override
@@ -437,11 +437,8 @@
     protected void onPause() {
         super.onPause();
 
-        RecentsDebugFlags flags = Recents.getDebugFlags();
-        if (flags.isFastToggleRecentsEnabled()) {
-            // Stop the fast-toggle dozer
-            mIterateTrigger.stopDozing();
-        }
+        // Stop the fast-toggle dozer
+        mIterateTrigger.stopDozing();
     }
 
     @Override
@@ -648,6 +645,7 @@
     }
 
     public final void onBusEvent(UserInteractionEvent event) {
+        // Stop the fast-toggle dozer
         mIterateTrigger.stopDozing();
     }
 
@@ -694,21 +692,6 @@
         }
     }
 
-    public final void onBusEvent(EnterRecentsTaskStackAnimationCompletedEvent event) {
-        RecentsDebugFlags debugFlags = Recents.getDebugFlags();
-        RecentsActivityLaunchState launchState = Recents.getConfiguration().getLaunchState();
-        if (!launchState.launchedWithAltTab && debugFlags.isFastToggleRecentsEnabled() &&
-                RecentsDebugFlags.Static.EnableFastToggleTimeoutOnEnter) {
-            mIterateTrigger.setDozeDuration(
-                    getResources().getInteger(R.integer.recents_auto_advance_duration));
-            if (!mIterateTrigger.isDozing()) {
-                mIterateTrigger.startDozing();
-            } else {
-                mIterateTrigger.poke();
-            }
-        }
-    }
-
     public final void onBusEvent(EnterRecentsWindowLastAnimationFrameEvent event) {
         EventBus.getDefault().send(new UpdateFreeformTaskViewVisibilityEvent(true));
         mRecentsView.getViewTreeObserver().addOnPreDrawListener(this);
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivityLaunchState.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivityLaunchState.java
index 0afa1f6..177e841 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivityLaunchState.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivityLaunchState.java
@@ -52,10 +52,23 @@
      * Returns the task to focus given the current launch state.
      */
     public int getInitialFocusTaskIndex(int numTasks) {
+        RecentsDebugFlags debugFlags = Recents.getDebugFlags();
         if (launchedFromAppWithThumbnail) {
+            if (debugFlags.isFastToggleRecentsEnabled()) {
+                // If fast toggling, focus the front most task so that the next tap will focus the
+                // N-1 task
+                return numTasks - 1;
+            }
+
             // If coming from another app, focus the next task
             return numTasks - 2;
         } else {
+            if (debugFlags.isFastToggleRecentsEnabled()) {
+                // If fast toggling, defer focusing until the next tap (which will automatically
+                // focus the front most task)
+                return -1;
+            }
+
             // If coming from home, focus the first task
             return numTasks - 1;
         }
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsDebugFlags.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsDebugFlags.java
index 6b8968f..fc14758 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsDebugFlags.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsDebugFlags.java
@@ -39,8 +39,6 @@
         public static final boolean EnableAffiliatedTaskGroups = true;
         // Overrides the Tuner flags and enables the fast toggle and timeout
         public static final boolean EnableFastToggleTimeoutOverride = true;
-        // Enables toggling the fast-toggle timeout immediately after entering Recents
-        public static final boolean EnableFastToggleTimeoutOnEnter = true;
 
         // Enables us to create mock recents tasks
         public static final boolean EnableMockTasks = false;
@@ -90,9 +88,6 @@
      * @return whether the initial stack state is paging.
      */
     public boolean isInitialStatePaging() {
-        if (Static.EnableFastToggleTimeoutOnEnter) {
-            return true;
-        }
         return mInitialStatePaging;
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
index 1cceef4..dd7b7c1 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
@@ -38,6 +38,7 @@
 import android.view.AppTransitionAnimationSpec;
 import android.view.LayoutInflater;
 import android.view.View;
+import android.view.ViewConfiguration;
 
 import com.android.internal.logging.MetricsLogger;
 import com.android.systemui.Prefs;
@@ -48,6 +49,7 @@
 import com.android.systemui.recents.events.activity.EnterRecentsWindowLastAnimationFrameEvent;
 import com.android.systemui.recents.events.activity.HideRecentsEvent;
 import com.android.systemui.recents.events.activity.IterateRecentsEvent;
+import com.android.systemui.recents.events.activity.LaunchNextTaskRequestEvent;
 import com.android.systemui.recents.events.activity.RecentsActivityStartingEvent;
 import com.android.systemui.recents.events.activity.ToggleRecentsEvent;
 import com.android.systemui.recents.events.component.RecentsVisibilityChangedEvent;
@@ -81,8 +83,10 @@
 public class RecentsImpl implements ActivityOptions.OnAnimationFinishedListener {
 
     private final static String TAG = "RecentsImpl";
+
     // The minimum amount of time between each recents button press that we will handle
     private final static int MIN_TOGGLE_DELAY_MS = 350;
+
     // The duration within which the user releasing the alt tab (from when they pressed alt tab)
     // that the fast alt-tab animation will run.  If the user's alt-tab takes longer than this
     // duration, then we will toggle recents after this duration.
@@ -337,21 +341,31 @@
         mTriggeredFromAltTab = false;
 
         try {
+            ViewConfiguration viewConfig = ViewConfiguration.get(mContext);
             SystemServicesProxy ssp = Recents.getSystemServices();
             ActivityManager.RunningTaskInfo topTask = ssp.getTopMostTask();
             MutableBoolean isTopTaskHome = new MutableBoolean(true);
+            long elapsedTime = SystemClock.elapsedRealtime() - mLastToggleTime;
+
             if (topTask != null && ssp.isRecentsTopMost(topTask, isTopTaskHome)) {
                 RecentsConfiguration config = Recents.getConfiguration();
                 RecentsActivityLaunchState launchState = config.getLaunchState();
                 if (!launchState.launchedWithAltTab) {
-                    // Notify recents to move onto the next task
-                    EventBus.getDefault().post(new IterateRecentsEvent());
+                    // If the user taps quickly
+                    if (ViewConfiguration.getDoubleTapMinTime() < elapsedTime &&
+                            elapsedTime < ViewConfiguration.getDoubleTapTimeout()) {
+                        // Launch the next focused task
+                        EventBus.getDefault().post(new LaunchNextTaskRequestEvent());
+                    } else {
+                        // Notify recents to move onto the next task
+                        EventBus.getDefault().post(new IterateRecentsEvent());
+                    }
                 } else {
                     // If the user has toggled it too quickly, then just eat up the event here (it's
                     // better than showing a janky screenshot).
                     // NOTE: Ideally, the screenshot mechanism would take the window transform into
                     // account
-                    if ((SystemClock.elapsedRealtime() - mLastToggleTime) < MIN_TOGGLE_DELAY_MS) {
+                    if (elapsedTime < MIN_TOGGLE_DELAY_MS) {
                         return;
                     }
 
@@ -364,7 +378,7 @@
                 // better than showing a janky screenshot).
                 // NOTE: Ideally, the screenshot mechanism would take the window transform into
                 // account
-                if ((SystemClock.elapsedRealtime() - mLastToggleTime) < MIN_TOGGLE_DELAY_MS) {
+                if (elapsedTime < MIN_TOGGLE_DELAY_MS) {
                     return;
                 }
 
@@ -551,11 +565,14 @@
     public void dockTopTask(int topTaskId, int dragMode,
             int stackCreateMode, Rect initialBounds) {
         SystemServicesProxy ssp = Recents.getSystemServices();
+
+        // Make sure we inform DividerView before we actually start the activity so we can change
+        // the resize mode already.
+        EventBus.getDefault().send(new DockingTopTaskEvent(dragMode));
         ssp.moveTaskToDockedStack(topTaskId, stackCreateMode, initialBounds);
         showRecents(false /* triggeredFromAltTab */,
                 dragMode == NavigationBarGestureHelper.DRAG_MODE_RECENTS, false /* animate */,
                 true /* reloadTasks*/);
-        EventBus.getDefault().send(new DockingTopTaskEvent(dragMode));
     }
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/activity/LaunchNextTaskRequestEvent.java b/packages/SystemUI/src/com/android/systemui/recents/events/activity/LaunchNextTaskRequestEvent.java
new file mode 100644
index 0000000..11604b5
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/recents/events/activity/LaunchNextTaskRequestEvent.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.recents.events.activity;
+
+import com.android.systemui.recents.events.EventBus;
+
+/**
+ * This event is sent to request that the next task is launched after a double-tap on the Recents
+ * button.
+ */
+public class LaunchNextTaskRequestEvent extends EventBus.Event {
+    // Simple event
+}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/activity/UndockingTaskEvent.java b/packages/SystemUI/src/com/android/systemui/recents/events/activity/UndockingTaskEvent.java
new file mode 100644
index 0000000..d5083a8
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/recents/events/activity/UndockingTaskEvent.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.recents.events.activity;
+
+import com.android.systemui.recents.events.EventBus;
+
+/**
+ * Fires when the user invoked the gesture to undock the task in the docked stack.
+ */
+public class UndockingTaskEvent extends EventBus.Event {
+
+    public UndockingTaskEvent() {
+    }
+}
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 ee3eb02..5eeda72 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/history/RecentsHistoryAdapter.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/history/RecentsHistoryAdapter.java
@@ -67,9 +67,30 @@
     public static class ViewHolder extends RecyclerView.ViewHolder implements Task.TaskCallbacks {
         public final View content;
 
-        public ViewHolder(View v) {
-            super(v);
-            content = v;
+        private Task mTask;
+
+        public ViewHolder(View content) {
+            super(content);
+            this.content = content;
+        }
+
+        /**
+         * Binds this view holder to the given task.
+         */
+        public void bindToTask(Task newTask) {
+            unbindFromTask();
+            mTask = newTask;
+            mTask.addCallback(this);
+        }
+
+        /**
+         * Unbinds this view holder from the
+         */
+        public void unbindFromTask() {
+            if (mTask != null) {
+                mTask.removeCallback(this);
+                mTask = null;
+            }
         }
 
         @Override
@@ -267,12 +288,13 @@
             }
             case TASK_ROW_VIEW_TYPE: {
                 TaskRow taskRow = (TaskRow) row;
-                taskRow.task.addCallback(holder);
                 TextView tv = (TextView) holder.content.findViewById(R.id.description);
                 tv.setText(taskRow.task.title);
                 ImageView iv = (ImageView) holder.content.findViewById(R.id.icon);
                 iv.setAlpha(0f);
                 holder.content.setOnClickListener(taskRow);
+
+                holder.bindToTask(taskRow.task);
                 loader.loadTaskData(taskRow.task, false /* fetchAndInvalidateThumbnails */);
                 break;
             }
@@ -289,7 +311,7 @@
             if (viewType == TASK_ROW_VIEW_TYPE) {
                 TaskRow taskRow = (TaskRow) row;
                 loader.unloadTaskData(taskRow.task);
-                taskRow.task.removeCallback(holder);
+                holder.unbindFromTask();
             }
         }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/SystemBarScrimViews.java b/packages/SystemUI/src/com/android/systemui/recents/views/SystemBarScrimViews.java
index e8fa398..1cd0850 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/SystemBarScrimViews.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/SystemBarScrimViews.java
@@ -83,13 +83,11 @@
      * going home).
      */
     public final void onBusEvent(DismissRecentsToHomeAnimationStarted event) {
-        int taskViewExitToAppDuration = mContext.getResources().getInteger(
-                R.integer.recents_task_exit_to_app_duration);
         if (mHasNavBarScrim && mShouldAnimateNavBarScrim) {
             mNavBarScrimView.animate()
                     .translationY(mNavBarScrimView.getMeasuredHeight())
                     .setStartDelay(0)
-                    .setDuration(taskViewExitToAppDuration)
+                    .setDuration(TaskStackAnimationHelper.EXIT_TO_HOME_TRANSLATION_DURATION)
                     .setInterpolator(Interpolators.FAST_OUT_SLOW_IN)
                     .start();
         }
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackAnimationHelper.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackAnimationHelper.java
index 0eae183..7eaa193 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackAnimationHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackAnimationHelper.java
@@ -108,7 +108,7 @@
             return;
         }
 
-        int offscreenY = stackLayout.mStackRect.bottom;
+        int offscreenYOffset = stackLayout.mStackRect.height();
         int taskViewAffiliateGroupEnterOffset = res.getDimensionPixelSize(
                 R.dimen.recents_task_view_affiliate_group_enter_offset);
 
@@ -145,7 +145,7 @@
             } else if (launchState.launchedFromHome) {
                 // Move the task view off screen (below) so we can animate it in
                 RectF bounds = new RectF(mTmpTransform.rect);
-                bounds.offsetTo(bounds.left, offscreenY);
+                bounds.offset(0, offscreenYOffset);
                 tv.setLeftTopRightBottom((int) bounds.left, (int) bounds.top, (int) bounds.right,
                         (int) bounds.bottom);
             }
@@ -247,7 +247,7 @@
             return;
         }
 
-        int offscreenY = stackLayout.mStackRect.bottom;
+        int offscreenYOffset = stackLayout.mStackRect.height();
 
         // Create the animations for each of the tasks
         List<TaskView> taskViews = mStackView.getTaskViews();
@@ -277,7 +277,7 @@
 
             stackLayout.getStackTransform(task, stackScroller.getStackScroll(), mTmpTransform,
                     null);
-            mTmpTransform.rect.offsetTo(mTmpTransform.rect.left, offscreenY);
+            mTmpTransform.rect.offset(0, offscreenYOffset);
             mStackView.updateTaskViewToTransform(tv, mTmpTransform, taskAnimation);
         }
     }
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 46fdb2a..bd37c3b 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackLayoutAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackLayoutAlgorithm.java
@@ -383,6 +383,7 @@
      */
     void update(TaskStack stack, ArraySet<Task.TaskKey> ignoreTasksSet) {
         SystemServicesProxy ssp = Recents.getSystemServices();
+        RecentsActivityLaunchState launchState = Recents.getConfiguration().getLaunchState();
 
         // Clear the progress map
         mTaskIndexMap.clear();
@@ -449,7 +450,6 @@
             if (!ssp.hasFreeformWorkspaceSupport() && mNumStackTasks == 1) {
                 mInitialScrollP = mMinScrollP;
             } else if (getDefaultFocusState() > 0f) {
-                RecentsActivityLaunchState launchState = Recents.getConfiguration().getLaunchState();
                 if (launchState.launchedFromHome) {
                     mInitialScrollP = Math.max(mMinScrollP, Math.min(mMaxScrollP, launchTaskIndex));
                 } else {
@@ -568,7 +568,7 @@
             boolean isFrontMostTaskInGroup = task.group == null || task.group.isFrontMostTask(task);
             if (isFrontMostTaskInGroup) {
                 getStackTransform(taskProgress, mInitialScrollP, tmpTransform, null,
-                        false /* ignoreSingleTaskCase */);
+                        false /* ignoreSingleTaskCase */, false /* forceUpdate */);
                 float screenY = tmpTransform.rect.top;
                 boolean hasVisibleThumbnail = (prevScreenY - screenY) > taskBarHeight;
                 if (hasVisibleThumbnail) {
@@ -601,6 +601,12 @@
      */
     public TaskViewTransform getStackTransform(Task task, float stackScroll,
             TaskViewTransform transformOut, TaskViewTransform frontTransform) {
+        return getStackTransform(task, stackScroll, transformOut, frontTransform,
+                false /* forceUpdate */);
+    }
+
+    public TaskViewTransform getStackTransform(Task task, float stackScroll,
+        TaskViewTransform transformOut, TaskViewTransform frontTransform, boolean forceUpdate) {
         if (mFreeformLayoutAlgorithm.isTransformAvailable(task, this)) {
             mFreeformLayoutAlgorithm.getTransform(task, transformOut, this);
             return transformOut;
@@ -610,8 +616,9 @@
                 transformOut.reset();
                 return transformOut;
             }
-            return getStackTransform(mTaskIndexMap.get(task.key), stackScroll, transformOut,
-                    frontTransform, false /* ignoreSingleTaskCase */);
+            getStackTransform(mTaskIndexMap.get(task.key), stackScroll, transformOut,
+                    frontTransform, false /* ignoreSingleTaskCase */, forceUpdate);
+            return transformOut;
         }
     }
 
@@ -635,9 +642,9 @@
      *                             internally to ensure that we can calculate the transform for any
      *                             position in the stack.
      */
-    public TaskViewTransform getStackTransform(float taskProgress, float stackScroll,
+    public void getStackTransform(float taskProgress, float stackScroll,
             TaskViewTransform transformOut, TaskViewTransform frontTransform,
-            boolean ignoreSingleTaskCase) {
+            boolean ignoreSingleTaskCase, boolean forceUpdate) {
         SystemServicesProxy ssp = Recents.getSystemServices();
 
         // Compute the focused and unfocused offset
@@ -658,9 +665,9 @@
         }
 
         // Skip if the task is not visible
-        if (!unfocusedVisible && !focusedVisible) {
+        if (!forceUpdate && !unfocusedVisible && !focusedVisible) {
             transformOut.reset();
-            return transformOut;
+            return;
         }
 
         int x = (mStackRect.width() - mTaskRect.width()) / 2;
@@ -700,7 +707,6 @@
         transformOut.visible = (transformOut.rect.top < mStackRect.bottom) &&
                 (frontTransform == null || transformOut.rect.top != frontTransform.rect.top);
         transformOut.p = relP;
-        return transformOut;
     }
 
     /**
@@ -797,8 +803,10 @@
                 mFocusState * (mFocusedRange.relativeMin - mUnfocusedRange.relativeMin);
         float max = mUnfocusedRange.relativeMax +
                 mFocusState * (mFocusedRange.relativeMax - mUnfocusedRange.relativeMax);
-        getStackTransform(min, 0f, mBackOfStackTransform, null, true /* ignoreSingleTaskCase */);
-        getStackTransform(max, 0f, mFrontOfStackTransform, null, true /* ignoreSingleTaskCase */);
+        getStackTransform(min, 0f, mBackOfStackTransform, null, true /* ignoreSingleTaskCase */,
+                true /* forceUpdate */);
+        getStackTransform(max, 0f, mFrontOfStackTransform, null, true /* ignoreSingleTaskCase */,
+                true /* forceUpdate */);
         mBackOfStackTransform.visible = true;
         mFrontOfStackTransform.visible = true;
     }
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 1c97b5a..bb74de4 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
@@ -58,6 +58,7 @@
 import com.android.systemui.recents.events.activity.HideHistoryButtonEvent;
 import com.android.systemui.recents.events.activity.HideHistoryEvent;
 import com.android.systemui.recents.events.activity.IterateRecentsEvent;
+import com.android.systemui.recents.events.activity.LaunchNextTaskRequestEvent;
 import com.android.systemui.recents.events.activity.LaunchTaskEvent;
 import com.android.systemui.recents.events.activity.LaunchTaskStartedEvent;
 import com.android.systemui.recents.events.activity.PackagesChangedEvent;
@@ -654,7 +655,7 @@
                 transform.fillIn(tv);
             } else {
                 mLayoutAlgorithm.getStackTransform(task, mStackScroller.getStackScroll(),
-                        transform, null);
+                        transform, null, true /* forceUpdate */);
             }
             transform.visible = true;
         }
@@ -1544,6 +1545,24 @@
         mUIDozeTrigger.stopDozing();
     }
 
+    public final void onBusEvent(LaunchNextTaskRequestEvent event) {
+        int launchTaskIndex = mStack.indexOfStackTask(mStack.getLaunchTarget());
+        if (launchTaskIndex != -1) {
+            launchTaskIndex = Math.max(0, launchTaskIndex - 1);
+        } else {
+            launchTaskIndex = mStack.getTaskCount() - 1;
+        }
+        if (launchTaskIndex != -1) {
+            // Stop all animations
+            mUIDozeTrigger.stopDozing();
+            cancelAllTaskViewAnimations();
+
+            Task launchTask = mStack.getStackTasks().get(launchTaskIndex);
+            EventBus.getDefault().send(new LaunchTaskEvent(getChildViewForTask(launchTask),
+                    launchTask, null, INVALID_STACK_ID, false /* screenPinningRequested */));
+        }
+    }
+
     public final void onBusEvent(LaunchTaskStartedEvent event) {
         mAnimationHelper.startLaunchTaskAnimation(event.taskView, event.screenPinningRequested,
                 event.getAnimationTrigger());
@@ -1762,21 +1781,6 @@
         }
     }
 
-    public final void onBusEvent(EnterRecentsTaskStackAnimationCompletedEvent event) {
-        RecentsDebugFlags debugFlags = Recents.getDebugFlags();
-        RecentsActivityLaunchState launchState = Recents.getConfiguration().getLaunchState();
-        if (!launchState.launchedWithAltTab && debugFlags.isFastToggleRecentsEnabled() &&
-                RecentsDebugFlags.Static.EnableFastToggleTimeoutOnEnter) {
-            if (mFocusedTask != null) {
-                int timerIndicatorDuration = getResources().getInteger(
-                        R.integer.recents_auto_advance_duration);
-                int focusedTaskIndex = mStack.indexOfStackTask(mFocusedTask);
-                setFocusedTask(focusedTaskIndex, false /* scrollToTask */,
-                        false /* requestViewFocus */, timerIndicatorDuration);
-            }
-        }
-    }
-
     public final void onBusEvent(UpdateFreeformTaskViewVisibilityEvent event) {
         List<TaskView> taskViews = getTaskViews();
         int taskViewCount = taskViews.size();
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java
index b8b5068..d6680fd 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java
@@ -29,6 +29,7 @@
 import android.view.View;
 import android.view.ViewConfiguration;
 import android.view.ViewParent;
+import android.view.animation.Animation;
 import android.view.animation.Interpolator;
 import android.view.animation.PathInterpolator;
 
@@ -497,6 +498,7 @@
         // onBeginDrag().
         mSv.removeIgnoreTask(tv.getTask());
         mSv.updateLayoutAlgorithm(false /* boundScroll */);
+        mSv.relayoutTaskViews(AnimationProps.IMMEDIATE);
         mSwipeHelperAnimations.remove(v);
     }
 
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 703005f..439d96f 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
@@ -242,7 +242,7 @@
     }
 
     void updateViewPropertiesToTaskTransform(TaskViewTransform toTransform,
-                                             AnimationProps toAnimation, ValueAnimator.AnimatorUpdateListener updateCallback) {
+            AnimationProps toAnimation, ValueAnimator.AnimatorUpdateListener updateCallback) {
         RecentsConfiguration config = Recents.getConfiguration();
         cancelTransformAnimation();
 
@@ -261,14 +261,16 @@
                 updateCallback.onAnimationUpdate(null);
             }
         } else {
+            // Both the progress and the update are a function of the bounds movement of the task
             if (Float.compare(getTaskProgress(), toTransform.p) != 0) {
-                mTmpAnimators.add(ObjectAnimator.ofFloat(this, TASK_PROGRESS, getTaskProgress(),
-                        toTransform.p));
+                ObjectAnimator anim = ObjectAnimator.ofFloat(this, TASK_PROGRESS, getTaskProgress(),
+                        toTransform.p);
+                mTmpAnimators.add(toAnimation.apply(AnimationProps.BOUNDS, anim));
             }
             if (updateCallback != null) {
                 ValueAnimator updateCallbackAnim = ValueAnimator.ofInt(0, 1);
                 updateCallbackAnim.addUpdateListener(updateCallback);
-                mTmpAnimators.add(updateCallbackAnim);
+                mTmpAnimators.add(toAnimation.apply(AnimationProps.BOUNDS, updateCallbackAnim));
             }
 
             // Create the animator
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerHandleView.java b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerHandleView.java
index 5ef56f3..12e2713 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerHandleView.java
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerHandleView.java
@@ -26,10 +26,9 @@
 import android.graphics.Paint;
 import android.util.AttributeSet;
 import android.util.Property;
-import android.view.animation.AnimationUtils;
-import android.view.animation.Interpolator;
 import android.widget.ImageButton;
 
+import com.android.systemui.Interpolators;
 import com.android.systemui.R;
 
 /**
@@ -71,7 +70,6 @@
     private final int mWidth;
     private final int mHeight;
     private final int mCircleDiameter;
-    private final Interpolator mFastOutSlowInInterpolator;
     private int mCurrentWidth;
     private int mCurrentHeight;
     private AnimatorSet mAnimator;
@@ -85,8 +83,6 @@
         mCurrentWidth = mWidth;
         mCurrentHeight = mHeight;
         mCircleDiameter = (mWidth + mHeight) / 3;
-        mFastOutSlowInInterpolator = AnimationUtils.loadInterpolator(getContext(),
-                android.R.interpolator.fast_out_slow_in);
     }
 
     public void setTouching(boolean touching, boolean animate) {
@@ -120,8 +116,8 @@
                 ? DividerView.TOUCH_ANIMATION_DURATION
                 : DividerView.TOUCH_RELEASE_ANIMATION_DURATION);
         mAnimator.setInterpolator(touching
-                ? DividerView.TOUCH_RESPONSE_INTERPOLATOR
-                : mFastOutSlowInInterpolator);
+                ? Interpolators.TOUCH_RESPONSE
+                : Interpolators.FAST_OUT_SLOW_IN);
         mAnimator.addListener(new AnimatorListenerAdapter() {
             @Override
             public void onAnimationEnd(Animator animation) {
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java
index 1e11fa8..83c22b1 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java
@@ -48,10 +48,12 @@
 import com.android.internal.policy.DividerSnapAlgorithm;
 import com.android.internal.policy.DividerSnapAlgorithm.SnapTarget;
 import com.android.internal.policy.DockedDividerUtils;
+import com.android.systemui.Interpolators;
 import com.android.systemui.R;
 import com.android.systemui.recents.events.EventBus;
 import com.android.systemui.recents.events.activity.DockingTopTaskEvent;
 import com.android.systemui.recents.events.activity.RecentsActivityStartingEvent;
+import com.android.systemui.recents.events.activity.UndockingTaskEvent;
 import com.android.systemui.recents.events.ui.RecentsDrawnEvent;
 import com.android.systemui.statusbar.FlingAnimationUtils;
 import com.android.systemui.statusbar.phone.NavigationBarGestureHelper;
@@ -67,8 +69,6 @@
 
     static final long TOUCH_ANIMATION_DURATION = 150;
     static final long TOUCH_RELEASE_ANIMATION_DURATION = 200;
-    static final Interpolator TOUCH_RESPONSE_INTERPOLATOR =
-            new PathInterpolator(0.3f, 0f, 0.1f, 1f);
 
     private static final String TAG = "DividerView";
 
@@ -116,7 +116,6 @@
     private final Rect mOtherInsetRect = new Rect();
     private final Rect mLastResizeRect = new Rect();
     private final WindowManagerProxy mWindowManagerProxy = WindowManagerProxy.getInstance();
-    private Interpolator mFastOutSlowInInterpolator;
     private DividerWindowManager mWindowManager;
     private VelocityTracker mVelocityTracker;
     private FlingAnimationUtils mFlingAnimationUtils;
@@ -158,8 +157,6 @@
         mTouchElevation = getResources().getDimensionPixelSize(
                 R.dimen.docked_stack_divider_lift_elevation);
         mGrowRecents = getResources().getBoolean(R.bool.recents_grow_in_multiwindow);
-        mFastOutSlowInInterpolator = AnimationUtils.loadInterpolator(getContext(),
-                android.R.interpolator.fast_out_slow_in);
         mTouchSlop = ViewConfiguration.get(mContext).getScaledTouchSlop();
         mFlingAnimationUtils = new FlingAnimationUtils(getContext(), 0.3f);
         updateDisplayInfo();
@@ -192,7 +189,7 @@
                     insets.getStableInsetRight(), insets.getStableInsetBottom());
             if (mSnapAlgorithm != null) {
                 mSnapAlgorithm = null;
-                getSnapAlgorithm();
+                initializeSnapAlgorithm();
             }
         }
         return super.onApplyWindowInsets(insets);
@@ -211,17 +208,13 @@
             mHandle.setTouching(true, animate);
         }
         mDockSide = mWindowManagerProxy.getDockSide();
-        getSnapAlgorithm();
-        if (mDockSide != WindowManager.DOCKED_INVALID) {
-            mWindowManagerProxy.setResizing(true);
-            mWindowManager.setSlippery(false);
-            if (touching) {
-                liftBackground();
-            }
-            return true;
-        } else {
-            return false;
+        initializeSnapAlgorithm();
+        mWindowManagerProxy.setResizing(true);
+        mWindowManager.setSlippery(false);
+        if (touching) {
+            liftBackground();
         }
+        return mDockSide != WindowManager.DOCKED_INVALID;
     }
 
     public void stopDragging(int position, float velocity, boolean avoidDismissStart) {
@@ -233,17 +226,36 @@
 
     public void stopDragging(int position, SnapTarget target, long duration,
             Interpolator interpolator) {
+        stopDragging(position, target, duration, 0 /* startDelay*/, interpolator);
+    }
+
+    public void stopDragging(int position, SnapTarget target, long duration, long startDelay,
+            Interpolator interpolator) {
         mHandle.setTouching(false, true /* animate */);
-        flingTo(position, target, duration, interpolator);
+        flingTo(position, target, duration, startDelay, interpolator);
         mWindowManager.setSlippery(true);
         releaseBackground();
     }
 
-    public DividerSnapAlgorithm getSnapAlgorithm() {
+    private void stopDragging() {
+        mHandle.setTouching(false, true /* animate */);
+        mWindowManager.setSlippery(true);
+        releaseBackground();
+    }
+
+    private void updateDockSide() {
+        mDockSide = mWindowManagerProxy.getDockSide();
+    }
+
+    private void initializeSnapAlgorithm() {
         if (mSnapAlgorithm == null) {
             mSnapAlgorithm = new DividerSnapAlgorithm(getContext().getResources(), mDisplayWidth,
                     mDisplayHeight, mDividerSize, isHorizontalDivision(), mStableInsets);
         }
+    }
+
+    public DividerSnapAlgorithm getSnapAlgorithm() {
+        initializeSnapAlgorithm();
         return mSnapAlgorithm;
     }
 
@@ -267,6 +279,11 @@
                 mStartX = (int) event.getX();
                 mStartY = (int) event.getY();
                 boolean result = startDragging(true /* animate */, true /* touching */);
+                if (!result) {
+
+                    // Weren't able to start dragging successfully, so cancel it again.
+                    stopDragging();
+                }
                 mStartPosition = getCurrentPosition();
                 mMoving = false;
                 return result;
@@ -320,10 +337,11 @@
         anim.start();
     }
 
-    private void flingTo(int position, SnapTarget target, long duration,
+    private void flingTo(int position, SnapTarget target, long duration, long startDelay,
             Interpolator interpolator) {
         ValueAnimator anim = getFlingAnimator(position, target);
         anim.setDuration(duration);
+        anim.setStartDelay(startDelay);
         anim.setInterpolator(interpolator);
         anim.start();
     }
@@ -377,7 +395,7 @@
             mBackground.animate().scaleX(1.4f);
         }
         mBackground.animate()
-                .setInterpolator(TOUCH_RESPONSE_INTERPOLATOR)
+                .setInterpolator(Interpolators.TOUCH_RESPONSE)
                 .setDuration(TOUCH_ANIMATION_DURATION)
                 .translationZ(mTouchElevation)
                 .start();
@@ -385,7 +403,7 @@
         // Lift handle as well so it doesn't get behind the background, even though it doesn't
         // cast shadow.
         mHandle.animate()
-                .setInterpolator(TOUCH_RESPONSE_INTERPOLATOR)
+                .setInterpolator(Interpolators.TOUCH_RESPONSE)
                 .setDuration(TOUCH_ANIMATION_DURATION)
                 .translationZ(mTouchElevation)
                 .start();
@@ -393,14 +411,14 @@
 
     private void releaseBackground() {
         mBackground.animate()
-                .setInterpolator(mFastOutSlowInInterpolator)
+                .setInterpolator(Interpolators.FAST_OUT_SLOW_IN)
                 .setDuration(TOUCH_RELEASE_ANIMATION_DURATION)
                 .translationZ(0)
                 .scaleX(1f)
                 .scaleY(1f)
                 .start();
         mHandle.animate()
-                .setInterpolator(mFastOutSlowInInterpolator)
+                .setInterpolator(Interpolators.FAST_OUT_SLOW_IN)
                 .setDuration(TOUCH_RELEASE_ANIMATION_DURATION)
                 .translationZ(0)
                 .start();
@@ -421,6 +439,7 @@
         mDisplayWidth = info.logicalWidth;
         mDisplayHeight = info.logicalHeight;
         mSnapAlgorithm = null;
+        initializeSnapAlgorithm();
     }
 
     private int calculatePosition(int touchX, int touchY) {
@@ -725,13 +744,29 @@
     public final void onBusEvent(RecentsDrawnEvent drawnEvent) {
         if (mAnimateAfterRecentsDrawn) {
             mAnimateAfterRecentsDrawn = false;
+            updateDockSide();
             stopDragging(getCurrentPosition(), mSnapAlgorithm.getMiddleTarget(), 250,
-                    TOUCH_RESPONSE_INTERPOLATOR);
+                    Interpolators.TOUCH_RESPONSE);
         }
         if (mGrowAfterRecentsDrawn) {
             mGrowAfterRecentsDrawn = false;
+            updateDockSide();
             stopDragging(getCurrentPosition(), mSnapAlgorithm.getMiddleTarget(), 250,
-                    TOUCH_RESPONSE_INTERPOLATOR);
+                    Interpolators.TOUCH_RESPONSE);
+        }
+    }
+
+    public final void onBusEvent(UndockingTaskEvent undockingTaskEvent) {
+        int dockSide = mWindowManagerProxy.getDockSide();
+        if (dockSide != WindowManager.DOCKED_INVALID) {
+            startDragging(false /* animate */, false /* touching */);
+            SnapTarget target = dockSideTopLeft(dockSide)
+                    ? mSnapAlgorithm.getDismissEndTarget()
+                    : mSnapAlgorithm.getDismissStartTarget();
+
+            // Don't start immediately - give a little bit time to settle the drag resize change.
+            stopDragging(getCurrentPosition(), target, 336 /* duration */, 100 /* startDelay */,
+                    Interpolators.TOUCH_RESPONSE);
         }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/WindowManagerProxy.java b/packages/SystemUI/src/com/android/systemui/stackdivider/WindowManagerProxy.java
index 24ab506..15bcaf8 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/WindowManagerProxy.java
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/WindowManagerProxy.java
@@ -97,7 +97,7 @@
         @Override
         public void run() {
             try {
-                ActivityManagerNative.getDefault().resizeStack(DOCKED_STACK_ID, null, true, false,
+                ActivityManagerNative.getDefault().resizeStack(DOCKED_STACK_ID, null, true, true,
                         false);
             } catch (RemoteException e) {
                 Log.w(TAG, "Failed to resize stack: " + e);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/DismissView.java b/packages/SystemUI/src/com/android/systemui/statusbar/DismissView.java
index d9276bf..3067714 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/DismissView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/DismissView.java
@@ -17,14 +17,12 @@
 package com.android.systemui.statusbar;
 
 import android.content.Context;
-import android.graphics.Rect;
 import android.util.AttributeSet;
 import android.view.View;
 
 import com.android.systemui.R;
 
 public class DismissView extends StackScrollerDecorView {
-    private boolean mDismissAllInProgress;
     private DismissViewButton mDismissButton;
 
     public DismissView(Context context, AttributeSet attrs) {
@@ -53,27 +51,7 @@
                 || touchY > mContent.getY() + mContent.getHeight();
     }
 
-    public void showClearButton() {
-        mDismissButton.showButton();
-    }
-
-    public void setDismissAllInProgress(boolean dismissAllInProgress) {
-        if (dismissAllInProgress) {
-            setClipBounds(null);
-        }
-        mDismissAllInProgress = dismissAllInProgress;
-    }
-
-    @Override
-    public void setClipBounds(Rect clipBounds) {
-        if (mDismissAllInProgress) {
-            // we don't want any clipping to happen!
-            return;
-        }
-        super.setClipBounds(clipBounds);
-    }
-
     public boolean isButtonVisible() {
-        return mDismissButton.isButtonStatic();
+        return mDismissButton.getAlpha() != 0.0f;
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/DismissViewButton.java b/packages/SystemUI/src/com/android/systemui/statusbar/DismissViewButton.java
index 46060f1..b608d67 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/DismissViewButton.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/DismissViewButton.java
@@ -17,22 +17,13 @@
 package com.android.systemui.statusbar;
 
 import android.content.Context;
-import android.graphics.Canvas;
 import android.graphics.Rect;
-import android.graphics.drawable.AnimatedVectorDrawable;
-import android.graphics.drawable.Drawable;
 import android.util.AttributeSet;
-import android.view.View;
 import android.view.ViewGroup;
-import android.widget.Button;
 
-import com.android.systemui.R;
 import com.android.systemui.statusbar.stack.NotificationStackScrollLayout;
 
-public class DismissViewButton extends Button {
-    private AnimatedVectorDrawable mAnimatedDismissDrawable;
-    private final Drawable mStaticDismissDrawable;
-    private Drawable mActiveDrawable;
+public class DismissViewButton extends AlphaOptimizedButton {
 
     public DismissViewButton(Context context) {
         this(context, null);
@@ -49,55 +40,6 @@
     public DismissViewButton(Context context, AttributeSet attrs, int defStyleAttr,
             int defStyleRes) {
         super(context, attrs, defStyleAttr, defStyleRes);
-        mAnimatedDismissDrawable = (AnimatedVectorDrawable) getContext().getDrawable(
-                R.drawable.dismiss_all_shape_animation).mutate();
-        mAnimatedDismissDrawable.setCallback(this);
-        mAnimatedDismissDrawable.setBounds(0,
-                0,
-                mAnimatedDismissDrawable.getIntrinsicWidth(),
-                mAnimatedDismissDrawable.getIntrinsicHeight());
-        mStaticDismissDrawable = getContext().getDrawable(R.drawable.dismiss_all_shape);
-        mStaticDismissDrawable.setBounds(0,
-                0,
-                mStaticDismissDrawable.getIntrinsicWidth(),
-                mStaticDismissDrawable.getIntrinsicHeight());
-        mStaticDismissDrawable.setCallback(this);
-        mActiveDrawable = mStaticDismissDrawable;
-    }
-
-    @Override
-    protected void onDraw(Canvas canvas) {
-        super.onDraw(canvas);
-        canvas.save();
-        int drawableHeight = mActiveDrawable.getBounds().height();
-        boolean isRtl = (getLayoutDirection() == View.LAYOUT_DIRECTION_RTL);
-        int dx = isRtl ? getWidth() / 2 + drawableHeight / 2 : getWidth() / 2 - drawableHeight / 2;
-        canvas.translate(dx, getHeight() / 2.0f + drawableHeight /
-                2.0f);
-        canvas.scale(isRtl ? -1.0f : 1.0f, -1.0f);
-        mActiveDrawable.draw(canvas);
-        canvas.restore();
-    }
-
-    @Override
-    public boolean performClick() {
-        if (!mAnimatedDismissDrawable.isRunning()) {
-            mActiveDrawable = mAnimatedDismissDrawable;
-            mAnimatedDismissDrawable.start();
-        }
-        return super.performClick();
-    }
-
-    @Override
-    protected boolean verifyDrawable(Drawable who) {
-        return super.verifyDrawable(who)
-                || who == mAnimatedDismissDrawable
-                || who == mStaticDismissDrawable;
-    }
-
-    @Override
-    public boolean hasOverlappingRendering() {
-        return false;
     }
 
     /**
@@ -118,16 +60,4 @@
         outRect.top += translationY;
         outRect.bottom += translationY;
     }
-
-    public void showButton() {
-        mActiveDrawable = mStaticDismissDrawable;
-        invalidate();
-    }
-
-    /**
-     * @return Whether the button is currently static and not being animated.
-     */
-    public boolean isButtonStatic() {
-        return mActiveDrawable == mStaticDismissDrawable;
-    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcuts.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcuts.java
index 963920c..0b7bfa8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcuts.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcuts.java
@@ -23,17 +23,15 @@
 import android.content.DialogInterface.OnClickListener;
 import android.os.Handler;
 import android.os.Looper;
-import android.util.DisplayMetrics;
+import android.view.ContextThemeWrapper;
 import android.view.KeyEvent;
 import android.view.KeyboardShortcutGroup;
 import android.view.KeyboardShortcutInfo;
 import android.view.LayoutInflater;
 import android.view.View;
-import android.view.ViewGroup.LayoutParams;
 import android.view.Window;
 import android.view.WindowManager.KeyboardShortcutsReceiver;
 import android.widget.LinearLayout;
-import android.widget.ScrollView;
 import android.widget.TextView;
 
 import com.android.systemui.R;
@@ -65,7 +63,7 @@
     private Dialog mKeyboardShortcutsDialog;
 
     public KeyboardShortcuts(Context context) {
-        this.mContext = context;
+        this.mContext = new ContextThemeWrapper(context, android.R.style.Theme_Material_Light);
     }
 
     public void toggleKeyboardShortcuts() {
@@ -108,40 +106,28 @@
         mHandler.post(new Runnable() {
             @Override
             public void run() {
-                // TODO: break all this code out into a handleShowKeyboard...
-                // Might add more things posted; should consider adding a custom handler so
-                // you can send the keyboardShortcutsGroups as part of the message.
-                AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(mContext);
-                LayoutInflater inflater = (LayoutInflater) mContext.getSystemService(
-                        LAYOUT_INFLATER_SERVICE);
-                final View keyboardShortcutsView = inflater.inflate(
-                        R.layout.keyboard_shortcuts_view, null);
-                DisplayMetrics dm = mContext.getResources().getDisplayMetrics();
-                ScrollView scrollView = (ScrollView) keyboardShortcutsView.findViewById(
-                        R.id.keyboard_shortcuts_scroll_view);
-                // TODO: find a better way to set the height.
-                scrollView.setLayoutParams(new LinearLayout.LayoutParams(
-                        LayoutParams.WRAP_CONTENT,
-                        (int) (dm.heightPixels * dm.density)));
-
-                populateKeyboardShortcuts((LinearLayout) keyboardShortcutsView.findViewById(
-                        R.id.keyboard_shortcuts_container), keyboardShortcutGroups);
-                dialogBuilder.setView(keyboardShortcutsView);
-                dialogBuilder.setPositiveButton(R.string.quick_settings_done, dialogCloseListener);
-                mKeyboardShortcutsDialog = dialogBuilder.create();
-                mKeyboardShortcutsDialog.setCanceledOnTouchOutside(true);
-
-                // Setup window.
-                Window keyboardShortcutsWindow = mKeyboardShortcutsDialog.getWindow();
-                keyboardShortcutsWindow.setType(TYPE_SYSTEM_DIALOG);
-                keyboardShortcutsWindow.setBackgroundDrawable(
-                        mContext.getDrawable(R.color.ksh_dialog_background_color));
-                keyboardShortcutsWindow.setGravity(TOP);
-                mKeyboardShortcutsDialog.show();
+                handleShowKeyboardShortcuts(keyboardShortcutGroups);
             }
         });
     }
 
+    private void handleShowKeyboardShortcuts(List<KeyboardShortcutGroup> keyboardShortcutGroups) {
+        AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(mContext);
+        LayoutInflater inflater = (LayoutInflater) mContext.getSystemService(
+                LAYOUT_INFLATER_SERVICE);
+        final View keyboardShortcutsView = inflater.inflate(
+                R.layout.keyboard_shortcuts_view, null);
+        populateKeyboardShortcuts((LinearLayout) keyboardShortcutsView.findViewById(
+                R.id.keyboard_shortcuts_container), keyboardShortcutGroups);
+        dialogBuilder.setView(keyboardShortcutsView);
+        dialogBuilder.setPositiveButton(R.string.quick_settings_done, dialogCloseListener);
+        mKeyboardShortcutsDialog = dialogBuilder.create();
+        mKeyboardShortcutsDialog.setCanceledOnTouchOutside(true);
+        Window keyboardShortcutsWindow = mKeyboardShortcutsDialog.getWindow();
+        keyboardShortcutsWindow.setType(TYPE_SYSTEM_DIALOG);
+        mKeyboardShortcutsDialog.show();
+    }
+
     private void populateKeyboardShortcuts(LinearLayout keyboardShortcutsLayout,
             List<KeyboardShortcutGroup> keyboardShortcutGroups) {
         LayoutInflater inflater = LayoutInflater.from(mContext);
@@ -156,36 +142,37 @@
                     : mContext.getColor(R.color.ksh_application_group_color));
             keyboardShortcutsLayout.addView(categoryTitle);
 
-            LinearLayout shortcutWrapper = (LinearLayout) inflater.inflate(
-                    R.layout.keyboard_shortcuts_wrapper, null);
+            LinearLayout shortcutContainer = (LinearLayout) inflater.inflate(
+                    R.layout.keyboard_shortcuts_container, keyboardShortcutsLayout, false);
             final int itemsSize = group.getItems().size();
             for (int j = 0; j < itemsSize; j++) {
                 KeyboardShortcutInfo info = group.getItems().get(j);
-                View shortcutView = inflater.inflate(R.layout.keyboard_shortcut_app_item, null);
+                View shortcutView = inflater.inflate(R.layout.keyboard_shortcut_app_item,
+                        shortcutContainer, false);
                 TextView textView = (TextView) shortcutView
                         .findViewById(R.id.keyboard_shortcuts_keyword);
                 textView.setText(info.getLabel());
 
+                LinearLayout shortcutItemsContainer = (LinearLayout) shortcutView
+                        .findViewById(R.id.keyboard_shortcuts_item_container);
                 List<String> shortcutKeys = getHumanReadableShortcutKeys(info);
                 final int shortcutKeysSize = shortcutKeys.size();
                 for (int k = 0; k < shortcutKeysSize; k++) {
                     String shortcutKey = shortcutKeys.get(k);
                     TextView shortcutKeyView = (TextView) inflater.inflate(
-                            R.layout.keyboard_shortcuts_key_view, null);
+                            R.layout.keyboard_shortcuts_key_view, shortcutItemsContainer, false);
                     shortcutKeyView.setText(shortcutKey);
-                    LinearLayout shortcutItemsContainer = (LinearLayout) shortcutView
-                            .findViewById(R.id.keyboard_shortcuts_item_container);
                     shortcutItemsContainer.addView(shortcutKeyView);
                 }
-                shortcutWrapper.addView(shortcutView);
+                shortcutContainer.addView(shortcutView);
             }
-
-            // TODO: merge container and wrapper into one xml file - wrapper is always a child of
-            // container.
-            LinearLayout shortcutsContainer = (LinearLayout) inflater.inflate(
-                    R.layout.keyboard_shortcuts_container, null);
-            shortcutsContainer.addView(shortcutWrapper);
-            keyboardShortcutsLayout.addView(shortcutsContainer);
+            keyboardShortcutsLayout.addView(shortcutContainer);
+            if (i < keyboardShortcutGroupsSize - 1) {
+                View separator = inflater.inflate(
+                        R.layout.keyboard_shortcuts_category_separator, keyboardShortcutsLayout,
+                        false);
+                keyboardShortcutsLayout.addView(separator);
+            }
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationHeaderUtil.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationHeaderUtil.java
index 98a37f9..c70aad2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationHeaderUtil.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationHeaderUtil.java
@@ -74,8 +74,7 @@
         }
 
         private void applyToChild(View view, boolean shouldApply, int originalColor) {
-            if (view.getVisibility() == View.VISIBLE
-                    && originalColor != NotificationHeaderView.NO_COLOR) {
+            if (originalColor != NotificationHeaderView.NO_COLOR) {
                 ImageView imageView = (ImageView) view;
                 imageView.getDrawable().mutate();
                 if (shouldApply) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/HybridNotificationView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/HybridNotificationView.java
index ec73935..81144d5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/HybridNotificationView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/HybridNotificationView.java
@@ -107,12 +107,13 @@
 
     public void bind(CharSequence title, CharSequence text) {
         mTitleView.setText(title);
-        if (TextUtils.isEmpty(title)) {
-            mTitleView.setVisibility(GONE);
-        }
-        mTextView.setText(text);
+        mTitleView.setVisibility(TextUtils.isEmpty(title) ? GONE : VISIBLE);
         if (TextUtils.isEmpty(text)) {
             mTextView.setVisibility(GONE);
+            mTextView.setText(null);
+        } else {
+            mTextView.setVisibility(VISIBLE);
+            mTextView.setText(text.toString());
         }
         requestLayout();
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/HybridNotificationViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/HybridNotificationViewManager.java
index 285d53f..28bb66f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/HybridNotificationViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/HybridNotificationViewManager.java
@@ -18,8 +18,13 @@
 
 import android.app.Notification;
 import android.content.Context;
+import android.text.BidiFormatter;
+import android.text.Spannable;
+import android.text.SpannableStringBuilder;
 import android.text.TextUtils;
+import android.text.style.TextAppearanceSpan;
 import android.view.LayoutInflater;
+import android.view.View;
 import android.view.ViewGroup;
 
 import com.android.systemui.R;
@@ -34,12 +39,12 @@
 
     private final Context mContext;
     private ViewGroup mParent;
-    private String mConcadenationString;
+    private String mDivider;
 
     public HybridNotificationViewManager(Context ctx, ViewGroup parent) {
         mContext = ctx;
         mParent = parent;
-        mConcadenationString = mContext.getString(R.string.group_summary_concadenation);
+        mDivider = " • ";
     }
 
     private HybridNotificationView inflateHybridView() {
@@ -83,7 +88,7 @@
         if (reusableView == null) {
             reusableView = inflateHybridView();
         }
-        CharSequence summary = null;
+        SpannableStringBuilder summary = new SpannableStringBuilder();
         int childCount = group.size();
         for (int i = startIndex; i < childCount; i++) {
             ExpandableNotificationRow child = group.get(i);
@@ -92,15 +97,18 @@
             if (titleText == null) {
                 continue;
             }
-            if (TextUtils.isEmpty(summary)) {
-                summary = titleText;
-            } else if (reusableView.isLayoutRtl()) {
-                summary = titleText + mConcadenationString + summary;
-            } else {
-                summary = summary + mConcadenationString + titleText;
+            if (!TextUtils.isEmpty(summary)) {
+                summary.append(mDivider,
+                        new TextAppearanceSpan(mContext, R.style.
+                                TextAppearance_Material_Notification_HybridNotificationDivider),
+                        Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
             }
+            summary.append(BidiFormatter.getInstance().unicodeWrap(titleText));
         }
-        reusableView.bind(summary);
+        // We want to force the same orientation as the layout RTL mode
+        BidiFormatter formater = BidiFormatter.getInstance(
+                reusableView.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL);
+        reusableView.bind(formater.unicodeWrap(summary));
         return reusableView;
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationBigTextTemplateViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationBigTextTemplateViewWrapper.java
new file mode 100644
index 0000000..487a7a0
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationBigTextTemplateViewWrapper.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.statusbar.notification;
+
+import android.content.Context;
+import android.service.notification.StatusBarNotification;
+import android.view.View;
+
+import com.android.internal.widget.ImageFloatingTextView;
+import com.android.systemui.statusbar.TransformableView;
+
+/**
+ * Wraps a notification containing a big text template
+ */
+public class NotificationBigTextTemplateViewWrapper extends NotificationTemplateViewWrapper {
+
+    private ImageFloatingTextView mBigtext;
+
+    protected NotificationBigTextTemplateViewWrapper(Context ctx, View view) {
+        super(ctx, view);
+    }
+
+    private void resolveViews(StatusBarNotification notification) {
+        mBigtext = (ImageFloatingTextView) mView.findViewById(com.android.internal.R.id.big_text);
+    }
+
+    @Override
+    public void notifyContentUpdated(StatusBarNotification notification) {
+        // Reinspect the notification. Before the super call, because the super call also updates
+        // the transformation types and we need to have our values set by then.
+        resolveViews(notification);
+        super.notifyContentUpdated(notification);
+    }
+
+    @Override
+    protected void updateTransformedTypes() {
+        // This also clears the existing types
+        super.updateTransformedTypes();
+        if (mBigtext != null) {
+            mTransformationHelper.addTransformedView(TransformableView.TRANSFORMING_VIEW_TEXT,
+                    mBigtext);
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationTemplateViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationTemplateViewWrapper.java
index b060245..0c21f0b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationTemplateViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationTemplateViewWrapper.java
@@ -55,11 +55,12 @@
                         }
                         TransformState otherState = notification.getCurrentState(
                                 TRANSFORMING_VIEW_TITLE);
-                        CrossFadeHelper.fadeOut(mText, endRunnable);
+                        final View text = ownState.getTransformedView();
+                        CrossFadeHelper.fadeOut(text, endRunnable);
                         if (otherState != null) {
                             int[] otherStablePosition = otherState.getLaidOutLocationOnScreen();
                             int[] ownPosition = ownState.getLaidOutLocationOnScreen();
-                            mText.animate()
+                            text.animate()
                                     .translationY((otherStablePosition[1]
                                             + otherState.getTransformedView().getHeight()
                                             - ownPosition[1]) * 0.33f)
@@ -72,11 +73,11 @@
                                             if (endRunnable != null) {
                                                 endRunnable.run();
                                             }
-                                            TransformState.setClippingDeactivated(mText,
+                                            TransformState.setClippingDeactivated(text,
                                                     false);
                                         }
                                     });
-                            TransformState.setClippingDeactivated(mText, true);
+                            TransformState.setClippingDeactivated(text, true);
                             otherState.recycle();
                         }
                         return true;
@@ -90,17 +91,18 @@
                         }
                         TransformState otherState = notification.getCurrentState(
                                 TRANSFORMING_VIEW_TITLE);
-                        boolean isVisible = mText.getVisibility() == View.VISIBLE;
-                        CrossFadeHelper.fadeIn(mText);
+                        final View text = ownState.getTransformedView();
+                        boolean isVisible = text.getVisibility() == View.VISIBLE;
+                        CrossFadeHelper.fadeIn(text);
                         if (otherState != null) {
                             int[] otherStablePosition = otherState.getLaidOutLocationOnScreen();
                             int[] ownStablePosition = ownState.getLaidOutLocationOnScreen();
                             if (!isVisible) {
-                                mText.setTranslationY((otherStablePosition[1]
+                                text.setTranslationY((otherStablePosition[1]
                                         + otherState.getTransformedView().getHeight()
                                         - ownStablePosition[1]) * 0.33f);
                             }
-                            mText.animate()
+                            text.animate()
                                     .translationY(0)
                                     .setDuration(
                                             StackStateAnimator.ANIMATION_DURATION_STANDARD)
@@ -108,11 +110,11 @@
                                     .withEndAction(new Runnable() {
                                         @Override
                                         public void run() {
-                                            TransformState.setClippingDeactivated(mText,
+                                            TransformState.setClippingDeactivated(text,
                                                     false);
                                         }
                                     });
-                            TransformState.setClippingDeactivated(mText, true);
+                            TransformState.setClippingDeactivated(text, true);
                             otherState.recycle();
                         }
                         return true;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationViewWrapper.java
index f50b976..a2b4c5d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationViewWrapper.java
@@ -37,6 +37,8 @@
         if (v.getId() == com.android.internal.R.id.status_bar_latest_event_content) {
             if ("bigPicture".equals(v.getTag())) {
                 return new NotificationBigPictureTemplateViewWrapper(ctx, v);
+            } else if ("bigText".equals(v.getTag())) {
+                return new NotificationBigTextTemplateViewWrapper(ctx, v);
             }
             return new NotificationTemplateViewWrapper(ctx, v);
         } else if (v instanceof NotificationHeaderView) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarGestureHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarGestureHelper.java
index a2586f1..bb03454 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarGestureHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarGestureHelper.java
@@ -304,8 +304,7 @@
     public void onTuningChanged(String key, String newValue) {
         switch (key) {
             case KEY_DOCK_WINDOW_GESTURE:
-                mDockWindowEnabled = (newValue == null) ||
-                        (Integer.parseInt(newValue) != 0);
+                mDockWindowEnabled = newValue != null && (Integer.parseInt(newValue) != 0);
                 break;
         }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarInflaterView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarInflaterView.java
index 9359301..d625fc2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarInflaterView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarInflaterView.java
@@ -27,7 +27,6 @@
 import android.widget.ImageView;
 import android.widget.LinearLayout;
 import android.widget.Space;
-
 import com.android.systemui.R;
 import com.android.systemui.statusbar.policy.KeyButtonView;
 import com.android.systemui.tuner.TunerService;
@@ -58,8 +57,9 @@
     public static final String KEY_IMAGE_DELIM = ":";
     public static final String KEY_CODE_END = ")";
 
-    protected final LayoutInflater mLayoutInflater;
-    protected final LayoutInflater mLandscapeInflater;
+    protected LayoutInflater mLayoutInflater;
+    protected LayoutInflater mLandscapeInflater;
+    private int mDensity;
 
     protected FrameLayout mRot0;
     protected FrameLayout mRot90;
@@ -69,11 +69,27 @@
 
     public NavigationBarInflaterView(Context context, AttributeSet attrs) {
         super(context, attrs);
-        mLayoutInflater = LayoutInflater.from(context);
+        mDensity = context.getResources().getConfiguration().densityDpi;
+        createInflaters();
+    }
+
+    private void createInflaters() {
+        mLayoutInflater = LayoutInflater.from(mContext);
         Configuration landscape = new Configuration();
-        landscape.setTo(context.getResources().getConfiguration());
+        landscape.setTo(mContext.getResources().getConfiguration());
         landscape.orientation = Configuration.ORIENTATION_LANDSCAPE;
-        mLandscapeInflater = LayoutInflater.from(context.createConfigurationContext(landscape));
+        mLandscapeInflater = LayoutInflater.from(mContext.createConfigurationContext(landscape));
+    }
+
+    @Override
+    protected void onConfigurationChanged(Configuration newConfig) {
+        super.onConfigurationChanged(newConfig);
+        if (mDensity != newConfig.densityDpi) {
+            mDensity = newConfig.densityDpi;
+            createInflaters();
+            clearViews();
+            inflateLayout(mCurrentLayout);
+        }
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java
index 3130eb9..d3681b7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java
@@ -154,10 +154,14 @@
         }
         mBarState = newState;
         if (mBarState == StatusBarState.KEYGUARD) {
-            for (NotificationGroup group : mGroupMap.values()) {
-                if (group.expanded) {
-                    setGroupExpanded(group, false);
-                }
+            collapseAllGroups();
+        }
+    }
+
+    public void collapseAllGroups() {
+        for (NotificationGroup group : mGroupMap.values()) {
+            if (group.expanded) {
+                setGroupExpanded(group, false);
             }
         }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
index 575eda7..f822bd5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -39,8 +39,6 @@
 import android.view.ViewTreeObserver;
 import android.view.WindowInsets;
 import android.view.accessibility.AccessibilityEvent;
-import android.view.animation.Interpolator;
-import android.view.animation.PathInterpolator;
 import android.widget.FrameLayout;
 import android.widget.TextView;
 
@@ -212,10 +210,6 @@
         }
     };
 
-    /** Interpolator to be used for animations that respond directly to a touch */
-    private final Interpolator mTouchResponseInterpolator =
-            new PathInterpolator(0.3f, 0f, 0.1f, 1f);
-
     public NotificationPanelView(Context context, AttributeSet attrs) {
         super(context, attrs);
         setWillNotDraw(!DEBUG);
@@ -1447,7 +1441,7 @@
         mScrollView.setBlockFlinging(true);
         ValueAnimator animator = ValueAnimator.ofFloat(mQsExpansionHeight, target);
         if (isClick) {
-            animator.setInterpolator(mTouchResponseInterpolator);
+            animator.setInterpolator(Interpolators.TOUCH_RESPONSE);
             animator.setDuration(368);
         } else {
             mFlingAnimationUtils.apply(animator, mQsExpansionHeight, target, vel);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationsQuickSettingsContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationsQuickSettingsContainer.java
index cbb71c5..fd28b09 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationsQuickSettingsContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationsQuickSettingsContainer.java
@@ -17,6 +17,7 @@
 package com.android.systemui.statusbar.phone;
 
 import android.content.Context;
+import android.content.res.Configuration;
 import android.graphics.Canvas;
 import android.util.AttributeSet;
 import android.view.View;
@@ -55,6 +56,20 @@
     }
 
     @Override
+    protected void onConfigurationChanged(Configuration newConfig) {
+        super.onConfigurationChanged(newConfig);
+        reloadWidth(mScrollView);
+        reloadWidth(mStackScroller);
+    }
+
+    private void reloadWidth(View view) {
+        LayoutParams params = (LayoutParams) view.getLayoutParams();
+        params.width = getContext().getResources().getDimensionPixelSize(
+                R.dimen.notification_panel_width);
+        view.setLayoutParams(params);
+    }
+
+    @Override
     public WindowInsets onApplyWindowInsets(WindowInsets insets) {
         setPadding(0, 0, 0, insets.getSystemWindowInsetBottom());
         return insets;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
index 07dc4fd..4dee51d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -118,7 +118,10 @@
 import com.android.systemui.keyguard.KeyguardViewMediator;
 import com.android.systemui.qs.QSPanel;
 import com.android.systemui.recents.ScreenPinningRequest;
+import com.android.systemui.recents.events.EventBus;
+import com.android.systemui.recents.events.activity.UndockingTaskEvent;
 import com.android.systemui.stackdivider.Divider;
+import com.android.systemui.stackdivider.WindowManagerProxy;
 import com.android.systemui.statusbar.ActivatableNotificationView;
 import com.android.systemui.statusbar.BackDropView;
 import com.android.systemui.statusbar.BaseStatusBar;
@@ -1112,26 +1115,25 @@
         @Override
         public boolean onLongClick(View v) {
             if (mRecents != null) {
-                Point realSize = new Point();
-                mContext.getSystemService(DisplayManager.class).getDisplay(Display.DEFAULT_DISPLAY)
-                        .getRealSize(realSize);
-                Rect initialBounds;
-
-                // Hack level over 9000: Make it one pixel smaller so activity manager doesn't
-                // dismiss it immediately again. Remove once b/26777526 is fixed.
-                if (mContext.getResources().getConfiguration().orientation
-                        == Configuration.ORIENTATION_LANDSCAPE) {
-                    initialBounds = new Rect(0, 0, realSize.x - 1, realSize.y);
+                int dockSide = WindowManagerProxy.getInstance().getDockSide();
+                if (dockSide == WindowManager.DOCKED_INVALID) {
+                    Point realSize = new Point();
+                    mContext.getSystemService(DisplayManager.class).getDisplay(Display.DEFAULT_DISPLAY)
+                            .getRealSize(realSize);
+                    Rect initialBounds= new Rect(0, 0, realSize.x, realSize.y);
+                    boolean docked = mRecents.dockTopTask(NavigationBarGestureHelper.DRAG_MODE_NONE,
+                            ActivityManager.DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT,
+                            initialBounds);
+                    if (docked) {
+                        MetricsLogger.action(mContext, MetricsEvent.ACTION_WINDOW_DOCK_LONGPRESS);
+                        return true;
+                    }
                 } else {
-                    initialBounds = new Rect(0, 0, realSize.x, realSize.y - 1);
-                }
-                boolean docked = mRecents.dockTopTask(NavigationBarGestureHelper.DRAG_MODE_NONE,
-                        ActivityManager.DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT,
-                        initialBounds);
-                if (docked) {
-                    MetricsLogger.action(mContext, MetricsEvent.ACTION_WINDOW_DOCK_LONGPRESS);
+                    EventBus.getDefault().send(new UndockingTaskEvent());
+                    MetricsLogger.action(mContext, MetricsEvent.ACTION_WINDOW_UNDOCK_LONGPRESS);
                     return true;
                 }
+
             }
             return false;
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java
index 9996b75..45aae2d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java
@@ -30,12 +30,12 @@
 import android.text.TextUtils;
 import android.util.ArraySet;
 import android.util.TypedValue;
+import android.view.Gravity;
 import android.view.View;
 import android.view.ViewGroup;
 import android.widget.ImageView;
 import android.widget.LinearLayout;
 import android.widget.TextView;
-
 import com.android.internal.statusbar.StatusBarIcon;
 import com.android.systemui.BatteryMeterView;
 import com.android.systemui.FontSizeUtils;
@@ -226,12 +226,25 @@
 
     public void setExternalIcon(String slot) {
         int viewIndex = getViewIndex(getSlotIndex(slot));
+        int height = mContext.getResources().getDimensionPixelSize(
+                R.dimen.status_bar_icon_drawing_size);
         ImageView imageView = (ImageView) mStatusIcons.getChildAt(viewIndex);
         imageView.setScaleType(ImageView.ScaleType.FIT_CENTER);
         imageView.setAdjustViewBounds(true);
+        setHeightAndCenter(imageView, height);
         imageView = (ImageView) mStatusIconsKeyguard.getChildAt(viewIndex);
         imageView.setScaleType(ImageView.ScaleType.FIT_CENTER);
         imageView.setAdjustViewBounds(true);
+        setHeightAndCenter(imageView, height);
+    }
+
+    private void setHeightAndCenter(ImageView imageView, int height) {
+        ViewGroup.LayoutParams params = imageView.getLayoutParams();
+        params.height = height;
+        if (params instanceof LinearLayout.LayoutParams) {
+            ((LinearLayout.LayoutParams) params).gravity = Gravity.CENTER_VERTICAL;
+        }
+        imageView.setLayoutParams(params);
     }
 
     public void setIcon(String slot, StatusBarIcon icon) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HotspotController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HotspotController.java
index b036936..500d603 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HotspotController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HotspotController.java
@@ -20,7 +20,6 @@
     void addCallback(Callback callback);
     void removeCallback(Callback callback);
     boolean isHotspotEnabled();
-    boolean isHotspotSupported();
     void setHotspotEnabled(boolean enabled);
     boolean isTetheringAllowed();
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HotspotControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HotspotControllerImpl.java
index 61d26c7..07b7409 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HotspotControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HotspotControllerImpl.java
@@ -27,8 +27,6 @@
 import android.os.UserManager;
 import android.util.Log;
 
-import com.android.settingslib.TetherUtil;
-
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
 import java.util.ArrayList;
@@ -98,11 +96,6 @@
     }
 
     @Override
-    public boolean isHotspotSupported() {
-        return TetherUtil.isTetheringSupported(mContext);
-    }
-
-    @Override
     public boolean isTetheringAllowed() {
         return !mUserManager.hasUserRestriction(UserManager.DISALLOW_CONFIG_TETHERING,
                 UserHandle.of(mCurrentUser));
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationChildrenContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationChildrenContainer.java
index 409dac1..5cfcd89 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationChildrenContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationChildrenContainer.java
@@ -17,6 +17,7 @@
 package com.android.systemui.statusbar.stack;
 
 import android.content.Context;
+import android.content.res.Configuration;
 import android.util.AttributeSet;
 import android.view.LayoutInflater;
 import android.view.View;
@@ -58,6 +59,7 @@
     private HybridNotificationView mGroupOverflowContainer;
     private ViewState mGroupOverFlowState;
     private int mRealHeight;
+    private int mLayoutDirection = LAYOUT_DIRECTION_UNDEFINED;
 
     public NotificationChildrenContainer(Context context) {
         this(context, null);
@@ -209,6 +211,16 @@
         }
     }
 
+    @Override
+    protected void onConfigurationChanged(Configuration newConfig) {
+        super.onConfigurationChanged(newConfig);
+        int layoutDirection = getLayoutDirection();
+        if (layoutDirection != mLayoutDirection) {
+            updateGroupOverflow();
+            mLayoutDirection = layoutDirection;
+        }
+    }
+
     private View inflateDivider() {
         return LayoutInflater.from(mContext).inflate(
                 R.layout.notification_children_divider, this, false);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
index d6276b8..cc0e67d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
@@ -1648,6 +1648,9 @@
                 bottom = (int) (lastView.getTranslationY() + lastView.getActualHeight());
                 bottom = Math.min(bottom, getHeight());
             }
+        } else if (mPhoneStatusBar.getBarState() == StatusBarState.KEYGUARD) {
+            top = mTopPadding;
+            bottom = top;
         }
         mBackgroundBounds.top = Math.max(0, top);
         mBackgroundBounds.bottom = Math.min(getHeight(), bottom);
@@ -2093,12 +2096,6 @@
         generateAddAnimation(child, false /* fromMoreCard */);
         updateAnimationState(child);
         updateChronometerForChild(child);
-        if (canChildBeDismissed(child)) {
-            // Make sure the dismissButton is visible and not in the animated state.
-            // We need to do this to avoid a race where a clearable notification is added after the
-            // dismiss animation is finished
-            mDismissView.showClearButton();
-        }
     }
 
     private void updateHideSensitiveForChild(View child) {
@@ -2608,6 +2605,9 @@
         mIsExpanded = isExpanded;
         mStackScrollAlgorithm.setIsExpanded(isExpanded);
         if (changed) {
+            if (!mIsExpanded) {
+                mGroupManager.collapseAllGroups();
+            }
             updateNotificationAnimationStates();
             updateChronometers();
         }
@@ -2977,7 +2977,6 @@
                     mDismissView.performVisibilityAnimation(false, dimissHideFinishRunnable);
                 } else {
                     dimissHideFinishRunnable.run();
-                    mDismissView.showClearButton();
                 }
             }
         }
@@ -2985,7 +2984,6 @@
 
     public void setDismissAllInProgress(boolean dismissAllInProgress) {
         mDismissAllInProgress = dismissAllInProgress;
-        mDismissView.setDismissAllInProgress(dismissAllInProgress);
         mAmbientState.setDismissAllInProgress(dismissAllInProgress);
         if (dismissAllInProgress) {
             disableClipOptimization();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java
index bc8c825..784f610 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java
@@ -80,10 +80,6 @@
             boolean showImeSwitcher) {
     }
 
-    @Override
-    public void toggleRecentApps() {
-    }
-
     @Override // CommandQueue
     public void setWindowState(int window, int state) {
     }
diff --git a/packages/SystemUI/src/com/android/systemui/tuner/ColorMatrixTile.java b/packages/SystemUI/src/com/android/systemui/tuner/ColorMatrixTile.java
index 7b06393..d8cf2e2 100644
--- a/packages/SystemUI/src/com/android/systemui/tuner/ColorMatrixTile.java
+++ b/packages/SystemUI/src/com/android/systemui/tuner/ColorMatrixTile.java
@@ -56,7 +56,7 @@
     }
 
     @Override
-    protected State newTileState() {
+    public State newTileState() {
         return new State();
     }
 
diff --git a/proto/src/metrics_constants.proto b/proto/src/metrics_constants.proto
index 0e67a24..e1dd87f 100644
--- a/proto/src/metrics_constants.proto
+++ b/proto/src/metrics_constants.proto
@@ -348,5 +348,9 @@
     // OS: 6.1
     // GMS: 7.8.99
     USER_CREDENTIALS = 285;
+
+    // Logged when the user undocks a previously docked window by long pressing recents while in
+    // docked mode.
+    ACTION_WINDOW_UNDOCK_LONGPRESS = 286;
   }
 }
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 3fd8b40..4300920 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -2670,18 +2670,21 @@
     // if ro.tether.denied = true we default to no tethering
     // gservices could set the secure setting to 1 though to enable it on a build where it
     // had previously been turned off.
+    @Override
     public boolean isTetheringSupported() {
         enforceTetherAccessPermission();
         int defaultVal = (SystemProperties.get("ro.tether.denied").equals("true") ? 0 : 1);
         boolean tetherEnabledInSettings = (Settings.Global.getInt(mContext.getContentResolver(),
                 Settings.Global.TETHER_SUPPORTED, defaultVal) != 0)
                 && !mUserManager.hasUserRestriction(UserManager.DISALLOW_CONFIG_TETHERING);
-        return tetherEnabledInSettings && ((mTethering.getTetherableUsbRegexs().length != 0 ||
+        return tetherEnabledInSettings && mUserManager.isAdminUser() &&
+                ((mTethering.getTetherableUsbRegexs().length != 0 ||
                 mTethering.getTetherableWifiRegexs().length != 0 ||
                 mTethering.getTetherableBluetoothRegexs().length != 0) &&
                 mTethering.getUpstreamIfaceTypes().length != 0);
     }
 
+    @Override
     public void startTethering(int type, ResultReceiver receiver,
             boolean showProvisioningUi) {
         ConnectivityManager.enforceTetherChangePermission(mContext);
@@ -2692,6 +2695,7 @@
         mTethering.startTethering(type, receiver, showProvisioningUi);
     }
 
+    @Override
     public void stopTethering(int type) {
         ConnectivityManager.enforceTetherChangePermission(mContext);
         mTethering.stopTethering(type);
diff --git a/services/core/java/com/android/server/DeviceIdleController.java b/services/core/java/com/android/server/DeviceIdleController.java
index 9bd79c9..423ef84 100644
--- a/services/core/java/com/android/server/DeviceIdleController.java
+++ b/services/core/java/com/android/server/DeviceIdleController.java
@@ -1649,6 +1649,7 @@
             // Whoops, there is an upcoming alarm.  We don't actually want to go idle.
             if (mState != STATE_ACTIVE) {
                 becomeActiveLocked("alarm", Process.myUid());
+                becomeInactiveIfAppropriateLocked();
             }
             return;
         }
diff --git a/services/core/java/com/android/server/MountService.java b/services/core/java/com/android/server/MountService.java
index 3ce4452..5120e1b 100644
--- a/services/core/java/com/android/server/MountService.java
+++ b/services/core/java/com/android/server/MountService.java
@@ -2800,14 +2800,13 @@
     }
 
     @Override
-    public void prepareUserStorage(
-            String volumeUuid, int userId, int serialNumber, boolean ephemeral) {
+    public void prepareUserStorage(String volumeUuid, int userId, int serialNumber, int flags) {
         enforcePermission(android.Manifest.permission.STORAGE_INTERNAL);
         waitForReady();
 
         try {
             mCryptConnector.execute("cryptfs", "prepare_user_storage", escapeNull(volumeUuid),
-                    userId, serialNumber, ephemeral ? 1 : 0);
+                    userId, serialNumber, flags);
         } catch (NativeDaemonConnectorException e) {
             throw e.rethrowAsParcelableException();
         }
diff --git a/services/core/java/com/android/server/am/ActivityManagerDebugConfig.java b/services/core/java/com/android/server/am/ActivityManagerDebugConfig.java
index 4f0d4d9..bef6f0a 100644
--- a/services/core/java/com/android/server/am/ActivityManagerDebugConfig.java
+++ b/services/core/java/com/android/server/am/ActivityManagerDebugConfig.java
@@ -62,7 +62,7 @@
     static final boolean DEBUG_LRU = DEBUG_ALL || false;
     static final boolean DEBUG_MU = DEBUG_ALL || false;
     static final boolean DEBUG_OOM_ADJ = DEBUG_ALL || false;
-    static final boolean DEBUG_PAUSE = DEBUG_ALL || false;
+    static final boolean DEBUG_PAUSE = DEBUG_ALL || true;
     static final boolean DEBUG_POWER = DEBUG_ALL || false;
     static final boolean DEBUG_POWER_QUICK = DEBUG_POWER || false;
     static final boolean DEBUG_PROCESS_OBSERVERS = DEBUG_ALL || false;
@@ -77,7 +77,7 @@
     static final boolean DEBUG_SERVICE = DEBUG_ALL || false;
     static final boolean DEBUG_SERVICE_EXECUTING = DEBUG_ALL || false;
     static final boolean DEBUG_STACK = DEBUG_ALL || false;
-    static final boolean DEBUG_STATES = DEBUG_ALL_ACTIVITIES || false;
+    static final boolean DEBUG_STATES = DEBUG_ALL_ACTIVITIES || true;
     static final boolean DEBUG_SWITCH = DEBUG_ALL || false;
     static final boolean DEBUG_TASKS = DEBUG_ALL || false;
     static final boolean DEBUG_THUMBNAILS = DEBUG_ALL || false;
@@ -85,7 +85,7 @@
     static final boolean DEBUG_UID_OBSERVERS = DEBUG_ALL || false;
     static final boolean DEBUG_URI_PERMISSION = DEBUG_ALL || false;
     static final boolean DEBUG_USER_LEAVING = DEBUG_ALL || false;
-    static final boolean DEBUG_VISIBILITY = DEBUG_ALL || false;
+    static final boolean DEBUG_VISIBILITY = DEBUG_ALL || true;
     static final boolean DEBUG_VISIBLE_BEHIND = DEBUG_ALL_ACTIVITIES || false;
     static final boolean DEBUG_USAGE_STATS = DEBUG_ALL || false;
     static final boolean DEBUG_PERMISSIONS_REVIEW = DEBUG_ALL || false;
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 54c4ced..c021f4c 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -252,10 +252,12 @@
 import static android.app.ActivityManager.DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT;
 import static android.app.ActivityManager.RESIZE_MODE_PRESERVE_WINDOW;
 import static android.app.ActivityManager.StackId.DOCKED_STACK_ID;
+import static android.app.ActivityManager.StackId.FIRST_STATIC_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.app.ActivityManager.StackId.HOME_STACK_ID;
 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.pm.PackageManager.FEATURE_FREEFORM_WINDOW_MANAGEMENT;
 import static android.content.pm.PackageManager.FEATURE_PICTURE_IN_PICTURE;
@@ -600,6 +602,8 @@
 
     final AppErrors mAppErrors;
 
+    boolean mDoingSetFocusedActivity;
+
     public boolean canShowErrorDialogs() {
         return mShowDialogs && !mSleeping && !mShuttingDown;
     }
@@ -2730,6 +2734,12 @@
         }
 
         if (DEBUG_FOCUS) Slog.d(TAG_FOCUS, "setFocusedActivityLocked: r=" + r);
+
+        final boolean wasDoingSetFocusedActivity = mDoingSetFocusedActivity;
+        if (wasDoingSetFocusedActivity) Slog.w(TAG,
+                "setFocusedActivityLocked: called recursively, r=" + r + ", reason=" + reason);
+        mDoingSetFocusedActivity = true;
+
         final ActivityRecord last = mFocusedActivity;
         mFocusedActivity = r;
         if (r.task.isApplicationTask()) {
@@ -2781,6 +2791,12 @@
             mLastFocusedUserId = mFocusedActivity.userId;
         }
 
+        // Log a warning if the focused app is changed during the process. This could
+        // indicate a problem of the focus setting logic!
+        if (mFocusedActivity != r) Slog.w(TAG,
+                "setFocusedActivityLocked: r=" + r + " but focused to " + mFocusedActivity);
+        mDoingSetFocusedActivity = wasDoingSetFocusedActivity;
+
         EventLogTags.writeAmFocusedActivity(
                 mFocusedActivity == null ? -1 : mFocusedActivity.userId,
                 mFocusedActivity == null ? "NULL" : mFocusedActivity.shortComponentName,
@@ -5303,28 +5319,42 @@
 
     @Override
     public void killAllBackgroundProcesses() {
+        killAllBackgroundProcesses(-1);
+    }
+
+    /**
+     * Kills all background processes with targetSdkVersion below the specified
+     * target SDK version.
+     *
+     * @param targetSdkVersion the target SDK version below which to kill
+     *                         processes, or {@code -1} to kill all processes
+     */
+    private void killAllBackgroundProcesses(int targetSdkVersion) {
         if (checkCallingPermission(android.Manifest.permission.KILL_BACKGROUND_PROCESSES)
                 != PackageManager.PERMISSION_GRANTED) {
-            String msg = "Permission Denial: killAllBackgroundProcesses() from pid="
-                    + Binder.getCallingPid()
-                    + ", uid=" + Binder.getCallingUid()
+            final String msg = "Permission Denial: killAllBackgroundProcesses() from pid="
+                    + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid()
                     + " requires " + android.Manifest.permission.KILL_BACKGROUND_PROCESSES;
             Slog.w(TAG, msg);
             throw new SecurityException(msg);
         }
 
-        long callingId = Binder.clearCallingIdentity();
+        final long callingId = Binder.clearCallingIdentity();
         try {
-            synchronized(this) {
-                ArrayList<ProcessRecord> procs = new ArrayList<ProcessRecord>();
+            synchronized (this) {
+                final ArrayList<ProcessRecord> procs = new ArrayList<>();
                 final int NP = mProcessNames.getMap().size();
-                for (int ip=0; ip<NP; ip++) {
-                    SparseArray<ProcessRecord> apps = mProcessNames.getMap().valueAt(ip);
+                for (int ip = 0; ip < NP; ip++) {
+                    final SparseArray<ProcessRecord> apps = mProcessNames.getMap().valueAt(ip);
                     final int NA = apps.size();
-                    for (int ia=0; ia<NA; ia++) {
-                        ProcessRecord app = apps.valueAt(ia);
+                    for (int ia = 0; ia < NA; ia++) {
+                        final ProcessRecord app = apps.valueAt(ia);
                         if (app.persistent) {
-                            // we don't kill persistent processes
+                            // We don't kill persistent processes.
+                            continue;
+                        }
+                        if (targetSdkVersion > 0
+                                && app.info.targetSdkVersion >= targetSdkVersion) {
                             continue;
                         }
                         if (app.removed) {
@@ -5336,11 +5366,13 @@
                     }
                 }
 
-                int N = procs.size();
-                for (int i=0; i<N; i++) {
+                final int N = procs.size();
+                for (int i = 0; i < N; i++) {
                     removeProcessLocked(procs.get(i), false, true, "kill all background");
                 }
+
                 mAllowLowerMemLevel = true;
+
                 updateOomAdjLocked();
                 doLowMemReportIfNeededLocked(null);
             }
@@ -8738,6 +8770,13 @@
                         continue;
                     }
 
+                    if (!tr.mUserSetupComplete) {
+                        // Don't include task launched while user is not done setting-up.
+                        if (DEBUG_RECENTS) Slog.d(TAG_RECENTS,
+                                "Skipping, user setup not complete: " + tr);
+                        continue;
+                    }
+
                     ActivityManager.RecentTaskInfo rti = createRecentTaskInfoFromTaskRecord(tr);
                     if (!detailed) {
                         rti.baseIntent.replaceExtras((Bundle)null);
@@ -12393,9 +12432,8 @@
             mLocalDeviceIdleController
                     = LocalServices.getService(DeviceIdleController.LocalService.class);
 
-            // Make sure we have the current profile info, since it is needed for
-            // security checks.
-            mUserController.updateCurrentProfileIdsLocked();
+            // Make sure we have the current profile info, since it is needed for security checks.
+            mUserController.onSystemReady();
 
             mRecentTasks.onSystemReady();
             // Check to see if there are any update receivers to run.
@@ -17616,6 +17654,22 @@
             final long origId = Binder.clearCallingIdentity();
             final ActivityStack stack = mStackSupervisor.getStack(fromStackId);
             if (stack != null) {
+                if (fromStackId == DOCKED_STACK_ID) {
+
+                    // We are moving all tasks from the docked stack to the fullscreen stack, which
+                    // is dismissing the docked stack, so resize all other stacks to fullscreen here
+                    // already so we don't end up with resize trashing.
+                    for (int i = FIRST_STATIC_STACK_ID; i <= LAST_STATIC_STACK_ID; i++) {
+                        if (StackId.isResizeableByDockedStack(i)) {
+                            ActivityStack otherStack = mStackSupervisor.getStack(i);
+                            if (otherStack != null) {
+                                mStackSupervisor.resizeStackLocked(i,
+                                        null, null, null, PRESERVE_WINDOWS,
+                                        true /* allowResizeInDockedMode */);
+                            }
+                        }
+                    }
+                }
                 final ArrayList<TaskRecord> tasks = stack.getAllTasks();
                 final int size = tasks.size();
                 if (onTop) {
@@ -17754,7 +17808,7 @@
                     if (values.getLocales().size() == 1) {
                         // This is an optimization to avoid the JNI call when the result of
                         // getFirstMatch() does not depend on the supported locales.
-                        locale = values.getLocales().getPrimary();
+                        locale = values.getLocales().get(0);
                     } else {
                         if (mSupportedSystemLocales == null) {
                             mSupportedSystemLocales =
@@ -17805,6 +17859,11 @@
                     mHandler.sendMessage(msg);
                 }
 
+                final boolean isDensityChange = (changes & ActivityInfo.CONFIG_DENSITY) != 0;
+                if (isDensityChange) {
+                    killAllBackgroundProcesses(Build.VERSION_CODES.N);
+                }
+
                 for (int i=mLruProcesses.size()-1; i>=0; i--) {
                     ProcessRecord app = mLruProcesses.get(i);
                     try {
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index 5a0c1c1..c352fc8 100644
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -2168,7 +2168,9 @@
             if (DEBUG_SWITCH) Slog.v(TAG_SWITCH, "Resume running: " + next);
 
             // This activity is now becoming visible.
-            mWindowManager.setAppVisibility(next.appToken, true);
+            if (!next.visible) {
+                mWindowManager.setAppVisibility(next.appToken, true);
+            }
 
             // schedule launch ticks to collect information about slow apps.
             next.startLaunchTickingLocked();
@@ -4304,6 +4306,12 @@
             oldTaskOverride = record.task.extractOverrideConfig(record.configuration);
         }
 
+        // Conversely, do the same when going the other direction.
+        if (Configuration.EMPTY.equals(taskConfig)
+                && !Configuration.EMPTY.equals(oldTaskOverride)) {
+            taskConfig = record.task.extractOverrideConfig(record.configuration);
+        }
+
         // Determine what has changed.  May be nothing, if this is a config
         // that has come back from the app after going idle.  In that case
         // we just want to leave the official config object now in the
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index 95bc95a..0b2ff65 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -583,7 +583,7 @@
         }
 
         final ActivityRecord r = topRunningActivityLocked();
-        if (mService.mFocusedActivity != r) {
+        if (!mService.mDoingSetFocusedActivity && mService.mFocusedActivity != r) {
             // The focus activity should always be the top activity in the focused stack.
             // There will be chaos and anarchy if it isn't...
             mService.setFocusedActivityLocked(r, reason + " setFocusStack");
@@ -1889,12 +1889,6 @@
 
     private void resizeStackUncheckedLocked(ActivityStack stack, Rect bounds, Rect tempTaskBounds,
             Rect tempTaskInsetBounds) {
-        if (bounds != null && mWindowManager.isFullscreenBounds(stack.mStackId, bounds)) {
-            // The bounds passed in corresponds to the fullscreen bounds which we normally
-            // represent with null. Go ahead and set it to null so that all tasks configuration
-            // can have the right fullscreen state.
-            bounds = null;
-        }
         bounds = TaskRecord.validateBounds(bounds);
 
         mTmpBounds.clear();
@@ -1913,7 +1907,8 @@
                     task.updateOverrideConfiguration(tempRect2);
                 } else {
                     task.updateOverrideConfiguration(
-                            tempTaskBounds != null ? tempTaskBounds : bounds);
+                            tempTaskBounds != null ? tempTaskBounds : bounds,
+                            tempTaskInsetBounds != null ? tempTaskInsetBounds : bounds);
                 }
             }
 
@@ -2213,9 +2208,9 @@
         // Temporarily disable resizeablility of task we are moving. We don't want it to be resized
         // if a docked stack is created below which will lead to the stack we are moving from and
         // its resizeable tasks being resized.
-        task.mResizeMode = RESIZE_MODE_UNRESIZEABLE;
+        task.mTemporarilyUnresizable = true;
         final ActivityStack stack = getStack(stackId, CREATE_IF_NEEDED, toTop);
-        task.mResizeMode = resizeMode;
+        task.mTemporarilyUnresizable = false;
         mWindowManager.moveTaskToStack(task.taskId, stack.mStackId, toTop);
         stack.addTask(task, toTop, reason);
 
@@ -2606,9 +2601,11 @@
 
         stack.setVisibleBehindActivity(visible ? r : null);
         if (!visible) {
-            // Make the activity immediately above r opaque.
+            // If there is a translucent home activity, we need to force it stop being translucent,
+            // because we can't depend on the application to necessarily perform that operation.
+            // Check out b/14469711 for details.
             final ActivityRecord next = stack.findNextTranslucentActivity(r);
-            if (next != null) {
+            if (next != null && next.isHomeActivity()) {
                 mService.convertFromTranslucent(next.appToken);
             }
         }
diff --git a/services/core/java/com/android/server/am/TaskRecord.java b/services/core/java/com/android/server/am/TaskRecord.java
index a7d948c..10f0977 100644
--- a/services/core/java/com/android/server/am/TaskRecord.java
+++ b/services/core/java/com/android/server/am/TaskRecord.java
@@ -16,37 +16,7 @@
 
 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.app.ActivityManager.StackId.HOME_STACK_ID;
-import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
-import static android.app.ActivityManager.StackId.PINNED_STACK_ID;
-import static android.content.Intent.FLAG_ACTIVITY_NEW_DOCUMENT;
-import static android.content.Intent.FLAG_ACTIVITY_RETAIN_IN_RECENTS;
-import static android.content.pm.ActivityInfo.RESIZE_MODE_CROP_WINDOWS;
-import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE;
-import static android.content.pm.ActivityInfo.RESIZE_MODE_UNRESIZEABLE;
-import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_ALWAYS;
-import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_DEFAULT;
-import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_IF_WHITELISTED;
-import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_NEVER;
-import static android.content.pm.ApplicationInfo.PRIVATE_FLAG_PRIVILEGED;
-import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_ADD_REMOVE;
-import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_LOCKTASK;
-import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_RECENTS;
-import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_TASKS;
-import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_ADD_REMOVE;
-import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_LOCKTASK;
-import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_RECENTS;
-import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_TASKS;
-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.LOCK_SCREEN_SHOWN;
-import static com.android.server.am.ActivityRecord.APPLICATION_ACTIVITY_TYPE;
-import static com.android.server.am.ActivityRecord.HOME_ACTIVITY_TYPE;
-import static com.android.server.am.ActivityRecord.RECENTS_ACTIVITY_TYPE;
-
+import android.annotation.Nullable;
 import android.app.Activity;
 import android.app.ActivityManager;
 import android.app.ActivityManager.StackId;
@@ -55,6 +25,7 @@
 import android.app.ActivityManager.TaskThumbnailInfo;
 import android.app.ActivityOptions;
 import android.app.AppGlobals;
+import android.app.IActivityManager;
 import android.content.ComponentName;
 import android.content.Intent;
 import android.content.pm.ActivityInfo;
@@ -86,6 +57,37 @@
 import java.util.ArrayList;
 import java.util.Objects;
 
+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.app.ActivityManager.StackId.HOME_STACK_ID;
+import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
+import static android.app.ActivityManager.StackId.PINNED_STACK_ID;
+import static android.content.Intent.FLAG_ACTIVITY_NEW_DOCUMENT;
+import static android.content.Intent.FLAG_ACTIVITY_RETAIN_IN_RECENTS;
+import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_ALWAYS;
+import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_DEFAULT;
+import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_IF_WHITELISTED;
+import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_NEVER;
+import static android.content.pm.ActivityInfo.RESIZE_MODE_CROP_WINDOWS;
+import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE;
+import static android.content.pm.ActivityInfo.RESIZE_MODE_UNRESIZEABLE;
+import static android.content.pm.ApplicationInfo.PRIVATE_FLAG_PRIVILEGED;
+import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_ADD_REMOVE;
+import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_LOCKTASK;
+import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_RECENTS;
+import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_TASKS;
+import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_ADD_REMOVE;
+import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_LOCKTASK;
+import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_RECENTS;
+import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_TASKS;
+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.LOCK_SCREEN_SHOWN;
+import static com.android.server.am.ActivityRecord.APPLICATION_ACTIVITY_TYPE;
+import static com.android.server.am.ActivityRecord.HOME_ACTIVITY_TYPE;
+import static com.android.server.am.ActivityRecord.RECENTS_ACTIVITY_TYPE;
+
 final class TaskRecord {
     private static final String TAG = TAG_WITH_CLASS_NAME ? "TaskRecord" : TAG_AM;
     private static final String TAG_ADD_REMOVE = TAG + POSTFIX_ADD_REMOVE;
@@ -106,6 +108,7 @@
     private static final String ATTR_AUTOREMOVERECENTS = "auto_remove_recents";
     private static final String ATTR_ASKEDCOMPATMODE = "asked_compat_mode";
     private static final String ATTR_USERID = "user_id";
+    private static final String ATTR_USER_SETUP_COMPLETE = "user_setup_complete";
     private static final String ATTR_EFFECTIVE_UID = "effective_uid";
     private static final String ATTR_TASKTYPE = "task_type";
     private static final String ATTR_FIRSTACTIVETIME = "first_active_time";
@@ -155,11 +158,15 @@
 
     String stringName;      // caching of toString() result.
     int userId;             // user for which this task was created
+    boolean mUserSetupComplete; // The user set-up is complete as of the last time the task activity
+                                // was changed.
 
     int numFullscreen;      // Number of fullscreen activities.
 
     int mResizeMode;        // The resize mode of this task and its activities.
                             // Based on the {@link ActivityInfo#resizeMode} of the root activity.
+    boolean mTemporarilyUnresizable; // Separate flag from mResizeMode used to suppress resize
+                                     // changes on a temporary basis.
     int mLockTaskMode;      // Which tasklock mode to launch this task in. One of
                             // ActivityManager.LOCK_TASK_LAUNCH_MODE_*
     private boolean mPrivileged;    // The root activity application of this task holds
@@ -238,6 +245,9 @@
 
     // Bounds of the Task. null for fullscreen tasks.
     Rect mBounds = null;
+    private final Rect mTmpRect = new Rect();
+    private final Rect mTmpRect2 = new Rect();
+
     // Last non-fullscreen bounds the task was launched in or resized to.
     // The information is persisted and used to determine the appropriate stack to launch the
     // task into on restore.
@@ -311,7 +321,8 @@
             boolean neverRelinquishIdentity, TaskDescription _lastTaskDescription,
             TaskThumbnailInfo lastThumbnailInfo, int taskAffiliation, int prevTaskId,
             int nextTaskId, int taskAffiliationColor, int callingUid, String callingPackage,
-            int resizeMode, boolean privileged, boolean _realActivitySuspended) {
+            int resizeMode, boolean privileged, boolean _realActivitySuspended,
+            boolean userSetupComplete) {
         mService = service;
         mFilename = String.valueOf(_taskId) + TASK_THUMBNAIL_SUFFIX +
                 TaskPersister.IMAGE_EXTENSION;
@@ -334,6 +345,7 @@
         taskType = _taskType;
         mTaskToReturnTo = HOME_ACTIVITY_TYPE;
         userId = _userId;
+        mUserSetupComplete = userSetupComplete;
         effectiveUid = _effectiveUid;
         firstActiveTime = _firstActiveTime;
         lastActiveTime = _lastActiveTime;
@@ -434,6 +446,7 @@
         }
 
         userId = UserHandle.getUserId(info.applicationInfo.uid);
+        mUserSetupComplete = mService.mUserController.isUserSetupCompleteLocked(userId);
         if ((info.flags & ActivityInfo.FLAG_AUTO_REMOVE_FROM_RECENTS) != 0) {
             // If the activity itself has requested auto-remove, then just always do it.
             autoRemoveRecents = true;
@@ -934,7 +947,7 @@
 
     boolean isResizeable() {
         return !isHomeTask() && (mService.mForceResizableActivities
-                || ActivityInfo.isResizeableMode(mResizeMode));
+                || ActivityInfo.isResizeableMode(mResizeMode)) && !mTemporarilyUnresizable;
     }
 
     boolean inCropWindowsResizeMode() {
@@ -1064,6 +1077,7 @@
         out.attribute(null, ATTR_AUTOREMOVERECENTS, String.valueOf(autoRemoveRecents));
         out.attribute(null, ATTR_ASKEDCOMPATMODE, String.valueOf(askedCompatMode));
         out.attribute(null, ATTR_USERID, String.valueOf(userId));
+        out.attribute(null, ATTR_USER_SETUP_COMPLETE, String.valueOf(mUserSetupComplete));
         out.attribute(null, ATTR_EFFECTIVE_UID, String.valueOf(effectiveUid));
         out.attribute(null, ATTR_TASKTYPE, String.valueOf(taskType));
         out.attribute(null, ATTR_FIRSTACTIVETIME, String.valueOf(firstActiveTime));
@@ -1133,6 +1147,7 @@
         boolean askedCompatMode = false;
         int taskType = ActivityRecord.APPLICATION_ACTIVITY_TYPE;
         int userId = 0;
+        boolean userSetupComplete = true;
         int effectiveUid = -1;
         String lastDescription = null;
         long firstActiveTime = -1;
@@ -1179,6 +1194,8 @@
                 askedCompatMode = Boolean.valueOf(attrValue);
             } else if (ATTR_USERID.equals(attrName)) {
                 userId = Integer.valueOf(attrValue);
+            } else if (ATTR_USER_SETUP_COMPLETE.equals(attrName)) {
+                userSetupComplete = Boolean.valueOf(attrValue);
             } else if (ATTR_EFFECTIVE_UID.equals(attrName)) {
                 effectiveUid = Integer.valueOf(attrValue);
             } else if (ATTR_TASKTYPE.equals(attrName)) {
@@ -1278,7 +1295,7 @@
                 activities, firstActiveTime, lastActiveTime, lastTimeOnTop, neverRelinquishIdentity,
                 taskDescription, thumbnailInfo, taskAffiliation, prevTaskId, nextTaskId,
                 taskAffiliationColor, callingUid, callingPackage, resizeMode, privileged,
-                realActivitySuspended);
+                realActivitySuspended, userSetupComplete);
         task.updateOverrideConfiguration(bounds);
 
         for (int activityNdx = activities.size() - 1; activityNdx >=0; --activityNdx) {
@@ -1291,9 +1308,22 @@
 
     /**
      * Update task's override configuration based on the bounds.
+     * @param bounds The bounds of the task.
      * @return Update configuration or null if there is no change.
      */
     Configuration updateOverrideConfiguration(Rect bounds) {
+        return updateOverrideConfiguration(bounds, null /* insetBounds */);
+    }
+
+    /**
+     * Update task's override configuration based on the bounds.
+     * @param bounds The bounds of the task.
+     * @param insetBounds The bounds used to calculate the system insets, which is used here to
+     *                    subtract the navigation bar/status bar size from the screen size reported
+     *                    to the application. See {@link IActivityManager#resizeDockedStack}.
+     * @return Update configuration or null if there is no change.
+     */
+    Configuration updateOverrideConfiguration(Rect bounds, @Nullable Rect insetBounds) {
         if (Objects.equals(mBounds, bounds)) {
             return null;
         }
@@ -1316,7 +1346,12 @@
             if (stack == null || StackId.persistTaskBounds(stack.mStackId)) {
                 mLastNonFullscreenBounds = mBounds;
             }
-            mOverrideConfig = calculateOverrideConfig(mBounds);
+
+            // Stable insets need to be subtracted because we also subtract it in the fullscreen
+            // configuration.
+            mTmpRect.set(bounds);
+            subtractStableInsets(mTmpRect, insetBounds != null ? insetBounds : mTmpRect);
+            mOverrideConfig = calculateOverrideConfig(mTmpRect);
         }
 
         if (mFullscreen != oldFullscreen) {
@@ -1326,6 +1361,16 @@
         return !mOverrideConfig.equals(oldConfig) ? mOverrideConfig : null;
     }
 
+    private void subtractStableInsets(Rect inOutBounds, Rect inInsetBounds) {
+        mTmpRect2.set(inInsetBounds);
+        mService.mWindowManager.subtractStableInsets(mTmpRect2);
+        int leftInset = mTmpRect2.left - inInsetBounds.left;
+        int topInset = mTmpRect2.top - inInsetBounds.top;
+        int rightInset = inInsetBounds.right - mTmpRect2.right;
+        int bottomInset = inInsetBounds.bottom - mTmpRect2.bottom;
+        inOutBounds.inset(leftInset, topInset, rightInset, bottomInset);
+    }
+
     Configuration calculateOverrideConfig(Rect bounds) {
         final Configuration serviceConfig = mService.mConfiguration;
         final Configuration config = new Configuration(Configuration.EMPTY);
@@ -1341,8 +1386,9 @@
                 ? Configuration.ORIENTATION_PORTRAIT
                 : Configuration.ORIENTATION_LANDSCAPE;
         final int sl = Configuration.resetScreenLayout(serviceConfig.screenLayout);
-        config.screenLayout = Configuration.reduceScreenLayout(
-                sl, config.screenWidthDp, config.screenHeightDp);
+        int longSize = Math.max(config.screenWidthDp, config.screenHeightDp);
+        int shortSize = Math.min(config.screenWidthDp, config.screenHeightDp);
+        config.screenLayout = Configuration.reduceScreenLayout(sl, longSize, shortSize);
         return config;
     }
 
diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java
index 551f332..2f63b2d3 100644
--- a/services/core/java/com/android/server/am/UserController.java
+++ b/services/core/java/com/android/server/am/UserController.java
@@ -24,6 +24,7 @@
 import static android.app.ActivityManager.USER_OP_SUCCESS;
 import static android.content.Context.KEYGUARD_SERVICE;
 import static android.os.Process.SYSTEM_UID;
+import static android.provider.Settings.Secure.USER_SETUP_COMPLETE;
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_MU;
 import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
 import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
@@ -45,11 +46,14 @@
 import android.app.IStopUserCallback;
 import android.app.IUserSwitchObserver;
 import android.app.KeyguardManager;
+import android.content.ContentResolver;
 import android.content.Context;
 import android.content.IIntentReceiver;
 import android.content.Intent;
 import android.content.pm.PackageManager;
 import android.content.pm.UserInfo;
+import android.database.ContentObserver;
+import android.net.Uri;
 import android.os.BatteryStats;
 import android.os.Binder;
 import android.os.Bundle;
@@ -66,10 +70,12 @@
 import android.os.UserManager;
 import android.os.storage.IMountService;
 import android.os.storage.StorageManager;
+import android.provider.Settings;
 import android.util.IntArray;
 import android.util.Pair;
 import android.util.Slog;
 import android.util.SparseArray;
+import android.util.SparseBooleanArray;
 import android.util.SparseIntArray;
 
 import com.android.internal.R;
@@ -145,6 +151,34 @@
 
     private final LockPatternUtils mLockPatternUtils;
 
+    // Set of users who have completed the set-up process.
+    private final SparseBooleanArray mSetupCompletedUsers = new SparseBooleanArray();
+    private final UserSetupCompleteContentObserver mUserSetupCompleteContentObserver;
+
+    private class UserSetupCompleteContentObserver extends ContentObserver {
+        private final Uri mUserSetupComplete = Settings.Secure.getUriFor(USER_SETUP_COMPLETE);
+
+        public UserSetupCompleteContentObserver(Handler handler) {
+            super(handler);
+        }
+
+        void register(ContentResolver resolver) {
+            resolver.registerContentObserver(mUserSetupComplete, false, this, UserHandle.USER_ALL);
+            synchronized (mService) {
+                updateCurrentUserSetupCompleteLocked();
+            }
+        }
+
+        @Override
+        public void onChange(boolean selfChange, Uri uri) {
+            if (mUserSetupComplete.equals(uri)) {
+                synchronized (mService) {
+                    updateCurrentUserSetupCompleteLocked();
+                }
+            }
+        }
+    }
+
     UserController(ActivityManagerService service) {
         mService = service;
         mHandler = mService.mHandler;
@@ -154,6 +188,7 @@
         mUserLru.add(UserHandle.USER_SYSTEM);
         mLockPatternUtils = new LockPatternUtils(mService.mContext);
         updateStartedUserArrayLocked();
+        mUserSetupCompleteContentObserver = new UserSetupCompleteContentObserver(mHandler);
     }
 
     void finishUserSwitch(UserState uss) {
@@ -424,6 +459,7 @@
                 mStartedUsers.remove(userId);
                 mUserLru.remove(Integer.valueOf(userId));
                 updateStartedUserArrayLocked();
+                mSetupCompletedUsers.delete(userId);
 
                 mService.onUserStoppedLocked(userId);
                 // Clean up all state and processes associated with the user.
@@ -619,6 +655,7 @@
                 final Integer userIdInt = userId;
                 mUserLru.remove(userIdInt);
                 mUserLru.add(userIdInt);
+                updateCurrentUserSetupCompleteLocked();
 
                 if (foreground) {
                     mCurrentUserId = userId;
@@ -833,6 +870,17 @@
         mUserSwitchObservers.finishBroadcast();
     }
 
+    void updateCurrentUserSetupCompleteLocked() {
+        final ContentResolver cr = mService.mContext.getContentResolver();
+        final boolean setupComplete =
+                Settings.Secure.getIntForUser(cr, USER_SETUP_COMPLETE, 0, mCurrentUserId) != 0;
+        mSetupCompletedUsers.put(mCurrentUserId, setupComplete);
+    }
+
+    boolean isUserSetupCompleteLocked(int userId) {
+        return mSetupCompletedUsers.get(userId);
+    }
+
     private void stopBackgroundUsersIfEnforced(int oldUserId) {
         // Never stop system user
         if (oldUserId == UserHandle.USER_SYSTEM) {
@@ -1141,12 +1189,17 @@
         }
     }
 
+    void onSystemReady() {
+        updateCurrentProfileIdsLocked();
+        mUserSetupCompleteContentObserver.register(mService.mContext.getContentResolver());
+    }
+
     /**
      * Refreshes the list of users related to the current user when either a
      * user switch happens or when a new related user is started in the
      * background.
      */
-    void updateCurrentProfileIdsLocked() {
+    private void updateCurrentProfileIdsLocked() {
         final List<UserInfo> profiles = getUserManager().getProfiles(mCurrentUserId,
                 false /* enabledOnly */);
         int[] currentProfileIds = new int[profiles.size()]; // profiles will not be null
diff --git a/services/core/java/com/android/server/connectivity/Tethering.java b/services/core/java/com/android/server/connectivity/Tethering.java
index a73a67a..760b218 100644
--- a/services/core/java/com/android/server/connectivity/Tethering.java
+++ b/services/core/java/com/android/server/connectivity/Tethering.java
@@ -451,7 +451,7 @@
                 ((BluetoothPan) proxy).setBluetoothTethering(enable);
                 // TODO: Enabling bluetooth tethering can fail asynchronously here.
                 // We should figure out a way to bubble up that failure instead of sending success.
-                int result = ((BluetoothPan) proxy).isTetheringOn() ?
+                int result = ((BluetoothPan) proxy).isTetheringOn() == enable ?
                         ConnectivityManager.TETHER_ERROR_NO_ERROR :
                         ConnectivityManager.TETHER_ERROR_MASTER_ERROR;
                 sendTetherResult(receiver, result);
diff --git a/services/core/java/com/android/server/job/controllers/JobStatus.java b/services/core/java/com/android/server/job/controllers/JobStatus.java
index be55799..e749433 100644
--- a/services/core/java/com/android/server/job/controllers/JobStatus.java
+++ b/services/core/java/com/android/server/job/controllers/JobStatus.java
@@ -349,9 +349,9 @@
         pw.print(prefix); UserHandle.formatUid(pw, uId);
         pw.print(" tag="); pw.println(tag);
         pw.print(prefix);
-        pw.print("Source: uid="); UserHandle.formatUid(pw, sourceUid);
-        pw.print(" user="); pw.print(sourceUserId);
-        pw.print(" pkg="); pw.println(sourcePackageName);
+        pw.print("Source: uid="); UserHandle.formatUid(pw, getSourceUid());
+        pw.print(" user="); pw.print(getSourceUserId());
+        pw.print(" pkg="); pw.println(getSourcePackageName());
         pw.print(prefix); pw.println("JobInfo:");
         pw.print(prefix); pw.print("  Service: ");
         pw.println(job.getService().flattenToShortString());
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index 544f255..7f8099e 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -192,7 +192,7 @@
  * enforcement.
  */
 public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
-    private static final String TAG = "NetworkPolicy";
+    static final String TAG = "NetworkPolicy";
     private static final boolean LOGD = false;
     private static final boolean LOGV = false;
 
@@ -1689,6 +1689,7 @@
             }
             writePolicy = true;
         }
+        updateRulesForGlobalChangeLocked(true);
 
         // Remove associated UID policies
         int[] uids = new int[0];
@@ -1862,8 +1863,8 @@
         Slog.i(TAG, "adding uid " + uid + " to restrict background whitelist");
         synchronized (mRulesLock) {
             mRestrictBackgroundWhitelistUids.append(uid, true);
+            updateRulesForGlobalChangeLocked(true);
             writePolicyLocked();
-            // TODO: call other update methods like updateNetworkRulesLocked?
         }
         mHandler.obtainMessage(MSG_RESTRICT_BACKGROUND_WHITELIST_CHANGED, uid, 0).sendToTarget();
     }
@@ -1878,9 +1879,10 @@
         mHandler.obtainMessage(MSG_RESTRICT_BACKGROUND_WHITELIST_CHANGED, uid, 0).sendToTarget();
     }
 
-    private void removeRestrictBackgroundWhitelistedUidLocked(int uid, boolean writePolicy) {
+    private void removeRestrictBackgroundWhitelistedUidLocked(int uid, boolean updateNow) {
         mRestrictBackgroundWhitelistUids.delete(uid);
-        if (writePolicy) {
+        if (updateNow) {
+            updateRulesForGlobalChangeLocked(true);
             writePolicyLocked();
         }
     }
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerShellCommand.java b/services/core/java/com/android/server/net/NetworkPolicyManagerShellCommand.java
index 5830b0e..281c3d0 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerShellCommand.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerShellCommand.java
@@ -16,19 +16,24 @@
 
 package com.android.server.net;
 
-import java.io.PrintWriter;
+import static com.android.server.net.NetworkPolicyManagerService.TAG;
 
-import android.content.Intent;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.List;
+
 import android.net.INetworkPolicyManager;
+import android.net.NetworkPolicy;
 import android.os.Binder;
 import android.os.RemoteException;
 import android.os.ShellCommand;
+import android.util.Log;
 
-public class NetworkPolicyManagerShellCommand extends ShellCommand {
+class NetworkPolicyManagerShellCommand extends ShellCommand {
 
     final INetworkPolicyManager mInterface;
 
-    NetworkPolicyManagerShellCommand(NetworkPolicyManagerService service) {
+    NetworkPolicyManagerShellCommand(INetworkPolicyManager service) {
         mInterface = service;
     }
 
@@ -66,16 +71,24 @@
         pw.println("  help");
         pw.println("    Print this help text.");
         pw.println("");
-        pw.println("  get restrict-background");
-        pw.println("    Gets the global restrict background usage status.");
-        pw.println("  set restrict-background BOOLEAN");
-        pw.println("    Sets the global restrict background usage status.");
-        pw.println("  list restrict-background-whitelist");
-        pw.println("    Prints UID that are whitelisted for restrict background usage.");
         pw.println("  add restrict-background-whitelist UID");
         pw.println("    Adds a UID to the whitelist for restrict background usage.");
+        pw.println("  get metered-network ID");
+        pw.println("    Checks whether the given non-mobile network is metered or not.");
+        pw.println("  get restrict-background");
+        pw.println("    Gets the global restrict background usage status.");
+        pw.println("  list metered-networks [BOOLEAN]");
+        pw.println("    Lists all non-mobile networks and whether they are metered or not.");
+        pw.println("    If a boolean argument is passed, filters just the metered (or unmetered)");
+        pw.println("    networks.");
+        pw.println("  list restrict-background-whitelist");
+        pw.println("    Lists UIDs that are whitelisted for restrict background usage.");
         pw.println("  remove restrict-background-whitelist UID");
         pw.println("    Removes a UID from the whitelist for restrict background usage.");
+        pw.println("  set metered-network ID BOOLEAN");
+        pw.println("    Toggles whether the given non-mobile network is metered.");
+        pw.println("  set restrict-background BOOLEAN");
+        pw.println("    Sets the global restrict background usage status.");
     }
 
     private int runGet() throws RemoteException {
@@ -86,6 +99,8 @@
             return -1;
         }
         switch(type) {
+            case "metered-network":
+                return getNonMobileMeteredNetwork();
             case "restrict-background":
                 return getRestrictBackground();
         }
@@ -101,6 +116,8 @@
             return -1;
         }
         switch(type) {
+            case "metered-network":
+                return setNonMobileMeteredNetwork();
             case "restrict-background":
                 return setRestrictBackground();
         }
@@ -116,6 +133,8 @@
             return -1;
         }
         switch(type) {
+            case "metered-networks":
+                return listNonMobileMeteredNetworks();
             case "restrict-background-whitelist":
                 return runListRestrictBackgroundWhitelist();
         }
@@ -196,7 +215,12 @@
       if (uid < 0) {
           return uid;
       }
-      mInterface.addRestrictBackgroundWhitelistedUid(uid);
+      final long token = Binder.clearCallingIdentity();
+      try {
+          mInterface.addRestrictBackgroundWhitelistedUid(uid);
+      } finally {
+          Binder.restoreCallingIdentity(token);
+      }
       return 0;
     }
 
@@ -205,10 +229,95 @@
         if (uid < 0) {
             return uid;
         }
-        mInterface.removeRestrictBackgroundWhitelistedUid(uid);
+        final long token = Binder.clearCallingIdentity();
+        try {
+            mInterface.removeRestrictBackgroundWhitelistedUid(uid);
+        } finally {
+            Binder.restoreCallingIdentity(token);
+        }
         return 0;
     }
 
+    private int listNonMobileMeteredNetworks() throws RemoteException {
+        final PrintWriter pw = getOutPrintWriter();
+        final String arg = getNextArg();
+        final Boolean filter = arg == null ? null : Boolean.valueOf(arg);
+        for (NetworkPolicy policy : getNonMobilePolicies()) {
+            if (filter != null && filter.booleanValue() != policy.metered) {
+                continue;
+            }
+            pw.print(getNetworkId(policy));
+            pw.print(';');
+            pw.println(policy.metered);
+        }
+        return 0;
+    }
+
+    private int getNonMobileMeteredNetwork() throws RemoteException {
+        final PrintWriter pw = getOutPrintWriter();
+        final String id = getNextArg();
+        if (id == null) {
+            pw.println("Error: didn't specify ID");
+            return -1;
+        }
+        final List<NetworkPolicy> policies = getNonMobilePolicies();
+        for (NetworkPolicy policy: policies) {
+            if (id.equals(getNetworkId(policy))) {
+                pw.println(policy.metered);
+                return 0;
+            }
+        }
+        return 0;
+    }
+
+    private int setNonMobileMeteredNetwork() throws RemoteException {
+        final PrintWriter pw = getOutPrintWriter();
+        final String id = getNextArg();
+        if (id == null) {
+            pw.println("Error: didn't specify ID");
+            return -1;
+        }
+        final String arg = getNextArg();
+        if (arg == null) {
+            pw.println("Error: didn't specify BOOLEAN");
+            return -1;
+        }
+        final boolean metered = Boolean.valueOf(arg);
+        final NetworkPolicy[] policies = mInterface.getNetworkPolicies(null);
+        boolean changed = false;
+        for (NetworkPolicy policy : policies) {
+            if (policy.template.isMatchRuleMobile() || policy.metered == metered) {
+                continue;
+            }
+            final String networkId = getNetworkId(policy);
+            if (id.equals(networkId)) {
+                Log.i(TAG, "Changing " + networkId + " metered status to " + metered);
+                policy.metered = metered;
+                changed = true;
+            }
+        }
+        if (changed) {
+            mInterface.setNetworkPolicies(policies);
+        }
+        return 0;
+    }
+
+    private List<NetworkPolicy> getNonMobilePolicies() throws RemoteException {
+        final NetworkPolicy[] policies = mInterface.getNetworkPolicies(null);
+        final List<NetworkPolicy> nonMobilePolicies = new ArrayList<NetworkPolicy>(policies.length);
+        for (NetworkPolicy policy: policies) {
+            if (!policy.template.isMatchRuleMobile()) {
+                nonMobilePolicies.add(policy);
+            }
+        }
+        return nonMobilePolicies;
+    }
+
+    private String getNetworkId(NetworkPolicy policy) {
+        // ids are typically enclosed on double quotes (")
+        return policy.template.getNetworkId().replaceAll("^\"|\"$", "");
+    }
+
     private int getNextBooleanArg() {
         final PrintWriter pw = getOutPrintWriter();
         final String arg = getNextArg();
diff --git a/services/core/java/com/android/server/notification/ManagedServices.java b/services/core/java/com/android/server/notification/ManagedServices.java
index f5da52e..29d52c1 100644
--- a/services/core/java/com/android/server/notification/ManagedServices.java
+++ b/services/core/java/com/android/server/notification/ManagedServices.java
@@ -250,6 +250,12 @@
         rebindServices();
     }
 
+    public void onUserUnlocked(int user) {
+        if (DEBUG) Slog.d(TAG, "onUserUnlocked u=" + user);
+        rebuildRestoredPackages();
+        rebindServices();
+    }
+
     public ManagedServiceInfo getServiceFromTokenLocked(IInterface service) {
         if (service == null) {
             return null;
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 078094c..bcb2c59 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -816,6 +816,12 @@
             } else if (action.equals(Intent.ACTION_USER_REMOVED)) {
                 final int user = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL);
                 mZenModeHelper.onUserRemoved(user);
+            } else if (action.equals(Intent.ACTION_USER_UNLOCKED)) {
+                final int user = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL);
+                mConditionProviders.onUserUnlocked(user);
+                mListeners.onUserUnlocked(user);
+                mAssistant.onUserUnlocked(user);
+                mZenModeHelper.onUserUnlocked(user);
             }
         }
     };
@@ -994,6 +1000,7 @@
         filter.addAction(Intent.ACTION_USER_SWITCHED);
         filter.addAction(Intent.ACTION_USER_ADDED);
         filter.addAction(Intent.ACTION_USER_REMOVED);
+        filter.addAction(Intent.ACTION_USER_UNLOCKED);
         filter.addAction(Intent.ACTION_MANAGED_PROFILE_AVAILABILITY_CHANGED);
         getContext().registerReceiver(mIntentReceiver, filter);
 
diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java
index b7abce21..7518c6e 100644
--- a/services/core/java/com/android/server/notification/ZenModeHelper.java
+++ b/services/core/java/com/android/server/notification/ZenModeHelper.java
@@ -16,7 +16,6 @@
 
 package com.android.server.notification;
 
-import static android.media.AudioAttributes.USAGE_ALARM;
 import static android.media.AudioAttributes.USAGE_NOTIFICATION;
 import static android.media.AudioAttributes.USAGE_NOTIFICATION_RINGTONE;
 import static android.media.AudioAttributes.USAGE_UNKNOWN;
@@ -195,19 +194,7 @@
     }
 
     public void onUserSwitched(int user) {
-        if (mUser == user || user < UserHandle.USER_SYSTEM) return;
-        mUser = user;
-        if (DEBUG) Log.d(TAG, "onUserSwitched u=" + user);
-        ZenModeConfig config = mConfigs.get(user);
-        if (config == null) {
-            if (DEBUG) Log.d(TAG, "onUserSwitched: generating default config for user " + user);
-            config = mDefaultConfig.copy();
-            config.user = user;
-        }
-        synchronized (mConfig) {
-            setConfigLocked(config, "onUserSwitched");
-        }
-        cleanUpZenRules();
+        loadConfigForUser(user, "onUserSwitched");
     }
 
     public void onUserRemoved(int user) {
@@ -216,6 +203,26 @@
         mConfigs.remove(user);
     }
 
+    public void onUserUnlocked(int user) {
+        loadConfigForUser(user, "onUserUnlocked");
+    }
+
+    private void loadConfigForUser(int user, String reason) {
+        if (mUser == user || user < UserHandle.USER_SYSTEM) return;
+        mUser = user;
+        if (DEBUG) Log.d(TAG, reason + " u=" + user);
+        ZenModeConfig config = mConfigs.get(user);
+        if (config == null) {
+            if (DEBUG) Log.d(TAG, reason + " generating default config for user " + user);
+            config = mDefaultConfig.copy();
+            config.user = user;
+        }
+        synchronized (mConfig) {
+            setConfigLocked(config, reason);
+        }
+        cleanUpZenRules();
+    }
+
     public int getZenModeListenerInterruptionFilter() {
         return NotificationManager.zenModeToInterruptionFilter(mZenMode);
     }
diff --git a/services/core/java/com/android/server/pm/Installer.java b/services/core/java/com/android/server/pm/Installer.java
index dac89ec..c08f713 100644
--- a/services/core/java/com/android/server/pm/Installer.java
+++ b/services/core/java/com/android/server/pm/Installer.java
@@ -16,11 +16,11 @@
 
 package com.android.server.pm;
 
-import android.annotation.IntDef;
 import android.annotation.Nullable;
 import android.content.Context;
 import android.content.pm.PackageStats;
 import android.os.Build;
+import android.os.storage.StorageManager;
 import android.util.Slog;
 
 import com.android.internal.os.InstallerConnection;
@@ -29,9 +29,6 @@
 
 import dalvik.system.VMRuntime;
 
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-
 public final class Installer extends SystemService {
     private static final String TAG = "Installer";
 
@@ -52,19 +49,9 @@
     /** This is an OTA update dexopt */
     public static final int DEXOPT_OTA          = 1 << 6;
 
-    /** @hide */
-    @IntDef(flag = true, value = {
-            FLAG_DE_STORAGE,
-            FLAG_CE_STORAGE,
-    })
-    @Retention(RetentionPolicy.SOURCE)
-    public @interface StorageFlags {}
-
-    public static final int FLAG_DE_STORAGE = 1 << 0;
-    public static final int FLAG_CE_STORAGE = 1 << 1;
-
-    public static final int FLAG_CLEAR_CACHE_ONLY = 1 << 2;
-    public static final int FLAG_CLEAR_CODE_CACHE_ONLY = 1 << 3;
+    // NOTE: keep in sync with installd
+    public static final int FLAG_CLEAR_CACHE_ONLY = 1 << 8;
+    public static final int FLAG_CLEAR_CODE_CACHE_ONLY = 1 << 9;
 
     private final InstallerConnection mInstaller;
 
diff --git a/services/core/java/com/android/server/pm/PackageDexOptimizer.java b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
index 64af213..a3af561 100644
--- a/services/core/java/com/android/server/pm/PackageDexOptimizer.java
+++ b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
@@ -176,8 +176,14 @@
                 dexoptNeeded = adjustDexoptNeeded(dexoptNeeded);
 
                 if (dexoptNeeded == DexFile.NO_DEXOPT_NEEDED) {
-                    // No dexopt needed and we don't use profiles. Nothing to do.
-                    continue;
+                    if (useProfiles) {
+                        // Profiles may trigger re-compilation. The final decision is taken in
+                        // installd.
+                        dexoptNeeded = DexFile.DEX2OAT_NEEDED;
+                    } else {
+                        // No dexopt needed and we don't use profiles. Nothing to do.
+                        continue;
+                    }
                 }
                 final String dexoptType;
                 String oatDir = null;
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 553a9a2..504ce31 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -235,7 +235,6 @@
 import com.android.server.ServiceThread;
 import com.android.server.SystemConfig;
 import com.android.server.Watchdog;
-import com.android.server.pm.Installer.StorageFlags;
 import com.android.server.pm.PermissionsState.PermissionState;
 import com.android.server.pm.Settings.DatabaseVersion;
 import com.android.server.pm.Settings.VersionInfo;
@@ -2388,9 +2387,9 @@
             // can't wait for user to start
             final int flags;
             if (StorageManager.isFileBasedEncryptionEnabled()) {
-                flags = Installer.FLAG_DE_STORAGE;
+                flags = StorageManager.FLAG_STORAGE_DE;
             } else {
-                flags = Installer.FLAG_DE_STORAGE | Installer.FLAG_CE_STORAGE;
+                flags = StorageManager.FLAG_STORAGE_DE | StorageManager.FLAG_STORAGE_CE;
             }
             reconcileAppsData(StorageManager.UUID_PRIVATE_INTERNAL, UserHandle.USER_SYSTEM, flags);
 
@@ -6860,7 +6859,7 @@
 
     private boolean removeDataDirsLI(String volumeUuid, String packageName) {
         // TODO: triage flags as part of 26466827
-        final int flags = Installer.FLAG_CE_STORAGE | Installer.FLAG_DE_STORAGE;
+        final int flags = StorageManager.FLAG_STORAGE_CE | StorageManager.FLAG_STORAGE_DE;
 
         boolean res = true;
         final int[] users = sUserManager.getUserIds();
@@ -6906,7 +6905,7 @@
 
     private void deleteCodeCacheDirsLI(String volumeUuid, String packageName) {
         // TODO: triage flags as part of 26466827
-        final int flags = Installer.FLAG_CE_STORAGE | Installer.FLAG_DE_STORAGE;
+        final int flags = StorageManager.FLAG_STORAGE_CE | StorageManager.FLAG_STORAGE_DE;
 
         final int[] users = sUserManager.getUserIds();
         for (int user : users) {
@@ -13947,7 +13946,8 @@
                 outInfo.removedUsers = new int[] {removeUser};
             }
             // TODO: triage flags as part of 26466827
-            final int installerFlags = Installer.FLAG_CE_STORAGE | Installer.FLAG_DE_STORAGE;
+            final int installerFlags = StorageManager.FLAG_STORAGE_CE
+                    | StorageManager.FLAG_STORAGE_DE;
             try {
                 mInstaller.destroyAppData(ps.volumeUuid, packageName, removeUser, installerFlags);
             } catch (InstallerException e) {
@@ -14127,7 +14127,7 @@
         // record of app. This helps users recover from UID mismatches without
         // resorting to a full data wipe.
         // TODO: triage flags as part of 26466827
-        final int flags = Installer.FLAG_CE_STORAGE | Installer.FLAG_DE_STORAGE;
+        final int flags = StorageManager.FLAG_STORAGE_CE | StorageManager.FLAG_STORAGE_DE;
         try {
             mInstaller.clearAppData(pkg.volumeUuid, packageName, userId, flags);
         } catch (InstallerException e) {
@@ -14362,7 +14362,7 @@
             return false;
         }
         // TODO: triage flags as part of 26466827
-        final int flags = Installer.FLAG_CE_STORAGE | Installer.FLAG_DE_STORAGE;
+        final int flags = StorageManager.FLAG_STORAGE_CE | StorageManager.FLAG_STORAGE_DE;
         try {
             mInstaller.clearAppData(p.volumeUuid, packageName, userId,
                     flags | Installer.FLAG_CLEAR_CACHE_ONLY);
@@ -14463,7 +14463,7 @@
         }
 
         // TODO: triage flags as part of 26466827
-        final int flags = Installer.FLAG_CE_STORAGE | Installer.FLAG_DE_STORAGE;
+        final int flags = StorageManager.FLAG_STORAGE_CE | StorageManager.FLAG_STORAGE_DE;
         try {
             mInstaller.getAppSize(p.volumeUuid, packageName, userHandle, flags, apkPath,
                     libDirRoot, publicSrcDir, asecPath, dexCodeInstructionSets, pStats);
@@ -16787,16 +16787,20 @@
         }
 
         // Reconcile app data for all started/unlocked users
+        final StorageManager sm = mContext.getSystemService(StorageManager.class);
         final UserManager um = mContext.getSystemService(UserManager.class);
         for (UserInfo user : um.getUsers()) {
+            final int flags;
             if (um.isUserUnlocked(user.id)) {
-                reconcileAppsData(volumeUuid, user.id,
-                        Installer.FLAG_DE_STORAGE | Installer.FLAG_CE_STORAGE);
+                flags = StorageManager.FLAG_STORAGE_DE | StorageManager.FLAG_STORAGE_CE;
             } else if (um.isUserRunning(user.id)) {
-                reconcileAppsData(volumeUuid, user.id, Installer.FLAG_DE_STORAGE);
+                flags = StorageManager.FLAG_STORAGE_DE;
             } else {
                 continue;
             }
+
+            sm.prepareUserStorage(volumeUuid, user.id, user.serialNumber, flags);
+            reconcileAppsData(volumeUuid, user.id, flags);
         }
 
         synchronized (mPackages) {
@@ -16905,20 +16909,6 @@
                 }
             }
         }
-
-        final StorageManager sm = mContext.getSystemService(StorageManager.class);
-        final UserManager um = mContext.getSystemService(UserManager.class);
-        for (UserInfo user : um.getUsers()) {
-            final File userDir = Environment.getDataUserDirectory(volumeUuid, user.id);
-            if (userDir.exists()) continue;
-
-            try {
-                sm.prepareUserStorage(volumeUuid, user.id, user.serialNumber, user.isEphemeral());
-                UserManagerService.enforceSerialNumber(userDir, user.serialNumber);
-            } catch (IOException e) {
-                Log.wtf(TAG, "Failed to create user directory on " + volumeUuid, e);
-            }
-        }
     }
 
     private void assertPackageKnown(String volumeUuid, String packageName)
@@ -16988,7 +16978,7 @@
      * Verifies that directories exist and that ownership and labeling is
      * correct for all installed apps on all mounted volumes.
      */
-    void reconcileAppsData(int userId, @StorageFlags int flags) {
+    void reconcileAppsData(int userId, int flags) {
         final StorageManager storage = mContext.getSystemService(StorageManager.class);
         for (VolumeInfo vol : storage.getWritablePrivateVolumes()) {
             final String volumeUuid = vol.getFsUuid();
@@ -17005,7 +16995,7 @@
      * Verifies that directories exist and that ownership and labeling is
      * correct for all installed apps.
      */
-    private void reconcileAppsData(String volumeUuid, int userId, @StorageFlags int flags) {
+    private void reconcileAppsData(String volumeUuid, int userId, int flags) {
         Slog.v(TAG, "reconcileAppsData for " + volumeUuid + " u" + userId + " 0x"
                 + Integer.toHexString(flags));
 
@@ -17016,7 +17006,7 @@
 
         // First look for stale data that doesn't belong, and check if things
         // have changed since we did our last restorecon
-        if ((flags & Installer.FLAG_CE_STORAGE) != 0) {
+        if ((flags & StorageManager.FLAG_STORAGE_CE) != 0) {
             if (!isUserKeyUnlocked(userId)) {
                 throw new RuntimeException(
                         "Yikes, someone asked us to reconcile CE storage while " + userId
@@ -17034,12 +17024,12 @@
                     logCriticalInfo(Log.WARN, "Destroying " + file + " due to: " + e);
                     synchronized (mInstallLock) {
                         destroyAppDataLI(volumeUuid, packageName, userId,
-                                Installer.FLAG_CE_STORAGE);
+                                StorageManager.FLAG_STORAGE_CE);
                     }
                 }
             }
         }
-        if ((flags & Installer.FLAG_DE_STORAGE) != 0) {
+        if ((flags & StorageManager.FLAG_STORAGE_DE) != 0) {
             restoreconNeeded |= SELinuxMMAC.isRestoreconNeeded(deDir);
 
             final File[] files = FileUtils.listFilesOrEmpty(deDir);
@@ -17051,7 +17041,7 @@
                     logCriticalInfo(Log.WARN, "Destroying " + file + " due to: " + e);
                     synchronized (mInstallLock) {
                         destroyAppDataLI(volumeUuid, packageName, userId,
-                                Installer.FLAG_DE_STORAGE);
+                                StorageManager.FLAG_STORAGE_DE);
                     }
                 }
             }
@@ -17080,10 +17070,10 @@
         }
 
         if (restoreconNeeded) {
-            if ((flags & Installer.FLAG_CE_STORAGE) != 0) {
+            if ((flags & StorageManager.FLAG_STORAGE_CE) != 0) {
                 SELinuxMMAC.setRestoreconDone(ceDir);
             }
-            if ((flags & Installer.FLAG_DE_STORAGE) != 0) {
+            if ((flags & StorageManager.FLAG_STORAGE_DE) != 0) {
                 SELinuxMMAC.setRestoreconDone(deDir);
             }
         }
@@ -17112,9 +17102,9 @@
         for (UserInfo user : um.getUsers()) {
             final int flags;
             if (um.isUserUnlocked(user.id)) {
-                flags = Installer.FLAG_DE_STORAGE | Installer.FLAG_CE_STORAGE;
+                flags = StorageManager.FLAG_STORAGE_DE | StorageManager.FLAG_STORAGE_CE;
             } else if (um.isUserRunning(user.id)) {
-                flags = Installer.FLAG_DE_STORAGE;
+                flags = StorageManager.FLAG_STORAGE_DE;
             } else {
                 continue;
             }
@@ -17135,7 +17125,7 @@
      * will try recovering system apps by wiping data; third-party app data is
      * left intact.
      */
-    private void prepareAppData(String volumeUuid, int userId, @StorageFlags int flags,
+    private void prepareAppData(String volumeUuid, int userId, int flags,
             PackageParser.Package pkg, boolean restoreconNeeded) {
         if (DEBUG_APP_DATA) {
             Slog.v(TAG, "prepareAppData for " + pkg.packageName + " u" + userId + " 0x"
@@ -17173,7 +17163,7 @@
                 restoreconAppDataLI(volumeUuid, packageName, userId, flags, appId, app.seinfo);
             }
 
-            if ((flags & Installer.FLAG_CE_STORAGE) != 0) {
+            if ((flags & StorageManager.FLAG_STORAGE_CE) != 0) {
                 // Create a native library symlink only if we have native libraries
                 // and if the native libraries are 32 bit libraries. We do not provide
                 // this symlink for 64 bit libraries.
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index bbbe693..fcb777b 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -3787,7 +3787,7 @@
                 continue;
             }
             // TODO: triage flags!
-            final int flags = Installer.FLAG_CE_STORAGE | Installer.FLAG_DE_STORAGE;
+            final int flags = StorageManager.FLAG_STORAGE_CE | StorageManager.FLAG_STORAGE_DE;
             try {
                 installer.createAppData(volumeUuids[i], names[i], userHandle, flags, appIds[i],
                         seinfos[i], targetSdkVersions[i]);
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index 5f46567..3cc7b10 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -25,7 +25,6 @@
 import android.app.ActivityManagerNative;
 import android.app.IActivityManager;
 import android.app.IStopUserCallback;
-import android.app.admin.DevicePolicyManager;
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
@@ -78,7 +77,9 @@
 import com.android.internal.util.XmlUtils;
 import com.android.internal.widget.LockPatternUtils;
 import com.android.server.LocalServices;
-import com.android.server.pm.Installer.StorageFlags;
+
+import libcore.io.IoUtils;
+import libcore.util.Objects;
 
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
@@ -96,9 +97,6 @@
 import java.util.ArrayList;
 import java.util.List;
 
-import libcore.io.IoUtils;
-import libcore.util.Objects;
-
 /**
  * Service for {@link UserManager}.
  *
@@ -1893,17 +1891,8 @@
             }
             final StorageManager storage = mContext.getSystemService(StorageManager.class);
             storage.createUserKey(userId, userInfo.serialNumber, userInfo.isEphemeral());
-            for (VolumeInfo vol : storage.getWritablePrivateVolumes()) {
-                final String volumeUuid = vol.getFsUuid();
-                try {
-                    final File userDir = Environment.getDataUserDirectory(volumeUuid, userId);
-                    storage.prepareUserStorage(
-                            volumeUuid, userId, userInfo.serialNumber, userInfo.isEphemeral());
-                    enforceSerialNumber(userDir, userInfo.serialNumber);
-                } catch (IOException e) {
-                    Log.wtf(LOG_TAG, "Failed to create user directory on " + volumeUuid, e);
-                }
-            }
+            prepareUserStorage(userId, userInfo.serialNumber,
+                    StorageManager.FLAG_STORAGE_DE | StorageManager.FLAG_STORAGE_CE);
             mPm.createNewUser(userId);
             userInfo.partial = false;
             synchronized (mPackagesLock) {
@@ -2466,11 +2455,24 @@
     }
 
     /**
+     * Prepare storage areas for given user on all mounted devices.
+     */
+    private void prepareUserStorage(int userId, int userSerial, int flags) {
+        final StorageManager storage = mContext.getSystemService(StorageManager.class);
+        for (VolumeInfo vol : storage.getWritablePrivateVolumes()) {
+            final String volumeUuid = vol.getFsUuid();
+            storage.prepareUserStorage(volumeUuid, userId, userSerial, flags);
+        }
+    }
+
+    /**
      * Called right before a user is started. This gives us a chance to prepare
      * app storage and apply any user restrictions.
      */
     public void onBeforeStartUser(int userId) {
-        mPm.reconcileAppsData(userId, Installer.FLAG_DE_STORAGE);
+        final int userSerial = getUserSerialNumber(userId);
+        prepareUserStorage(userId, userSerial, StorageManager.FLAG_STORAGE_DE);
+        mPm.reconcileAppsData(userId, StorageManager.FLAG_STORAGE_DE);
 
         if (userId != UserHandle.USER_SYSTEM) {
             synchronized (mRestrictionsLock) {
@@ -2484,7 +2486,9 @@
      * app storage.
      */
     public void onBeforeUnlockUser(int userId) {
-        mPm.reconcileAppsData(userId, Installer.FLAG_CE_STORAGE);
+        final int userSerial = getUserSerialNumber(userId);
+        prepareUserStorage(userId, userSerial, StorageManager.FLAG_STORAGE_CE);
+        mPm.reconcileAppsData(userId, StorageManager.FLAG_STORAGE_CE);
     }
 
     /**
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 43b82e9..a92cc31 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -6877,7 +6877,12 @@
         final boolean dockedStackVisible = mWindowManagerInternal.isStackVisible(DOCKED_STACK_ID);
         final boolean freeformStackVisible =
                 mWindowManagerInternal.isStackVisible(FREEFORM_WORKSPACE_STACK_ID);
-        final boolean forceShowSystemBars = dockedStackVisible || freeformStackVisible;
+        final boolean resizing = mWindowManagerInternal.isDockedDividerResizing();
+
+        // We need to force system bars when the docked stack is visible, when the freeform stack
+        // is visible but also when we are resizing for the transitions when docked stack
+        // visibility changes.
+        final boolean forceShowSystemBars = dockedStackVisible || freeformStackVisible || resizing;
         final boolean forceOpaqueSystemBars = forceShowSystemBars && !mForceStatusBarFromKeyguard;
 
         // apply translucent bar vis flags
diff --git a/services/core/java/com/android/server/tv/TvInputManagerService.java b/services/core/java/com/android/server/tv/TvInputManagerService.java
index b065e85..342c078 100644
--- a/services/core/java/com/android/server/tv/TvInputManagerService.java
+++ b/services/core/java/com/android/server/tv/TvInputManagerService.java
@@ -584,14 +584,14 @@
         for (IBinder sessionToken : serviceState.sessionTokens) {
             SessionState sessionState = userState.sessionStateMap.get(sessionToken);
             if (sessionState.session == null && (inputId == null
-                    || sessionState.info.getId().equals(inputId))) {
+                    || sessionState.inputId.equals(inputId))) {
                 sessionsToAbort.add(sessionState);
             }
         }
         for (SessionState sessionState : sessionsToAbort) {
             removeSessionStateLocked(sessionState.sessionToken, sessionState.userId);
             sendSessionTokenToClientLocked(sessionState.client,
-                    sessionState.info.getId(), null, null, sessionState.seq);
+                    sessionState.inputId, null, null, sessionState.seq);
         }
         updateServiceConnectionLocked(serviceState.component, userId);
     }
@@ -601,7 +601,7 @@
         UserState userState = getOrCreateUserStateLocked(userId);
         SessionState sessionState = userState.sessionStateMap.get(sessionToken);
         if (DEBUG) {
-            Slog.d(TAG, "createSessionInternalLocked(inputId=" + sessionState.info.getId() + ")");
+            Slog.d(TAG, "createSessionInternalLocked(inputId=" + sessionState.inputId + ")");
         }
         InputChannel[] channels = InputChannel.openInputChannelPair(sessionToken.toString());
 
@@ -611,14 +611,14 @@
         // Create a session. When failed, send a null token immediately.
         try {
             if (sessionState.isRecordingSession) {
-                service.createRecordingSession(callback, sessionState.info.getId());
+                service.createRecordingSession(callback, sessionState.inputId);
             } else {
-                service.createSession(channels[1], callback, sessionState.info.getId());
+                service.createSession(channels[1], callback, sessionState.inputId);
             }
         } catch (RemoteException e) {
             Slog.e(TAG, "error in createSession", e);
             removeSessionStateLocked(sessionToken, userId);
-            sendSessionTokenToClientLocked(sessionState.client, sessionState.info.getId(), null,
+            sendSessionTokenToClientLocked(sessionState.client, sessionState.inputId, null,
                     null, sessionState.seq);
         }
         channels[1].dispose();
@@ -684,14 +684,11 @@
             }
         }
 
-        TvInputInfo info = sessionState.info;
-        if (info != null) {
-            ServiceState serviceState = userState.serviceStateMap.get(info.getComponent());
-            if (serviceState != null) {
-                serviceState.sessionTokens.remove(sessionToken);
-            }
+        ServiceState serviceState = userState.serviceStateMap.get(sessionState.componentName);
+        if (serviceState != null) {
+            serviceState.sessionTokens.remove(sessionToken);
         }
-        updateServiceConnectionLocked(sessionState.info.getComponent(), userId);
+        updateServiceConnectionLocked(sessionState.componentName, userId);
 
         // Log the end of watch.
         SomeArgs args = SomeArgs.obtain();
@@ -707,7 +704,7 @@
                 sessionState = getSessionStateLocked(sessionState.hardwareSessionToken,
                         Process.SYSTEM_UID, userId);
             }
-            ServiceState serviceState = getServiceStateLocked(sessionState.info.getComponent(), userId);
+            ServiceState serviceState = getServiceStateLocked(sessionState.componentName, userId);
             if (!serviceState.isHardware) {
                 return;
             }
@@ -1091,8 +1088,9 @@
 
                     // Create a new session token and a session state.
                     IBinder sessionToken = new Binder();
-                    SessionState sessionState = new SessionState(sessionToken, info,
-                            isRecordingSession, client, seq, callingUid, resolvedUserId);
+                    SessionState sessionState = new SessionState(sessionToken, info.getId(),
+                            info.getComponent(), isRecordingSession, client, seq, callingUid,
+                            resolvedUserId);
 
                     // Add them to the global session state map of the current user.
                     userState.sessionStateMap.put(sessionToken, sessionState);
@@ -1273,7 +1271,7 @@
 
                         // Log the start of watch.
                         SomeArgs args = SomeArgs.obtain();
-                        args.arg1 = sessionState.info.getComponent().getPackageName();
+                        args.arg1 = sessionState.componentName.getPackageName();
                         args.arg2 = System.currentTimeMillis();
                         args.arg3 = ContentUris.parseId(channelUri);
                         args.arg4 = params;
@@ -1779,10 +1777,10 @@
                         return false;
                     }
                     for (SessionState sessionState : userState.sessionStateMap.values()) {
-                        if (sessionState.info.getId().equals(inputId)
+                        if (sessionState.inputId.equals(inputId)
                                 && sessionState.hardwareSessionToken != null) {
                             hardwareInputId = userState.sessionStateMap.get(
-                                    sessionState.hardwareSessionToken).info.getId();
+                                    sessionState.hardwareSessionToken).inputId;
                             break;
                         }
                     }
@@ -1918,7 +1916,7 @@
                         pw.println(entry.getKey() + ": " + session);
 
                         pw.increaseIndent();
-                        pw.println("info: " + session.info);
+                        pw.println("inputId: " + session.inputId);
                         pw.println("client: " + session.client);
                         pw.println("seq: " + session.seq);
                         pw.println("callingUid: " + session.callingUid);
@@ -2046,7 +2044,8 @@
     }
 
     private final class SessionState implements IBinder.DeathRecipient {
-        private final TvInputInfo info;
+        private final String inputId;
+        private final ComponentName componentName;
         private final boolean isRecordingSession;
         private final ITvInputClient client;
         private final int seq;
@@ -2058,10 +2057,12 @@
         // Not null if this session represents an external device connected to a hardware TV input.
         private IBinder hardwareSessionToken;
 
-        private SessionState(IBinder sessionToken, TvInputInfo info, boolean isRecordingSession,
-                ITvInputClient client, int seq, int callingUid, int userId) {
+        private SessionState(IBinder sessionToken, String inputId, ComponentName componentName,
+                boolean isRecordingSession, ITvInputClient client, int seq, int callingUid,
+                int userId) {
             this.sessionToken = sessionToken;
-            this.info = info;
+            this.inputId = inputId;
+            this.componentName = componentName;
             this.isRecordingSession = isRecordingSession;
             this.client = client;
             this.seq = seq;
@@ -2274,19 +2275,19 @@
         @Override
         public void onSessionCreated(ITvInputSession session, IBinder hardwareSessionToken) {
             if (DEBUG) {
-                Slog.d(TAG, "onSessionCreated(inputId=" + mSessionState.info.getId() + ")");
+                Slog.d(TAG, "onSessionCreated(inputId=" + mSessionState.inputId + ")");
             }
             synchronized (mLock) {
                 mSessionState.session = session;
                 mSessionState.hardwareSessionToken = hardwareSessionToken;
                 if (session != null && addSessionTokenToClientStateLocked(session)) {
                     sendSessionTokenToClientLocked(mSessionState.client,
-                            mSessionState.info.getId(), mSessionState.sessionToken, mChannels[0],
+                            mSessionState.inputId, mSessionState.sessionToken, mChannels[0],
                             mSessionState.seq);
                 } else {
                     removeSessionStateLocked(mSessionState.sessionToken, mSessionState.userId);
                     sendSessionTokenToClientLocked(mSessionState.client,
-                            mSessionState.info.getId(), null, null, mSessionState.seq);
+                            mSessionState.inputId, null, null, mSessionState.seq);
                 }
                 mChannels[0].dispose();
             }
diff --git a/services/core/java/com/android/server/wm/BoundsAnimationController.java b/services/core/java/com/android/server/wm/BoundsAnimationController.java
index 5f97478..1bfdcce 100644
--- a/services/core/java/com/android/server/wm/BoundsAnimationController.java
+++ b/services/core/java/com/android/server/wm/BoundsAnimationController.java
@@ -22,6 +22,7 @@
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
 
 import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
 import android.animation.ValueAnimator;
 import android.graphics.Rect;
 import android.util.ArrayMap;
@@ -50,13 +51,15 @@
         private final Rect mFrom;
         private final Rect mTo;
         private final Rect mTmpRect;
+        private final boolean mMoveToFullScreen;
 
-        BoundsAnimator(AnimateBoundsUser target, Rect from, Rect to) {
+        BoundsAnimator(AnimateBoundsUser target, Rect from, Rect to, boolean moveToFullScreen) {
             super();
             mTarget = target;
             mFrom = from;
             mTo = to;
             mTmpRect = new Rect();
+            mMoveToFullScreen = moveToFullScreen;
             addUpdateListener(this);
             addListener(this);
         }
@@ -88,6 +91,9 @@
         @Override
         public void onAnimationEnd(Animator animation) {
             finishAnimation();
+            if (mMoveToFullScreen) {
+                mTarget.moveToFullscreen();
+            }
         }
 
         @Override
@@ -125,14 +131,25 @@
          * necessary cleanup.
          */
         void finishBoundsAnimation();
+
+        void moveToFullscreen();
+
+        void getFullScreenBounds(Rect bounds);
     }
 
-    void animateBounds(AnimateBoundsUser target, Rect from, Rect to) {
+    void animateBounds(final AnimateBoundsUser target, Rect from, Rect to) {
+        boolean moveToFullscreen = false;
+        if (to == null) {
+            to = new Rect();
+            target.getFullScreenBounds(to);
+            moveToFullscreen = true;
+        }
+
         final BoundsAnimator existing = mRunningAnimations.get(target);
         if (existing != null) {
             existing.cancel();
         }
-        BoundsAnimator animator = new BoundsAnimator(target, from, to);
+        BoundsAnimator animator = new BoundsAnimator(target, from, to, moveToFullscreen);
         mRunningAnimations.put(target, animator);
         animator.setFloatValues(0f, 1f);
         animator.setDuration(DEFAULT_APP_TRANSITION_DURATION);
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index be1b85c..a06d3fc 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -289,11 +289,9 @@
         if (displayContent != null) {
             displayContent.getLogicalDisplayRect(mTmpRect);
             rotation = displayContent.getDisplayInfo().rotation;
-            if (bounds == null) {
+            mFullscreen = bounds == null;
+            if (mFullscreen) {
                 bounds = mTmpRect;
-                mFullscreen = true;
-            } else {
-                mFullscreen = mTmpRect.equals(bounds);
             }
         }
 
diff --git a/services/core/java/com/android/server/wm/TaskStack.java b/services/core/java/com/android/server/wm/TaskStack.java
index 4659131..40ca1c5 100644
--- a/services/core/java/com/android/server/wm/TaskStack.java
+++ b/services/core/java/com/android/server/wm/TaskStack.java
@@ -255,11 +255,9 @@
         if (mDisplayContent != null) {
             mDisplayContent.getLogicalDisplayRect(mTmpRect);
             rotation = mDisplayContent.getDisplayInfo().rotation;
-            if (bounds == null) {
+            mFullscreen = bounds == null;
+            if (mFullscreen) {
                 bounds = mTmpRect;
-                mFullscreen = true;
-            } else {
-                mFullscreen = mTmpRect.equals(bounds);
             }
         }
 
@@ -587,7 +585,8 @@
     }
 
     void getStackDockedModeBoundsLocked(Rect outBounds, boolean ignoreVisibility) {
-        if (!StackId.isResizeableByDockedStack(mStackId) || mDisplayContent == null) {
+        if ((mStackId != DOCKED_STACK_ID && !StackId.isResizeableByDockedStack(mStackId))
+                || mDisplayContent == null) {
             outBounds.set(mBounds);
             return;
         }
@@ -616,8 +615,7 @@
 
         mDisplayContent.getLogicalDisplayRect(mTmpRect);
         dockedStack.getRawBounds(mTmpRect2);
-        final boolean dockedOnTopOrLeft = dockedSide == DOCKED_TOP
-                || dockedSide == DOCKED_LEFT;
+        final boolean dockedOnTopOrLeft = dockedSide == DOCKED_TOP || dockedSide == DOCKED_LEFT;
         getStackDockedModeBounds(mTmpRect, outBounds, mStackId, mTmpRect2,
                 mDisplayContent.mDividerControllerLocked.getContentWidth(), dockedOnTopOrLeft);
 
@@ -722,6 +720,19 @@
         }
     }
 
+    void resetDockedStackToMiddle() {
+        if (mStackId != DOCKED_STACK_ID) {
+            throw new IllegalStateException("Not a docked stack=" + this);
+        }
+
+        mService.mDockedStackCreateBounds = null;
+
+        final Rect bounds = new Rect();
+        getStackDockedModeBoundsLocked(bounds, true /*ignoreVisibility*/);
+        mService.mH.obtainMessage(RESIZE_STACK, DOCKED_STACK_ID,
+                1 /*allowResizeInDockedMode*/, bounds).sendToTarget();
+    }
+
     void detachDisplay() {
         EventLog.writeEvent(EventLogTags.WM_STACK_REMOVED, mStackId);
 
@@ -865,14 +876,14 @@
         final int orientation = mService.mCurConfiguration.orientation;
         if (orientation == Configuration.ORIENTATION_PORTRAIT) {
             // Portrait mode, docked either at the top or the bottom.
-            if (bounds.top - mTmpRect.top < mTmpRect.bottom - bounds.bottom) {
+            if (bounds.top - mTmpRect.top <= mTmpRect.bottom - bounds.bottom) {
                 return DOCKED_TOP;
             } else {
                 return DOCKED_BOTTOM;
             }
         } else if (orientation == Configuration.ORIENTATION_LANDSCAPE) {
             // Landscape mode, docked either on the left or on the right.
-            if (bounds.left - mTmpRect.left < mTmpRect.right - bounds.right) {
+            if (bounds.left - mTmpRect.left <= mTmpRect.right - bounds.right) {
                 return DOCKED_LEFT;
             } else {
                 return DOCKED_RIGHT;
@@ -927,4 +938,18 @@
             }
         }
     }
+
+    @Override
+    public void moveToFullscreen() {
+        try {
+            mService.mActivityManager.moveTasksToFullscreenStack(mStackId, true);
+        } catch (RemoteException e) {
+            e.printStackTrace();
+        }
+    }
+
+    @Override
+    public void getFullScreenBounds(Rect bounds) {
+        getDisplayContent().getContentRect(bounds);
+    }
 }
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index a26430e..ae6c89a 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -464,6 +464,8 @@
     EmulatorDisplayOverlay mEmulatorDisplayOverlay;
 
     final float[] mTmpFloats = new float[9];
+    final Rect mTmpRect = new Rect();
+    final Rect mTmpRect2 = new Rect();
 
     boolean mDisplayReady;
     boolean mSafeMode;
@@ -4845,17 +4847,6 @@
         }
     }
 
-    /** Returns true if the input bounds corresponds to the fullscreen bounds the stack is on. */
-    public boolean isFullscreenBounds(int stackId, Rect bounds) {
-        synchronized (mWindowMap) {
-            final TaskStack stack = mStackIdToStack.get(stackId);
-            if (stack == null || bounds == null) {
-                return true;
-            }
-            return stack.isFullscreenBounds(bounds);
-        }
-    }
-
     /**
      * Re-sizes a stack and its containing tasks.
      * @param stackId Id of stack to resize.
@@ -5373,8 +5364,18 @@
             mWindowPlacerLocked.performSurfacePlacement();
 
             // Notify whether the docked stack exists for the current user
-            getDefaultDisplayContentLocked().mDividerControllerLocked
+            final DisplayContent displayContent = getDefaultDisplayContentLocked();
+            displayContent.mDividerControllerLocked
                     .notifyDockedStackExistsChanged(hasDockedTasksForUser(newUserId));
+
+            // If the display is already prepared, update the density.
+            // Otherwise, we'll update it when it's prepared.
+            if (mDisplayReady) {
+                final int forcedDensity = getForcedDisplayDensityForUserLocked(newUserId);
+                final int targetDensity = forcedDensity != 0 ? forcedDensity
+                        : displayContent.mInitialDisplayDensity;
+                setForcedDisplayDensityLocked(displayContent, targetDensity);
+            }
         }
     }
 
@@ -8370,21 +8371,9 @@
         }
 
         // Display density.
-        String densityStr = Settings.Global.getString(mContext.getContentResolver(),
-                Settings.Global.DISPLAY_DENSITY_FORCED);
-        if (densityStr == null || densityStr.length() == 0) {
-            densityStr = SystemProperties.get(DENSITY_OVERRIDE, null);
-        }
-        if (densityStr != null && densityStr.length() > 0) {
-            int density;
-            try {
-                density = Integer.parseInt(densityStr);
-                if (displayContent.mBaseDisplayDensity != density) {
-                    Slog.i(TAG_WM, "FORCED DISPLAY DENSITY: " + density);
-                    displayContent.mBaseDisplayDensity = density;
-                }
-            } catch (NumberFormatException ex) {
-            }
+        final int density = getForcedDisplayDensityForUserLocked(mCurrentUserId);
+        if (density != 0) {
+            displayContent.mBaseDisplayDensity = density;
         }
 
         // Display scaling mode.
@@ -8470,8 +8459,9 @@
                 final DisplayContent displayContent = getDisplayContentLocked(displayId);
                 if (displayContent != null) {
                     setForcedDisplayDensityLocked(displayContent, density);
-                    Settings.Global.putString(mContext.getContentResolver(),
-                            Settings.Global.DISPLAY_DENSITY_FORCED, Integer.toString(density));
+                    Settings.Secure.putStringForUser(mContext.getContentResolver(),
+                            Settings.Secure.DISPLAY_DENSITY_FORCED,
+                            Integer.toString(density), mCurrentUserId);
                 }
             }
         } finally {
@@ -8479,13 +8469,6 @@
         }
     }
 
-    // displayContent must not be null
-    private void setForcedDisplayDensityLocked(DisplayContent displayContent, int density) {
-        Slog.i(TAG_WM, "Using new display density: " + density);
-        displayContent.mBaseDisplayDensity = density;
-        reconfigureDisplayLocked(displayContent);
-    }
-
     @Override
     public void clearForcedDisplayDensity(int displayId) {
         if (mContext.checkCallingOrSelfPermission(
@@ -8504,8 +8487,8 @@
                 if (displayContent != null) {
                     setForcedDisplayDensityLocked(displayContent,
                             displayContent.mInitialDisplayDensity);
-                    Settings.Global.putString(mContext.getContentResolver(),
-                            Settings.Global.DISPLAY_DENSITY_FORCED, "");
+                    Settings.Secure.putStringForUser(mContext.getContentResolver(),
+                            Settings.Secure.DISPLAY_DENSITY_FORCED, "", mCurrentUserId);
                 }
             }
         } finally {
@@ -8513,6 +8496,38 @@
         }
     }
 
+    /**
+     * @param userId the ID of the user
+     * @return the forced display density for the specified user, if set, or
+     *         {@code 0} if not set
+     */
+    private int getForcedDisplayDensityForUserLocked(int userId) {
+        String densityStr = Settings.Secure.getStringForUser(mContext.getContentResolver(),
+                Settings.Secure.DISPLAY_DENSITY_FORCED, userId);
+        if (densityStr == null || densityStr.length() == 0) {
+            densityStr = SystemProperties.get(DENSITY_OVERRIDE, null);
+        }
+        if (densityStr != null && densityStr.length() > 0) {
+            try {
+                return Integer.parseInt(densityStr);
+            } catch (NumberFormatException ex) {
+            }
+        }
+        return 0;
+    }
+
+    /**
+     * Forces the given display to the use the specified density.
+     *
+     * @param displayContent the display to modify
+     * @param density the density in DPI to use
+     */
+    private void setForcedDisplayDensityLocked(@NonNull DisplayContent displayContent,
+            int density) {
+        displayContent.mBaseDisplayDensity = density;
+        reconfigureDisplayLocked(displayContent);
+    }
+
     // displayContent must not be null
     private void reconfigureDisplayLocked(DisplayContent displayContent) {
         // TODO: Multidisplay: for now only use with default display.
@@ -10394,8 +10409,28 @@
     @Override
     public void getStableInsets(Rect outInsets) throws RemoteException {
         synchronized (mWindowMap) {
+            getStableInsetsLocked(outInsets);
+        }
+    }
+
+    private void getStableInsetsLocked(Rect outInsets) {
+        final DisplayInfo di = getDefaultDisplayInfoLocked();
+        mPolicy.getStableInsetsLw(di.rotation, di.logicalWidth, di.logicalHeight, outInsets);
+    }
+
+    /**
+     * Intersects the specified {@code inOutBounds} with the display frame that excludes the stable
+     * inset areas.
+     *
+     * @param inOutBounds The inOutBounds to subtract the stable inset areas from.
+     */
+    public void subtractStableInsets(Rect inOutBounds) {
+        synchronized (mWindowMap) {
+            getStableInsetsLocked(mTmpRect2);
             final DisplayInfo di = getDefaultDisplayInfoLocked();
-            mPolicy.getStableInsetsLw(di.rotation, di.logicalWidth, di.logicalHeight, outInsets);
+            mTmpRect.set(0, 0, di.logicalWidth, di.logicalHeight);
+            mTmpRect.inset(mTmpRect2);
+            inOutBounds.intersect(mTmpRect);
         }
     }
 
@@ -10599,5 +10634,12 @@
                 return WindowManagerService.this.isStackVisibleLocked(stackId);
             }
         }
+
+        @Override
+        public boolean isDockedDividerResizing() {
+            synchronized (mWindowMap) {
+                return getDefaultDisplayContentLocked().getDockedDividerController().isResizing();
+            }
+        }
     }
 }
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 6dd0d29..880514c 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -16,8 +16,6 @@
 
 package com.android.server.wm;
 
-import com.android.server.input.InputWindowHandle;
-
 import android.app.ActivityManager;
 import android.app.AppOpsManager;
 import android.content.Context;
@@ -53,10 +51,13 @@
 import android.view.WindowManager;
 import android.view.WindowManagerPolicy;
 
+import com.android.server.input.InputWindowHandle;
+
 import java.io.PrintWriter;
 import java.util.ArrayList;
 
 import static android.app.ActivityManager.StackId;
+import static android.app.ActivityManager.StackId.DOCKED_STACK_ID;
 import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
 import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
 import static android.view.ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_CONTENT;
@@ -75,8 +76,8 @@
 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_LAYOUT_CHILD_WINDOW_IN_PARENT_FRAME;
 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_WILL_NOT_REPLACE_ON_RELAUNCH;
 import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE;
 import static android.view.WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST;
@@ -1611,6 +1612,14 @@
                             win.mAppToken.appDied = true;
                         }
                         mService.removeWindowLocked(win);
+                        if (win.mAttrs.type == TYPE_DOCK_DIVIDER) {
+                            // The owner of the docked divider died :( We reset the docked stack,
+                            // just in case they have the divider at an unstable position.
+                            final TaskStack stack = mService.mStackIdToStack.get(DOCKED_STACK_ID);
+                            if (stack != null) {
+                                stack.resetDockedStackToMiddle();
+                            }
+                        }
                     } else if (mHasSurface) {
                         Slog.e(TAG, "!!! LEAK !!! Window removed but surface still valid.");
                         mService.removeWindowLocked(WindowState.this);
@@ -2136,7 +2145,8 @@
         // background.
         return (mDisplayContent.mDividerControllerLocked.isResizing()
                         || mAppToken != null && !mAppToken.mFrozenBounds.isEmpty()) &&
-                !task.inFreeformWorkspace() && !task.isFullscreen();
+                !task.inFreeformWorkspace();
+
     }
 
     void setDragResizing() {
@@ -2330,6 +2340,12 @@
         if (mDrawLock != null) {
             pw.print(prefix); pw.println("mDrawLock=" + mDrawLock);
         }
+        if (isDragResizing()) {
+            pw.print(prefix); pw.println("isDragResizing=" + isDragResizing());
+        }
+        if (computeDragResizing()) {
+            pw.print(prefix); pw.println("computeDragResizing=" + computeDragResizing());
+        }
     }
 
     String makeInputChannelName() {
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 908d2f0..f296d68 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -4132,7 +4132,7 @@
     public void choosePrivateKeyAlias(final int uid, final Uri uri, final String alias,
             final IBinder response) {
         // Caller UID needs to be trusted, so we restrict this method to SYSTEM_UID callers.
-        if (!UserHandle.isSameApp(mInjector.binderGetCallingUid(), Process.SYSTEM_UID)) {
+        if (!isCallerWithSystemUid()) {
             return;
         }
 
@@ -5860,8 +5860,7 @@
         }
         mContext.enforceCallingOrSelfPermission(
                 android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS, null);
-        if (hasUserSetupCompleted(userHandle)
-                && !UserHandle.isSameApp(callingUid, Process.SYSTEM_UID)) {
+        if (hasUserSetupCompleted(userHandle) && !isCallerWithSystemUid()) {
             throw new IllegalStateException("Cannot set the profile owner on a user which is "
                     + "already set-up");
         }
@@ -5921,8 +5920,7 @@
 
     private void enforceManageUsers() {
         final int callingUid = mInjector.binderGetCallingUid();
-        if (!(UserHandle.isSameApp(callingUid, Process.SYSTEM_UID)
-                || callingUid == Process.ROOT_UID)) {
+        if (!(isCallerWithSystemUid() || callingUid == Process.ROOT_UID)) {
             mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USERS, null);
         }
     }
@@ -5945,8 +5943,7 @@
         if (userHandle == UserHandle.getUserId(callingUid)) {
             return;
         }
-        if (!(UserHandle.isSameApp(callingUid, Process.SYSTEM_UID)
-                || callingUid == Process.ROOT_UID)) {
+        if (!(isCallerWithSystemUid() || callingUid == Process.ROOT_UID)) {
             mContext.enforceCallingOrSelfPermission(permission,
                     "Must be system or have " + permission + " permission");
         }
@@ -5964,6 +5961,10 @@
         }
     }
 
+    private boolean isCallerWithSystemUid() {
+        return UserHandle.isSameApp(mInjector.binderGetCallingUid(), Process.SYSTEM_UID);
+    }
+
     private int getProfileParentId(int userHandle) {
         final long ident = mInjector.binderClearCallingIdentity();
         try {
@@ -6248,7 +6249,7 @@
     @Override
     public ComponentName getRestrictionsProvider(int userHandle) {
         synchronized (this) {
-            if (!UserHandle.isSameApp(mInjector.binderGetCallingUid(), Process.SYSTEM_UID)) {
+            if (!isCallerWithSystemUid()) {
                 throw new SecurityException("Only the system can query the permission provider");
             }
             DevicePolicyData userData = getUserData(userHandle);
@@ -6321,8 +6322,7 @@
      * permittedList or are a system app.
      */
     private boolean checkPackagesInPermittedListOrSystem(List<String> enabledPackages,
-            List<String> permittedList) {
-        int userIdToCheck = UserHandle.getCallingUserId();
+            List<String> permittedList, int userIdToCheck) {
         long id = mInjector.binderClearCallingIdentity();
         try {
             // If we have an enabled packages list for a managed profile the packages
@@ -6389,7 +6389,8 @@
                 for (AccessibilityServiceInfo service : enabledServices) {
                     enabledPackages.add(service.getResolveInfo().serviceInfo.packageName);
                 }
-                if (!checkPackagesInPermittedListOrSystem(enabledPackages, packageList)) {
+                if (!checkPackagesInPermittedListOrSystem(enabledPackages, packageList,
+                        userId)) {
                     Slog.e(LOG_TAG, "Cannot set permitted accessibility services, "
                             + "because it contains already enabled accesibility services.");
                     return false;
@@ -6481,6 +6482,28 @@
         }
     }
 
+    @Override
+    public boolean isAccessibilityServicePermittedByAdmin(ComponentName who, String packageName,
+            int userHandle) {
+        if (!mHasFeature) {
+            return true;
+        }
+        Preconditions.checkNotNull(who, "ComponentName is null");
+        Preconditions.checkStringNotEmpty(packageName, "packageName is null");
+        if (!isCallerWithSystemUid()){
+            throw new SecurityException(
+                    "Only the system can query if an accessibility service is disabled by admin");
+        }
+        synchronized (this) {
+            ActiveAdmin admin = getActiveAdminUncheckedLocked(who, userHandle);
+            if (admin.permittedAccessiblityServices == null) {
+                return true;
+            }
+            return checkPackagesInPermittedListOrSystem(Arrays.asList(packageName),
+                    admin.permittedAccessiblityServices, userHandle);
+        }
+    }
+
     private boolean checkCallerIsCurrentUserOrProfile() {
         int callingUserId = UserHandle.getCallingUserId();
         long token = mInjector.binderClearCallingIdentity();
@@ -6536,7 +6559,8 @@
                 for (InputMethodInfo ime : enabledImes) {
                     enabledPackages.add(ime.getPackageName());
                 }
-                if (!checkPackagesInPermittedListOrSystem(enabledPackages, packageList)) {
+                if (!checkPackagesInPermittedListOrSystem(enabledPackages, packageList,
+                        mInjector.binderGetCallingUserHandle().getIdentifier())) {
                     Slog.e(LOG_TAG, "Cannot set permitted input methods, "
                             + "because it contains already enabled input method.");
                     return false;
@@ -6629,6 +6653,28 @@
     }
 
     @Override
+    public boolean isInputMethodPermittedByAdmin(ComponentName who, String packageName,
+            int userHandle) {
+        if (!mHasFeature) {
+            return true;
+        }
+        Preconditions.checkNotNull(who, "ComponentName is null");
+        Preconditions.checkStringNotEmpty(packageName, "packageName is null");
+        if (!isCallerWithSystemUid()) {
+            throw new SecurityException(
+                    "Only the system can query if an input method is disabled by admin");
+        }
+        synchronized (this) {
+            ActiveAdmin admin = getActiveAdminUncheckedLocked(who, userHandle);
+            if (admin.permittedInputMethods == null) {
+                return true;
+            }
+            return checkPackagesInPermittedListOrSystem(Arrays.asList(packageName),
+                    admin.permittedInputMethods, userHandle);
+        }
+    }
+
+    @Override
     public UserHandle createUser(ComponentName who, String name) {
         Preconditions.checkNotNull(who, "ComponentName is null");
         synchronized (this) {
@@ -7425,7 +7471,7 @@
 
     @Override
     public void notifyLockTaskModeChanged(boolean isEnabled, String pkg, int userHandle) {
-        if (!UserHandle.isSameApp(mInjector.binderGetCallingUid(), Process.SYSTEM_UID)) {
+        if (!isCallerWithSystemUid()) {
             throw new SecurityException("notifyLockTaskModeChanged can only be called by system");
         }
         synchronized (this) {
@@ -8180,7 +8226,7 @@
             return null;
         }
         Preconditions.checkNotNull(who, "ComponentName is null");
-        if (!UserHandle.isSameApp(mInjector.binderGetCallingUid(), Process.SYSTEM_UID)) {
+        if (!isCallerWithSystemUid()) {
             throw new SecurityException("Only the system can query support message for user");
         }
         synchronized (this) {
@@ -8198,7 +8244,7 @@
             return null;
         }
         Preconditions.checkNotNull(who, "ComponentName is null");
-        if (!UserHandle.isSameApp(mInjector.binderGetCallingUid(), Process.SYSTEM_UID)) {
+        if (!isCallerWithSystemUid()) {
             throw new SecurityException("Only the system can query support message for user");
         }
         synchronized (this) {
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index b64db57..0cf9328 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -496,6 +496,7 @@
         boolean disableNonCoreServices = SystemProperties.getBoolean("config.disable_noncore", false);
         boolean disableNetwork = SystemProperties.getBoolean("config.disable_network", false);
         boolean disableNetworkTime = SystemProperties.getBoolean("config.disable_networktime", false);
+        boolean disableRtt = SystemProperties.getBoolean("config.disable_rtt", false);
         boolean isEmulator = SystemProperties.get("ro.kernel.qemu").equals("1");
 
         try {
@@ -788,7 +789,9 @@
                 mSystemServiceManager.startService(
                             "com.android.server.wifi.WifiScanningService");
 
-                mSystemServiceManager.startService("com.android.server.wifi.RttService");
+                if (!disableRtt) {
+                    mSystemServiceManager.startService("com.android.server.wifi.RttService");
+                }
 
                 if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_ETHERNET) ||
                     mPackageManager.hasSystemFeature(PackageManager.FEATURE_USB_HOST)) {
@@ -1088,7 +1091,9 @@
 
                 mSystemServiceManager.startService(TrustManagerService.class);
 
-                mSystemServiceManager.startService(FingerprintService.class);
+                if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_FINGERPRINT)) {
+                    mSystemServiceManager.startService(FingerprintService.class);
+                }
 
                 traceBeginAndSlog("StartBackgroundDexOptService");
                 try {
diff --git a/services/tests/servicestests/Android.mk b/services/tests/servicestests/Android.mk
index 25cb64c..071ec1b0 100644
--- a/services/tests/servicestests/Android.mk
+++ b/services/tests/servicestests/Android.mk
@@ -15,6 +15,7 @@
     services.core \
     services.devicepolicy \
     services.net \
+    services.usage \
     easymocklib \
     guava \
     android-support-test \
@@ -26,7 +27,9 @@
 
 LOCAL_CERTIFICATE := platform
 
-LOCAL_JNI_SHARED_LIBRARIES := libapfjni
+LOCAL_JNI_SHARED_LIBRARIES := \
+    libapfjni \
+    libnativehelper
 
 include $(BUILD_PACKAGE)
 
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DpmTestBase.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DpmTestBase.java
index 3dc1a9a..53ca45d 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DpmTestBase.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DpmTestBase.java
@@ -29,6 +29,7 @@
 import java.io.File;
 import java.util.List;
 
+import static org.mockito.Matchers.anyInt;
 import static org.mockito.Matchers.eq;
 import static org.mockito.Mockito.doReturn;
 
@@ -135,8 +136,7 @@
 
         doReturn(realResolveInfo).when(mMockContext.packageManager).queryBroadcastReceiversAsUser(
                 MockUtils.checkIntentComponent(admin),
-                eq(PackageManager.GET_META_DATA
-                        | PackageManager.GET_DISABLED_UNTIL_USED_COMPONENTS),
+                anyInt(),
                 eq(UserHandle.getUserId(packageUid)));
 
         // Set up getPackageInfo().
diff --git a/services/tests/servicestests/src/com/android/server/usage/AppIdleHistoryTests.java b/services/tests/servicestests/src/com/android/server/usage/AppIdleHistoryTests.java
new file mode 100644
index 0000000..9ccb1a6
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/usage/AppIdleHistoryTests.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.usage;
+
+import android.os.FileUtils;
+import android.test.AndroidTestCase;
+
+import java.io.File;
+
+public class AppIdleHistoryTests extends AndroidTestCase {
+
+    File mStorageDir;
+
+    final static String PACKAGE_1 = "com.android.testpackage1";
+    final static String PACKAGE_2 = "com.android.testpackage2";
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        mStorageDir = new File(getContext().getFilesDir(), "appidle");
+        mStorageDir.mkdirs();
+    }
+
+    @Override
+    protected void tearDown() throws Exception {
+        FileUtils.deleteContents(mStorageDir);
+        super.tearDown();
+    }
+
+    public void testFilesCreation() {
+        final int userId = 0;
+        AppIdleHistory aih = new AppIdleHistory(mStorageDir, 0);
+
+        aih.updateDisplayLocked(true, /* elapsedRealtime= */ 1000);
+        aih.updateDisplayLocked(false, /* elapsedRealtime= */ 2000);
+        // Screen On time file should be written right away
+        assertTrue(aih.getScreenOnTimeFile().exists());
+
+        aih.writeAppIdleTimesLocked(userId);
+        // stats file should be written now
+        assertTrue(new File(new File(mStorageDir, "users/" + userId),
+                AppIdleHistory.APP_IDLE_FILENAME).exists());
+    }
+
+    public void testScreenOnTime() {
+        AppIdleHistory aih = new AppIdleHistory(mStorageDir, 1000);
+        aih.updateDisplayLocked(false, 2000);
+        assertEquals(aih.getScreenOnTimeLocked(2000), 0);
+        aih.updateDisplayLocked(true, 3000);
+        assertEquals(aih.getScreenOnTimeLocked(4000), 1000);
+        assertEquals(aih.getScreenOnTimeLocked(5000), 2000);
+        aih.updateDisplayLocked(false, 6000);
+        // Screen on time should not keep progressing with screen is off
+        assertEquals(aih.getScreenOnTimeLocked(7000), 3000);
+        assertEquals(aih.getScreenOnTimeLocked(8000), 3000);
+        aih.writeElapsedTimeLocked();
+
+        // Check if the screen on time is persisted across instantiations
+        AppIdleHistory aih2 = new AppIdleHistory(mStorageDir, 0);
+        assertEquals(aih2.getScreenOnTimeLocked(11000), 3000);
+        aih2.updateDisplayLocked(true, 4000);
+        aih2.updateDisplayLocked(false, 5000);
+        assertEquals(aih2.getScreenOnTimeLocked(13000), 4000);
+    }
+
+    public void testPackageEvents() {
+        AppIdleHistory aih = new AppIdleHistory(mStorageDir, 1000);
+        aih.setThresholds(4000, 1000);
+        aih.updateDisplayLocked(true, 1000);
+        // App is not-idle by default
+        assertFalse(aih.isIdleLocked(PACKAGE_1, 0, 1500));
+        // Still not idle
+        assertFalse(aih.isIdleLocked(PACKAGE_1, 0, 3000));
+        // Idle now
+        assertTrue(aih.isIdleLocked(PACKAGE_1, 0, 8000));
+        // Not idle
+        assertFalse(aih.isIdleLocked(PACKAGE_2, 0, 9000));
+
+        // Screen off
+        aih.updateDisplayLocked(false, 9100);
+        // Still idle after 10 seconds because screen hasn't been on long enough
+        assertFalse(aih.isIdleLocked(PACKAGE_2, 0, 20000));
+        aih.updateDisplayLocked(true, 21000);
+        assertTrue(aih.isIdleLocked(PACKAGE_2, 0, 23000));
+    }
+}
\ No newline at end of file
diff --git a/services/usage/java/com/android/server/usage/AppIdleHistory.java b/services/usage/java/com/android/server/usage/AppIdleHistory.java
index e3c0868..3e2b43d 100644
--- a/services/usage/java/com/android/server/usage/AppIdleHistory.java
+++ b/services/usage/java/com/android/server/usage/AppIdleHistory.java
@@ -16,19 +16,45 @@
 
 package com.android.server.usage;
 
+import android.os.Environment;
+import android.os.SystemClock;
+import android.os.UserHandle;
 import android.util.ArrayMap;
+import android.util.AtomicFile;
+import android.util.Slog;
 import android.util.SparseArray;
+import android.util.TimeUtils;
+import android.util.Xml;
 
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.FastXmlSerializer;
 import com.android.internal.util.IndentingPrintWriter;
 
+import libcore.io.IoUtils;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.BufferedOutputStream;
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.FileReader;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+
 /**
  * Keeps track of recent active state changes in apps.
  * Access should be guarded by a lock by the caller.
  */
 public class AppIdleHistory {
 
-    private SparseArray<ArrayMap<String,byte[]>> mIdleHistory = new SparseArray<>();
-    private long lastPeriod = 0;
+    private static final String TAG = "AppIdleHistory";
+
+    // History for all users and all packages
+    private SparseArray<ArrayMap<String,PackageHistory>> mIdleHistory = new SparseArray<>();
+    private long mLastPeriod = 0;
     private static final long ONE_MINUTE = 60 * 1000;
     private static final int HISTORY_SIZE = 100;
     private static final int FLAG_LAST_STATE = 2;
@@ -36,77 +62,353 @@
     private static final long PERIOD_DURATION = UsageStatsService.COMPRESS_TIME ? ONE_MINUTE
             : 60 * ONE_MINUTE;
 
-    public void addEntry(String packageName, int userId, boolean idle, long timeNow) {
-        ArrayMap<String, byte[]> userHistory = getUserHistory(userId);
-        byte[] packageHistory = getPackageHistory(userHistory, packageName);
+    @VisibleForTesting
+    static final String APP_IDLE_FILENAME = "app_idle_stats.xml";
+    private static final String TAG_PACKAGES = "packages";
+    private static final String TAG_PACKAGE = "package";
+    private static final String ATTR_NAME = "name";
+    // Screen on timebase time when app was last used
+    private static final String ATTR_SCREEN_IDLE = "screenIdleTime";
+    // Elapsed timebase time when app was last used
+    private static final String ATTR_ELAPSED_IDLE = "elapsedIdleTime";
 
-        long thisPeriod = timeNow / PERIOD_DURATION;
+    // device on time = mElapsedDuration + (timeNow - mElapsedSnapshot)
+    private long mElapsedSnapshot; // Elapsed time snapshot when last write of mDeviceOnDuration
+    private long mElapsedDuration; // Total device on duration since device was "born"
+
+    // screen on time = mScreenOnDuration + (timeNow - mScreenOnSnapshot)
+    private long mScreenOnSnapshot; // Elapsed time snapshot when last write of mScreenOnDuration
+    private long mScreenOnDuration; // Total screen on duration since device was "born"
+
+    private long mElapsedTimeThreshold;
+    private long mScreenOnTimeThreshold;
+    private final File mStorageDir;
+
+    private boolean mScreenOn;
+
+    private static class PackageHistory {
+        final byte[] recent = new byte[HISTORY_SIZE];
+        long lastUsedElapsedTime;
+        long lastUsedScreenTime;
+    }
+
+    AppIdleHistory(long elapsedRealtime) {
+        this(Environment.getDataSystemDirectory(), elapsedRealtime);
+    }
+
+    @VisibleForTesting
+    AppIdleHistory(File storageDir, long elapsedRealtime) {
+        mElapsedSnapshot = elapsedRealtime;
+        mScreenOnSnapshot = elapsedRealtime;
+        mStorageDir = storageDir;
+        readScreenOnTimeLocked();
+    }
+
+    public void setThresholds(long elapsedTimeThreshold, long screenOnTimeThreshold) {
+        mElapsedTimeThreshold = elapsedTimeThreshold;
+        mScreenOnTimeThreshold = screenOnTimeThreshold;
+    }
+
+    public void updateDisplayLocked(boolean screenOn, long elapsedRealtime) {
+        if (screenOn == mScreenOn) return;
+
+        mScreenOn = screenOn;
+        if (mScreenOn) {
+            mScreenOnSnapshot = elapsedRealtime;
+        } else {
+            mScreenOnDuration += elapsedRealtime - mScreenOnSnapshot;
+            mElapsedDuration += elapsedRealtime - mElapsedSnapshot;
+            writeScreenOnTimeLocked();
+            mElapsedSnapshot = elapsedRealtime;
+        }
+    }
+
+    public long getScreenOnTimeLocked(long elapsedRealtime) {
+        long screenOnTime = mScreenOnDuration;
+        if (mScreenOn) {
+            screenOnTime += elapsedRealtime - mScreenOnSnapshot;
+        }
+        return screenOnTime;
+    }
+
+    @VisibleForTesting
+    File getScreenOnTimeFile() {
+        return new File(mStorageDir, "screen_on_time");
+    }
+
+    private void readScreenOnTimeLocked() {
+        File screenOnTimeFile = getScreenOnTimeFile();
+        if (screenOnTimeFile.exists()) {
+            try {
+                BufferedReader reader = new BufferedReader(new FileReader(screenOnTimeFile));
+                mScreenOnDuration = Long.parseLong(reader.readLine());
+                mElapsedDuration = Long.parseLong(reader.readLine());
+                reader.close();
+            } catch (IOException | NumberFormatException e) {
+            }
+        } else {
+            writeScreenOnTimeLocked();
+        }
+    }
+
+    private void writeScreenOnTimeLocked() {
+        AtomicFile screenOnTimeFile = new AtomicFile(getScreenOnTimeFile());
+        FileOutputStream fos = null;
+        try {
+            fos = screenOnTimeFile.startWrite();
+            fos.write((Long.toString(mScreenOnDuration) + "\n"
+                    + Long.toString(mElapsedDuration) + "\n").getBytes());
+            screenOnTimeFile.finishWrite(fos);
+        } catch (IOException ioe) {
+            screenOnTimeFile.failWrite(fos);
+        }
+    }
+
+    /**
+     * To be called periodically to keep track of elapsed time when app idle times are written
+     */
+    public void writeElapsedTimeLocked() {
+        final long elapsedRealtime = SystemClock.elapsedRealtime();
+        // Only bump up and snapshot the elapsed time. Don't change screen on duration.
+        mElapsedDuration += elapsedRealtime - mElapsedSnapshot;
+        mElapsedSnapshot = elapsedRealtime;
+        writeScreenOnTimeLocked();
+    }
+
+    public void reportUsageLocked(String packageName, int userId, long elapsedRealtime) {
+        ArrayMap<String, PackageHistory> userHistory = getUserHistoryLocked(userId);
+        PackageHistory packageHistory = getPackageHistoryLocked(userHistory, packageName,
+                elapsedRealtime);
+
+        shiftHistoryToNow(userHistory, elapsedRealtime);
+
+        packageHistory.lastUsedElapsedTime = mElapsedDuration
+                + (elapsedRealtime - mElapsedSnapshot);
+        packageHistory.lastUsedScreenTime = getScreenOnTimeLocked(elapsedRealtime);
+        packageHistory.recent[HISTORY_SIZE - 1] = FLAG_LAST_STATE | FLAG_PARTIAL_ACTIVE;
+    }
+
+    public void setIdle(String packageName, int userId, long elapsedRealtime) {
+        ArrayMap<String, PackageHistory> userHistory = getUserHistoryLocked(userId);
+        PackageHistory packageHistory = getPackageHistoryLocked(userHistory, packageName,
+                elapsedRealtime);
+
+        shiftHistoryToNow(userHistory, elapsedRealtime);
+
+        packageHistory.recent[HISTORY_SIZE - 1] &= ~FLAG_LAST_STATE;
+    }
+
+    private void shiftHistoryToNow(ArrayMap<String, PackageHistory> userHistory,
+            long elapsedRealtime) {
+        long thisPeriod = elapsedRealtime / PERIOD_DURATION;
         // Has the period switched over? Slide all users' package histories
-        if (lastPeriod != 0 && lastPeriod < thisPeriod
-                && (thisPeriod - lastPeriod) < HISTORY_SIZE - 1) {
-            int diff = (int) (thisPeriod - lastPeriod);
+        if (mLastPeriod != 0 && mLastPeriod < thisPeriod
+                && (thisPeriod - mLastPeriod) < HISTORY_SIZE - 1) {
+            int diff = (int) (thisPeriod - mLastPeriod);
             final int NUSERS = mIdleHistory.size();
             for (int u = 0; u < NUSERS; u++) {
                 userHistory = mIdleHistory.valueAt(u);
-                for (byte[] history : userHistory.values()) {
+                for (PackageHistory idleState : userHistory.values()) {
                     // Shift left
-                    System.arraycopy(history, diff, history, 0, HISTORY_SIZE - diff);
+                    System.arraycopy(idleState.recent, diff, idleState.recent, 0,
+                            HISTORY_SIZE - diff);
                     // Replicate last state across the diff
                     for (int i = 0; i < diff; i++) {
-                        history[HISTORY_SIZE - i - 1] =
-                                (byte) (history[HISTORY_SIZE - diff - 1] & FLAG_LAST_STATE);
+                        idleState.recent[HISTORY_SIZE - i - 1] =
+                            (byte) (idleState.recent[HISTORY_SIZE - diff - 1] & FLAG_LAST_STATE);
                     }
                 }
             }
         }
-        lastPeriod = thisPeriod;
-        if (!idle) {
-            packageHistory[HISTORY_SIZE - 1] = FLAG_LAST_STATE | FLAG_PARTIAL_ACTIVE;
-        } else {
-            packageHistory[HISTORY_SIZE - 1] &= ~FLAG_LAST_STATE;
-        }
+        mLastPeriod = thisPeriod;
     }
 
-    private ArrayMap<String, byte[]> getUserHistory(int userId) {
-        ArrayMap<String, byte[]> userHistory = mIdleHistory.get(userId);
+    private ArrayMap<String, PackageHistory> getUserHistoryLocked(int userId) {
+        ArrayMap<String, PackageHistory> userHistory = mIdleHistory.get(userId);
         if (userHistory == null) {
             userHistory = new ArrayMap<>();
             mIdleHistory.put(userId, userHistory);
+            readAppIdleTimesLocked(userId, userHistory);
         }
         return userHistory;
     }
 
-    private byte[] getPackageHistory(ArrayMap<String, byte[]> userHistory, String packageName) {
-        byte[] packageHistory = userHistory.get(packageName);
+    private PackageHistory getPackageHistoryLocked(ArrayMap<String, PackageHistory> userHistory,
+            String packageName, long elapsedRealtime) {
+        PackageHistory packageHistory = userHistory.get(packageName);
         if (packageHistory == null) {
-            packageHistory = new byte[HISTORY_SIZE];
+            packageHistory = new PackageHistory();
+            packageHistory.lastUsedElapsedTime = getElapsedTimeLocked(elapsedRealtime);
+            packageHistory.lastUsedScreenTime = getScreenOnTimeLocked(elapsedRealtime);
             userHistory.put(packageName, packageHistory);
         }
         return packageHistory;
     }
 
-    public void removeUser(int userId) {
+    public void onUserRemoved(int userId) {
         mIdleHistory.remove(userId);
     }
 
-    public boolean isIdle(int userId, String packageName) {
-        ArrayMap<String, byte[]> userHistory = getUserHistory(userId);
-        byte[] packageHistory = getPackageHistory(userHistory, packageName);
-        return (packageHistory[HISTORY_SIZE - 1] & FLAG_LAST_STATE) == 0;
+    public boolean isIdleLocked(String packageName, int userId, long elapsedRealtime) {
+        ArrayMap<String, PackageHistory> userHistory = getUserHistoryLocked(userId);
+        PackageHistory packageHistory =
+                getPackageHistoryLocked(userHistory, packageName, elapsedRealtime);
+        if (packageHistory == null) {
+            return false; // Default to not idle
+        } else {
+            return hasPassedThresholdsLocked(packageHistory, elapsedRealtime);
+        }
+    }
+
+    private long getElapsedTimeLocked(long elapsedRealtime) {
+        return (elapsedRealtime - mElapsedSnapshot + mElapsedDuration);
+    }
+
+    public void setIdleLocked(String packageName, int userId, boolean idle, long elapsedRealtime) {
+        ArrayMap<String, PackageHistory> userHistory = getUserHistoryLocked(userId);
+        PackageHistory packageHistory = getPackageHistoryLocked(userHistory, packageName,
+                elapsedRealtime);
+        packageHistory.lastUsedElapsedTime = getElapsedTimeLocked(elapsedRealtime)
+                - mElapsedTimeThreshold;
+        packageHistory.lastUsedScreenTime = getScreenOnTimeLocked(elapsedRealtime)
+                - (idle ? mScreenOnTimeThreshold : 0) - 1000 /* just a second more */;
+    }
+
+    private boolean hasPassedThresholdsLocked(PackageHistory packageHistory, long elapsedRealtime) {
+        return (packageHistory.lastUsedScreenTime
+                    <= getScreenOnTimeLocked(elapsedRealtime) - mScreenOnTimeThreshold)
+                && (packageHistory.lastUsedElapsedTime
+                        <= getElapsedTimeLocked(elapsedRealtime) - mElapsedTimeThreshold);
+    }
+
+    private File getUserFile(int userId) {
+        return new File(new File(new File(mStorageDir, "users"),
+                Integer.toString(userId)), APP_IDLE_FILENAME);
+    }
+
+    private void readAppIdleTimesLocked(int userId, ArrayMap<String, PackageHistory> userHistory) {
+        FileInputStream fis = null;
+        try {
+            AtomicFile appIdleFile = new AtomicFile(getUserFile(userId));
+            fis = appIdleFile.openRead();
+            XmlPullParser parser = Xml.newPullParser();
+            parser.setInput(fis, StandardCharsets.UTF_8.name());
+
+            int type;
+            while ((type = parser.next()) != XmlPullParser.START_TAG
+                    && type != XmlPullParser.END_DOCUMENT) {
+                // Skip
+            }
+
+            if (type != XmlPullParser.START_TAG) {
+                Slog.e(TAG, "Unable to read app idle file for user " + userId);
+                return;
+            }
+            if (!parser.getName().equals(TAG_PACKAGES)) {
+                return;
+            }
+            while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) {
+                if (type == XmlPullParser.START_TAG) {
+                    final String name = parser.getName();
+                    if (name.equals(TAG_PACKAGE)) {
+                        final String packageName = parser.getAttributeValue(null, ATTR_NAME);
+                        PackageHistory packageHistory = new PackageHistory();
+                        packageHistory.lastUsedElapsedTime =
+                                Long.parseLong(parser.getAttributeValue(null, ATTR_ELAPSED_IDLE));
+                        packageHistory.lastUsedScreenTime =
+                                Long.parseLong(parser.getAttributeValue(null, ATTR_SCREEN_IDLE));
+                        userHistory.put(packageName, packageHistory);
+                    }
+                }
+            }
+        } catch (IOException | XmlPullParserException e) {
+            Slog.e(TAG, "Unable to read app idle file for user " + userId);
+        } finally {
+            IoUtils.closeQuietly(fis);
+        }
+    }
+
+    public void writeAppIdleTimesLocked(int userId) {
+        FileOutputStream fos = null;
+        AtomicFile appIdleFile = new AtomicFile(getUserFile(userId));
+        try {
+            fos = appIdleFile.startWrite();
+            final BufferedOutputStream bos = new BufferedOutputStream(fos);
+
+            FastXmlSerializer xml = new FastXmlSerializer();
+            xml.setOutput(bos, StandardCharsets.UTF_8.name());
+            xml.startDocument(null, true);
+            xml.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);
+
+            xml.startTag(null, TAG_PACKAGES);
+
+            ArrayMap<String,PackageHistory> userHistory = getUserHistoryLocked(userId);
+            final int N = userHistory.size();
+            for (int i = 0; i < N; i++) {
+                String packageName = userHistory.keyAt(i);
+                PackageHistory history = userHistory.valueAt(i);
+                xml.startTag(null, TAG_PACKAGE);
+                xml.attribute(null, ATTR_NAME, packageName);
+                xml.attribute(null, ATTR_ELAPSED_IDLE,
+                        Long.toString(history.lastUsedElapsedTime));
+                xml.attribute(null, ATTR_SCREEN_IDLE,
+                        Long.toString(history.lastUsedScreenTime));
+                xml.endTag(null, TAG_PACKAGE);
+            }
+
+            xml.endTag(null, TAG_PACKAGES);
+            xml.endDocument();
+            appIdleFile.finishWrite(fos);
+        } catch (Exception e) {
+            appIdleFile.failWrite(fos);
+            Slog.e(TAG, "Error writing app idle file for user " + userId);
+        }
     }
 
     public void dump(IndentingPrintWriter idpw, int userId) {
-        ArrayMap<String, byte[]> userHistory = mIdleHistory.get(userId);
+        idpw.println("Package idle stats:");
+        idpw.increaseIndent();
+        ArrayMap<String, PackageHistory> userHistory = mIdleHistory.get(userId);
+        final long elapsedRealtime = SystemClock.elapsedRealtime();
+        final long totalElapsedTime = getElapsedTimeLocked(elapsedRealtime);
+        final long screenOnTime = getScreenOnTimeLocked(elapsedRealtime);
         if (userHistory == null) return;
         final int P = userHistory.size();
         for (int p = 0; p < P; p++) {
             final String packageName = userHistory.keyAt(p);
-            final byte[] history = userHistory.valueAt(p);
+            final PackageHistory packageHistory = userHistory.valueAt(p);
+            idpw.print("package=" + packageName);
+            idpw.print(" lastUsedElapsed=");
+            TimeUtils.formatDuration(totalElapsedTime - packageHistory.lastUsedElapsedTime, idpw);
+            idpw.print(" lastUsedScreenOn=");
+            TimeUtils.formatDuration(screenOnTime - packageHistory.lastUsedScreenTime, idpw);
+            idpw.print(" idle=" + (isIdleLocked(packageName, userId, elapsedRealtime) ? "y" : "n"));
+            idpw.println();
+        }
+        idpw.println();
+        idpw.print("totalElapsedTime=");
+        TimeUtils.formatDuration(getElapsedTimeLocked(elapsedRealtime), idpw);
+        idpw.println();
+        idpw.print("totalScreenOnTime=");
+        TimeUtils.formatDuration(getScreenOnTimeLocked(elapsedRealtime), idpw);
+        idpw.println();
+        idpw.decreaseIndent();
+    }
+
+    public void dumpHistory(IndentingPrintWriter idpw, int userId) {
+        ArrayMap<String, PackageHistory> userHistory = mIdleHistory.get(userId);
+        final long elapsedRealtime = SystemClock.elapsedRealtime();
+        if (userHistory == null) return;
+        final int P = userHistory.size();
+        for (int p = 0; p < P; p++) {
+            final String packageName = userHistory.keyAt(p);
+            final byte[] history = userHistory.valueAt(p).recent;
             for (int i = 0; i < HISTORY_SIZE; i++) {
                 idpw.print(history[i] == 0 ? '.' : 'A');
             }
+            idpw.print(" idle=" + (isIdleLocked(packageName, userId, elapsedRealtime) ? "y" : "n"));
             idpw.print("  " + packageName);
             idpw.println();
         }
     }
-}
\ No newline at end of file
+}
diff --git a/services/usage/java/com/android/server/usage/IntervalStats.java b/services/usage/java/com/android/server/usage/IntervalStats.java
index 7f379fe..f541f70 100644
--- a/services/usage/java/com/android/server/usage/IntervalStats.java
+++ b/services/usage/java/com/android/server/usage/IntervalStats.java
@@ -48,7 +48,6 @@
             usageStats.mPackageName = getCachedStringRef(packageName);
             usageStats.mBeginTimeStamp = beginTime;
             usageStats.mEndTimeStamp = endTime;
-            usageStats.mBeginIdleTime = 0;
             packageStats.put(usageStats.mPackageName, usageStats);
         }
         return usageStats;
@@ -113,7 +112,6 @@
         if (eventType != UsageEvents.Event.SYSTEM_INTERACTION) {
             usageStats.mLastTimeUsed = timeStamp;
         }
-        usageStats.mLastTimeSystemUsed = timeStamp;
         usageStats.mEndTimeStamp = timeStamp;
 
         if (eventType == UsageEvents.Event.MOVE_TO_FOREGROUND) {
@@ -123,22 +121,6 @@
         endTime = timeStamp;
     }
 
-    /**
-     * Updates the last active time for the package. The timestamp uses a timebase that
-     * tracks the device usage time.
-     * @param packageName
-     * @param timeStamp
-     */
-    void updateBeginIdleTime(String packageName, long timeStamp) {
-        UsageStats usageStats = getOrCreateUsageStats(packageName);
-        usageStats.mBeginIdleTime = timeStamp;
-    }
-
-    void updateSystemLastUsedTime(String packageName, long lastUsedTime) {
-        UsageStats usageStats = getOrCreateUsageStats(packageName);
-        usageStats.mLastTimeSystemUsed = lastUsedTime;
-    }
-
     void updateConfigurationStats(Configuration config, long timeStamp) {
         if (activeConfiguration != null) {
             ConfigurationStats activeStats = configurations.get(activeConfiguration);
diff --git a/services/usage/java/com/android/server/usage/UsageStatsService.java b/services/usage/java/com/android/server/usage/UsageStatsService.java
index 77740387..46ad8a10 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsService.java
@@ -40,6 +40,7 @@
 import android.content.pm.PackageManager;
 import android.content.pm.ParceledListSlice;
 import android.content.pm.UserInfo;
+import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.res.Configuration;
 import android.database.ContentObserver;
 import android.hardware.display.DisplayManager;
@@ -62,7 +63,6 @@
 import android.provider.Settings;
 import android.telephony.TelephonyManager;
 import android.util.ArraySet;
-import android.util.AtomicFile;
 import android.util.KeyValueListParser;
 import android.util.Slog;
 import android.util.SparseArray;
@@ -77,12 +77,8 @@
 import com.android.internal.util.IndentingPrintWriter;
 import com.android.server.SystemService;
 
-import java.io.BufferedReader;
 import java.io.File;
 import java.io.FileDescriptor;
-import java.io.FileOutputStream;
-import java.io.FileReader;
-import java.io.IOException;
 import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -106,7 +102,7 @@
     private static final long FLUSH_INTERVAL = COMPRESS_TIME ? TEN_SECONDS : TWENTY_MINUTES;
     private static final long TIME_CHANGE_THRESHOLD_MILLIS = 2 * 1000; // Two seconds.
 
-    long mAppIdleDurationMillis;
+    long mAppIdleScreenThresholdMillis;
     long mCheckIdleIntervalMillis;
     long mAppIdleWallclockThresholdMillis;
     long mAppIdleParoleIntervalMillis;
@@ -147,11 +143,8 @@
 
     private volatile boolean mPendingOneTimeCheckIdleStates;
 
-    long mScreenOnTime;
-    long mLastScreenOnEventRealtime;
-
     @GuardedBy("mLock")
-    private AppIdleHistory mAppIdleHistory = new AppIdleHistory();
+    private AppIdleHistory mAppIdleHistory;
 
     private ArrayList<UsageStatsManagerInternal.AppIdleStateChangeListener>
             mPackageAccessListeners = new ArrayList<>();
@@ -191,8 +184,7 @@
 
         synchronized (mLock) {
             cleanUpRemovedUsersLocked();
-            mLastScreenOnEventRealtime = SystemClock.elapsedRealtime();
-            mScreenOnTime = readScreenOnTimeLocked();
+            mAppIdleHistory = new AppIdleHistory(SystemClock.elapsedRealtime());
         }
 
         mRealTimeSnapshot = SystemClock.elapsedRealtime();
@@ -221,7 +213,7 @@
 
             mDisplayManager.registerDisplayListener(mDisplayListener, mHandler);
             synchronized (mLock) {
-                updateDisplayLocked();
+                mAppIdleHistory.updateDisplayLocked(isDisplayOn(), SystemClock.elapsedRealtime());
             }
 
             if (mPendingOneTimeCheckIdleStates) {
@@ -232,6 +224,11 @@
         }
     }
 
+    private boolean isDisplayOn() {
+        return mDisplayManager
+                .getDisplay(Display.DEFAULT_DISPLAY).getState() == Display.STATE_ON;
+    }
+
     private class UserActionsReceiver extends BroadcastReceiver {
 
         @Override
@@ -274,7 +271,8 @@
         @Override public void onDisplayChanged(int displayId) {
             if (displayId == Display.DEFAULT_DISPLAY) {
                 synchronized (UsageStatsService.this.mLock) {
-                    updateDisplayLocked();
+                    mAppIdleHistory.updateDisplayLocked(isDisplayOn(),
+                            SystemClock.elapsedRealtime());
                 }
             }
         }
@@ -291,8 +289,25 @@
     }
 
     @Override
-    public long getAppIdleRollingWindowDurationMillis() {
-        return mAppIdleWallclockThresholdMillis * 2;
+    public void onNewUpdate(int userId) {
+        initializeDefaultsForSystemApps(userId);
+    }
+
+    private void initializeDefaultsForSystemApps(int userId) {
+        Slog.d(TAG, "Initializing defaults for system apps on user " + userId);
+        final long elapsedRealtime = SystemClock.elapsedRealtime();
+        List<PackageInfo> packages = getContext().getPackageManager().getInstalledPackagesAsUser(
+                PackageManager.MATCH_DISABLED_COMPONENTS
+                | PackageManager.MATCH_UNINSTALLED_PACKAGES,
+                userId);
+        final int packageCount = packages.size();
+        for (int i = 0; i < packageCount; i++) {
+            final PackageInfo pi = packages.get(i);
+            String packageName = pi.packageName;
+            if (pi.applicationInfo != null && pi.applicationInfo.isSystemApp()) {
+                mAppIdleHistory.reportUsageLocked(packageName, userId, elapsedRealtime);
+            }
+        }
     }
 
     private void cleanUpRemovedUsersLocked() {
@@ -350,7 +365,7 @@
         if (timeLeft < 0) {
             timeLeft = 0;
         }
-        mHandler.sendEmptyMessageDelayed(MSG_CHECK_PAROLE_TIMEOUT, timeLeft / 10);
+        mHandler.sendEmptyMessageDelayed(MSG_CHECK_PAROLE_TIMEOUT, timeLeft);
     }
 
     private void postParoleEndTimeout() {
@@ -400,28 +415,27 @@
             return;
         }
 
+        final long elapsedRealtime = SystemClock.elapsedRealtime();
         for (int i = 0; i < userIds.length; i++) {
             final int userId = userIds[i];
             List<PackageInfo> packages =
                     getContext().getPackageManager().getInstalledPackagesAsUser(
-                            PackageManager.GET_DISABLED_COMPONENTS
-                                | PackageManager.GET_UNINSTALLED_PACKAGES,
+                            PackageManager.MATCH_DISABLED_COMPONENTS
+                                | PackageManager.MATCH_UNINSTALLED_PACKAGES,
                             userId);
             synchronized (mLock) {
-                final long timeNow = checkAndGetTimeLocked();
-                final long screenOnTime = getScreenOnTimeLocked();
-                UserUsageStatsService service = getUserDataAndInitializeIfNeededLocked(userId,
-                        timeNow);
                 final int packageCount = packages.size();
                 for (int p = 0; p < packageCount; p++) {
                     final PackageInfo pi = packages.get(p);
                     final String packageName = pi.packageName;
                     final boolean isIdle = isAppIdleFiltered(packageName,
                             UserHandle.getAppId(pi.applicationInfo.uid),
-                            userId, service, timeNow, screenOnTime);
+                            userId, elapsedRealtime);
                     mHandler.sendMessage(mHandler.obtainMessage(MSG_INFORM_LISTENERS,
                             userId, isIdle ? 1 : 0, packageName));
-                    mAppIdleHistory.addEntry(packageName, userId, isIdle, timeNow);
+                    if (isIdle) {
+                        mAppIdleHistory.setIdle(packageName, userId, elapsedRealtime);
+                    }
                 }
             }
         }
@@ -458,62 +472,6 @@
         }
     }
 
-    void updateDisplayLocked() {
-        boolean screenOn = mDisplayManager.getDisplay(Display.DEFAULT_DISPLAY).getState()
-                == Display.STATE_ON;
-
-        if (screenOn == mScreenOn) return;
-
-        mScreenOn = screenOn;
-        long now = SystemClock.elapsedRealtime();
-        if (mScreenOn) {
-            mLastScreenOnEventRealtime = now;
-        } else {
-            mScreenOnTime += now - mLastScreenOnEventRealtime;
-            writeScreenOnTimeLocked(mScreenOnTime);
-        }
-    }
-
-    long getScreenOnTimeLocked() {
-        long screenOnTime = mScreenOnTime;
-        if (mScreenOn) {
-            screenOnTime += SystemClock.elapsedRealtime() - mLastScreenOnEventRealtime;
-        }
-        return screenOnTime;
-    }
-
-    private File getScreenOnTimeFile() {
-        return new File(mUsageStatsDir, UserHandle.USER_SYSTEM + "/screen_on_time");
-    }
-
-    private long readScreenOnTimeLocked() {
-        long screenOnTime = 0;
-        File screenOnTimeFile = getScreenOnTimeFile();
-        if (screenOnTimeFile.exists()) {
-            try {
-                BufferedReader reader = new BufferedReader(new FileReader(screenOnTimeFile));
-                screenOnTime = Long.parseLong(reader.readLine());
-                reader.close();
-            } catch (IOException | NumberFormatException e) {
-            }
-        } else {
-            writeScreenOnTimeLocked(screenOnTime);
-        }
-        return screenOnTime;
-    }
-
-    private void writeScreenOnTimeLocked(long screenOnTime) {
-        AtomicFile screenOnTimeFile = new AtomicFile(getScreenOnTimeFile());
-        FileOutputStream fos = null;
-        try {
-            fos = screenOnTimeFile.startWrite();
-            fos.write(Long.toString(screenOnTime).getBytes());
-            screenOnTimeFile.finishWrite(fos);
-        } catch (IOException ioe) {
-            screenOnTimeFile.failWrite(fos);
-        }
-    }
-
     void onDeviceIdleModeChanged() {
         final boolean deviceIdle = mPowerManager.isDeviceIdleMode();
         if (DEBUG) Slog.i(TAG, "DeviceIdleMode changed to " + deviceIdle);
@@ -549,7 +507,7 @@
         if (service == null) {
             service = new UserUsageStatsService(getContext(), userId,
                     new File(mUsageStatsDir, Integer.toString(userId)), this);
-            service.init(currentTimeMillis, getScreenOnTimeLocked());
+            service.init(currentTimeMillis);
             mUserState.put(userId, service);
         }
         return service;
@@ -569,8 +527,7 @@
             final int userCount = mUserState.size();
             for (int i = 0; i < userCount; i++) {
                 final UserUsageStatsService service = mUserState.valueAt(i);
-                service.onTimeChanged(expectedSystemTime, actualSystemTime, getScreenOnTimeLocked(),
-                        false);
+                service.onTimeChanged(expectedSystemTime, actualSystemTime);
             }
             mRealTimeSnapshot = actualRealtime;
             mSystemTimeSnapshot = actualSystemTime;
@@ -602,26 +559,26 @@
     void reportEvent(UsageEvents.Event event, int userId) {
         synchronized (mLock) {
             final long timeNow = checkAndGetTimeLocked();
-            final long screenOnTime = getScreenOnTimeLocked();
+            final long elapsedRealtime = SystemClock.elapsedRealtime();
             convertToSystemTimeLocked(event);
 
             final UserUsageStatsService service =
                     getUserDataAndInitializeIfNeededLocked(userId, timeNow);
-            final long beginIdleTime = service.getBeginIdleTime(event.mPackage);
-            final long lastUsedTime = service.getSystemLastUsedTime(event.mPackage);
-            final boolean previouslyIdle = hasPassedIdleTimeoutLocked(beginIdleTime,
-                    lastUsedTime, screenOnTime, timeNow);
-            service.reportEvent(event, screenOnTime);
+            // TODO: Ideally this should call isAppIdleFiltered() to avoid calling back
+            // about apps that are on some kind of whitelist anyway.
+            final boolean previouslyIdle = mAppIdleHistory.isIdleLocked(
+                    event.mPackage, userId, elapsedRealtime);
+            service.reportEvent(event);
             // Inform listeners if necessary
             if ((event.mEventType == Event.MOVE_TO_FOREGROUND
                     || event.mEventType == Event.MOVE_TO_BACKGROUND
                     || event.mEventType == Event.SYSTEM_INTERACTION
                     || event.mEventType == Event.USER_INTERACTION)) {
+                mAppIdleHistory.reportUsageLocked(event.mPackage, userId, elapsedRealtime);
                 if (previouslyIdle) {
                     mHandler.sendMessage(mHandler.obtainMessage(MSG_INFORM_LISTENERS, userId,
                             /* idle = */ 0, event.mPackage));
                     notifyBatteryStats(event.mPackage, userId, false);
-                    mAppIdleHistory.addEntry(event.mPackage, userId, false, timeNow);
                 }
             }
         }
@@ -655,28 +612,23 @@
      * the threshold for idle.
      */
     void forceIdleState(String packageName, int userId, boolean idle) {
+        final int appId = getAppId(packageName);
+        if (appId < 0) return;
         synchronized (mLock) {
-            final long timeNow = checkAndGetTimeLocked();
-            final long screenOnTime = getScreenOnTimeLocked();
-            final long deviceUsageTime = screenOnTime - (idle ? mAppIdleDurationMillis : 0) - 5000;
+            final long elapsedRealtime = SystemClock.elapsedRealtime();
 
-            final UserUsageStatsService service =
-                    getUserDataAndInitializeIfNeededLocked(userId, timeNow);
-            final long beginIdleTime = service.getBeginIdleTime(packageName);
-            final long lastUsedTime = service.getSystemLastUsedTime(packageName);
-            final boolean previouslyIdle = hasPassedIdleTimeoutLocked(beginIdleTime,
-                    lastUsedTime, screenOnTime, timeNow);
-            service.setBeginIdleTime(packageName, deviceUsageTime);
-            service.setSystemLastUsedTime(packageName,
-                    timeNow - (idle ? mAppIdleWallclockThresholdMillis : 0) - 5000);
+            final boolean previouslyIdle = isAppIdleFiltered(packageName, appId,
+                    userId, elapsedRealtime);
+            mAppIdleHistory.setIdleLocked(packageName, userId, idle, elapsedRealtime);
+            final boolean stillIdle = isAppIdleFiltered(packageName, appId,
+                    userId, elapsedRealtime);
             // Inform listeners if necessary
-            if (previouslyIdle != idle) {
+            if (previouslyIdle != stillIdle) {
                 mHandler.sendMessage(mHandler.obtainMessage(MSG_INFORM_LISTENERS, userId,
-                        /* idle = */ idle ? 1 : 0, packageName));
-                if (!idle) {
+                        /* idle = */ stillIdle ? 1 : 0, packageName));
+                if (!stillIdle) {
                     notifyBatteryStats(packageName, userId, idle);
                 }
-                mAppIdleHistory.addEntry(packageName, userId, idle, timeNow);
             }
         }
     }
@@ -693,10 +645,11 @@
     /**
      * Called by the Binder stub.
      */
-    void removeUser(int userId) {
+    void onUserRemoved(int userId) {
         synchronized (mLock) {
             Slog.i(TAG, "Removing user " + userId + " and all data.");
             mUserState.remove(userId);
+            mAppIdleHistory.onUserRemoved(userId);
             cleanUpRemovedUsersLocked();
         }
     }
@@ -750,29 +703,12 @@
         }
     }
 
-    private boolean isAppIdleUnfiltered(String packageName, UserUsageStatsService userService,
-            long timeNow, long screenOnTime) {
+    private boolean isAppIdleUnfiltered(String packageName, int userId, long elapsedRealtime) {
         synchronized (mLock) {
-            long beginIdleTime = userService.getBeginIdleTime(packageName);
-            long lastUsedTime = userService.getSystemLastUsedTime(packageName);
-            return hasPassedIdleTimeoutLocked(beginIdleTime, lastUsedTime, screenOnTime,
-                    timeNow);
+            return mAppIdleHistory.isIdleLocked(packageName, userId, elapsedRealtime);
         }
     }
 
-    /**
-     * @param beginIdleTime when the app was last used in device usage timebase
-     * @param lastUsedTime wallclock time of when the app was last used
-     * @param screenOnTime screen-on timebase time
-     * @param currentTime current time in device usage timebase
-     * @return whether it's been used far enough in the past to be considered inactive
-     */
-    boolean hasPassedIdleTimeoutLocked(long beginIdleTime, long lastUsedTime,
-            long screenOnTime, long currentTime) {
-        return (beginIdleTime <= screenOnTime - mAppIdleDurationMillis)
-                && (lastUsedTime <= currentTime - mAppIdleWallclockThresholdMillis);
-    }
-
     void addListener(AppIdleStateChangeListener listener) {
         synchronized (mLock) {
             if (!mPackageAccessListeners.contains(listener)) {
@@ -787,32 +723,22 @@
         }
     }
 
-    boolean isAppIdleFilteredOrParoled(String packageName, int userId, long timeNow) {
+    int getAppId(String packageName) {
+        try {
+            ApplicationInfo ai = getContext().getPackageManager().getApplicationInfo(packageName,
+                    PackageManager.MATCH_UNINSTALLED_PACKAGES
+                            | PackageManager.MATCH_DISABLED_COMPONENTS);
+            return ai.uid;
+        } catch (NameNotFoundException re) {
+            return -1;
+        }
+    }
+
+    boolean isAppIdleFilteredOrParoled(String packageName, int userId, long elapsedRealtime) {
         if (mAppIdleParoled) {
             return false;
         }
-        try {
-            ApplicationInfo ai = getContext().getPackageManager().getApplicationInfo(packageName,
-                    PackageManager.GET_UNINSTALLED_PACKAGES
-                            | PackageManager.GET_DISABLED_COMPONENTS);
-            return isAppIdleFiltered(packageName, ai.uid, userId, timeNow);
-        } catch (PackageManager.NameNotFoundException e) {
-        }
-        return false;
-    }
-
-    boolean isAppIdleFiltered(String packageName, int uidForAppId, int userId, long timeNow) {
-        final UserUsageStatsService userService;
-        final long screenOnTime;
-        synchronized (mLock) {
-            if (timeNow == -1) {
-                timeNow = checkAndGetTimeLocked();
-            }
-            userService = getUserDataAndInitializeIfNeededLocked(userId, timeNow);
-            screenOnTime = getScreenOnTimeLocked();
-        }
-        return isAppIdleFiltered(packageName, UserHandle.getAppId(uidForAppId), userId,
-                userService, timeNow, screenOnTime);
+        return isAppIdleFiltered(packageName, getAppId(packageName), userId, elapsedRealtime);
     }
 
     /**
@@ -822,7 +748,7 @@
      * Called by interface impls.
      */
     private boolean isAppIdleFiltered(String packageName, int appId, int userId,
-            UserUsageStatsService userService, long timeNow, long screenOnTime) {
+            long elapsedRealtime) {
         if (packageName == null) return false;
         // If not enabled at all, of course nobody is ever idle.
         if (!mAppIdleEnabled) {
@@ -864,7 +790,7 @@
             return false;
         }
 
-        return isAppIdleUnfiltered(packageName, userService, timeNow, screenOnTime);
+        return isAppIdleUnfiltered(packageName, userId, elapsedRealtime);
     }
 
     int[] getIdleUidsForUser(int userId) {
@@ -872,14 +798,7 @@
             return new int[0];
         }
 
-        final long timeNow;
-        final UserUsageStatsService userService;
-        final long screenOnTime;
-        synchronized (mLock) {
-            timeNow = checkAndGetTimeLocked();
-            userService = getUserDataAndInitializeIfNeededLocked(userId, timeNow);
-            screenOnTime = getScreenOnTimeLocked();
-        }
+        final long elapsedRealtime = SystemClock.elapsedRealtime();
 
         List<ApplicationInfo> apps;
         try {
@@ -899,12 +818,12 @@
 
         // Now resolve all app state.  Iterating over all apps, keeping track of how many
         // we find for each uid and how many of those are idle.
-        for (int i = apps.size()-1; i >= 0; i--) {
+        for (int i = apps.size() - 1; i >= 0; i--) {
             ApplicationInfo ai = apps.get(i);
 
             // Check whether this app is idle.
             boolean idle = isAppIdleFiltered(ai.packageName, UserHandle.getAppId(ai.uid),
-                    userId, userService, timeNow, screenOnTime);
+                    userId, elapsedRealtime);
 
             int index = uidStates.indexOfKey(ai.uid);
             if (index < 0) {
@@ -990,8 +909,11 @@
         for (int i = 0; i < userCount; i++) {
             UserUsageStatsService service = mUserState.valueAt(i);
             service.persistActiveStats();
+            mAppIdleHistory.writeAppIdleTimesLocked(mUserState.keyAt(i));
         }
-
+        // Persist elapsed time periodically, in case screen doesn't get toggled
+        // until the next boot
+        mAppIdleHistory.writeElapsedTimeLocked();
         mHandler.removeMessages(MSG_FLUSH_TO_DISK);
     }
 
@@ -1000,7 +922,6 @@
      */
     void dump(String[] args, PrintWriter pw) {
         synchronized (mLock) {
-            final long screenOnTime = getScreenOnTimeLocked();
             IndentingPrintWriter idpw = new IndentingPrintWriter(pw, "  ");
             ArraySet<String> argSet = new ArraySet<>();
             argSet.addAll(Arrays.asList(args));
@@ -1011,27 +932,28 @@
                 idpw.println();
                 idpw.increaseIndent();
                 if (argSet.contains("--checkin")) {
-                    mUserState.valueAt(i).checkin(idpw, screenOnTime);
+                    mUserState.valueAt(i).checkin(idpw);
                 } else {
-                    mUserState.valueAt(i).dump(idpw, screenOnTime);
+                    mUserState.valueAt(i).dump(idpw);
                     idpw.println();
-                    if (args.length > 0 && "history".equals(args[0])) {
-                        mAppIdleHistory.dump(idpw, mUserState.keyAt(i));
+                    if (args.length > 0) {
+                        if ("history".equals(args[0])) {
+                            mAppIdleHistory.dumpHistory(idpw, mUserState.keyAt(i));
+                        } else if ("flush".equals(args[0])) {
+                            UsageStatsService.this.flushToDiskLocked();
+                            pw.println("Flushed stats to disk");
+                        }
                     }
                 }
+                mAppIdleHistory.dump(idpw, mUserState.keyAt(i));
                 idpw.decreaseIndent();
             }
-            pw.print("Screen On Timebase: ");
-            pw.print(screenOnTime);
-            pw.print(" (");
-            TimeUtils.formatDuration(screenOnTime, pw);
-            pw.println(")");
 
             pw.println();
             pw.println("Settings:");
 
             pw.print("  mAppIdleDurationMillis=");
-            TimeUtils.formatDuration(mAppIdleDurationMillis, pw);
+            TimeUtils.formatDuration(mAppIdleScreenThresholdMillis, pw);
             pw.println();
 
             pw.print("  mAppIdleWallclockThresholdMillis=");
@@ -1057,11 +979,6 @@
             pw.print("mLastAppIdleParoledTime=");
             TimeUtils.formatDuration(mLastAppIdleParoledTime, pw);
             pw.println();
-            pw.print("mScreenOnTime="); TimeUtils.formatDuration(mScreenOnTime, pw);
-            pw.println();
-            pw.print("mLastScreenOnEventRealtime=");
-            TimeUtils.formatDuration(mLastScreenOnEventRealtime, pw);
-            pw.println();
         }
     }
 
@@ -1082,7 +999,7 @@
                     break;
 
                 case MSG_REMOVE_USER:
-                    removeUser(msg.arg1);
+                    onUserRemoved(msg.arg1);
                     break;
 
                 case MSG_INFORM_LISTENERS:
@@ -1179,13 +1096,13 @@
                 }
 
                 // Default: 12 hours of screen-on time sans dream-time
-                mAppIdleDurationMillis = mParser.getLong(KEY_IDLE_DURATION,
+                mAppIdleScreenThresholdMillis = mParser.getLong(KEY_IDLE_DURATION,
                        COMPRESS_TIME ? ONE_MINUTE * 4 : 12 * 60 * ONE_MINUTE);
 
                 mAppIdleWallclockThresholdMillis = mParser.getLong(KEY_WALLCLOCK_THRESHOLD,
                         COMPRESS_TIME ? ONE_MINUTE * 8 : 2L * 24 * 60 * ONE_MINUTE); // 2 days
 
-                mCheckIdleIntervalMillis = Math.min(mAppIdleDurationMillis / 4,
+                mCheckIdleIntervalMillis = Math.min(mAppIdleScreenThresholdMillis / 4,
                         COMPRESS_TIME ? ONE_MINUTE : 8 * 60 * ONE_MINUTE); // 8 hours
 
                 // Default: 24 hours between paroles
@@ -1194,6 +1111,8 @@
 
                 mAppIdleParoleDurationMillis = mParser.getLong(KEY_PAROLE_DURATION,
                         COMPRESS_TIME ? ONE_MINUTE : 10 * ONE_MINUTE); // 10 minutes
+                mAppIdleHistory.setThresholds(mAppIdleWallclockThresholdMillis,
+                        mAppIdleScreenThresholdMillis);
             }
         }
     }
@@ -1284,7 +1203,8 @@
             }
             final long token = Binder.clearCallingIdentity();
             try {
-                return UsageStatsService.this.isAppIdleFilteredOrParoled(packageName, userId, -1);
+                return UsageStatsService.this.isAppIdleFilteredOrParoled(packageName, userId,
+                        SystemClock.elapsedRealtime());
             } finally {
                 Binder.restoreCallingIdentity(token);
             }
@@ -1304,11 +1224,9 @@
                     "No permission to change app idle state");
             final long token = Binder.clearCallingIdentity();
             try {
-                PackageInfo pi = AppGlobals.getPackageManager().getPackageInfo(packageName,
-                        PackageManager.MATCH_UNINSTALLED_PACKAGES, userId);
-                if (pi == null) return;
+                final int appId = getAppId(packageName);
+                if (appId < 0) return;
                 UsageStatsService.this.setAppIdle(packageName, idle, userId);
-            } catch (RemoteException re) {
             } finally {
                 Binder.restoreCallingIdentity(token);
             }
@@ -1335,8 +1253,6 @@
             }
             UsageStatsService.this.dump(args, pw);
         }
-
-
     }
 
     /**
@@ -1411,7 +1327,8 @@
 
         @Override
         public boolean isAppIdle(String packageName, int uidForAppId, int userId) {
-            return UsageStatsService.this.isAppIdleFiltered(packageName, uidForAppId, userId, -1);
+            return UsageStatsService.this.isAppIdleFiltered(packageName, uidForAppId, userId,
+                    SystemClock.elapsedRealtime());
         }
 
         @Override
diff --git a/services/usage/java/com/android/server/usage/UsageStatsXmlV1.java b/services/usage/java/com/android/server/usage/UsageStatsXmlV1.java
index f2ca3a4..c95ff23 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsXmlV1.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsXmlV1.java
@@ -26,7 +26,6 @@
 import android.app.usage.UsageEvents;
 import android.app.usage.UsageStats;
 import android.content.res.Configuration;
-import android.text.TextUtils;
 
 import java.io.IOException;
 import java.net.ProtocolException;
@@ -55,13 +54,11 @@
 
     // Time attributes stored as an offset of the beginTime.
     private static final String LAST_TIME_ACTIVE_ATTR = "lastTimeActive";
-    private static final String LAST_TIME_ACTIVE_SYSTEM_ATTR = "lastTimeActiveSystem";
-    private static final String BEGIN_IDLE_TIME_ATTR = "beginIdleTime";
     private static final String END_TIME_ATTR = "endTime";
     private static final String TIME_ATTR = "time";
 
     private static void loadUsageStats(XmlPullParser parser, IntervalStats statsOut)
-            throws XmlPullParserException, IOException {
+            throws IOException {
         final String pkg = parser.getAttributeValue(null, PACKAGE_ATTR);
         if (pkg == null) {
             throw new ProtocolException("no " + PACKAGE_ATTR + " attribute present");
@@ -72,20 +69,6 @@
         // Apply the offset to the beginTime to find the absolute time.
         stats.mLastTimeUsed = statsOut.beginTime + XmlUtils.readLongAttribute(
                 parser, LAST_TIME_ACTIVE_ATTR);
-
-        final String lastTimeUsedSystem = parser.getAttributeValue(null,
-                LAST_TIME_ACTIVE_SYSTEM_ATTR);
-        if (TextUtils.isEmpty(lastTimeUsedSystem)) {
-            // If the field isn't present, use the old one.
-            stats.mLastTimeSystemUsed = stats.mLastTimeUsed;
-        } else {
-            stats.mLastTimeSystemUsed = statsOut.beginTime + Long.parseLong(lastTimeUsedSystem);
-        }
-
-        final String beginIdleTime = parser.getAttributeValue(null, BEGIN_IDLE_TIME_ATTR);
-        if (!TextUtils.isEmpty(beginIdleTime)) {
-            stats.mBeginIdleTime = Long.parseLong(beginIdleTime);
-        }
         stats.mTotalTimeInForeground = XmlUtils.readLongAttribute(parser, TOTAL_TIME_ACTIVE_ATTR);
         stats.mLastEvent = XmlUtils.readIntAttribute(parser, LAST_EVENT_ATTR);
     }
@@ -141,13 +124,10 @@
         // Write the time offset.
         XmlUtils.writeLongAttribute(xml, LAST_TIME_ACTIVE_ATTR,
                 usageStats.mLastTimeUsed - stats.beginTime);
-        XmlUtils.writeLongAttribute(xml, LAST_TIME_ACTIVE_SYSTEM_ATTR,
-                usageStats.mLastTimeSystemUsed - stats.beginTime);
 
         XmlUtils.writeStringAttribute(xml, PACKAGE_ATTR, usageStats.mPackageName);
         XmlUtils.writeLongAttribute(xml, TOTAL_TIME_ACTIVE_ATTR, usageStats.mTotalTimeInForeground);
         XmlUtils.writeIntAttribute(xml, LAST_EVENT_ATTR, usageStats.mLastEvent);
-        XmlUtils.writeLongAttribute(xml, BEGIN_IDLE_TIME_ATTR, usageStats.mBeginIdleTime);
 
         xml.endTag(null, PACKAGE_TAG);
     }
@@ -255,7 +235,6 @@
         }
         xml.endTag(null, PACKAGES_TAG);
 
-
         xml.startTag(null, CONFIGURATIONS_TAG);
         final int configCount = stats.configurations.size();
         for (int i = 0; i < configCount; i++) {
diff --git a/services/usage/java/com/android/server/usage/UserUsageStatsService.java b/services/usage/java/com/android/server/usage/UserUsageStatsService.java
index f2045d3..7d003f3 100644
--- a/services/usage/java/com/android/server/usage/UserUsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UserUsageStatsService.java
@@ -59,7 +59,6 @@
     private final Context mContext;
     private final UsageStatsDatabase mDatabase;
     private final IntervalStats[] mCurrentStats;
-    private IntervalStats mAppIdleRollingWindow;
     private boolean mStatsChanged = false;
     private final UnixCalendar mDailyExpiryDate;
     private final StatsUpdatedListener mListener;
@@ -74,7 +73,11 @@
     interface StatsUpdatedListener {
         void onStatsUpdated();
         void onStatsReloaded();
-        long getAppIdleRollingWindowDurationMillis();
+        /**
+         * Callback that a system update was detected
+         * @param mUserId user that needs to be initialized
+         */
+        void onNewUpdate(int mUserId);
     }
 
     UserUsageStatsService(Context context, int userId, File usageStatsDir,
@@ -88,7 +91,7 @@
         mUserId = userId;
     }
 
-    void init(final long currentTimeMillis, final long deviceUsageTime) {
+    void init(final long currentTimeMillis) {
         mDatabase.init(currentTimeMillis);
 
         int nullCount = 0;
@@ -112,7 +115,7 @@
 
             // By calling loadActiveStats, we will
             // generate new stats for each bucket.
-            loadActiveStats(currentTimeMillis, /*resetBeginIdleTime=*/ false);
+            loadActiveStats(currentTimeMillis);
         } else {
             // Set up the expiry date to be one day from the latest daily stat.
             // This may actually be today and we will rollover on the first event
@@ -136,54 +139,18 @@
             stat.updateConfigurationStats(null, stat.lastTimeSaved);
         }
 
-        refreshAppIdleRollingWindow(currentTimeMillis, deviceUsageTime);
-
         if (mDatabase.isNewUpdate()) {
-            initializeDefaultsForApps(currentTimeMillis, deviceUsageTime,
-                    mDatabase.isFirstUpdate());
+            notifyNewUpdate();
         }
     }
 
-    /**
-     * If any of the apps don't have a last-used entry, add one now.
-     * @param currentTimeMillis the current time
-     * @param firstUpdate if it is the first update, touch all installed apps, otherwise only
-     *        touch the system apps
-     */
-    private void initializeDefaultsForApps(long currentTimeMillis, long deviceUsageTime,
-            boolean firstUpdate) {
-        PackageManager pm = mContext.getPackageManager();
-        List<PackageInfo> packages = pm.getInstalledPackagesAsUser(0, mUserId);
-        final int packageCount = packages.size();
-        for (int i = 0; i < packageCount; i++) {
-            final PackageInfo pi = packages.get(i);
-            String packageName = pi.packageName;
-            if (pi.applicationInfo != null && (firstUpdate || pi.applicationInfo.isSystemApp())
-                    && getBeginIdleTime(packageName) == -1) {
-                for (IntervalStats stats : mCurrentStats) {
-                    stats.update(packageName, currentTimeMillis, Event.SYSTEM_INTERACTION);
-                    stats.updateBeginIdleTime(packageName, deviceUsageTime);
-                }
-
-                mAppIdleRollingWindow.update(packageName, currentTimeMillis,
-                        Event.SYSTEM_INTERACTION);
-                mAppIdleRollingWindow.updateBeginIdleTime(packageName, deviceUsageTime);
-                mStatsChanged = true;
-            }
-        }
-        // Persist the new OTA-related access stats.
-        persistActiveStats();
-    }
-
-    void onTimeChanged(long oldTime, long newTime, long deviceUsageTime,
-                       boolean resetBeginIdleTime) {
+    void onTimeChanged(long oldTime, long newTime) {
         persistActiveStats();
         mDatabase.onTimeChanged(newTime - oldTime);
-        loadActiveStats(newTime, resetBeginIdleTime);
-        refreshAppIdleRollingWindow(newTime, deviceUsageTime);
+        loadActiveStats(newTime);
     }
 
-    void reportEvent(UsageEvents.Event event, long deviceUsageTime) {
+    void reportEvent(UsageEvents.Event event) {
         if (DEBUG) {
             Slog.d(TAG, mLogPrefix + "Got usage event for " + event.mPackage
                     + "[" + event.mTimeStamp + "]: "
@@ -192,7 +159,7 @@
 
         if (event.mTimeStamp >= mDailyExpiryDate.getTimeInMillis()) {
             // Need to rollover
-            rolloverStats(event.mTimeStamp, deviceUsageTime);
+            rolloverStats(event.mTimeStamp);
         }
 
         final IntervalStats currentDailyStats = mCurrentStats[UsageStatsManager.INTERVAL_DAILY];
@@ -218,35 +185,9 @@
                 stats.updateConfigurationStats(newFullConfig, event.mTimeStamp);
             } else {
                 stats.update(event.mPackage, event.mTimeStamp, event.mEventType);
-                stats.updateBeginIdleTime(event.mPackage, deviceUsageTime);
             }
         }
 
-        if (event.mEventType != Event.CONFIGURATION_CHANGE) {
-            mAppIdleRollingWindow.update(event.mPackage, event.mTimeStamp, event.mEventType);
-            mAppIdleRollingWindow.updateBeginIdleTime(event.mPackage, deviceUsageTime);
-        }
-
-        notifyStatsChanged();
-    }
-
-    /**
-     * Sets the beginIdleTime for each of the intervals.
-     * @param beginIdleTime
-     */
-    void setBeginIdleTime(String packageName, long beginIdleTime) {
-        for (IntervalStats stats : mCurrentStats) {
-            stats.updateBeginIdleTime(packageName, beginIdleTime);
-        }
-        mAppIdleRollingWindow.updateBeginIdleTime(packageName, beginIdleTime);
-        notifyStatsChanged();
-    }
-
-    void setSystemLastUsedTime(String packageName, long lastUsedTime) {
-        for (IntervalStats stats : mCurrentStats) {
-            stats.updateSystemLastUsedTime(packageName, lastUsedTime);
-        }
-        mAppIdleRollingWindow.updateSystemLastUsedTime(packageName, lastUsedTime);
         notifyStatsChanged();
     }
 
@@ -404,24 +345,6 @@
         return new UsageEvents(results, table);
     }
 
-    long getBeginIdleTime(String packageName) {
-        UsageStats packageUsage;
-        if ((packageUsage = mAppIdleRollingWindow.packageStats.get(packageName)) == null) {
-            return -1;
-        } else {
-            return packageUsage.getBeginIdleTime();
-        }
-    }
-
-    long getSystemLastUsedTime(String packageName) {
-        UsageStats packageUsage;
-        if ((packageUsage = mAppIdleRollingWindow.packageStats.get(packageName)) == null) {
-            return -1;
-        } else {
-            return packageUsage.getLastTimeSystemUsed();
-        }
-    }
-
     void persistActiveStats() {
         if (mStatsChanged) {
             Slog.i(TAG, mLogPrefix + "Flushing usage stats to disk");
@@ -436,7 +359,7 @@
         }
     }
 
-    private void rolloverStats(final long currentTimeMillis, final long deviceUsageTime) {
+    private void rolloverStats(final long currentTimeMillis) {
         final long startTime = SystemClock.elapsedRealtime();
         Slog.i(TAG, mLogPrefix + "Rolling over usage stats");
 
@@ -463,7 +386,7 @@
 
         persistActiveStats();
         mDatabase.prune(currentTimeMillis);
-        loadActiveStats(currentTimeMillis, /*resetBeginIdleTime=*/ false);
+        loadActiveStats(currentTimeMillis);
 
         final int continueCount = continuePreviousDay.size();
         for (int i = 0; i < continueCount; i++) {
@@ -477,8 +400,6 @@
         }
         persistActiveStats();
 
-        refreshAppIdleRollingWindow(currentTimeMillis, deviceUsageTime);
-
         final long totalTime = SystemClock.elapsedRealtime() - startTime;
         Slog.i(TAG, mLogPrefix + "Rolling over usage stats complete. Took " + totalTime
                 + " milliseconds");
@@ -491,7 +412,11 @@
         }
     }
 
-    private void loadActiveStats(final long currentTimeMillis, boolean resetBeginIdleTime) {
+    private void notifyNewUpdate() {
+        mListener.onNewUpdate(mUserId);
+    }
+
+    private void loadActiveStats(final long currentTimeMillis) {
         for (int intervalType = 0; intervalType < mCurrentStats.length; intervalType++) {
             final IntervalStats stats = mDatabase.getLatestUsageStats(intervalType);
             if (stats != null && currentTimeMillis - 500 >= stats.endTime &&
@@ -514,12 +439,6 @@
                 mCurrentStats[intervalType].beginTime = currentTimeMillis;
                 mCurrentStats[intervalType].endTime = currentTimeMillis + 1;
             }
-
-            if (resetBeginIdleTime) {
-                for (UsageStats usageStats : mCurrentStats[intervalType].packageStats.values()) {
-                    usageStats.mBeginIdleTime = 0;
-                }
-            }
         }
 
         mStatsChanged = false;
@@ -538,96 +457,28 @@
                 mDailyExpiryDate.getTimeInMillis() + ")");
     }
 
-    private static void mergePackageStats(IntervalStats dst, IntervalStats src,
-                                          final long deviceUsageTime) {
-        dst.endTime = Math.max(dst.endTime, src.endTime);
-
-        final int srcPackageCount = src.packageStats.size();
-        for (int i = 0; i < srcPackageCount; i++) {
-            final String packageName = src.packageStats.keyAt(i);
-            final UsageStats srcStats = src.packageStats.valueAt(i);
-            UsageStats dstStats = dst.packageStats.get(packageName);
-            if (dstStats == null) {
-                dstStats = new UsageStats(srcStats);
-                dst.packageStats.put(packageName, dstStats);
-            } else {
-                dstStats.add(src.packageStats.valueAt(i));
-            }
-
-            // App idle times can not begin in the future. This happens if we had a time change.
-            if (dstStats.mBeginIdleTime > deviceUsageTime) {
-                dstStats.mBeginIdleTime = deviceUsageTime;
-            }
-        }
-    }
-
-    /**
-     * App idle operates on a rolling window of time. When we roll over time, we end up with a
-     * period of time where in-memory stats are empty and we don't hit the disk for older stats
-     * for performance reasons. Suddenly all apps will become idle.
-     *
-     * Instead, at times we do a deep query to find all the apps that have run in the past few
-     * days and keep the cached data up to date.
-     *
-     * @param currentTimeMillis
-     */
-    void refreshAppIdleRollingWindow(final long currentTimeMillis, final long deviceUsageTime) {
-        // Start the rolling window for AppIdle requests.
-        final long startRangeMillis = currentTimeMillis -
-                mListener.getAppIdleRollingWindowDurationMillis();
-
-        List<IntervalStats> stats = mDatabase.queryUsageStats(UsageStatsManager.INTERVAL_DAILY,
-                startRangeMillis, currentTimeMillis, new StatCombiner<IntervalStats>() {
-                    @Override
-                    public void combine(IntervalStats stats, boolean mutable,
-                                        List<IntervalStats> accumulatedResult) {
-                        IntervalStats accum;
-                        if (accumulatedResult.isEmpty()) {
-                            accum = new IntervalStats();
-                            accum.beginTime = stats.beginTime;
-                            accumulatedResult.add(accum);
-                        } else {
-                            accum = accumulatedResult.get(0);
-                        }
-
-                        mergePackageStats(accum, stats, deviceUsageTime);
-                    }
-                });
-
-        if (stats == null || stats.isEmpty()) {
-            mAppIdleRollingWindow = new IntervalStats();
-            mergePackageStats(mAppIdleRollingWindow,
-                    mCurrentStats[UsageStatsManager.INTERVAL_YEARLY], deviceUsageTime);
-        } else {
-            mAppIdleRollingWindow = stats.get(0);
-        }
-    }
-
     //
     // -- DUMP related methods --
     //
 
-    void checkin(final IndentingPrintWriter pw, final long screenOnTime) {
+    void checkin(final IndentingPrintWriter pw) {
         mDatabase.checkinDailyFiles(new UsageStatsDatabase.CheckinAction() {
             @Override
             public boolean checkin(IntervalStats stats) {
-                printIntervalStats(pw, stats, screenOnTime, false);
+                printIntervalStats(pw, stats, false);
                 return true;
             }
         });
     }
 
-    void dump(IndentingPrintWriter pw, final long screenOnTime) {
+    void dump(IndentingPrintWriter pw) {
         // This is not a check-in, only dump in-memory stats.
         for (int interval = 0; interval < mCurrentStats.length; interval++) {
             pw.print("In-memory ");
             pw.print(intervalToString(interval));
             pw.println(" stats");
-            printIntervalStats(pw, mCurrentStats[interval], screenOnTime, true);
+            printIntervalStats(pw, mCurrentStats[interval], true);
         }
-
-        pw.println("AppIdleRollingWindow cache");
-        printIntervalStats(pw, mAppIdleRollingWindow, screenOnTime, true);
     }
 
     private String formatDateTime(long dateTime, boolean pretty) {
@@ -644,7 +495,7 @@
         return Long.toString(elapsedTime);
     }
 
-    void printIntervalStats(IndentingPrintWriter pw, IntervalStats stats, long screenOnTime,
+    void printIntervalStats(IndentingPrintWriter pw, IntervalStats stats,
             boolean prettyDates) {
         if (prettyDates) {
             pw.printPair("timeRange", "\"" + DateUtils.formatDateRange(mContext,
@@ -665,10 +516,6 @@
             pw.printPair("totalTime",
                     formatElapsedTime(usageStats.mTotalTimeInForeground, prettyDates));
             pw.printPair("lastTime", formatDateTime(usageStats.mLastTimeUsed, prettyDates));
-            pw.printPair("lastTimeSystem",
-                    formatDateTime(usageStats.mLastTimeSystemUsed, prettyDates));
-            pw.printPair("inactiveTime",
-                    formatElapsedTime(screenOnTime - usageStats.mBeginIdleTime, prettyDates));
             pw.println();
         }
         pw.decreaseIndent();
diff --git a/services/usb/java/com/android/server/usb/MtpNotificationManager.java b/services/usb/java/com/android/server/usb/MtpNotificationManager.java
index 203d35e..17039bb 100644
--- a/services/usb/java/com/android/server/usb/MtpNotificationManager.java
+++ b/services/usb/java/com/android/server/usb/MtpNotificationManager.java
@@ -30,7 +30,6 @@
 import android.hardware.usb.UsbInterface;
 import android.hardware.usb.UsbManager;
 import android.os.UserHandle;
-import android.util.Log;
 
 /**
  * Manager for MTP storage notification.
diff --git a/telecomm/java/android/telecom/TelecomManager.java b/telecomm/java/android/telecom/TelecomManager.java
index 72ff272..c122c5a 100644
--- a/telecomm/java/android/telecom/TelecomManager.java
+++ b/telecomm/java/android/telecom/TelecomManager.java
@@ -1414,6 +1414,23 @@
         return result;
     }
 
+    /**
+     * Launches the {@link android.app.Activity} to manage blocked numbers.
+     * <p> This method displays the UI to manage blocked numbers only if
+     * {@link android.provider.BlockedNumberContract#canCurrentUserBlockNumbers(Context)} returns
+     * {@code true} for the current user.
+     */
+    public void launchManageBlockedNumbersActivity() {
+        ITelecomService service = getTelecomService();
+        if (service != null) {
+            try {
+                service.launchManageBlockedNumbersActivity(mContext.getPackageName());
+            } catch (RemoteException e) {
+                Log.e(TAG, "Error calling ITelecomService#manageBlockedNumbers", e);
+            }
+        }
+    }
+
     private ITelecomService getTelecomService() {
         if (mTelecomServiceOverride != null) {
             return mTelecomServiceOverride;
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 4368b81..3ad7d34 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -591,6 +591,14 @@
     @SystemApi
     public static final String KEY_USE_RCS_PRESENCE_BOOL = "use_rcs_presence_bool";
 
+    /**
+     * The duration in seconds that platform call and message blocking is disabled after the user
+     * contacts emergency services. Platform considers values in the range 0 to 604800 (one week) as
+     * valid. See {@link android.provider.BlockedNumberContract#isBlocked(Context, String)}).
+     */
+    public static final String KEY_DURATION_BLOCKING_DISABLED_AFTER_EMERGENCY_INT =
+            "duration_blocking_disabled_after_emergency_int";
+
     /** The default value for every variable. */
     private final static PersistableBundle sDefaults;
 
@@ -660,6 +668,7 @@
                 "max_retries=3, 5000, 5000, 5000");
         sDefaults.putLong(KEY_CARRIER_DATA_CALL_APN_DELAY_DEFAULT_LONG, 20000);
         sDefaults.putLong(KEY_CARRIER_DATA_CALL_APN_DELAY_FASTER_LONG, 3000);
+        sDefaults.putInt(KEY_DURATION_BLOCKING_DISABLED_AFTER_EMERGENCY_INT, 7200);
 
         sDefaults.putStringArray(KEY_GSM_ROAMING_NETWORKS_STRING_ARRAY, null);
         sDefaults.putStringArray(KEY_GSM_NONROAMING_NETWORKS_STRING_ARRAY, null);
diff --git a/telephony/java/android/telephony/SubscriptionInfo.java b/telephony/java/android/telephony/SubscriptionInfo.java
index d1d6e0d..6229ed9 100644
--- a/telephony/java/android/telephony/SubscriptionInfo.java
+++ b/telephony/java/android/telephony/SubscriptionInfo.java
@@ -25,6 +25,7 @@
 import android.graphics.PorterDuffColorFilter;
 import android.graphics.Rect;
 import android.graphics.Typeface;
+import android.os.Build;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.util.DisplayMetrics;
@@ -338,7 +339,7 @@
     public static String givePrintableIccid(String iccId) {
         String iccIdToPrint = null;
         if (iccId != null) {
-            if (iccId.length() > 9) {
+            if (iccId.length() > 9 && !Build.IS_DEBUGGABLE) {
                 iccIdToPrint = iccId.substring(0, 9) + "XXXXXXXXXXX";
             } else {
                 iccIdToPrint = iccId;
diff --git a/telephony/java/com/android/ims/ImsCallForwardInfo.java b/telephony/java/com/android/ims/ImsCallForwardInfo.java
index 3f8fd19..eeee0fc 100644
--- a/telephony/java/com/android/ims/ImsCallForwardInfo.java
+++ b/telephony/java/com/android/ims/ImsCallForwardInfo.java
@@ -31,6 +31,8 @@
     public int mStatus;
     // 0x91: International, 0x81: Unknown
     public int mToA;
+    // Service class
+    public int mServiceClass;
     // Number (it will not include the "sip" or "tel" URI scheme)
     public String mNumber;
     // No reply timer for CF
@@ -55,13 +57,16 @@
         out.writeInt(mToA);
         out.writeString(mNumber);
         out.writeInt(mTimeSeconds);
+        out.writeInt(mServiceClass);
     }
 
     @Override
     public String toString() {
         return super.toString() + ", Condition: " + mCondition
             + ", Status: " + ((mStatus == 0) ? "disabled" : "enabled")
-            + ", ToA: " + mToA + ", Number=" + mNumber
+            + ", ToA: " + mToA
+            + ", Service Class: " + mServiceClass
+            + ", Number=" + mNumber
             + ", Time (seconds): " + mTimeSeconds;
     }
 
@@ -71,6 +76,7 @@
         mToA = in.readInt();
         mNumber = in.readString();
         mTimeSeconds = in.readInt();
+        mServiceClass = in.readInt();
     }
 
     public static final Creator<ImsCallForwardInfo> CREATOR =
diff --git a/telephony/java/com/android/ims/ImsReasonInfo.java b/telephony/java/com/android/ims/ImsReasonInfo.java
index 2769a2b..c909c6d 100644
--- a/telephony/java/com/android/ims/ImsReasonInfo.java
+++ b/telephony/java/com/android/ims/ImsReasonInfo.java
@@ -84,6 +84,8 @@
     public static final int CODE_LOCAL_CALL_VOLTE_RETRY_REQUIRED = 147;
     // IMS call is already terminated (in TERMINATED state)
     public static final int CODE_LOCAL_CALL_TERMINATED = 148;
+    // Handover not feasible
+    public static final int CODE_LOCAL_HO_NOT_FEASIBLE = 149;
 
     /**
      * TIMEOUT (IMS -> Telephony)
@@ -153,6 +155,9 @@
     public static final int CODE_SIP_USER_REJECTED = 361;
     // Others
     public static final int CODE_SIP_GLOBAL_ERROR = 362;
+    // Emergency failure
+    public static final int CODE_EMERGENCY_TEMP_FAILURE = 363;
+    public static final int CODE_EMERGENCY_PERM_FAILURE = 364;
 
     /**
      * MEDIA (IMS -> Telephony)
@@ -236,6 +241,14 @@
     public static final int CODE_ANSWERED_ELSEWHERE = 1014;
 
     /**
+     * Supplementary services (HOLD/RESUME) failure error codes.
+     * Values for Supplemetary services failure - Failed, Cancelled and Re-Invite collision.
+     */
+    public static final int CODE_SUPP_SVC_FAILED = 1201;
+    public static final int CODE_SUPP_SVC_CANCELLED = 1202;
+    public static final int CODE_SUPP_SVC_REINVITE_COLLISION = 1203;
+
+    /**
      * Network string error messages.
      * mExtraMessage may have these values.
      */
diff --git a/telephony/java/com/android/ims/ImsStreamMediaProfile.java b/telephony/java/com/android/ims/ImsStreamMediaProfile.java
index 5a99212..216cef5 100644
--- a/telephony/java/com/android/ims/ImsStreamMediaProfile.java
+++ b/telephony/java/com/android/ims/ImsStreamMediaProfile.java
@@ -51,6 +51,16 @@
     public static final int AUDIO_QUALITY_GSM_EFR = 8;
     public static final int AUDIO_QUALITY_GSM_FR = 9;
     public static final int AUDIO_QUALITY_GSM_HR = 10;
+    public static final int AUDIO_QUALITY_G711U = 11;
+    public static final int AUDIO_QUALITY_G723 = 12;
+    public static final int AUDIO_QUALITY_G711A = 13;
+    public static final int AUDIO_QUALITY_G722 = 14;
+    public static final int AUDIO_QUALITY_G711AB = 15;
+    public static final int AUDIO_QUALITY_G729 = 16;
+    public static final int AUDIO_QUALITY_EVS_NB = 17;
+    public static final int AUDIO_QUALITY_EVS_WB = 18;
+    public static final int AUDIO_QUALITY_EVS_SWB = 19;
+    public static final int AUDIO_QUALITY_EVS_FB = 20;
 
    /**
      * Video information
diff --git a/tests/HwAccelerationTest/AndroidManifest.xml b/tests/HwAccelerationTest/AndroidManifest.xml
index b028ce6..de7b9c2 100644
--- a/tests/HwAccelerationTest/AndroidManifest.xml
+++ b/tests/HwAccelerationTest/AndroidManifest.xml
@@ -356,6 +356,15 @@
         </activity>
 
         <activity
+                android:name="MovingSurfaceViewActivity"
+                android:label="SurfaceView/Animated Movement">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="com.android.test.hwui.TEST" />
+            </intent-filter>
+        </activity>
+
+        <activity
                 android:name="GLTextureViewActivity"
                 android:label="TextureView/OpenGL">
             <intent-filter>
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/MovingSurfaceViewActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/MovingSurfaceViewActivity.java
new file mode 100644
index 0000000..cd15ef1
--- /dev/null
+++ b/tests/HwAccelerationTest/src/com/android/test/hwui/MovingSurfaceViewActivity.java
@@ -0,0 +1,125 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.test.hwui;
+
+import android.animation.ObjectAnimator;
+import android.app.Activity;
+import android.content.Context;
+import android.graphics.Canvas;
+import android.os.Bundle;
+import android.util.Log;
+import android.view.Gravity;
+import android.view.SurfaceHolder;
+import android.view.SurfaceHolder.Callback;
+import android.view.SurfaceView;
+import android.view.View;
+import android.view.animation.LinearInterpolator;
+import android.widget.FrameLayout;
+
+public class MovingSurfaceViewActivity extends Activity implements Callback {
+    static final String TAG = "MovingSurfaceView";
+    SurfaceView mSurfaceView;
+    ObjectAnimator mAnimator;
+
+    class MySurfaceView extends SurfaceView {
+        boolean mSlowToggled;
+
+        public MySurfaceView(Context context) {
+            super(context);
+            setOnClickListener(new OnClickListener() {
+                @Override
+                public void onClick(View v) {
+                    mSlowToggled = !mSlowToggled;
+                    Log.d(TAG, "SLOW MODE: " + mSlowToggled);
+                    invalidate();
+                }
+            });
+            setWillNotDraw(false);
+        }
+
+        @Override
+        public void draw(Canvas canvas) {
+            super.draw(canvas);
+            if (mSlowToggled) {
+                try {
+                    Thread.sleep(16);
+                } catch (InterruptedException e) {}
+            }
+        }
+
+        public void setMyTranslationY(float ty) {
+            setTranslationY(ty);
+            if (mSlowToggled) {
+                invalidate();
+            }
+        }
+
+        public float getMyTranslationY() {
+            return getTranslationY();
+        }
+    }
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        FrameLayout content = new FrameLayout(this);
+
+        mSurfaceView = new MySurfaceView(this);
+        mSurfaceView.getHolder().addCallback(this);
+
+        final float density = getResources().getDisplayMetrics().density;
+        int size = (int) (200 * density);
+
+        content.addView(mSurfaceView, new FrameLayout.LayoutParams(
+                size, size, Gravity.CENTER));
+        mAnimator = ObjectAnimator.ofFloat(mSurfaceView, "myTranslationY",
+                0, size);
+        mAnimator.setRepeatMode(ObjectAnimator.REVERSE);
+        mAnimator.setRepeatCount(ObjectAnimator.INFINITE);
+        mAnimator.setDuration(200);
+        mAnimator.setInterpolator(new LinearInterpolator());
+        setContentView(content);
+    }
+
+    @Override
+    public void surfaceCreated(SurfaceHolder holder) {
+    }
+
+    @Override
+    public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
+        Canvas canvas = holder.lockCanvas();
+        canvas.drawARGB(0xFF, 0x00, 0xFF, 0x00);
+        holder.unlockCanvasAndPost(canvas);
+    }
+
+    @Override
+    public void surfaceDestroyed(SurfaceHolder holder) {
+    }
+
+    @Override
+    protected void onResume() {
+        super.onResume();
+        mAnimator.start();
+    }
+
+    @Override
+    protected void onPause() {
+        mAnimator.pause();
+        super.onPause();
+    }
+}
diff --git a/wifi/java/android/net/wifi/nan/IWifiNanEventListener.aidl b/wifi/java/android/net/wifi/nan/IWifiNanEventListener.aidl
index 13efc36..fa666af 100644
--- a/wifi/java/android/net/wifi/nan/IWifiNanEventListener.aidl
+++ b/wifi/java/android/net/wifi/nan/IWifiNanEventListener.aidl
@@ -1,11 +1,11 @@
-/**
- * Copyright (c) 2016, The Android Open Source Project
+/*
+ * Copyright (C) 2016 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * You may obtain a copy of the License at
  *
- *     http://www.apache.org/licenses/LICENSE-2.0
+ *      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,
@@ -26,7 +26,7 @@
 oneway interface IWifiNanEventListener
 {
     void onConfigCompleted(in ConfigRequest completedConfig);
-    void onConfigFailed(int reason);
+    void onConfigFailed(in ConfigRequest failedConfig, int reason);
     void onNanDown(int reason);
     void onIdentityChanged();
 }
diff --git a/wifi/java/android/net/wifi/nan/IWifiNanManager.aidl b/wifi/java/android/net/wifi/nan/IWifiNanManager.aidl
index ec9e462..f382d97 100644
--- a/wifi/java/android/net/wifi/nan/IWifiNanManager.aidl
+++ b/wifi/java/android/net/wifi/nan/IWifiNanManager.aidl
@@ -1,11 +1,11 @@
-/**
- * Copyright (c) 2016, The Android Open Source Project
+/*
+ * Copyright (C) 2016 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * You may obtain a copy of the License at
  *
- *     http://www.apache.org/licenses/LICENSE-2.0
+ *      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,
diff --git a/wifi/java/android/net/wifi/nan/IWifiNanSessionListener.aidl b/wifi/java/android/net/wifi/nan/IWifiNanSessionListener.aidl
index 50c34d9..d60d8ca 100644
--- a/wifi/java/android/net/wifi/nan/IWifiNanSessionListener.aidl
+++ b/wifi/java/android/net/wifi/nan/IWifiNanSessionListener.aidl
@@ -1,11 +1,11 @@
-/**
- * Copyright (c) 2016, The Android Open Source Project
+/*
+ * Copyright (C) 2016 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * You may obtain a copy of the License at
  *
- *     http://www.apache.org/licenses/LICENSE-2.0
+ *      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,
diff --git a/wifi/java/android/net/wifi/nan/WifiNanEventListener.java b/wifi/java/android/net/wifi/nan/WifiNanEventListener.java
index eae0a55..5c18bd7 100644
--- a/wifi/java/android/net/wifi/nan/WifiNanEventListener.java
+++ b/wifi/java/android/net/wifi/nan/WifiNanEventListener.java
@@ -47,7 +47,8 @@
 
     /**
      * Configuration failed callback event registration flag. Corresponding
-     * callback is {@link WifiNanEventListener#onConfigFailed(int)}.
+     * callback is
+     * {@link WifiNanEventListener#onConfigFailed(ConfigRequest, int)}.
      */
     public static final int LISTEN_CONFIG_FAILED = 0x1 << 1;
 
@@ -93,7 +94,7 @@
                         WifiNanEventListener.this.onConfigCompleted((ConfigRequest) msg.obj);
                         break;
                     case LISTEN_CONFIG_FAILED:
-                        WifiNanEventListener.this.onConfigFailed(msg.arg1);
+                        WifiNanEventListener.this.onConfigFailed((ConfigRequest) msg.obj, msg.arg1);
                         break;
                     case LISTEN_NAN_DOWN:
                         WifiNanEventListener.this.onNanDown(msg.arg1);
@@ -129,7 +130,7 @@
      *
      * @param reason Failure reason code, see {@code NanSessionListener.FAIL_*}.
      */
-    public void onConfigFailed(int reason) {
+    public void onConfigFailed(ConfigRequest failedConfig, int reason) {
         Log.w(TAG, "onConfigFailed: called in stub - override if interested or disable");
     }
 
@@ -173,11 +174,14 @@
         }
 
         @Override
-        public void onConfigFailed(int reason) {
-            if (VDBG) Log.v(TAG, "onConfigFailed: reason=" + reason);
+        public void onConfigFailed(ConfigRequest failedConfig, int reason) {
+            if (VDBG) {
+                Log.v(TAG, "onConfigFailed: failedConfig=" + failedConfig + ", reason=" + reason);
+            }
 
             Message msg = mHandler.obtainMessage(LISTEN_CONFIG_FAILED);
             msg.arg1 = reason;
+            msg.obj = failedConfig;
             mHandler.sendMessage(msg);
         }
 
diff --git a/wifi/java/android/net/wifi/nan/WifiNanSessionListener.java b/wifi/java/android/net/wifi/nan/WifiNanSessionListener.java
index d5e59f0..0925087 100644
--- a/wifi/java/android/net/wifi/nan/WifiNanSessionListener.java
+++ b/wifi/java/android/net/wifi/nan/WifiNanSessionListener.java
@@ -303,8 +303,8 @@
      * message). Override to implement your custom response.
      * <p>
      * Note that either this callback or
-     * {@link WifiNanSessionListener#onMessageSendFail(int)} will be received -
-     * never both.
+     * {@link WifiNanSessionListener#onMessageSendFail(int, int)} will be
+     * received - never both.
      */
     public void onMessageSendSuccess(int messageId) {
         if (VDBG) Log.v(TAG, "onMessageSendSuccess: called in stub - override if interested");
@@ -319,8 +319,8 @@
      * message). Override to implement your custom response.
      * <p>
      * Note that either this callback or
-     * {@link WifiNanSessionListener#onMessageSendSuccess()} will be received -
-     * never both
+     * {@link WifiNanSessionListener#onMessageSendSuccess(int)} will be received
+     * - never both
      *
      * @param reason The failure reason using {@code NanSessionListener.FAIL_*}
      *            codes.